12

I am fairly new to Protractor. I am trying to automate a scenario where I click on a button and its opens up a page in new tab and then we need to populate form in new page and submit.

Issue: when i click on button to open new page. My tests does not wait for new page to load and say test completed and success message.

I am using simple click event of that button to click the button.

element(by.id("newPlan")).click()

Am I missing something ? Do i need to do something so that my tests wait for new page to load and then I can perform some functions ?

Sumit
  • 2,932
  • 6
  • 32
  • 54

5 Answers5

32

You need to wait until the page opens by using callbacks. Try something in this sense:

    element(by.id("newPlan")).click().then(function () {
        browser.getAllWindowHandles().then(function (handles) {
            newWindowHandle = handles[1]; // this is your new window
            browser.switchTo().window(newWindowHandle).then(function () {
                // fill in the form here
                expect(browser.getCurrentUrl()).toMatch(/\/url/);
            });
        });
    });
Michal Lison
  • 489
  • 4
  • 7
  • 1
    I tried this but got an error UnknownError: unknown error: 'name' must be a nonempty string – Sumit Feb 16 '15 at 08:44
  • Try printing out handles param to see if the window that you're looking for is there. – Michal Lison Feb 16 '15 at 09:15
  • Are you doing anything on the spawned page? Make sure that page is also a angular js page. – Rahul Vig Feb 16 '15 at 13:10
  • Just in case if it is a non-angular page, see a similar problem described here: http://stackoverflow.com/questions/28511013/non-angular-page-opened-after-a-click. – alecxe Feb 16 '15 at 16:21
  • 3
    Doesn't work if it takes time to load a new tab! The handles[1] is undefined then. – Andrej Oct 02 '16 at 22:35
3

This is the solution that worked for me, but i've added a browser.sleep(500), to avoid the error mentioned above (UnknownError: unknown error: 'name' must be a nonempty string). The problem was that the new handle was not yet available. Just give it a moment after the click, to open the new tab and have it's handler available. Yes, it's adding an ugly sleep, but it's a short one...

element(by.id("newPlan")).click().then(function () {
        browser.sleep(500);
        browser.getAllWindowHandles().then(function (handles) {
            newWindowHandle = handles[1]; // this is your new window
            browser.switchTo().window(newWindowHandle).then(function () {
                // fill in the form here
                expect(browser.getCurrentUrl()).toMatch(/\/url/);
            });
        });
    });
2

There is another more convenient way. Just make use of the functions on the browser object.

element(by.id("newPlan")).click();
browser.sleep(10000);
browser.waitForAngular();
expect(browser.getCurrentUrl()).toMatch(/\/url/)
Blaise
  • 21,314
  • 28
  • 108
  • 169
  • 1
    I didn't want to use sleep functions as they cause troubles on slow servers and wait unnecessary on fast servers. – Sumit Mar 25 '15 at 04:46
  • True. Since `waitForAngular` does not always wait as expected, we have to add a `sleep` to make sure there is sufficient wait. Of course it is too arbitrary and can lead to unnecessary delay. But if we just start the test and let it run (probably after work hours), the tradeoff could be acceptable. – Blaise Mar 25 '15 at 12:43
2

"Making sure that the new page is also an AngularJS page" doesn't make too much sense to me, if I'm honest :)

The test should be valid regardless of the type of the page/app the browser redirects to, shouldn't it?

If you are facing issues with accessing new tab URL on the non-angular page, try

browser.driver.getCurrentUrl();

instead of

browser.getCurrentUrl();
Chanaka Amarasinghe
  • 3,489
  • 11
  • 26
  • 38
0

Here is implementation without using browser.sleep() method. Function waitForNewWindow() is created using async and underscorejs. Where async.until() method is used for calling getAllWindowHandles() synchronously.

element(by.id("newPlan")).click()
    .then(function () {
        return waitForNewWindow();
    })
    .then(function (newWindowHandle) {
        browser.switchTo().window(newWindowHandle).then(function () {
            expect(browser.getCurrentUrl()).toMatch(/\/url/);
        });
    });

 /**
 * Wait for new window is opened
 *
 * @param {Object} [params]
 * @param {number} [params.runs] - number of tries
 * @param {number} [params.interval] - interval for launching getAllWindowHandles()
 *
 * @returns {webdriver.promise.Promise}
 */
function waitForNewWindow(params) {
    var currentHandles = [];
    var deferred = protractor.promise.defer();
    var finish;
    var newHandle;
    var numberOfRuns = 0;

    params = params ? params : {};
    params.runs = params.runs || 10;
    params.interval = params.interval || 1000;

    browser.driver.getAllWindowHandles()
        .then(function (handles) {
            currentHandles = handles;
        })
        .then(function () {
            async.until(
                // function that tests condition
                function () {
                    return newHandle || finish;
                },
                // function that is executed until test condition is true
                function (callback) {
                    browser.driver.getAllWindowHandles()
                        .then(function (newHandles) {
                            numberOfRuns++;

                            if (numberOfRuns > params.runs) {
                                finish = true;

                                return callback(null, newHandle);
                            }

                            if (currentHandles.length < newHandles.length) {
                                newHandle = _.difference(newHandles, currentHandles);

                                return callback(null, newHandle);
                            }

                            setTimeout(function () {
                                callback(null, newHandle);
                            }, params.interval);
                        });
                },
                // callback when test condition is true
                function (error, result) {
                    if (!result) {
                        return deferred.reject('New browser window hasn\'t been opened');
                    }

                    if (result.length > 1) {
                        return deferred.reject('More than one new browser window were opened');
                    }

                    deferred.fulfill(result.toString());
                }
            );
        });

    return deferred.promise;
};
Andrei Surzhan
  • 151
  • 1
  • 8