Protractor: get url of not angular page - javascript

Case I try to test:
On Angular app page press button, that redirects you to some other site (not an Angular app).
it('should go to 3d party service when i click "auth" button' , function() {
browser.driver.sleep(3000);
element(by.id('files-services-icon')).click();
element(by.id('box-vendor-menu-item')).click();
browser.driver.sleep(2000);
expect( browser.driver.getLocationAbsUrl()).toContain('https://app.box.com/api/oauth2/authorize');
});
but I get:
UnknownError: unknown error: angular is not defined
How that can be achived? Thanks!

You need to do 2 things
Set browser.ignoreSynchronization = true; before trying to read the URL of the 3rd party page, so the browser doesn't wait for (and so require) Angular promises to resolve in the page (and set it to false afterwards);
Use browser.getCurrentUrl() as opposed to browser.getLocationAbsUrl(), as the former just uses the plain webdriver method of reading the URL, rather than accesing it via Angular.
The following should work:
it('should go to 3d party service when i click "auth" button' , function() {
element(by.id('files-services-icon')).click();
element(by.id('box-vendor-menu-item')).click();
browser.ignoreSynchronization = true;
expect(browser.getCurrentUrl()).toContain('https://app.box.com/api/oauth2/authorize');
browser.ignoreSynchronization = false;
});

You need to tweak ignoreSynchronization flag - set it to true before clicking on the link and set back to false in afterEach():
afterEach(function () {
browser.ignoreSynchronization = false;
});
it('should go to 3d party service when i click "auth" button' , function() {
element(by.id('files-services-icon')).click();
browser.ignoreSynchronization = true;
element(by.id('box-vendor-menu-item')).click();
expect(browser.getLocationAbsUrl()).toContain('https://app.box.com/api/oauth2/authorize');
});
See also:
Non-angular page opened after a click
You may also need to wait for URL to change, example here.

Related

Cucumber Protractor - Page timeout issues with Angular and Non Angular applications

We are in process of implementing BDD approach and use protractor for testing. The application under test has both Angular and Non Angular pages. The login page is non angular and home page is angular. The script runs fine on Login page and when it lands to non angular nothing happens (no action performed).
What could be the issue?
StepDefinition.js
Given(/^User lands on Login$/, function () {
var appUrl = properties.get('appUrl');
return browser.driver.get('appUrl');
browser.ignoreSynchronization = true;
});
When(/^User enters Username and Password$/, function () {
xph.get('Username').sendKeys(username);
return xph.get('Password').sendKeys('password');;
});
When(/^User Clicks Submit$/, function(){
browser.executeScript("arguments[0].click();",xph.get('Login'))
//return browser.sleep(7000);
browser.ignoreSynchronization = false;
browser.waitForAngular();
});
Then(/^User successfully logs$/, function() {
var hString= xph.get('LogOut');
hString.getText().then(function(text){expect(text).to.equal('LogOut');});
});
Then(/^User clicks Create Account$/, function () {
browser.executeScript("arguments[0].click();",xph.get('CreateAcct'))
});
Two issue in your code, try again after fix them as following:
1) you put browser.ignoreSynchronization = true behindreturn, it make no sense.
Inside browser.get(), it will detect the opening page is angular, except to put browser.ignoreSynchronization = true before browser.get() to tell protractor the opening page is non angular page.
Given(/^User lands on Login$/, function () {
var appUrl = properties.get('appUrl');
browser.ignoreSynchronization = true;
return browser.driver.get('appUrl');
});
2) You have to return a promise like object for each step definition, otherwise the runner will pause at that step definition until timeout.
waitForAngular() equivalent to browser.ignoreSynchronization = false;, The former is introduced in protractor higher version, the later can work protractor lower and higher version.
Why you not use the Protractor API xph.get('Login').click(), but using Javascript DOM API to click the Submit button.
When(/^User Clicks Submit$/, function(){
return element(<locator of Submit button>).click().then(function(){
return browser.ignoreSynchronization = false;
})
});

Protractor not waiting for page load

