【问题标题】:Protractor stale element reference when using the each() method使用 each() 方法时的量角器过时元素引用
【发布时间】:2019-01-16 05:23:42
【问题描述】:

由于在量角器中使用 each() 方法,我收到以下错误。过去它运行良好,但现在一直因此错误而失败。

失败:过时的元素引用:元素未附加到页面文档

element.all(bars).each((element) => element.getCssValue('width'))

是否有替代方案或原因?

(为了清楚起见,我要做的就是获取一组称为条形图的 web 元素中每个元素的宽度。)

谢谢!

【问题讨论】:

  • 你是在页面完全加载后执行这个吗?
  • 是的,我检查了加载包装器在执行之前是否已经消失。 browser.wait(EC.invisibilityOf(element(loadingWrapper)), 6000);
  • 但是该代码不会停止 javascript 的执行。您可能应该使用browser.wait 返回的承诺,并且只有在它解决时才执行element.all(bars).each 循环。
  • 嗨@trincot 谢谢你的帮助。它最终成为一个时间问题。我添加了一个 .then(() => browser.driver.sleep(1000)) ,它现在可以正常工作了。它最初让我感到困惑,因为它在过去工作得很好。我认为这必须归结为网络延迟或量角器的优先级。再次感谢:)
  • 这听起来像是一个 hacky 解决方案。最好等browser.wait解决。

标签: javascript protractor


【解决方案1】:

简而言之,这是因为each() 只是同时针对所有元素触发命令。在您的情况下,您可能需要这样做element.all(bars).getCssValue('width')).then(array => {/*do what you want with returned array here*/})

* 已编辑 *

你想做的是

element.all(bars).getCssValue('width')).then(array => {
    // tests is at least one element will not be "0px"
    let elementHasWidth = array.some(elem => elem !== "0px");
    expect(elementHasWidth).toBe(true);
})

【讨论】:

  • 感谢@SergeyPleshakov 我尝试了这种方法,但没有任何运气。我的原始代码element.all(bars).each((element) => element.getCssValue('width').then((value) => (value != '0px') ? isNotZero = true : null)) 其中isNotZero 是一个设置为false 的布尔变量。它检查是否至少有一个条的宽度大于 0px。当我尝试作为一个数组时,我遇到了和以前一样的问题。
  • 问题是他们都试图同时访问 isNotZero 吗?
  • 我是否理解正确,您正在尝试获取所有指定元素的width,如果其中任何一个大于0,则返回true
  • @Isabella 已更新^ 如果可行,请将答案标记为正确,否则无论如何我们都会让它发挥作用!
  • 感谢@SergeyPleshakov,不幸的是这也没有用。这是我尝试过的,它与您的略有不同,但我认为它应该与您的意图相似。 element.all(bars).each((element) => element.getCssValue('width')).then((array) => { let elementHasWidth = array.each((elem) => (elem !== "0px")? true: false); expect(elementHasWidth).toBe(true); })
【解决方案2】:

现有代码出现此类新问题可能是由于 Protractor 和 Selenium 从 Web Driver Control Flow 和 WebDriverJS Promise Manager 演变为原生 Promise。您曾经能够编写看起来像同步代码的代码,并且在后台,工具包会将其转换为等待 DOM 加载和页面上的 JavaScript 运行的异步代码。继续 need to convert your code 明确使用 async/await 或承诺。 (请参阅 Selenium 更改背后的原因 here。)

不幸的是,许多旧的(2019 年之前的)示例,甚至在 Protractor 文档中,都是以同步方式编写的,因此您应该小心遵循它们。更不幸的是,you cannot mix async code with Control Flow code,如果您尝试,所有控制流都将被禁用,并且您的一些依赖它的测试可能会开始失败。

顺便问一下,bars 的值是多少? Protractor by 对象的工作方式与本机 WebDriver 定位器不同,我不确定它们是否可重用。尝试使用by.name('bars') 或其他代替bars

由于涉及所有承诺,您的情况很棘手。 element.getCssValue 返回一个承诺。由于您试图从中获得truefalse 值,我建议使用reducer。

let nonZero = element.all(by.name('bars')).reduce((acc, elem) => {
  return acc || elem.getCssValue('width').then( (width) => width > 0 );
}, false);

在更复杂的情况下,您可以使用all().each(),但您必须小心确保您在each 中所做的任何事情都不会影响DOM,因为一旦这样做,它可能会使数组的其余部分无效。

如果您有可能通过最终操作修改页面,那么,尽管看起来很丑陋,但您需要循环查找元素:

for (var i = 0; true; i++) {
  let list = element.all(by.css('.items li'));
  if (i >= await list.count();)
    break;
  list.get(i).click();
};

【讨论】:

  • @emery 让我知道这是否解决了您的问题。
  • 好的,谢谢。我没有编写或编辑原始问题,我只是把赏金放在上面。您提到 async/await:您是否有任何具体理由相信使用它会清除 .each()、.forEach()、.filter() 块等内部的陈旧元素引用错误?如果是这样,那么听起来这可能是最优雅的解决方案。
  • @emery 是的,我在回答的前两段中列出了我认为 async/await 会有所帮助的具体原因。如果没有在上下文中看到您的完整代码,我无法更具体地了解您的情况。
【解决方案3】:

尝试在错误声明之前使用此代码:

  browser.wait(function() {
        return element.all(bars).isPresent().then(function(result) {
            return result;
        });
    }, 5000);

【讨论】:

  • 这不起作用,因为指定的函数将等待第一个元素出现而不是全部出现。 @isabella 如果在您的情况下它不相关,但是一旦我在使用 each() 但出于不同目的时通过 browser.sleep() 摆脱了相同的问题。仅供参考
【解决方案4】:

感谢所有提供帮助的人。如果导致错误,解决方案将重试。由于元素的实时性,元素引用在获得 css 宽度之前就发生了变化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    相关资源
    最近更新 更多