Noble-Surfer
Noble-Surfer

Reputation: 3172

AngularJS testing with Protractor - chaining promises- How to wait for animation to complete?

I am currently developing an automated test suite for an AngularJS application developed by my company. I have already designed & developed a number of test cases, which are running & passing/ failing as expected.

However, with the test that I am currently developing, I seem to have run into an issue when chaining promises, and am unable to solve it... The test script as it stands is:

it('should navigate to the browser page', function() {
    console.log("Start browser page test");
    browser.waitForAngularEnabled(false);
    browser.wait(EC.visibilityOf(pagesMenuBtn), 10000).then(
        browser.actions().mouseMove(pagesMenuBtn).perform().then(
            //expect(pageBrowserBtn.isDisplayed()).toBeTruthy();
            browser.wait(EC.visibilityOf(pageBrowserBtn), 12000).then(
                pageBrowserBtn.click().then(
                    function() {
                        console.log("Browser menu button clicked");
                        //browser.pause();
                    }).then(
                        browser.wait(EC.visibilityOf(browserPageTagsLink), 20000).then(
                            function(){
                                console.log("End browser page test (then call)");
                                expect(browser.getCurrentUrl()).toBe(VM + '/#/pages/browser');
                            }
                        )
                    )
                )
            )
    );
});

When I run my test script (I have a number of other tests that run before this one), the first few run & pass successfully, then when this test starts to execute, the console shows:

Started

....Start browser page test

Browser menu button clicked

F

Failures:

1) App should navigate to the browser page

Message:

Failed: Wait timed out after 20010ms

Stack:

TimeoutError: Wait timed out after 20010ms at WebDriverError (C:\Users\path\selenium-webdriver\lib\error.js:27:5)

So from the output displayed in the console, it's clear that the test executes correctly as far as the console.log("Browser menu button clicked); statement, which indicates that a click has been performed on the menu button as I intend (i.e. the menu button clicked is displayed on a popup menu that is only shown when the cursor is hovering over the pagesMenuBtn element, so that indicates that the line

browser.wait(EC.visibilityOf(pageBrowserBtn), 12000).then(

is executed correctly).

Since the console.log("Browser menu button clicked"); statement is also displayed in the console, that indicates that the

pageBrowserBtn.click().then(

is also executed correctly.

But for some reason, the test is then timing out after the 20 seconds it waits for the browserPageTagsLink element to be displayed.

This browserPageTagsLink element is just a hyperlink element displayed on the 'Browser' page that my test is trying to navigate to- I am waiting for it to be visible so that I know that the page has loaded before I check that the URL is correct...

But as I watch what's happening in the browser window where these tests are run, and what's displayed in the console while my tests are running, the script seems to 'get stuck'/ pause/ 'hang' for a while after the Browser menu button clicked message is displayed in the console, and I can see from watching the browser window that this button was never actually clicked- the popup menu where this button is displayed is shown very briefly: the line browser.actions().mouseMove(pagesMenuBtn).perform() is causing the cursor to hover over the button required to show the sub-menu, but it seems that the click is performed before the sub-menu element has fully finished loading (there is some animation on the element- it appears to 'fade into view'), but I think that the click is happening before the element has fully finished loading, and so it's possibly not registering correctly?

How can I cause my test to wait for the animation to complete before trying to click the sub-menu menu item, so that the click registers with the element correctly?

I tried doing this with the line:

browser.wait(EC.visibilityOf(pageBrowserBtn), 12000).then(

It seems that the EC.visibilityOf(...) condition is met as soon as the element starts to become visible, rather than waiting until it is fully opaque, but that the

pageTagBrowserBtn.click().then(

line, which is called as soon as the condition is true can't actually be performed at this point- presumably because the element is not 'fully' visible at the point at which it's clicked?

How can I make my test wait for the animation (which has been implemented using CSS) to complete before it tries to click on the element?

I had a look on the Protractor API for anything about animations, but it only seems to provide the function allowAnimations()...

Edit

The animation for this element is set in the CSS with:

/* Animation for moving lists in configuration */
.list-fade.ng-enter,
.list-fade-leave.ng-leave {
    -webkit-transition: all linear 0.5s;
    transition: all linear 0.5s;
}

i.e. it should take 0.5 seconds for the element to be displayed when the cursor hovers over its parent element.

I tried adding a call to browser.wait(), so that my test would wait for the element to be fully displayed before trying to click on it- I updated the part of my test that is sending the click to:

browser.actions().mouseMove(pagesMenuBtn).perform().then(
    //expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
    browser.wait(EC.visibilityOf(pageTagBrowserBtn), 12000).then(
        browser.wait(10000).then(
            pageTagBrowserBtn.click().then(
                function() {
                    console.log("Tag Browser menu button clicked");

I told it to wait for 10 seconds at this point to ensure that it definitely gave the element enough time to load (according to the CSS, it should only take 0.5 seconds to be displayed), but for some reason, my test is still failing due to the same timeout issue.

Anyone have any suggestions?

Upvotes: 0

Views: 232

Answers (1)

C. Peck
C. Peck

Reputation: 3711

Looking at your tests I'm wondering why you have to chain your promises like that. On a fully angular page Protractor is supposed to automatically chain promises, ie when I write a test like this

driver.get(“http://www.google.com”);
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
driver.findElement(webdriver.By.name('btnG')).click();
driver.getTitle().then(function(title) {
  console.log(title);
});

it actually is executed in a synchronous manner like this:

driver.get(“http://www.google.com”).
then(function() {
  return driver.findElement(webdriver.By.name('q'));
}).
then(function(q) {
  return q.sendKeys('webdriver');
}).
then(function() {
  return driver.findElement(webdriver.By.name('btnG'));
}).
then(function(btnG) {
  return btnG.click();
}).
then(function() {
  return driver.getTitle();
}).
then(function(title) {
  console.log(title);
});

If your page is fully Angular most of what you've written shouldn't need the promises, and that my be causing timing issues with your test. Have you tried writing the test without chaining promises?

That said, you might also try EC.elementToBeClickable(yourElement) instead of waiting for EC.visibilityOf() if you haven't already.

Upvotes: 0

Related Questions