【问题标题】:How to iterate <td> in table to find specific text using Protractor?如何使用量角器在表中迭代 <td> 以查找特定文本?
【发布时间】:2019-08-14 16:48:24
【问题描述】:

我有一个包含 2 列和 n 行的动态表。在第一列中会有一个标题,在第二列中它将是进入该标题详细信息的按钮。

我想遍历每一行以找到特定的文本标题,然后单击该标题的第二列按钮。

我尝试了很多方法搜索谷歌,但找不到任何东西。

在 HTML 中的 AngularJS 代码如下。

<table>
        <tbody>
            <tr ng-repeat="registration in Registrations | orderBy:'title'" class="tableRow ng-scope">
                <td style="padding-left: 20px;" ng-if="reg.title != 'Jabc'" class="ng-binding ng-scope">test</td>
            <td style="padding-left: 20px;">
                <button ng-click="enter(registration)" class="ng-binding">Enter community</button>
            </td>


            <tr ng-repeat="registration in Registrations | orderBy:'title'" class="tableRow ng-scope">
                <td style="padding-left: 20px;" ng-if="reg.title != 'Jabc'" class="ng-binding ng-scope">test1</td>
            <td style="padding-left: 20px;">
                <button ng-click="enter(registration)" class="ng-binding">Enter community</button>
            </td>



            <tr ng-repeat="registration in Registrations | orderBy:'title'" class="tableRow ng-scope">
                <td style="padding-left: 20px;" ng-if="reg.title != 'Jabc'" class="ng-binding ng-scope">test2</td>
            <td style="padding-left: 20px;">
                <button ng-click="enter(registration)" class="ng-binding">Enter community</button>
            </td>
    </tbody>
</table>

如上表所示,共有三行。我想按名称选择行以假设“test1”并想单击同一行的第二列按钮。

在这里,我通过编写下面的代码得到了我正在寻找的特定文本的索引。

    let elm = element.all(by.repeater('registration in myRegistrations'));
    let index;
    elm.getText().then(function(text){
        console.log(text);
        for(var i=0; i<text.length; i++){
            if(text[i] == 'protractor-testing Enter community'){
                index = i;
                console.log('Community found :: ' + text[i]);
            }    
        }

    });

    console.log("Index :: " + index);

现在,通过这个索引,我可以点击表格中的按钮 row = index 和同一行的第二列吗?

下面是我的整个脚本。

conf.js 文件..

// An example configuration file
var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');
exports.config = {
    // The address of a running selenium server.
    seleniumAddress: 'http://localhost:4444/wd/hub',

    baseUrl: 'http://localhost:' + (process.env.PORT || '9000'),
    // Capabilities to be passed to the webdriver instance.
    capabilities: {
      browserName: 'chrome'
    },

    params: {
      Firstname: 'virus',
      Lastname: 'virus',
      Email: 'virus@email.com',
      Username: 'virus',
      Password: 'virus',
      TempUsername : 'temp',
      TempPassword: 'temp',
      AdminUsername: 'admin',
      AdminPassword: 'admin',
      CreateTitle: 'protractor-testing',
      CreateKey: 'protractor-testing'
    },
    // Spec patterns are relative to the configuration file location passed
    // to protractor (in this example conf.js).
    // They may include glob patterns.
    // specs: ['../spec/2 Login/spec.js'],
    suites: {
      login: '../spec/2 Login/spec.js'
    },

    // Options to be passed to Jasmine-node.
    jasmineNodeOpts: {
      showColors: true, // Use colors in the command line report.
    },

    // onPrepare: function(){
    //   jasmine.getEnv().addReporter(new Jasmine2HtmlReporter({
    //     savePath: '../test/reports'
    //   })
    //   );
    // },
  };

下面是我的 spec.js 文件

describe('Testing', function(){
    it('perform login', function() {}
        browser.get(browser.baseUrl);
        browser.manage().window().maximize();
        console.log("Admin :: " + browser.params.AdminUsername + " " + browser.params.AdminPassword);
        element(by.model('user.userName')).sendKeys(browser.params.AdminUsername);
        element(by.model('user.password')).sendKeys(browser.params.AdminPassword);
        browser.sleep('2000');
        element(by.buttonText('Login')).click();
        element(by.model("newCommunity.title")).sendKeys(browser.params.CreateTitle);
        element(by.model("newCommunity.key")).sendKeys(browser.params.CreateKey);
        let buttons = element.all(by.css("button.ng-binding"));
        let tds = element.all(by.css("td.ng-binding"));
        console.log("Buttons :: " + buttons);
        console.log("tds :: " + tds);
        // var buttons = driver.findElements(By.css("button.ng-binding"));
        // var tds = driver.findElements(By.css("td.ng-binding"));
        for (var i = 0; i < tds.length; i++) {
            console.log("get text for index " + i + " Value :: " + tds[i].getText());
            if (tds[i].getText() == 'protractor-testing') {
                console.log("Got the actual community at : " + i);
                console.log("Clicking button :: " + buttons[i]);
                buttons[i].click();
                break;
            }
        }

    });
})

请注意,我不想使用 xPath。

