Protractor login to gmail fails headless, and works when not headless - protractor

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.

Related

cucumber stepdefinition file is not recognized by the protractor cucumber config file

unable to execute the steps in the step definition file
i tried the solutions present in the google but none of them are answer my questions.
my Config file
//seleniumAddress: 'http://localhost:4444/wd/hub',
directConnect: true,
//specs: ['/Users/bharaniravisankar/Desktop/PRO/SPECS/TEST.JS'],
framework: 'custom',
// path relative to the current config file
frameworkPath: require.resolve('protractor-cucumber-framework'),
capabilities: {
'browserName': 'chrome'
},
specs: [
'/Users/bharaniravisankar/Desktop/PRO/Feature/test.feature'
],
cucumberOpts: {
require: '/Users/bharaniravisankar/Desktop/PRO/Feature/Step Definition/login.js',
//tags: false,
//format: 'pretty',
profile: false,
'no-source': true
}
};
my console log
? Given User navigate to Home Page
Undefined. Implement with the following snippet:
Given('User navigate to Home Page', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? When User enter email and other details
Undefined. Implement with the following snippet:
When('User enter email and other details', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? And click on Submit button
Undefined. Implement with the following snippet:
When('click on Submit button', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
? Then Login is created sucessfully
Undefined. Implement with the following snippet:
Then('Login is created sucessfully', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
✔ After # CONG.js/node_modules/protractor-cucumber-framework/lib/resultsCapturer.js:26
1 scenario (1 undefined)
4 steps (4 undefined)
Stepdefinition file
var { Given, When, Then } = require("cucumber");
module.exports = function () {
this.Given(/^User navigate to Home Page$/, async function () {
// this.When('User enter email and other details', function () {
//launch AWS portal
browser.get('https://portal.aws.amazon.com/billing/signup#/start');
//Maximize the browser window
browser.driver.manage().window().maximize();
//Check the title of the Webpage
expect(await browser.getTitle()).toEqual('AWS Console - Signup');
});
this.When(/^User enter email and other details$/, function () {
// this.When('User enter email and other details', function () {
element(by.model('credentials.email')).sendKeys('bharaniravi36#gmail.com')
element(by.model('credentials.password')).sendKeys('Feb%1992');
element(by.model('credentials.rePassword')).sendKeys('Feb%1992');
element(by.model('credentials.fullName')).sendKeys('bharaniravi85');
});
//this.When('click on Submit button', function () {
this.When(/^click on Submit button$/, function () {
element(by.xpath("(//input[#class='a-button-input'])[1]")).click();
});
this.Then(/^Login is created sucessfully$/, function () {
expect(browser.getTitle()).toEqual('AWS Console - Signup');
element(by.id('company')).sendKeys('POLI');
//click on country drop down
element(by.model("address.countryCode")).click();
element(by.xpath("//option[#label='American Samoa']")).click();
// add sleep to give a time for te options to reveal
browser.sleep(3000)
});
};
in my research i didnt miss anything in my setup and every thing is setup in my system and required dependencies are downloaded.
First of all try creating the step definition folder outside the feature folder to keep separation of features and step definitions and folder name should be without spaces like 'stepdefinition'.
Secondly, make sure all the feature steps should be implemented in the step definition file and the text of each step should be the same as mentioned in the features.
Try using this and let me know, if it works for you.

In protractor, I want the code to handle based on if OTP triggers and if not, I can login to the home page or any page and cont do the work

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

Protractor - Ignore Synchronisation flag

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');
});
});
},

How to wait the page to test is loaded in non angular site?

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...

Waiting for Ionic Loading dialogs with Protractor

There are similar questions (linked below) but none solves this problem. I'm writing Protractor tests for an Ionic Project. I need to execute tests at times when an Ionic Loading dialog appears and disappears.
I've created a repo with the bare bones of the app and the tests that need to be made. Solve this and you solve the problem (I describe the problem below): https://github.com/TmanTman/StackoverflowQ. Just adapt the path to your Chrome for your system in conf.js.
To simulate an asynchronous Ionic Loading dialog I just add this to the controller in a blank Ionic project:
$interval( function() {
$ionicLoading.show({
template: 'Async ionicLoading',
duration: 5000
});
}, 5000 , 1);
})
I need to get protractor to wait for the dialog to appear, do some tests, wait for the dialog to disappear, and then do some more tests. My latest attempt in my test file is:
it('should only test when ionicLoading appears', function() {
browser.wait(function(){
return element(by.css('.loading-container.visible.active')).isPresent();
}, 10000);
var ionicLoadingText = element(by.css('.loading-container.visible.active')).getText();
expect(ionicLoadingText).toEqual('Async IonicLoading');
})
it('should only test once ionicLoading disappears', function() {
browser.wait(function() {
var deferred = protractor.promise.defer();
var q = element(by.css('.loading-container.visible.active')).isPresent()
q.then( function (isPresent) {
deferred.fulfill(!isPresent);
});
return deferred.promise;
});
expect(1).toEqual(1);
})
I'm trying to avoid using synchronous sleep function, as my code is highly asynchronous. I've tried countless variations but I can't get it to work. Links I've used for info includes:
Protractor blocking wait
Asynchronous Testing with Protractor's ControlFlow
Protractor wait for isElementPresent and click on waited element fails
The problem is two-fold:
1) From what I can deduce, the duration property of the $ionicLoading method is implemented with a timeout function. Protractor does not work well with $timeout. So instead of using the duration property, the $ionicLoading dialog can be hidden with a $interval call (adapting the code from the question):
$interval( function() {
$ionicLoading.show({
template: 'Async IonicLoading'
});
$interval( function() {
$ionicLoading.hide();
}, 5000, 1)
}, 5000 , 1);
2) The code to detect the asynchronous change is the following:
it('should only test when ionicLoading appears', function() {
browser.wait(function() {
var deferred = protractor.promise.defer();
var q = element(by.css('.loading-container.visible.active')).isPresent()
q.then( function (isPresent) {
deferred.fulfill(isPresent);
});
return deferred.promise;
}, 10000);
var ionicLoadingText = element(by.css('.loading-container.visible.active')).getText();
expect(ionicLoadingText).toEqual('Async IonicLoading');
})
it('should only test once ionicLoading disappears', function() {
browser.wait(function() {
var deferred = protractor.promise.defer();
var q = element(by.css('.loading-container.visible.active')).isPresent()
q.then( function (isPresent) {
deferred.fulfill(!isPresent);
});
return deferred.promise;
}, 10000);
expect(1).toEqual(1);
})
Then both tests pass.