79

In a test spec, I need to click a button on a web page, and wait for the new page completely loaded.

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');
Emna Ayadi
  • 2,430
  • 8
  • 37
  • 77
Zach
  • 5,715
  • 12
  • 47
  • 62
  • 2
    Could you be more specific on the page you are waiting for ? Are you just waiting for a page change or for some asynchronous loading ? – Furzel Feb 13 '14 at 12:35

10 Answers10

93

Depending on what you want to do, you can try:

browser.waitForAngular();

or

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

to solve the promise. It would be better if you can do that in the beforeEach.

NB: I noticed that the expect() waits for the promise inside (i.e. getCurrentUrl) to be solved before comparing.

glepretre
  • 8,154
  • 5
  • 43
  • 57
  • Thanks for sharing, helped to identify some weird intermittent issues. – Hakan Serce Feb 29 '16 at 22:28
  • 1
    I still have the issue : Timed out waiting for asynchronous script result – Emna Ayadi May 09 '16 at 18:32
  • 3
    This didn't solve my problem of waiting for javascript to finish modifying the page after the button click. Adding `browser.driver.sleep(1000)` as suggested [below](https://stackoverflow.com/a/27551014/2745116) did the trick. – stefanbschneider Feb 01 '18 at 09:57
  • 3
    `waitForAngular()` [should not be used](https://stackoverflow.com/questions/30882671/protractor-where-to-use-browser-waitforangular). About the `click().then()` it didn't work for me. I could only rely on `browser.wait(//use of protractor.ExpectedConditions)`. – ThCollignon Aug 30 '18 at 11:07
33

I just had a look at the source - Protractor is waiting for Angular only in a few cases (like when element.all is invoked, or setting / getting location).

So Protractor won't wait for Angular to stabilise after every command.

Also, it looks like sometimes in my tests I had a race between Angular digest cycle and click event, so sometimes I have to do:

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

using sleep to wait for execution to enter AngularJS context (triggered by click event).

RJFalconer
  • 10,890
  • 5
  • 51
  • 66
Filip Sobczak
  • 1,286
  • 11
  • 11
  • 1
    Protractor does not synchronize the .click(), it synchronized the next operation that involves ElementFinder/ElementArrayFinder. – Max Jul 03 '15 at 10:08
  • I know from experience that protractor will wait for angular when evaluating expectations using `expect`. In other cases I explicitly use `waitForAngular`. – jrharshath Jun 22 '16 at 16:46
  • 1
    although your post is almost two years old, I find that you're still right. I currently need to add a '.sleep(1000)' after a Protractor `element(by.css('my-css')).click()` . It seems that the .click is NOT waiting, although a promise is returned. – bob.mazzo Sep 27 '16 at 16:47
  • I always use promises. For example: `return elm.click().then(function () { return nextAction(); }` – Ed Greaves Nov 02 '16 at 19:00
  • Just a comment for others, browser.driver.sleep() takes an int number of milliseconds. So the above code waits for 1ms. Try: browser.driver.sleep(1000) for something more reasonable. – Ben Schmidt Feb 09 '17 at 23:22
  • 4
    I won't recommend using explicit sleeps, since you sleep even if your application loads faster than the configured sleep. – timbru31 Dec 13 '17 at 15:53
25

You don't need to wait. Protractor automatically waits for angular to be ready and then it executes the next step in the control flow.

Andres D
  • 8,910
  • 2
  • 26
  • 31
  • 5
    in theory yes, because `waitForAngular()` is called internally but I had to call it for some specs too. – glepretre Feb 14 '14 at 16:44
  • 8
    Does it automatically wait? Looking at something like `browser.get`, it explicitly says it exists to overwrite the thing it wraps. If there is no `click` method for `ElementFinder`, then it seems like it just delegates to the `webDriver.WebElement`. https://angular.github.io/protractor/#/api?view=ElementFinder – Snekse Sep 04 '14 at 20:44
  • 5
    @Snekse Protractor synchronized all operations over ElementFinder and ElementArrayFinder. So whenever your test try to lookup any element, the lookup itself will wait for angular to complete the digestion cycle and just then delegate the call to webdriver. Same happens in the expect() call. – Max Jul 03 '15 at 10:11
  • 9
    If we didn't need to wait then this thread wouldn't have been viewed 100,000 times. – Justin Mar 14 '18 at 18:18
8

With Protractor, you can use the following approach

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

So your code will look something like,

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');

Note: This may not mean that new page has finished loading and DOM is ready. The subsequent 'expect()' statement will ensure Protractor waits for DOM to be available for test.

Reference: Protractor ExpectedConditions

milan pandya
  • 147
  • 1
  • 10
3

In this case, you can used:

Page Object:

    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 Test:

page.waitForURLContain('abc#/efg', 30000);
Dao Minh Dam
  • 373
  • 1
  • 2
  • 14
2

I typically just add something to the control flow, i.e.:

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();
});

logfilePage:

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();
}
Justin Helmer
  • 185
  • 2
  • 4
1

Use this I think it's better

   *isAngularSite(false);*
    browser.get(crmUrl);


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

    *isAngularSite(true);*

For you to use this setting of isAngularSite should put this in your protractor.conf.js here:

        global.isAngularSite = function(flag) {
        browser.ignoreSynchronization = !flag;
        };
Dantinho
  • 57
  • 5
0

to wait until the click itself is complete (ie to resolve the Promise), use await keyword

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

This will stop the command queue until the click (sendKeys, sleep or any other command) is finished

If you're lucky and you're on angular page that is built well and doesn't have micro and macro tasks pending then Protractor should wait by itself until the page is ready. But sometimes you need to handle waiting yourself, for example when logging in through a page that is not Angular (read how to find out if page has pending tasks and how to work with non angular pages)

In the case you're handling the waiting manually, browser.wait is the way to go. Just pass a function to it that would have a condition which to wait for. For example wait until there is no loading animation on the page

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`
);

Make sure to use await

Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
-1

you can do something like this

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

btnLoginEl.click().then(function(){
browser.wait(5000);
});
ashish bansal
  • 291
  • 4
  • 3
  • browser.wait(5000) will return "Not assignable to type function", since browser.wait takes in > 1 arguments, the first being a condition, the second being the time... – IWI Jul 04 '19 at 16:22
-2
browser.waitForAngular();

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

to solve the promise.

CDspace
  • 2,639
  • 18
  • 30
  • 36