简体   繁体   English

使用 Selenium 获取子项的 XPath - Java

[英]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仅删除创建的卡我认为问题即将获得正确的 xpath PAGE

    package PageObject;包装页面对象;
    import org.openqa.selenium.By;导入 org.openqa.selenium.By; import org.openqa.selenium.Keys;导入 org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver;导入 org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement;导入 org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions;导入 org.openqa.selenium.interactions.Actions;
    public class Page { protected final WebDriver webDriver; 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 [org.openqa.selenium.ElementNotVisibleException: 元素不可见
(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, accept (Session info: chrome=58.0.3029.110) (Driver info: chromedriver=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57),platform=Linux 4.4.0-786N-generic stack 未提供任何命令或命令栈的持续时间:(6ee44a7247c639c0703f291d320bdf05c1531b57)超时:96 毫秒构建信息:版本:'未知',修订:'未知',时间:'未知'系统信息:主机:'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' 驱动程序信息:org.openqa.selenium.chrome.ChromeDriver Capabilities [{applicationCacheEnabled=false, rotatable =false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57), userDataDir=/tmp/.org.chromium.LoadStrategy.r handlesAlerts=true,hasTouchScreen=false,version=58.0.3029.110,platform=LINUX,browserConnectionEnabled=false,nativeEvents=true,接受SslCerts=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.Ca SslCerts=true、locationContextEnabled=true、webStorageEnabled=true、browserName=chrome、takesScreenshot=true、javascriptEnabled=true、cssSelectorsEnabled=true、unexpectedAlertBehaviour=}] 会话 ID:a33c710a9bae046f731a66bb56b344b8 at sun.newstructorNampleInstance(sun.reflectorN)。 .reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:4openqa.org) 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) 在 org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:272) 在 org.openqa.selenium.remote.RemoteWebElement.click(RemoteWebElement.java:82) 在 PageObject.Ca rdPage.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 rdPage.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) 在 org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 在 org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) 在 org. junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 在 org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 在 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) org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) 在 org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 在 org.junit.runners.ParentRunner$1.schedule(ParentRunner.java: 71) 在 org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 在 org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 在 org.junit.runners.ParentRunner$2.evaluate(ParentRunner) .java:268) 在 org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 在 org.junit.runners.ParentRunner.run(ParentRunner.java:363) 在 org.junit.runner。 JUnitCore.run(JUnitCore.java:137) 在 com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) 在 com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)在 com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) 在 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:我也尝试使用不同的 xpath,所以当我运行它时,我得到了另一种例外: org.openqa.selenium.NoSuchElementException: Unable to locate element:

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.您使用的是绝对 XPath,这总是不好的做法。 They are extremely fragile.它们极其脆弱。 You should spend some time reading some tutorials on handcrafting XPaths.您应该花一些时间阅读一些有关手工制作 XPath 的教程。 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.与其使用delete1stCard()方法, delete1stCard()创建一个使用deleteCard(int index)的方法deleteCard(int index) ,以便您可以删除所需的任何卡片。 You would probably want another method deleteCard(String cardName) that takes in a card name and deletes it.您可能需要另一种方法deleteCard(String cardName)接收卡片名称并将其删除。 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.您的名为Services课程似乎是一个包罗万象的东西。 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.我不知道一旦您将所有内容重新组织为适当的页面对象后, Services应该包含哪些内容……我认为您可能不需要它。

  4. I would also recommend that you create component page objects, eg 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.您可以使用Services类中的一些可用功能,并将它们移动到TimerMenuProjectBarTimerMenu ,以表示页面顶部的不同工具栏及其功能。

  5. You would have a CardsPage page object that represents the cards page, eg 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.你会有一个CardsPage页面对象来代表卡片页面,例如https://tmpapp.theamalgama.com/#/projects/681/cards ,但你也会有一个CardComponent代表页面上的实际卡片,这些东西包含在<card>标签中。 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. CardComponent将包含单个卡片的功能...名称、该卡片的播放按钮、该卡片的完成图标、该卡片的删除/编辑/等选项等等。

Now to the actual answer.现在到实际答案。 I wrote a CardComponent class, as I suggested you write.我写了一个CardComponent类,正如我建议你写的那样。 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.从那里,您只需调用card.delete()card.whatever()在您指定的卡片上完成该操作。 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.对卡片功能的更新很容易……在CardComponent页面对象等中进行更改。

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.我创建了几张新卡并测试了这段代码,它可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM