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