I created a basic test with protractor to click on a element and on the new page check if given element exists, but Protractor does not seem to wait and it runs the assertion just after the click but before the new page loads. The element I am looking for is available on both pages, so protractor sees the element on the old page, before the new page loads. Can someone please tell me what am I doing wrong?
it('should check when new page is loaded', function () {
button.click().then(function (){
return expect(newElement).to.exist;
});
First of all ,add "getPageTimeout" variable to your Protractor configuration file.
If you are not using it already.This is to set global page timeout based on your average page load time in your application.
conf.js
getPageTimeout: 120000,//change it based on your app response time
If it does not help even then verify the page title of next page(assuming its different from previous page) before checking the actual element you are looking for.
it('should check when new page is loaded', function () {
button.click().then(function (){
browser.getCurrentUrl();
browser.getTitle().then(function (title) {
expect(title).toEqual('Next Page Title');
});
return expect(newElement.isDisplayed()).toBeTruthy();
});
Even if that does not help you may use expected conditions.There are several predefined conditions to explicitly wait for. In case you want to wait for an element to become present:
var EC = protractor.ExpectedConditions;
var e = element(by.id('xyz'));
browser.wait(EC.presenceOf(e), 10000);
expect(e.isPresent()).toBeTruthy();
You can use below method in Configuration file
jasmineNodeOpts: {
showColors: true,
includeStackTrace: true,
defaultTimeoutInterval: 1440000
},
And In Spec file you can customise the wait time according to page and object visibility
browser.sleep(20000);
I hope above method will work fine in all the cases if you customise the wait time Properly.

Issue with Protractor while testing a non angular js website

I am currently testing a non Angular js website with protractor. My code is as follows :
describe("Login ", function () {
it("test", co.wrap(function* () {
browser.ignoreSynchronization = true;
yield browser.get("URL");
var elmOK = element(by.css('a[href="#partner"]'))
yield elmOK.click();
expect(browser.getCurrentUrl()).toContain('login');
}));
});
Test Scenario : My test opens the URL mentioned and selects the link with href=#partner. A login page which should pop up. But when I run the test and the link is clicked the login page doesn't popp up.
Please tell me what I am doing wrong?
Protractor appears synchronous because it waits for angular to be stable prior to moving to the next action. So if the above was an angular page, it would:
load the page, then Protractor would wait for angular (wait for the page to be stable)
tell selenium webdriver to find the element and click on it.
In a non-angular page, setting the ignore synchronization set to true in the right direction. What you need to do is to come up with your own sleep between getting the page and clicking the element. After you click on the element, you'll also need to wait for the next window to pop up, change focus to the next window to see if the url contains login.
browser.ignoreSynchronization = true;
browser.get("URL");
// let the page load
browser.sleep(2000);
element(by.css('a[href="#partner"]')).click().then(() => {
// may have to add sleep in for the page to load
browser.sleep(2000);
// switch to the next window for the popup
browser.driver.getAllWindowHandles().then((windowHandles) => {
let popupLoginHandle = windowHandles[1];
browser.driver.switchTo().window(popupLoginHandle).then(() => {
// check to see if the pop up has a url that contains 'login'
expect(browser.getCurrentUrl()).toContain('login');
});
});
});
Putting Hard coded waits is not an good practice.
If you are using jasmine for reporting , you may put the following code inside conf.js:
jasmineNodeOpts: {
defaultTimeoutInterval: 600000,
},

Non-angular page opened after a click

I'm trying to implement the following test scenario:
perform a click on a logo on the page
assert there is a new browser window opened (tab in Chrome) and check the current URL
The problem is that the page opened in a new browser window is a non-angular page while the main page I'm performing the click in is an angular page.
Here is my first attempt:
it("should show logo", function () {
var logo = scope.page.logo;
expect(logo.isDisplayed()).toEqual(true);
// opens a new page on click
logo.click().then(function () {
browser.getAllWindowHandles().then(function (handles) {
browser.switchTo().window(handles[1]).then(function () {
expect(browser.getCurrentUrl()).toEqual('http://myurl.com/');
});
// switch back to the main window
browser.switchTo().window(handles[0]);
});
});
});
which fails with:
Error: Error while waiting for Protractor to sync with the page:
"angular could not be found on the window"
which is understandable.
My second attempt was to play around with ignoreSynchronization boolean flag:
browser.ignoreSynchronization = true;
logo.click().then(function () {
browser.getAllWindowHandles().then(function (handles) {
browser.switchTo().window(handles[1]).then(function () {
expect(browser.getCurrentUrl()).toEqual('http://myurl.com/');
});
// switch back to the main window
browser.switchTo().window(handles[0]);
});
This actually makes this particular test pass without any errors, but it affects every test executed after this one, because browser is a global object and is shared between tests - protractor no longer syncs with angular on a page which results into all sorts of errors.
How should I implement the test?
As a workaround, I can change the restartBrowserBetweenTests setting to true and change ignoreSynchronization value without any problems - but it slows down the tests dramatically.
You can set ignoreSynchronization to be false once your verification is done. However, note that ignoreSynchronization is synchronous while everything else (click/get/etc) is asynchronous, so you need to be careful with that. Probably the safest way is to set it to true in beforeEach and false in afterEach, and just test that single logo click in that test.
Another solution: I have used sleep since getWindowHandles was
returning only one window name(parent/angular window). Let me know if there
is a better way.
this.validateProductPageInfo = function (productTitle) {
browser.sleep(5000);
browser.getAllWindowHandles().then(function (handles) {
if (handles.length === 2) {
browser.switchTo().window(handles[1]).then(function () {
var prodTitle = by.xpath('//h1[#id="fw-pagetitle"]');
browser.driver.findElement(prodTitle).getText().then(function (text) {
expect(text).toBe(productTitle);
});
});
browser.switchTo().window(handles[0]);
} else {
console.log("Error in switching to non angular window");
}
});
};

How to wait for a page to load or element to be present when using Protractor for a non-Angular page

I am new to Protractor. I think I have this down when dealing with an Angular page, but can't figure it out for a non-Angular page. Any help would be appreciated.
describe('Search', function() {
it('should click Search button and wait for results', function() {
browser.driver.findElement(by.id('search')).click();
});
});
Testing non-angular pages with Protractor can be tricky regarding waiting for stuff.
I suggest you upgrade Protractor to latest (1.5.0 as of now), use a custom function waitReady() that browser.wait for elements ready and rewrite your test like below. Note you can put everything within 1 spec if you like so.
// TODO: use page objects
var searchBtnElm = $('#search'); // use element(by.id('search')) if you prefer
it('waits for the elements present and visible (non-angular)', function() {
expect(searchBtnElm.waitReady()).toBeTruthy();
});
it('should click Search button', function() {
searchBtnElm.click();
});
it('wait for more results', function() {
// keep using waitReady() before interacting with the elements
// and before performing expectations on them
});
More details of why waitReady here.
Note: remember to set ignore synchronization for testing a non-angular page:
browser.ignoreSynchronization = true;
You can set it before browser.get the non-angular page.
I've suggested setting a high implicit wait in the past, e.g.
browser.manage().timeouts().implicitlyWait(5000);
That hack allows to you avoid waitReady and keep using the standard
expect(searchBtnElm.isPresent()).toBeTruthy();
But has an ugly disadvantage when testing for elements NOT present, i.e. when testing for absent or non visible elements in which case it will wait 5 seconds (5000ms) in vane, e.g. when doing
expect(someNonExistingElm.isPresent()).toBeFalsy();
Figured this out. I simply added the code below, after the click method:
describe('Search', function() {
it('should click Search button and wait for results', function() {
browser.driver.findElement(by.id('search')).click();
dvr.wait(function() {
return dvr.isElementPresent(by.xpath(
'/html/body/div/div[4]/div/div[2]/div/div/div/span'));
}, 20000);
});
});
Another Neat approach is to use "Expected Conditions" inside browser.wait - something like this:
var EC = protractor.ExpectedConditions;
var search = element(by.id('search'))
browser.wait(EC.visibilityOf(search), 2000).then(function(){
search.click()
})
You can get more details here: https://angular.github.io/protractor/#/api?view=ExpectedConditions
In protractor there are two types terms for on the page. isPresent ask if the element is exists on the page. isDisplayed asks if the element is visible. If you are waiting for a page to load you need to wait for isDisplayed, but that will error if it is not present, so wait for isPresent first. I use a function to wait for an element.
function waitForElement(el, waitTimeoutMilliseconds){
return browser.wait(function() { return el.isPresent(); }, waitTimeoutMilliseconds)
.then(function(){
return browser.wait(function() { return el.isDisplayed(); }, waitTimeoutMilliseconds);
});
}
Then just call that function in your test.
describe('Search', function() {
it('should click Search button and wait for results', function() {
var el = element(by.id('search'));
waitForElement(el, 5000);
el.click();
});
});

Categories