Protractor:如何在点击按钮后等待页面完全加载?

79
在一个测试规范中,我需要在网页上点击一个按钮,并等待新页面完全加载。
emailEl.sendKeys('jack');
passwordEl.sendKeys('123pwd');

btnLoginEl.click();

// ...Here need to wait for page complete... How?

ptor.waitForAngular();
expect(ptor.getCurrentUrl()).toEqual(url + 'abc#/efg');

2
你能具体说明一下你在等待哪个页面吗?你是在等待页面变化还是等待某些异步加载完成? - Furzel
10个回答

93

根据您想要做什么,您可以尝试:

browser.waitForAngular();

或者

btnLoginEl.click().then(function() {
  // do some stuff 
}); 

为了解决这个承诺,最好在beforeEach中完成。

NB: 我注意到expect()会等待内部的承诺(即getCurrentUrl)被解决后再进行比较。


感谢分享,帮助识别了一些奇怪的间歇性问题。 - Hakan Serce
1
我仍然有这个问题:等待异步脚本结果超时。 - Emna Ayadi
3
这并没有解决我等待JavaScript在按钮点击后修改页面的问题。如下面所建议的,加上browser.driver.sleep(1000)就解决了这个问题。 - CGFoX
3
waitForAngular()不应该使用(参见https://dev59.com/vl0Z5IYBdhLWcg3w8kH3)。关于click().then(),它对我无效。我只能依赖于browser.wait(//使用protractor.ExpectedConditions) - ThCollignon

33

我刚刚查看了源代码 - Protractor 只在少数情况下等待 Angular(例如在调用 element.all 或设置 / 获取位置时)。

因此,Protractor 不会在每个命令之后等待 Angular 稳定。

另外,在我的一些测试中,有时会出现 Angular digest 循环和点击事件之间的竞争,所以有时我必须执行以下操作:

elm.click();
browser.driver.sleep(1000);
browser.waitForAngular();

使用 sleep 来等待执行进入 AngularJS 上下文(由 click 事件触发)。


1
Protractor不会同步.click(),它会同步涉及到ElementFinder/ElementArrayFinder的下一个操作。 - Max
我从经验中知道,当使用expect评估期望时,Protractor会等待Angular。在其他情况下,我会明确地使用waitForAngular - jrharshath
1
尽管您的帖子已经两年了,但我发现您仍然是正确的。我目前需要在 Protractor 的 element(by.css('my-css')).click() 后添加一个 .sleep(1000)。似乎 .click 不会等待,尽管返回了一个 promise。 - bob.mazzo
我总是使用 Promise。例如:return elm.click().then(function () { return nextAction(); } - Ed Greaves
只是给其他人的一个注释,browser.driver.sleep() 接受一个整数毫秒数。因此上面的代码等待1毫秒。尝试使用 browser.driver.sleep(1000) 来等待更合理的时间。 - Ben Schmidt
4
我不建议使用明确的休眠,因为即使你的应用程序加载速度比配置的休眠时间更快,你也会进行休眠。 - timbru31

25

您无需等待。Protractor会自动等待Angular准备就绪,然后在控制流中执行下一步。


5
理论上是的,因为waitForAngular()会在内部调用,但我也必须为某些规格调用它。 - glepretre
8
它是否自动等待?看到像 browser.get 这样的内容,它明确表示它存在是为了覆盖它所包装的东西。如果 ElementFinder 没有 click 方法,那么它似乎只是委托给 webDriver.WebElement。 (注意:本文中的术语可能需要根据上下文进行调整翻译) - Snekse
5
@Snekse Protractor同步了对ElementFinder和ElementArrayFinder的所有操作。因此,每当您的测试尝试查找任何元素时,查找本身都会等待Angular完成消化周期,然后才将调用委托给webdriver。在expect()函数调用中也是如此。 - Max
10
如果我们不需要等待,那么这个帖子就不会被浏览 100,000 次了。 - Justin

8
使用Protractor时,可以采用以下方法:
var EC = protractor.ExpectedConditions;
// Wait for new page url to contain newPageName
browser.wait(EC.urlContains('newPageName'), 10000);

所以你的代码将会类似于:

emailEl.sendKeys('jack');
passwordEl.sendKeys('123pwd');

btnLoginEl.click();

var EC = protractor.ExpectedConditions;
// Wait for new page url to contain efg
ptor.wait(EC.urlContains('efg'), 10000);

expect(ptor.getCurrentUrl()).toEqual(url + 'abc#/efg');

注意:这并不意味着新页面已经加载完成且DOM准备就绪。接下来的 'expect()' 语句将确保Protractor等待DOM可用于测试。
参考:Protractor ExpectedConditions

3
在这种情况下,您可以使用: 页面对象:
    waitForURLContain(urlExpected: string, timeout: number) {
        try {
            const condition = browser.ExpectedConditions;
            browser.wait(condition.urlContains(urlExpected), timeout);
        } catch (e) {
            console.error('URL not contain text.', e);
        };
    }

页面测试:

page.waitForURLContain('abc#/efg', 30000);

2

通常我只是在控制流中添加一些内容,例如:

it('should navigate to the logfile page when attempting ' +
   'to access the user login page, after logging in', function() {
  userLoginPage.login(true);
  userLoginPage.get();
  logfilePage.expectLogfilePage();
});

日志文件页面:

function login() {
  element(by.buttonText('Login')).click();

  // Adding this to the control flow will ensure the resulting page is loaded before moving on
  browser.getLocationAbsUrl();
}

1
使用这个,我认为更好。
   *isAngularSite(false);*
    browser.get(crmUrl);


    login.username.sendKeys(username);
    login.password.sendKeys(password);
    login.submit.click();

    *isAngularSite(true);*

如果您想使用 isAngularSite 这个设置,需要将以下代码添加到您的 protractor.conf.js 文件中:

        global.isAngularSite = function(flag) {
        browser.ignoreSynchronization = !flag;
        };

0

要等到点击本身完成(即解决 Promise),请使用 await 关键字

it('test case 1', async () => {
  await login.submit.click();
})

这将停止命令队列,直到点击(sendKeys、sleep或任何其他命令)完成

如果你很幸运,你在一个构建良好且没有微观和宏观任务待处理的Angular页面上,那么Protractor应该自己等待页面准备就绪。但有时候你需要自己处理等待,例如通过非Angular页面登录(阅读如何找出页面是否有待处理任务以及如何处理非Angular页面

在你手动处理等待的情况下,browser.wait是正确的方法。只需传递一个函数给它,该函数将具有等待条件。例如,等待页面上没有加载动画。

let $animation = $$('.loading');

await browser.wait(
  async () => (await animation.count()) === 0, // function; if returns true it stops waiting; can wait for anything in the world if you get creative with it
  5000, // timeout
  `message on timeout`
);

请确保使用 await


-1

你可以像这样做

emailEl.sendKeys('jack');
passwordEl.sendKeys('123pwd');

btnLoginEl.click().then(function(){
browser.wait(5000);
});


browser.wait(5000) 会返回“不可分配给类型函数”,因为 browser.wait 接受 >1 个参数,第一个是条件,第二个是时间... - IWI

-2
browser.waitForAngular();

btnLoginEl.click().then(function() { Do Something });

解决 Promise 的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接