Protractor Button Click and open page in new tab

All we need is an easy explanation of the problem, so here it is.

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 ?

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

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

Method 2

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

Method 3

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/)

Method 4

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

Method 5

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

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply