javaseleniumxpathsubitem

Get sub-item's XPath using Selenium - Java


I'm having problem trying to click a sub-item from a menu, in third step..

  1. I do login

  2. Enter into a project

  3. Delete only card created I think problem its about to get the correct xpath PAGE

    package PageObject;
    import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions;
    public class Page { protected final WebDriver webDriver;

    public Page( final WebDriver driver ) {
        this.webDriver =driver;
    }
    
    protected WebElement element(By by ) {
        return webDriver.findElement( by );
    }
    
    public void pressEnter(){
        Actions builder = new Actions(webDriver);
        builder.sendKeys(Keys.RETURN).perform();
    }
    

    }

PAGE OBJECT: CARD PAGE

package Helpers;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
public class public class CardPage extends PageObject.Page{

    By close = By.xpath("//*[@id='ngdialog5']/div[2]/div/div[2]");
    By menuCard = By.xpath("html/body/div[1]/div/div/div/div/ui-view/project/div[1]/board/div/div/div/backlog-list/div[2]/div/ul/li/ul/div[1]/card/li/div/div[1]/a[3]");
    By container1stCard = By.xpath("html/body/div[1]/div/div/div/div/ui-view/project/div[1]/board/div/div/div/backlog-list/div[2]/div/ul/li/ul/div/card/li/div");
    By delete3 = By.xpath("//*[contains(text(), 'delete')]");

    By accceptWarning = By.xpath("html/body/div[4]/div/div[10]/button[1]");
    By openCard = By.xpath("//*[@id=\"scrollable\"]/div/backlog-list/div[2]/div/ul/li/ul/div/card/li/div");

    private Services services;
    public CardPage(WebDriver driver) {
        super(driver);
    }
    //SERVICES CARDS
    private void closeCard() {
        element(close).click();
    }
    public void delete1stCard(){
        WebElement Wcontainer1stCard = element(container1stCard);//Menu
        Actions builder = new Actions(super.webDriver);
        Actions hoverOverContainer = builder.moveToElement(Wcontainer1stCard);
        hoverOverContainer.perform();
        Services.waitMilisegundos(1000);
        element(menuCard).click();

        Services.waitMilisegundos(500);
        WebElement deleteBtn = element(delete3);
        deleteBtn.click();//Menu Item
        Services.waitMilisegundos(1000);
    }
    public void openCard(){
        element(openCard).click();
    }
}

SERVICES TO PAGE OBJECTS

package Helpers;

import PageObject.*;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.interactions.Actions;

import java.io.File;
import java.io.IOException;
import java.util.Date;

public class Services {
    WebDriver webDriver;
    private LandingPage landingPage;
    private MainPage mainPage;
    private LoginPage loginPage;
    private ProjectPage projectPage;
    private CardPage cardPage;
    String url;
    ////
    public Services(TmpEnvironment environment) {
        webDriver = new StartDriver( environment.url ).getWebDriver();
    }
    public Services() {
        webDriver = new StartDriver(TmpEnvironment.STAGING).getWebDriver();
    }

    ///LOGIN SERVICES
    public Services login(String email, String passw){
        getLandingPage().goToLoginPage();
        getLoginPage().writeUserName(email)
                .writePassword(passw)
                .clickOnLogin();
        return this;
    }
    public Services enterToProject() {
        getMainPage().enterToProject();
        return this;
    }
    //PAGES GETTERS
    public WebDriver getWebDriver() {
        return webDriver;
    }
    public MainPage getMainPage() {
        if (mainPage == null)
            mainPage = new MainPage(webDriver);
        return mainPage;
    }
    public LoginPage getLoginPage() {
        if (loginPage == null)
            loginPage = new LoginPage(webDriver);
        return loginPage;
    }
    public ProjectPage getProjectPage(){
        if (projectPage == null)
            projectPage = new  ProjectPage(webDriver);
        return projectPage;
    }
    public LandingPage getLandingPage() {
        if (landingPage == null)
            landingPage =  new LandingPage(webDriver);
        return landingPage;
    }
    public CardPage getCardPage() {
        if (cardPage == null)
            cardPage = new CardPage(webDriver);
        return cardPage;
    }
    //CARDS SERVICES
    public Services delete1stCard() {
        getCardPage().delete1stCard();
        return this;
    }
    public static void waitMilisegundos(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void pressEnter(){
        Actions builder = new Actions(webDriver);
        builder.sendKeys(Keys.RETURN).perform();
    }
    public void pressEsc(){
        Actions builder = new Actions(webDriver);
        builder.sendKeys(Keys.ESCAPE).perform();
    }
    //
    public Services goToLoginPage() {
        getLandingPage().goToLoginPage();
        return this;
    }

    public enum TmpEnvironment {
        DEVELOPMENT( "http://tmp-landing-dev.theamalgama.com/index" ),
        STAGING( "https://tmpapp.theamalgama.com" ),
        PRODUCTION( "https://tmpapp.com" );
        public final String url;
        TmpEnvironment( final String url ) {
            this.url = url;
        }
        public String getUrl(){
            return url;
        }
    }
}

TEST CLASS

package z_Test;

import Helpers.Services;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

import static Helpers.Services.waitMilisegundos;

public class CardTest {
    static String email, password;
    Services services;

