I have started doing a POC on Protractor as our e2e automation testing tool.
Our application is designed in angular which makes it a perfect fit.
However, I need to login via google which is a non-angular website and therefore at the start of my test I state
browser.ignoreSynchronization = true;
Then I go to
'https://accounts.google.com/ServiceLogin'
Enter my google credentials and click on signin
At this point I try to go to my application's URL, which is an angular application so I was hoping to turn
browser.ignoreSynchronization = false;
All the the above steps are part of a beforeEach so that I can login before each test
But when I turn ignoreSynchronization to false, all my tests start failing.
On the other hand, if I don't turn it to false, I am compelled to use a lot of browser.sleeps as Protractor is still treating it as a non-angular app and does not wait for angular to load fully
I have also tried to put the ignoreSynchronization = false in each individual test as opposed to beforeEach but even then all my tests start failing.
Below is my beforeEach code
browser.ignoreSynchronization = true;
browser.driver.manage().window().setSize(1280, 1024);
browser.get(googlelogin);
email.sendKeys('username');
next.click();
browser.wait(EC.visibilityOf(pwd), 5000);
pwd.sendKeys('pwd');
signin.click();
browser.ignoreSynchronization = false;
browser.driver.get(tdurl);
Few things to fix:
wait for the "click" to go through
use browser.get() on the Angular Page
Here are the modifications:
signin.click().then(function () {
browser.ignoreSynchronization = false;
browser.get(tdurl);
browser.waitForAngular(); // might not be necessary
});
You may also add a wait with an Expected Condition to wait for the login step to be completed - say, wait for a specific URL, or page title, or an element on the page.
Reconciliation_verifyExapanedDatainExpanedRow: function (HeaderName, texttobepresent) {
browser.waitForAngular().then(function () {
var EC = protractor.ExpectedConditions;
var columnHeaderActive = GUtils.$locatorXpath('//p-datatable//span[contains(text(),"' + HeaderName + '")]/..//span[#class="ui-cell-data"][contains(.,\'' + texttobepresent + '\')]');
browser.wait(EC.presenceOf(GUtils.$element(columnHeaderActive)), GUtils.shortDynamicWait()).then(function () {
console.log('PASS');
}, function (err) {
console.log('FAIL');
});
});
},
Related
I am new to coding and as well as to protractor.
In protractor, I want the code to handle based on if OTP triggers go and retrieve OTP and if not, login to the home page or any page and continue to do the actions in the home page. I was trying to do an if else check with
I tried as like below
browser.getcurrentUrl().toEqual().then function()
{
statements;
},
I don't think it works. Can someone help?
below is my code snippet.
Basically i was trying to check the url, if it contains specific texts in it, I dont want anything to perform further execution want to exit out of execution. If the url doesnt contain anything specified I want to proceed with further execution.
The if condition is working fine. but not the else part.
var HomePages = require('../Pages/HomePage.js');
var EC = protractor.ExpectedConditions;
describe(‘Check_url function’, function() {
browser.wait(EC.urlContains(’some url’),2000).then(result => {
if (result) {
console.log('Sorry!!!!!!!, Encountered PassCode Authentication Process.
Execution cant be proceed further');
} else {
HomePages.profile();
browser.driver.sleep(300);
}
});
});
//////////////////////////
HomePages.js -
'use strict';
module.exports = {
Homepage: {
usrname: element(by.className('profile-name')),
usricon: element(by.css('[title="profile"]')),
Cli_id: element(by.css('[title=“Client ID"]'))
},
profile: function() {
this.click_Profile();
},
click_Profile: function() {
var angular3 = this.Homepage;
angular3.usricon.click();
},
I have a routine that logs into gmail using Protractor, that is called from the middle of my script (which is why some things that appear unnecessary are there), but I have isolated it as much as I can. When I run it not headless it passes. When I run it headless, it fails. I looked at the related posts, and they did not seem to be Protractor specific, and they did seem to parallel my code here.
Here is the code:
const EC = ExpectedConditions;
beforeAll(function(){
});
beforeEach(function() {
//because I am using gmail after sending an email from an angular app with a link to get back into one
browser.waitForAngularEnabled(true);
browser.ignoreSynchronization = false;
});
afterEach(function() {
browser.waitForAngularEnabled(true);
browser.ignoreSynchronization = false;
});
var gmailLogin = function(){
browser.waitForAngularEnabled(false);//gmail screens not angular
browser.ignoreSynchronization = true;
browser.sleep(2000);//because ignore sync takes time to settle in
browser.driver.manage().timeouts().implicitlyWait(10000);//set in config, but seems to work only if here
browser.get("https://mail.google.com/mail");
browser.wait(EC.titleContains("Gmail"), 10000, "wait for gmail page");
$('[data-g-label="Sign in"]').click().then(
//this sometimes appears and sometimes is skipped, so ignore result
function(retval){},function(err){}
)
var variousInput = element(by.id('identifierId'));
browser.wait(EC.presenceOf(variousInput), 10000, "wait for identier ID prompt").then(
function(retVal){
var variousInput2 = browser.driver.findElement(by.id('identifierId'));
variousInput2.sendKeys("myemail here");
variousInput2=browser.driver.findElement(by.id("identifierNext"));
variousInput2.click();
variousInput2 = browser.driver.findElement(by.name('password'));
variousInput2.sendKeys('my password here');
variousInput2=browser.driver.findElement(by.id("passwordNext"));
variousInput2.click();
},
function(err){}//assume not found because cookie still around, proceed to next step
)
browser.wait(EC.titleContains("Inbox"), 10000, "wait for inbox");
}
describe('runs gmail test for so', function() {
it('tests gmail', function() {
gmailLogin();
expect(browser.getTitle()).toContain('Inbox');
}, 2 * 60 * 1000); //should always come up within 2 minutes
}); //end of describe
And here is the headed configuration file:
exports.config = {
directConnect: true,
allScriptsTimeout: 120000,
getPageTimeout: 60000,
// Capabilities to be passed to the webdriver instance.
capabilities: {
'browserName': 'chrome',
chromeOptions: {
//args: ["--headless","--disable-gpu","--no-sandbox"]
},
// Framework to use. Jasmine is recommended.
framework: 'jasmine',
// Spec patterns are relative to the current working directory when
// protractor is called.
specs: [
'./so.ts'
],
// Options to be passed to Jasmine.
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 180000
},
beforeLaunch: function() {
},
onPrepare() {
browser.manage().window().setSize(1600, 1000);
browser.driver.manage().timeouts().implicitlyWait(15000);
}
}
}
and here is the headless (you can see I threw the kitchen sink at the options).
exports.config = {
directConnect: true,
allScriptsTimeout: 60000,
getPageTimeout: 30000,
// Capabilities to be passed to the webdriver instance.
capabilities: {
'browserName': 'chrome',
chromeOptions: {
args: ["--headless","--disable-gpu","--window-size=1600,1000","--disable-infobars","--disable-extensions","--auth-server-whitelist","--remote-debugging-port=9222"]
},
// Framework to use. Jasmine is recommended.
framework: 'jasmine',
// Spec patterns are relative to the current working directory when
// protractor is called.
specs: [
'./so.ts'
],
// Options to be passed to Jasmine.
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 180000
},
beforeLaunch: function() {
},
onPrepare() {
// screen size set in chrome options
browser.driver.manage().timeouts().implicitlyWait(15000);
}
}
}
If there is some kind of underlying, undocumented wisdom about which locators work or do not work headless, I would love to know.
Thanks,
jk
SLIGHT UPDATE: I cleaned up the code to use only explicit waits and straight Protractor (which is how I had it originally before reading pieces on the web that were based in other languages). Here is the revised version, which still passes not headless and fails headless (I also removed the implicit wait setting in OnPrepare() and all but the first three chrome options when doing headless).
var gmailLogin = function() {
browser.waitForAngularEnabled(false); //gmail screens not angular
browser.ignoreSynchronization = true;
browser.sleep(2000); //because ignore sync takes time to settle in
browser.get("https://mail.google.com/mail");
browser.wait(EC.titleContains("Gmail"), 10000, "wait for gmail page");
$('[data-g-label="Sign in"]').click().then(
//this sometimes appears and sometimes is skipped, so ignore result
function(retval) {},
function(err) {}
);
var variousInput = element(by.id('identifierId'));
browser.wait(EC.presenceOf(variousInput), 10000, "wait for identifier ID prompt").then(
function(retVal) {
var variousInput2 = element(by.id('identifierId'));
variousInput2.sendKeys("email address");
variousInput2 = element(by.id("identifierNext"));
variousInput2.click();
variousInput2 = element(by.name('password'));
browser.wait(EC.presenceOf(variousInput2), 10000, "wait for password prompt");
browser.wait(EC.visibilityOf(variousInput2), 10000, "wait for password prompt");
variousInput2.sendKeys('my password');
variousInput2 = element(by.id("passwordNext"));
variousInput2.click();
},
function(err) {} //assume not found because cookie still around, proceed to next step
)
browser.wait(EC.titleContains("Inbox"), 10000, "wait for inbox");
}
BIGGER UPDATE: this may be something funky about headless after all. I added the following lines right before waiting for the identifier ID element(by.tagName('html')).getText().then(function(text){console.log(text);});
in not headless mode, that produced
Sign in
to continue to Gmail
Email or phone
Forgot email?
Not your computer? Use Guest mode to sign in privately.
Learn more
NEXT
Create account
English (United States)
HelpPrivacyTerms
in headless, it gave
One account. All of Google.
Sign in to continue to Gmail
Find my account
Create account
One Google Account for everything Google
About Google Privacy Terms Help
followed by a long list of languages from Afrikaans to 繁體中. So it seems almost as if in headless the browser has forgotten where it lives (at the very least the addition of One account all of Google and the languages says it is not apples to apples). It makes me wonder if then IdentifierId might also have a different name in such a case.
One last update for now:
To debug I added the following code when that first page loads:
var inputs=element.all(by.tagName('input'));
inputs.each(function(element,index){
element.getAttribute("Id").then(function(text){console.log('input '+index+' '+text);})
})
not headless, we get:
input 0 identifierId
input 1 null
input 2 ca
input 3 ct
input 4 pstMsg
input 5 checkConnection
input 6 checkedDomains
but headless we get:
input 0 null
input 1 null
input 2 null
input 3 null
input 4 null
input 5 null
input 6 null
input 7 null
input 8 null
input 9 null
input 10 null
input 11 profile-information
input 12 session-state
input 13 null
input 14 _utf8
input 15 bgresponse
input 16 Email
input 17 Passwd-hidden
input 18 next
So Protractor is right that it cannot find by ID identifierID. But how come?
FINAL:
So depending on headless or not, google was redirecting to two different urls with two different sets of Ids and names. I posted revised code that handles both in my answer.
Thanks for the guidance, all.
So it turns out that Google will redirect the mail service request to two different versions of its interface depending on whether one goes in headless or not. I rewrote the code to handle either one. I also tried to simplify where I could, including no more implicit waits and adding more chaining (I also dipped my toes into ES6 as encouraged by Oleksii's comment).
const EC = ExpectedConditions;
beforeAll(function(){
});
beforeEach(function() {
//because I am using gmail after sending an email from an angular app with a link to get back into one
browser.waitForAngularEnabled(true);
browser.ignoreSynchronization = false;
});
afterEach(function() {
browser.waitForAngularEnabled(true);
browser.ignoreSynchronization = false;
});
var waitForIds = (id1,id2)=>{//waits for one of two ids, assumes they must exist or else it is an error
var elm = element.all(by.xpath("//*[#id = '"+id1+"' or #id = '"+id2+"']")).first();
browser.wait(EC.presenceOf(elm), 30000, "wait for "+id1+" or "+ id2);
return elm;
}
var gmailLogin = () => {
browser.waitForAngularEnabled(false); //gmail screens not angular
browser.ignoreSynchronization = true;
browser.sleep(2000); //because ignore sync takes time to settle in
browser.get("https://accounts.google.com/ServiceLogin?service=mail");
browser.sleep(2000);
element(by.id('gbqfq')).isPresent().then((present) => {
//if present, we are already on the inbox screen, because we found the search pane
if (!present) { //still work to do to get there
browser.wait(EC.titleContains("Gmail"), 10000, "wait for a gmail page");
$('[data-g-label="Sign in"]').click().then(
//this sometimes appears and sometimes is skipped, so ignore result
(retval) => {}, (err) => {}
);
waitForIds('Email', 'identifierId').sendKeys("my email here");
waitForIds("identifierNext", "next").click();
waitForIds('Passwd', 'password').getAttribute('id').then((text) => {
element(by.name(text)).sendKeys('my password here');
waitForIds("signIn", "passwordNext").click();
})
}
})
browser.wait(EC.titleContains("Inbox"), 10000, "wait for inbox");
}
describe('runs gmail test for so', function() {
it('tests gmail', function() {
gmailLogin();
expect(browser.getTitle()).toContain('Inbox');
}, 2 * 60 * 1000); //should always come up within 2 minutes
}); //end of describe
UPDATE: I am accepting this because it addresses my question of what happens and why and how to address is directly. I totally accept that despite answering the question, there are better ways to go about what I actually wanted to do (getting the href from an email) either by catching the email on its way out or by using the gmail api.
I have to test a browser page that requires a login. Is there a way to keep the browser open between every it block so I don't need to login for every it block?
Thanks
Is there a way to keep the browser open between every it block so I don't need to login for every it block?
Yes: Provide the browser session, before starting protractor - it is called attach in Protractor lingo
Protractor will not close the browser session if you have started the browser session before starting Protractor.
I have created an NPM Package to start the browser easily. Let me know if this helps you.
https://github.com/andreasmarkussen/webdriver-reuse-session
Protractor closing browsers between different tests files is by design. However, there are a few ways to solve your problem.
1) Write all your tests in one file
Self explanatory, this will accomplish what you are trying to do, but only works as long as you aren't running tests in parallel. This will work for you, but I would recommend using the 2nd option. You are able to have multiple describes in the same test file which will help it be more readable if you go this route. Example test.js file:
describe('Testing login',function(){
it('Should login',function(){
//test here
});
});
describe('Testing foo',function(){
it('Should test foo here',function(){
//test here
});
it('Should test foo here as well',function(){
//test here
});
});
describe('Testing bar',function(){
it('Should test bar here',function(){
//test here
});
it('Should test bar here as well',function(){
//test here
});
});
2) Use a login function
This is what I personally use. I have a function that I call at the start of every test that logs in. The function accepts a Username and Password. While this doesn't keep the browser open, I personally think this is best way to go at this problem. Example:
Test1.js:
const helper = require util.js;
const util = new helper;
describe('Testing foo',function(){
beforeAll(function(){
util.login();
};
it('Should test foo here',function(){
//test here
});
it('Should test foo here as well',function(){
//test here
});
});
test2.js
const helper = require util.js;
const util = new helper;
describe('Testing bar',function(){
beforeAll(function(){
util.login();
};
it('Should test bar here',function(){
//test here
});
it('Should test bar here as well',function(){
//test here
});
});
util.js:
const helper = function(){
this.login = function(){
//do login here
}
}
Here is a useful stack post about reusing code, it goes more into depth about some syntax.
I have an angular application running inside iFrame. I must need to launch parent application URL as it provide some flag which makes angular app working as expected. Now I need to write protractor tests for angular app in iFrame.
Here is the code.
describe('French page', function() {
var IFRAME = "iframe",
TITLE_FR = 'Découverte automatique',
PAGE_URL = '/SAAS/admin/app/page',
pagePaths = browser.params.paths;;
beforeEach(function (done) {
LOGIN_PAGE.goToPageAndLogin().then(function (){
browser.driver.ignoreSynchronization = true;
browser.get(PAGE_URL); // application has angular app in iFrame
browser.sleep(5000);
browser.waitForAngular();
done();
});
});
afterEach(function (done) {
demoPause();
LOGIN_PAGE.logout().then(done);
});
it('should be able to launch with fr-FR locale', function (done) {
browser.driver.switchTo().frame(IFRAME); //Switch to angular app in iFrame
// Check if element text is in french
browser.driver.findElement(by.css('.app-menu li:nth-child(1) p')).then(function (elem) {
elem.getText().then(function (text) {
expect(text).toBe(TITLE_FR); // I can see that both texts are same here while debugging
browser.driver.ignoreSynchronization = true;
done();
});
});
});
});
The test condition passed but it exit with below error.
Message:
Failed: Angular could not be found on the page
https://host/abcd/admin/app/page
retries looking for angular exceeded
The issue got fixed by putting
browser.ignoreSynchronization = true;
before
browser.get(PAGE_URL);
Few things:
The parameter IFRAME passed into:
browser.driver.switchTo().frame(IFRAME); needs to be the ID
property of the element, not the tag name of the element, example:
your iframe element
<iframe id="myIframeId" name="frame3">...</iframe>
you would in this case do
browser.diver.switchTo().frame("myIframeId");
Don't call browser.waitForAngular(); on a non-angular page. Since only your iframe element is Angular, I suggest doing the following to make sure a specific element is present before continuing:
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.css("your_selector")).then(function(present) {
return present;
});
}, 20000);
This will wait for an element to be present for 20 seconds, regardless of the page being Angular or not and then continue.
After you switch to the iframe element, you should call browser.driver.ignoreSynchronization = false; to turn on Angular synchronization back on. Since your code inside your iframe is Angular.
I've tried this:
browser.wait(function () {
return browser.executeScript('return document.readyState==="complete" &&' +
' jQuery !== undefined && jQuery.active==0;').then(function (text) {
return text === true;
});
}, 30000);
If jQuery.active==0 then page is completely loaded. This should work for sites with JQuery and non angular pages.
However, I have many problems of instability to test for non angular sites.
How to fix this?
By default protractor waits until the page is loaded completely. If you are facing any error then it is because protractor is waiting for the default time to be completed, that you have specified in your conf.js file to wait until page loads. Change the value to wait a for longer time if you think your app is slow -
// How long to wait for a page to load.
getPageTimeout: 10000, //Increase this time to whatever you think is better
You can also increase the defaultTimeoutInterval to make protractor wait a little longer before the test fails -
jasmineNodeOpts: {
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 30000
},
If you want to wait for any particular element, then you can do so by using wait() function. Probably waiting for last element to load is the best way to test it. Here's how -
var EC = protractor.ExpectedConditions;
var lastElement = element(LOCATOR_OF_LAST_ELEMENT);
browser.wait(EC.visibilityOf(lastElement), 10000).then(function(){ //Alternatively change the visibilityOf to presenceOf to check for the element's presence only
//Perform operation on the last element
});
Hope it helps.
I use ExpectedConditions to wait for, and verify page loads. I walk through it a bit on my site, and example code on GitHub. Here's the gist...
Base Page: (gets extended by all page objects)
// wait for & verify correct page is loaded
this.at = function() {
var that = this;
return browser.wait(function() {
// call the page's pageLoaded method
return that.pageLoaded();
}, 5000);
};
// navigate to a page
this.to = function() {
browser.get(this.url, 5000);
// wait and verify we're on the expected page
return this.at();
};
...
Page Object:
var QsHomePage = function() {
this.url = 'http://qualityshepherd.com';
// pageLoaded uses Expected Conditions `and()`, that allows us to use
// any number of functions to wait for, and test we're on a given page
this.pageLoaded = this.and(
this.hasText($('h1.site-title'), 'Quality Shepherd')
...
};
QsHomePage.prototype = basePage; // extend basePage
module.exports = new QsHomePage();
The page object may contain a url (if direct access is possible), and a pageLoaded property that returns the ExepectedCondition function that we use to prove the page is loaded (and the right page).
Usage:
describe('Quality Shepherd blog', function() {
beforeEach(function() {
// go to page
qsHomePage.to();
});
it('home link should navigate home', function() {
qsHomePage.homeLink.click();
// wait and verify we're on expected page
expect(qsHomePage.at()).toBe(true);
});
});
Calling at() calls the ExpectedCondidion (which can be be an and() or an or(), etc...).
Hope this helps...