通过在Javascript中循环遍历findElements结果来查找特定元素

时间:2015-09-29 09:53:39

标签: javascript selenium selenium-webdriver

我正在尝试通过匹配项目列表中的文本来查找并单击特定项目。与.list_of_items匹配的元素是ul元素,其中包含li>a元素的列表。

我不确定如何将匹配的元素传递给下一个函数。没有id或类可用于识别元素。

driver.isElementPresent(By.css(".list_of_items")).then(function(trueFalse){
  if (trueFalse){
    return driver.findElements(By.css(".list_of_items a"));                         
  }else{
    console.log('err');
  }
}).then(function(eleArray){
  for (var i=0; i < eleArray.length; i++){
    eleArray[i].getInnerHtml().then(function(html){
      if (html.trim().toLowerCase() == item_to_search.toLowerCase()){
        //
        // how to pass the matched element to next function??
        //
        return true;
      }
    }).then(function(element){
        console.log(element);
      }
    });
  }
});

2 个答案:

答案 0 :(得分:2)

您可以尝试使用filter方法:

driver.isElementPresent(By.css(".list_of_items")).then(function(trueFalse){
  if (trueFalse){
    return driver.findElements(By.css(".list_of_items a"));                         
  }else{
    console.log('err');
    throw new Error('Element not found.'); // Changed, so skips all the next steps till an error handler is found.
  }
}).then(function(eleArray){
  return webdriver.promise.filter(eleArray, function(element){
    return element.getInnerHtml().then(function(innerText){
      return innerText.trim().toLowerCase() == item_to_search.toLowerCase();
    });    
  });
}).then(function(reducedElements){
  console.log('filtered elements: ', reducedElements.length);
});

答案 1 :(得分:1)

您的代码目前正在进行不区分大小写的搜索。请注意,如果您能够让搜索区分大小写,那么您可以使用以下命令找到您的元素:

browser.findElement(By.linkText(item_to_search)).then(...);

我已经用Selenium编写了数十个应用程序测试,总是能够通过链接文本进行搜索,而不必使搜索区分大小写。 我强烈建议您组织代码以便执行此操作。

如果你不能,那么你必须扫描每个元素以找到你想要的那个。请注意,可以编写一个匹配文本的XPath表达式,即使不区分大小写,但是当你必须匹配CSS类时,我不是XPath的粉丝。所以我更喜欢一种方法,就像你一样,你扫描你的元素。 请注意,您应使用getText()来测试元素的文本,而不是getInnerHtml()。根据HTML测试文本值很脆弱:HTML中可能出现的内容并未改变链接文本实际所说的内容。例如,您可以<a><b>This</b> is a test</a>文字为This is a test,但如果您获得<a>的内部HTML,则会获得<b>This</b> is a test,这不是您想要匹配的内容。< / p>

这是一个实现,其中还包括我之前提到的By.linkText方法的说明:

var webdriver = require('selenium-webdriver');
var By = webdriver.By;
var chrome = require('selenium-webdriver/chrome');
var browser = new chrome.Driver();

browser.get("http://www.example.com");

// Modify the page at example.com to add some test data.
browser.executeScript(function () {
    document.body.innerHTML = '\
<ul class="list_of_items">\
  <li><a>One</a></li>\
  <li><a>  Two  </a></li>\
  <li><a>Three</a></li>\
</ul>';
});

// Illustration of how to get it with By.linkText. This works if you
// are okay with having the test be case-sensitive.
browser.findElement(By.linkText("Two")).getOuterHtml().then(function (html) {
    console.log("case-sensitive: " + html);
});

var item_to_search = "TwO"; // Purposely funky case, for illustration.

browser.findElements(By.css(".list_of_items a")).then(function (els){
    if (els.length === 0)
        // You could put tests here to determine why it is not working.
        throw new Error("abort!");

    // Convert it once, and only once.
    var item_to_search_normalized = item_to_search.toLowerCase();

    function check(i) {
        if (i >= els.length)
            // Element not found!
            throw new Error("element not found!");

        var el = els[i];

        return el.getText().then(function (text) {

            if (text.trim().toLowerCase() === item_to_search_normalized)
                // Found the element, return it!
                return el;

            // Element not found yet, check the next item.
            return check(i + 1);
        });
    }

    return check(0);
}).then(function (el) {
    el.getOuterHtml().then(function (html) {
        console.log("case-insensitive: " + html);
    });
});


browser.quit();

附加说明:

  1. 您的原始代码首先测试.list_of_items是否存在。我没有这样做。通常,最好针对页面如您所期望的情况进行优化。你的代码是这样的,在这个页面是&#34;好&#34;你总是从至少两个操作开始(检查是否存在.list_of_items,然后获取锚点列表)。我的实现使用一个操作。如果您想确定没有元素的原因,您可以做的是更改throw new Error("abort!")以执行诊断。

  2. 您的原始代码在点击发生后立即结束搜索。我保持这种逻辑不变。所以我不使用webdriver.promise.filter因为这将必然扫描列表中的每个元素。即使第一个元素是你想要的元素,也会访问所有元素。这可能非常昂贵,因为每个测试都意味着您的Selenium脚本和浏览器之间的往返。 (当一切都在同一台机器上运行时,情况就不那么糟了。当您控制远程服务器场上的浏览器时,这一点非常明显。)我使用递归来扫描列表,这也是webdriver.promise.filter的作用,除了我在找到命中时提前返回。

  3. 这就是说,在我的代码中,我使用executeScript将搜索范围缩小到我的Selenium脚本和浏览器之间的一次往返:

    browser.executeScript(function () {
        // The item_to_search value we passed is accessible as arguments[0];
        var item_to_search = arguments[0].toLowerCase();
        var els = document.querySelectorAll(".list_of_items a");
        for (var i = 0; i < els.length; ++i) {
            var el = els[i];
            if (el.textContent.trim().toLowerCase() == item_to_search)
                return el;
        }
        return null;
    }, item_to_search).then(function (el) {
        if (!el)
            throw new Error("can't find the element!");
    
        el.getOuterHtml().then(function (html) {
            console.log("case-insensitive, using a script: " + html);
        });
    });
    

    请注意executeScript中的代码正在浏览器中执行 。这就是为什么你必须使用arguments来获取传递给它的内容以及为什么我们必须使用.then来获取返回值并在控制台上显示。 (如果您将console.log放在传递给executeScript的脚本中,它将在浏览器的控制台上显示内容。)