如何然后在量角器中执行一个元素然后再执行另一个元素

时间:2018-12-17 10:41:50

标签: javascript selenium async-await protractor automated-tests

以下这段代码随机运行,有时工作正常,有时会抛出类似Stale Element Reference Exception的错误

我想要的是我首先要在下面执行此操作

element(by.id('FiltItemTransDocNo')).sendKeys(grno);

之后,我希望在下面执行此操作

element.all(by.name('chkGrd')).first().click();

我已经尝试过这种方法,但是似乎没有用

element(by.id('FiltItemTransDocNo')).sendKeys(grno).then(function(el){
     element.all(by.name('chkGrd')).first().click();
});

帮我解决这个问题

我已附上图片 我将密钥发送到“采购申请”字段,并且据我想出了仅显示一个结果的结果,我想单击 如果我将可见性的条件定为true,那么我将导致同样的问题 enter image description here

3 个答案:

答案 0 :(得分:6)

关于sendKeyse1的简短说明

一个简短的说明,sendKeys不会返回WebElement或ElementFinder。这意味着上面示例中的e1可能是未定义的。

陈旧的元素和DOM发生变化

有关假设的简要说明:答案有一个假设,即向过滤器发送文本会改变屏幕上的行数或元素数。发送文本后,如果屏幕上元素的数量相同,则此方法将无效。我将在下面查看Florent对过时的参考错误的评论。

陈旧的元素通常在DOM更改时发生。如果您在Angular中使用某些结构性指令,则在使用* ngFor或* ngIf时,DOM将会更改。我的猜测是,在对元素进行过滤之后,您将在DOM根据过滤器实际更改的过程中或之前获取DOM中的元素。这将导致陈旧引用的Web元素。在下面的examplex中,我使用async / await并关闭了控制流。

在过滤器中明确等待

您可以显式设置睡眠状态,以便在您致电单击第一个元素之前更新DOM。这可能会导致测试不稳定,因为根据运行环境的不同,超时时间未知。

it('should do something', async () => {
  const filterItem = element(by.id('FiltItemTransDocNo'));
  await filterItem.sendKeys(grno);
  await browser.sleep(1000);  // or some other waits
  await element.all(by.name('chkGrd')).first().click();
});

比较element.all的行

或者,您可以对点击前后的element.all项数量进行比较检查,并且只有在更新内容后才能继续进行。

it('should do something', async () => {
  const filterItem = element(by.id('FiltItemTransDocNo'));
  const table = element.all(by.name('chkGrd'));
  const length = await table.count();

  await filterItem.sendKeys(grno);

  // After the filter keys are sent, check to see if the current table
  // count is not equal to the `table.count()`.
  let updated = false;
  await browser.wait(async () => {
    updated = length !== await table.count();
    return updated;
  }, 5000);

  // So if we use the entire 5 seconds and the count has not changed,
  // we should probably fail before clicking on stuff.
  expect(updated).toBeTruthy();

  // now we can click on the next element.
  await element.all(by.name('chkGrd')).first().click();
});

为什么呼叫length !== await table.count()可以工作?这是因为表代表了获取Web元素的承诺。当您调用count方法时,它首先解析Web元素来执行操作。根据DOM是否更改,这可能会有所不同。然后,我们将当前计数与上一个计数进行比较。

确保您使用的是异步/等待状态

在您的配置文件中,您需要指定您已脱离控制流程:

exports.config = {
    // In this case, I plan to use a selenium standalone server
    // at http://127.0.0.1:4444/wd/hub. You could also use other 
    // methods like direct connect or starting it up with 'local'.
    seleniumAddress: 'http://127.0.0.1:4444/wd/hub',

    // Required flag to tell Protractor you do not want to use the
    // control flow. This means that you will have to either chain
    // your promises or async / await your actions. Historically
    // jasminewd node module would resolve promises for you. This
    // package will no longer be used in future releases since the
    // control flow is deprecated.
    SELENIUM_PROMISE_MANAGER: false,

    // The rest of your config...
}

希望有帮助。

答案 1 :(得分:2)

有几种解决陈旧元素的方法。

第一:

    let filterItem = element(by.id('FiltItemTransDocNo'));
    browser.wait(ExpectedConditions.visibilityOf(filterItem), 5000, 'Element is not visible.');
    filterItem.sendKeys('some random text');

    let elementToClick = element.all(by.name('chkGrd')).first();
    browser.wait(ExpectedConditions.elementToBeClickable(elementToClick), 5000, 'Element is not clickable.');
    elementToClick.click();

您还可以将它们链接起来:

browser.wait(ExpectedConditions.visibilityOf(filterItem), 5000, 'Element is not visible.').then( () => {
                filterItem.sendKeys('some random text');

                browser.wait(ExpectedConditions.elementToBeClickable(elementToClick), 5000, 'Element is not clickable.').then( () => {
                    elementToClick.click();
                });
            });

第二种方法,在出现过时错误时刷新元素:

let filterItem = element(by.id('FiltItemTransDocNo'));

            try {
                filterItem.sendKeys('some random text');
            } catch (e) {
                if (e instanceof StaleElementReferenceError) {
                    filterItem = element(by.id('FiltItemTransDocNo'));
                    filterItem.sendKeys('text');
                }
            }

答案 2 :(得分:2)

正如我在answer to another question中所解释的那样,量角器及其使用的工具都在改变以使用本机JavaScript承诺,并期望每个人都可以将其代码迁移为完全异步的编码样式。不幸的是,由于您无法将旧样式的代码与新样式混合使用,因此它们并没有使其变得容易。因此首先,请确保您的整个套件都采用异步风格编写,并且已在配置中设置了SELENIUM_PROMISE_MANAGER: false

响应sendKeys会发生什么?如果这触发了AJAX调用,并且对该调用的响应更改了DOM,则您可能必须执行@cnishina的建议并轮询DOM,以等待更改生效。但是,如果仅发生Angular客户端更改,则此代码应该起作用:

it('should do something', async () => {
  await element(by.id('FiltItemTransDocNo')).sendKeys(grno);
  await element.all(by.name('chkGrd')).first().click();
});

两个element调用都与Angular进行同步,以确保在定位器继续执行之前Angular更新已完成,因此就Angular而言,您应该可以。