以下这段代码随机运行,有时工作正常,有时会抛出类似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
,那么我将导致同样的问题
答案 0 :(得分:6)
sendKeys
和e1
的简短说明一个简短的说明,sendKeys
不会返回WebElement或ElementFinder。这意味着上面示例中的e1可能是未定义的。
有关假设的简要说明:答案有一个假设,即向过滤器发送文本会改变屏幕上的行数或元素数。发送文本后,如果屏幕上元素的数量相同,则此方法将无效。我将在下面查看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
项数量进行比较检查,并且只有在更新内容后才能继续进行。
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而言,您应该可以。