【问题讨论】:

    标签: angularjs selenium selenium-webdriver css-selectors protractor


    【解决方案1】:

    一方面,如果我可以控制它,我个人不会在同一页面上拥有相同的元素(就 HTML 而言)。但是要让您的元素显示在 HTML 上,以下应该可以工作: (因为你的量角器标签,我正在编写 Javascript,我假设你的测试是这样编写的)

    buttons = driver.findElements(By.xpath("//button"))
    buttons[1].click();
    

    但是,如果您想通过名称专门找到它(对于任何用例),您可以这样做

    buttonWithText1 = driver.findElement(By.xpath("//td[text()[contains(., 'test1')]]/following-sibling::td/button");
    buttonWithText1.click();
    

    正如@Sers 在xpath 下方指出的那样,//td[.='test1']/following-sibling::td/button 也可以工作并且是最干净的。或者至少是最短的。

    如果您想动态使用该 xpath 来通过该文本查找按钮,您可以使用:

    var myText = "test1"
    driver.findElement(By.xpath("//td[.='" + myText + "']/following-sibling::td/button).click()
    

    编辑

    用户说他们不想使用 xpath。基于 HTML,您可以使用像这样的 css 选择器来选择某个字符串 myText 旁边的按钮。

    var buttons = browser.findElements(By.css(‘button.ng-binding’))
    var tds = browser.findElements(By.css(‘td.ng-binding’))
    for (i = 0; i < tds.length; i++) {
        if (tds[i].getText()==myText) {
            buttons[i].click()
            break;
        }
    }
    

    【讨论】:

    • 也可以是//td[.='test1']/following-sibling::td/button
    • 我同意轴是要走的路,但按钮不是包含标题文本的 td 的兄弟。我认为您需要//td[.='test1']/parent::tr[1]//button[1]//td[.='test1']/following::button[1]
    • @DublinDev 有很多方法可以做到这一点,一切都取决于其余的 HTML。 //td[.='test1']/following::button[1] 最干净的一个
    • @DublinDev 我认为格式有点古怪,它们应该是兄弟姐妹(也没有任何 tr 元素上的结束标签)
    • 嗨@C。 Peck,我想试试你的代码,但驱动程序未定义。
    【解决方案2】:

    您不使用 xpath 的要求似乎非常严格,因为它是迄今为止最适合您的用例的方法,主要是因为 基于 css 的定位不允许您对标记文本进行精确匹配,在你的情况下标题&lt;td&gt;s。

    要使用 css 实现这一点,您将不得不使用一些可能不可靠的链式定位器,例如

    element(by.cssContainingText('tr','test')).element(by.css('button'));
    

    我说不可靠,因为这个定位器实际上会识别所有三个按钮,因为你的所有三个标题标签都包含字符“测试”。它在这种情况下有效,但可能会在您的实际测试中引起问题。

    我强烈建议您取消没有 xpath 要求,并使用 C Peck 提供的以下同级答案,因为这将是最可靠的长期。

    【讨论】:

    • 我认为你的意思是将 ('td','test') 传递给cssContainingText() 使用cssContainingText() 有问题;例如在上面的代码中,(在将'td' 交换为'tr' 之后)它将匹配所有三个td 节点,因为字符串“test”、“test1”和“test2”都包含“test” .
    • @C.Peck 实际上我打算使用tr。通过使用 tr,它将查看来自 tr 标签的所有子元素的文本,包括标题和按钮标签。然后我们可以链接另一个可以找到孩子的定位器(按钮)。如果我们使用td,我们将无法链接定位器,因为tds 是同一节点上的兄弟姐妹。此外,我在回答中解决了cssContainingText 的潜在问题,但如果不使用 xpath 确实是一个不可避免的限制,我想为 OP 提供一个潜在的解决方案。
    • 好吧,我不知道我是怎么错过这个解释的,但它就是这样,哎呀。无论如何,关于cssContainingText,是什么让你说通过使用 tr,它会查看所有作为 tr 标签的子元素的文本?我正在查看源代码,发现 cssContainingText 只是将其参数传递给另一个函数 findByCssContainingText clientSideScripts 文件。这是cssContainingText 返回的内容:return driver.findElements(By.js( clientSideScripts.findByCssContainingText, cssSelector, searchText, using, rootSelector));
    • 转到clientSideScriptsl 文件,我在findByCssContainingText 的正文中发现了以下内容:var elements = using.querySelectorAll(cssSelector); var matches = []; for (var i = 0; i &lt; elements.length; ++i) { var element = elements[i]; var elementText = element.textContent || element.innerText || ''; var elementMatches = searchText instanceof RegExp ? searchText.test(elementText) : elementText.indexOf(searchText) &gt; -1; if (elementMatches) { matches.push(element); } } return matches;
    • @C.Peck 我无法指出负责它的代码段,但我从经验中知道,在包含子元素的元素上调用某些元素方法将运行在他们所有人身上。我复制了问题中的 html 代码并在本地运行了一些定位器,我从运行 element(by.cssContainingText('tr','test')).getText() 中看到的是 test Enter community
    【解决方案3】:

    您不想使用 并且除了innerHTML 之外,引用&lt;td&gt; 节点的所有其他属性值都是相同的。现在 不支持innerHTML,因此要定位与文本为 test1 的元素相邻的 按钮,您可以使用以下 css-selectors

    "table>tbody tr:nth-child(2) td:nth-child(2)>button"
    

    【讨论】:

    • OP 想要动态地执行它,并有一个 css 选择器,它可以根据按钮相邻标签的文本找到按钮——我已经提供了一个像你这样的解决方案,但他们回应说他们想要基于动态搜索在 td 的 innerHTML 上。我目前正在编写一个相当老套的方法来实现这一点。
    • 我同意 C. Peck。
    • @DebanjanB 题外话,但如果您要使用完整的 CSS 选择器,最好使用 driver.findElements(By.css('button'))[1].click(); :)
    【解决方案4】:

    您可以使用 xpath 来实现您的目标:

    //tr[contains(@class,'tableRow') and ./td[.='test']]//button
    

    您可以找到更多示例here

    【讨论】:

    • 很抱歉,我不想使用 xPath。
    • @Virus 为什么?这是最快的文字查找方式
    • 我知道,但我的要求已经给出,他们告诉他们不要使用 xPath。
    • @Sers 实际上 xpath 是识别元素最慢的方法,而 id 是最快的。 here 你可以找到 xpath 和 css 的实际性能数字; CSS 总是更快。
    • @C.Peck 对于这种情况,xpath 比迭代抛出每个元素要快得多。甚至我更喜欢 css,实际上在 xpath 和 css 之间,在测试目的方面没有关键差异。
    猜你喜欢
    • 2018-09-06
    • 2015-12-19
    • 1970-01-01
    • 2015-10-28
    • 2015-05-07
    • 1970-01-01
    • 2016-04-16
    • 1970-01-01
    • 2021-02-27
    相关资源
    最近更新 更多