    public CardTest(){
        services = new Services();
    }
    @BeforeClass
    public static void setData(){
        email = "test01@testing.com";
        password = "password";

    }
    @After
    public void CloseDriver(){
        services.getWebDriver().close();
    }


    /////////////////////////////////////////////////////////////
    @Test
    public void cardTest(){ 
        services.login(email,password);
        services.enterToProject()
        .delete1stCard();
        waitMilisegundos();
    }
}

and next its the error message when try to delete card.

[org.openqa.selenium.ElementNotVisibleException: element not visible
(Session info: chrome=58.0.3029.110) (Driver info: chromedriver=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57),platform=Linux 4.4.0-79-generic x86_64) (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 96 milliseconds Build info: version: 'unknown', revision: 'unknown', time: 'unknown' System info: host: 'julieta', ip: '127.0.1.1', os.name: 'Linux', os.arch: 'amd64', os.version: '4.4.0-79-generic', java.version: '1.8.0_131' Driver info: org.openqa.selenium.chrome.ChromeDriver Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57), userDataDir=/tmp/.org.chromium.Chromium.rQuYOu}, takesHeapSnapshot=true, pageLoadStrategy=normal, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=58.0.3029.110, platform=LINUX, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true, unexpectedAlertBehaviour=}] Session ID: a33c710a9bae046f731a66bb56b344b8 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:215) at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:167) at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:671) at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:272) at org.openqa.selenium.remote.RemoteWebElement.click(RemoteWebElement.java:82) at PageObject.CardPage.delete1stCard(CardPage.java:92) at Helpers.Services.delete1stCard(Services.java:201) at z_Test.CardTest.cardTest(CardTest.java:69) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

]

Also I tried using a diferent xpath, so wen i run it i get another kind of exeption: org.openqa.selenium.NoSuchElementException: Unable to locate element:


Solution

  • I think you've got a number of issues that need to be improved.

    1. You are using absolute XPaths which is always a bad practice. They are extremely fragile. You should spend some time reading some tutorials on handcrafting XPaths. They will be shorter, easier to read, but also much less fragile.

    2. Rather than having a method delete1stCard(), create a method deleteCard(int index) that takes an index so that you can delete whichever card you want. You would probably want another method deleteCard(String cardName) that takes in a card name and deletes it. I think that would probably be more useful than deleting by index but I don't know what your test cases look like. (or better yet, implement a card component class, as I suggest below, and it solves this problem.)

    3. Your class called Services seems to be a catch all bucket. Do a proper page object model and add a Login page that handles logins from the login page, a Projects page that handles that page, and so forth. I don't know what Services should hold once you've reorganized everything into a proper page object... I don't think you'll probably need it.

    4. I would also recommend that you create component page objects, e.g. Cards, some of the toolbars. A "page object" doesn't have to be an entire page. It can be any reusable component. You would take some of the functions available in the Services class and move them to components like TimerMenu, ProjectBar, and so on to represent the different toolbars at the top of the page and their functions.

    5. You would have a CardsPage page object that represents the cards page, e.g. https://tmpapp.theamalgama.com/#/projects/681/cards, but you would also have a CardComponent that represents an actual card on the page, the stuff contained within a <card> tag. The CardComponent would contain the functionality for a single card... the name, the play button for that card, the done icon for that card, the delete/edit/etc options for that card, and so on.

    Now to the actual answer. I wrote a CardComponent class, as I suggested you write. You pass in the name of the card and it gets a handle to it. From there, you can just call card.delete() or card.whatever() and that action will be done on the card you specified. It makes handling individual cards, projects, etc. so much easier. Updates to the functionality of a card is easy... changes go in the CardComponent page object, etc.

    public class CardComponent
    {
        private WebDriver driver;
        private WebElement card;
        private By deleteCardLocator = By.xpath(".//p[@class='option-list-option'][.='Delete']");
        private By acceptDeleteButtonLocator = By.xpath("//button[.='Accept']");
    
        public CardComponent(WebDriver webDriver, String cardName)
        {
            driver = webDriver;
            card = new WebDriverWait(webDriver, 10)
                    .until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//card[contains(., '" + cardName + "')]")));
        }
    
        public void delete()
        {
            openMenu();
            card.findElement(deleteCardLocator).click();
            driver.findElement(acceptDeleteButtonLocator).click();
        }
    
        private void openMenu()
        {
            new Actions(driver).moveToElement(card).perform();
            card.findElement(By.id("dots")).click();
        }
    }
    

    To use this code, you navigate to the cards page and call

    CardComponent card = new CardComponent(driver, "card to delete");
    card.delete();
    

    I created a couple new cards and tested this code and it works.