Working with protractor events using phantomjs\chrome - javascript

The following code works well when i'm running protractor using chrome browser. But when i'm changing browser to phantomjs, looks like he cant click at login button.
describe('Authentication capabilities', function() {
var siteUrl = 'http://localhost:5000/';
var loginURL = 'http://localhost:5000/views/account/login';
browser.get("http://localhost:5000/");
var username = element(by.model('credential.username'));
var password = element(by.model('credential.password'));
var loginButton = element(by.xpath('//form[1]/button[#type="submit"]'));
var error = element(by.xpath('//form[1]/div[1]'));
it('should redirect to the login page if trying to load protected page while not authenticated', function() {
browser.get(loginURL);
loginURL = browser.getCurrentUrl();
browser.get(siteUrl);
expect(browser.getCurrentUrl()).toEqual(loginURL);
});
it('should warn on missing/malformed credentials', function() {
username.clear();
password.clear();
password.sendKeys('test');
loginButton.click();
expect(error.getText()).toMatch('Missing credentials');
username.sendKeys('example');
password.clear();
loginButton.click();
expect(error.getText()).toMatch('Missing credentials');
username.clear();
username.sendKeys("admin");
password.sendKeys("someinvalidpassword");
loginButton.click();
expect(error.getText()).toMatch("Password not valid.");
username.sendKeys("admin2");
password.sendKeys("someinvalidpassword");
loginButton.click();
expect(error.getText()).toMatch("Incorrect username.");
});
it('should accept a valid email address and password', function() {
username.clear();
password.clear();
username.sendKeys('admin');
password.sendKeys('fubotv');
loginButton.click();
expect(element(by.binding('{{user.displayName}}')).getText()).toEqual("Administrator");
});
it('should return to the login page after logout', function() {
element(by.xpath('//li[#class="dropdown user-dropdown"]/a[1]')).click();
var logoutButton = element(by.xpath('//ul[#class="dropdown-menu"]/li[1]/a'));
logoutButton.click();
expect(browser.getCurrentUrl()).toEqual(loginURL);
});
});
Config for phantomjs:
// myConf.js
exports.config = {
seleniumAddress: 'http://localhost:9515',
capabilities: {
'browserName': 'phantom',
'phantomjs.binary.path':'./../../node_modules/phantomjs/bin/phantomjs',
'phantomjs.cli.args':['--logfile=PATH', '--loglevel=DEBUG']
},
specs: [
'e2e/**/*Test.js'
],
jasmineNodeOpts: {
onComplete: null,
isVerbose: false,
showColors: true,
includeStackTrace: false
}
}
i'm getting error:
Failures:
1) Authentication capabilities should accept a valid email address and password
Message:
Error: No element found using locator: by.binding("{{user.displayName}}")
2) Authentication capabilities should return to the login page after logout
Message:
Error: Error while waiting for Protractor to sync with the page: {"message":"Can't find variable: angular","line":4,"sourceId":140550656205136,"stack":"ReferenceError: Can't find variable: angular\n at :4\n at anonymous (:9)\n at Na (phantomjs://webpage.evaluate():14)\n at phantomjs://webpage.evaluate():15\n at phantomjs://webpage.evaluate():15\n at phantomjs://webpage.evaluate():16\n at phantomjs://webpage.evaluate():16\n at phantomjs://webpage.evaluate():16","stackArray":[{"sourceURL":"","line":4},{"function":"anonymous","sourceURL":"","line":9},{"function":"Na","sourceURL":"phantomjs://webpage.evaluate()","line":14},{"sourceURL":"phantomjs://webpage.evaluate()","line":15},{"sourceURL":"phantomjs://webpage.evaluate()","line":15},{"sourceURL":"phantomjs://webpage.evaluate()","line":16},{"sourceURL":"phantomjs://webpage.evaluate()","line":16},{"sourceURL":"phantomjs://webpage.evaluate()","line":16}],"name":"ReferenceError"}
Finished in 7.568 seconds
4 tests, 7 assertions, 2 failures
witch means it still at the login page, because this element {{user.displayName}} exists only at protected page.
when i'm running at chrome, it works as well.

Are you using twitter bootstrap on your site? It might be that phantomJS is being sized too small. I had the same issue until I did:
onPrepare() {
browser.driver.manage().window().setSize(1600, 800);
}

Related

Test failing only in headless chrome

I have some integration tests using wdio, they are all passing. However, when I run them in headless chrome, one of these tests fails. I get this error:
1) Reset password by submitting a new password "before all" hook:
element ("#identifierNext") still not existing after 15000ms
The problem is in this line of code:
browser.waitForExist('#identifierNext');
Which is weird, because I am using the waitForExist(<element id>) in other tests as well and they pass even in headless chrome. I also tried to increase the waitFor limit up to 30000ms, but it fails anyway.
This is my wdio config:
exports.config = {
specs: [
'./test/pageobjects/**/reset_password.spec.js'
],
maxInstances: 1,
capabilities: [{
maxInstances: 1,
browserName: 'chrome',
'goog:chromeOptions': {
args: ['--headless', '--disable-gpu', '--window-size=1280,800', '--no-sandbox', '--disable-dev-shm-usage']
}
}],
sync: true,
logLevel: 'silent',
coloredLogs: true,
deprecationWarnings: true,
bail: 0,
screenshotPath: './errorShots/',
baseUrl: 'http://127.0.0.1:3000',
waitforTimeout: 10000,
connectionRetryTimeout: 90000,
connectionRetryCount: 3,
services: ['selenium-standalone'],
framework: 'mocha',
reporters: ['spec'],
mochaOpts: {
ui: 'bdd',
timeout: 30000
},
}
When I remove headless from chromeOptions, this test passes normally. Any ideas why this happens?
EDIT: this is my reset_password.spec.js file:
describe ('Reset password by submitting a new password', function(){
//test fails in this before function
before(function(){
browser.url(ResetPassword.token_url(valid_email, email_password));
});
it ('Password reset without passwords', function(){
.
.
.
})
});
And my reset_password.page.js file:
const Page = require('./page');
class ResetPassword extends Page {
get email() {
return $('input[type="text"]');
}
get url() {
return browser.getUrl();
}
open() {
super.open('/reset-password');
}
get signIn(){
browser.waitForExist('*=Sign in');
return $('*=Sign in');
}
get enterEmail(){
browser.waitForExist('input[type="email"]');
return $('input[type="email"]');
}
get submitEmail(){
//this fails in headless mode
browser.waitForExist('#identifierNext');
return $('#identifierNext');
}
get enterPassword(){
browser.waitForExist('#password > div.aCsJod.oJeWuf > div > div.Xb9hP > input');
return $('#password > div.aCsJod.oJeWuf > div > div.Xb9hP > input');
}
get submitPassword(){
browser.waitForExist('#passwordNext');
return $('#passwordNext');
}
get tokenEmail(){
browser.waitForExist('span[email="profiq.ldap#gmail.com"]');
return $$('span[email="profiq.ldap#gmail.com"]');
}
get tokenURL(){
browser.waitForExist('a[id*=reset_link]');
const links = $$('a[id*=reset_link]');
return links[links.length-1].getAttribute('href');
}
token_url(email, password){
browser.url('https://www.google.com/gmail/about/#');
this.signIn.click();
browser.switchTab(browser.getTabIds()[1]);
this.enterEmail.setValue(email);
this.submitEmail.click();
this.enterPassword.setValue(password);
this.submitPassword.click();
this.tokenEmail[1].click();
browser.pause(3000);
return this.tokenURL;
}
}
module.exports = ResetPassword;
I found the problem. Gmail displays differently in different browsers. I took a screenshot using browser.saveScreenshot('screenshot.jpg'); just before the test fails, and it shows that the page looked different (was in my local language instead of english and had different appearance and buttons). So this is the reason why the test could not find the button with given identifier.

protractor, on login form sendkeys() are not working. Browser don't displays my value for the form fields

(I'm not using jQuery) Hi! I'm starting with protractor and cucumber and i'm testing a login page.
I'm trying to insert values on a form for the login, but when i lunch the configuration file to lunch the test, Chrome open the login page but don't displays the values that i setted for the required fields.
Clicking on sumbit button, the page should be redirect to another. Can anyone help me? How i have to change my step definition file to make it works? Thank you
I think that there are some problems like in configuration and others.
Here is step definition:
This is my new step definition file:
Given("I'm on site login page", function () {
//return pending;
browser.driver.get('https://www.websiteurl.com/login.html');
browser.sleep(20000);
});
When('I see the submit-button', function () {
// Write code here that turns the phrase above into concrete actions
//var sumbit = browser.executeScript('document.getElementById("submit-//wrap.cf.input-formFields")');
var sumbit = element(by.css('.submit-wrap.cf.input-formFields'));
expect(sumbit).to.not.be.null;
});
When('I filled all the mandatory fields', function () {
// Write code here that turns the phrase above into concrete actions
window.onload = function(){
var bookingNumberField = browser.executeScript('document.getElementById("variation2-bookingNumber").getText()');
var firstNameField = browser.executeScript('document.getElementById("variation2-firstName").getText()');
var lastNameField = browser.executeScript('document.getElementById("variation2-lastNameField").getText()');
var dobMonth = browser.executeScript('document.getElementById("month").getText()');
var dobDay = browser.executeScript('document.getElementById("day").getText()');
var dobYear = browser.executeScript('document.getElementById("year").getText()');
// var firstNameField = document.getElementById("variation2-firstName");
// var lastNameField = document.getElementById("variation2-lastName");
// var dobMonth = document.getElementById("month");
// var dobDay = document.getElementById("day");
// var dobYear = document.getElementById("year");
browser.sleep(1000);
//browser.waitForAngular();
//browser.executeScript to cut eventually
browser.executeScript.bookingNumberField.clear().sendKeys('WJXMHH');
browser.executeScript.firstNameField.clear().sendKeys('Mark');
browser.executeScript.lastNameField.clear().sendKeys('Altria');
element(by.cssContainingText('option', 'March')).click();
element(by.cssContainingText('option', '10')).click();
element(by.cssContainingText('option', '1991')).click();
TermsChkbxLabel = element(by.css("label[for='checkboxb']"));
TermsChkbxLabel.click();
}
}); //loginBtn.click();
Then('I am able to click on it to login', function () {
// var ptor = protractor.getInstance();
// ptor.ignoreSynchronization = true;
// var login_b = browser.executeScript('document.getElementById("login-button").click()');
//var login_b = document.getElementsByClassName("login-button").click();
// ptor.sleep(10000);
// var expectedUrl = ptor.getCurrentUrl();
// //expect(expectedUrl).toEqual('https://www.websiteurl.com/newpage.html');
});
Then('I am able to open the new page', function () {
var login_b = element(by.css('.login-button'));
login_b.click();
// var ptor = protractor.getInstance();
// ptor.ignoreSynchronization = true;
// var login_b = document.getElementsByClassName("login-button").click();
// ptor.sleep(10000);
// var expectedUrl = ptor.getCurrentUrl();
//
});
Here is the protractor conf.js:
var seleniumServer = require('selenium-server')
var nightwatchCucumber = require('nightwatch-cucumber')
require('babel-core/register');
var nightwatchCucumberConf = {
runner: 'nightwatch',
featureFiles: 'features',
stepDefinitions: 'features/step_definitions/testing_1_6.js',
closeSession: 'afterFeature'
};
exports.config = {
seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
getPageTimeout: 60000,
allScriptsTimeout: 500000,
framework: 'custom',
// path relative to the current config file
frameworkPath: require.resolve('protractor-cucumber-framework'),
plugins: [{
path: require.resolve('protractor-console'),
logLevels: ['severe']
}],
directConnect: true, //chrome only
jasmineNodeOpts: {},
onPrepare: function() {},
capabilities: {
'browserName': 'chrome'
},
// Spec patterns are relative to this directory.
specs: [
'features/*.feature'
],
baseURL: 'https://www.websiteurl.com/login.html',
cucumberOpts: {
//require: 'features/step_definitions/**/*.js',
require: 'features/step_definitions/testing_1_6.js',
tags: false,
// format: 'pretty',
profile: false,
'no-source': true
}
};
I have this error too:
[12:57:27] E/launcher - Error while waiting for Protractor to sync with the page: "both angularJS testability and angular testability are undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
[12:57:27] E/launcher - Error: Error while waiting for Protractor to sync with the page: "both angularJS testability and angular testability are undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping.
Step definition file testing_1_6.js:
// chai is an assertion library
var chai = require('chai');
// chai-as-promised is an helper to handle promise
var chaiAsPromised = require('chai-as-promised')
chai.use(chaiAsPromised);
expect = chai.expect;
Given("I'm on site login page", function() {
browser.driver.get('https://www.websiteurl.com/login.html');
return browser.sleep(20000);
});
When('I see the submit-button', function() {
var sumbitBtn = element(by.css('.submit-wrap.cf.input-formFields'));
return expect(sumbitBtn.isDisplayed()).to.eventually.be.true;
});
When('I filled all the mandatory fields', function() {
element(by.id('variation2-bookingNumber')).sendKeys('WJXMHH');
element(by.id('variation2-firstName')).sendKeys('Mark');
element(by.id('variation2-lastNameField')).sendKeys('Altria')
element(by.cssContainingText('#month > option', 'March')).click();
element(by.cssContainingText('#day > option', '10')).click();
element(by.cssContainingText('#year > option', '1991')).click();
return element(by.css("label[for='checkboxb']")).click();
});
Then('I am able to click on it to login', function() {
return element(by.id('login-button')).click();
});
Then('I am able to open the new page', function() {
var newPageUrl = 'https://www.websiteurl.com/newpage.html';
browser.sleep(10000);
return expect(browser.getCurrentUrl()).to.eventually.equal(newPageUrl);
});
protractor conf.js:
exports.config = {
seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
getPageTimeout: 60000,
allScriptsTimeout: 500000,
framework: 'custom',
frameworkPath: require.resolve('protractor-cucumber-framework'),
plugins: [{
path: require.resolve('protractor-console'),
logLevels: ['severe']
}],
directConnect: true, //chrome only
onPrepare: function() {
browser.ignoreSynchronization = true;
},
capabilities: {
browserName: 'chrome'
},
specs: [
'features/*.feature'
],
cucumberOpts: {
require: [
'features/step_definitions/testing_1_6.js'
]
}
};
browser.waitForAngularEnabled(false);

Protractor page objects error

I'm building my angularjs protractor e2e test to the page objects pattern. I'm facing trouble with converting my script in to page object.
Here is my conf.js
// conf.js
exports.config = {
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['employee.js']
}
Here is my employee.js
// spec.js
var EmpPageObject = require('./EmpPageObject.js');
describe('Protractor Demo App', function() {
it('should have a title', function() {
var empPageObject = new EmpPageObject();
empPageObject.get();
empPageObject.setName('mee');
empPageObject.setPassword('123');
});
});
Here is my EmpPageObject.js
var EmpPageObject = function() {
var nameInput = element(by.model('login.user_name'));
var passwordInput = element(by.model('login.password'));
var addButton = element(by.css('.btn'));
this.get = function() {
browser.get('http://');
};
this.setName = function(name) {
nameInput.sendKeys(name);
};
this.setPassword = function(password) {
passwordInput.sendKeys(password);
};
addButton.click();
};
But, my script fails giving the following error.
Failures:
1) Protractor Demo App should have a title
Message:
Failed: EmpPageObject is not defined
This may be a dumb question. But, I cannot find the error since this is my first test. :)
Look like you copy-paste code from here
https://github.com/angular/protractor/blob/f9c8a37f7dbec1dccec2dde0bd6884ad7ae3f5c7/docs/tutorial.md
describe('Protractor Demo App', function() {
it('should have a title', function() {
browser.get('http://juliemr.github.io/protractor-demo/');
expect(browser.getTitle()).toEqual('Super Calculator');
});
});
Here is protractor try to get resource and check - is it have title.
This function return true or false to make test. In your case, function return undefined, it is equal to false, test fail and you get error message.

Unable to retrieve email information in protractor

Referencing the information from the question Fetching values from email in protractor test case, I am still unable to reference the emails. In my test case, the 'expect' is not getting executed for some unknown reason to me.
Also if I use the line,
browser.controlFlow().await(getLastEmail()).then(...)
There is a 'browser.controlFlow(...).await is not a function error'
conf.js
var MailListener = require("mail-listener2")
exports.config = {
framework: 'jasmine2',
specs: ['./test.js'],
jasmineNodeOpts: { defaultTimeoutInterval: 360000 },
allScriptsTimeout: 60000,
onPrepare: function () {
var mailListener = new MailListener({
username: "username",
password: "password",
host: "imapPort",
port: 993, // imap port
secure: true,
tls: true,
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments
})
mailListener.start()
mailListener.on("server:connected", function(){
console.log("Mail listener initialized")
})
mailListener.on("error", function(err){
console.log(err)
})
mailListener.on("server:disconnected", function(){
console.log("imapDisconnected")
})
global.mailListener = mailListener
},
onCleanUp: function () {
mailListener.stop()
}
}
The test case:
describe('Email Testing', function () {
it('should login with a registration code sent to an email', function () {
//this line causes a 'browser.controlFlow(...).await is not a function' error
// browser.controlFlow().await(getLastEmail()).then(function (email) {
getLastEmail().then(function (email) {
// The expect does not get executed as it should fail
expect(email.subject).toEqual('My Subject')
})
})
})
function getLastEmail () {
var deferred = protractor.promise.defer()
console.log('Waiting for an email...')
mailListener.on('mail', function (mail) {
console.log('No Console Log Here!')
deferred.fulfill(mail)
})
return deferred.promise
}
I am not certain what I am missing in my test case to be able to read the subject or body of the email?
Ran into the same issue today. Turns out the API for the webdriver and ControlFlow has been updated and await has been changed to wait. Yeah, subtle difference. See the new API reference here: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/promise_exports_ControlFlow.html
To schedule a task for the wait condition, change your code to look like this:
browser.controlFlow().wait(getLastEmail()).then(...)
you would basically have to wrap that asynchronous code inside of a promise and the pass that promise/function into the flow.execute()
var flow = protractor.promise.controlFlow();
flow.execute( getLastEmail() ).then(function(email){
text = email.text
});

Fetching values from email in protractor test case

I need to test a protractor test case in which a user signs up, receives an email, goes to the link provided in the email and fills up his/her details in activation signup form.
The problem is how can I get the redeem token from the email. My email has a link to the activation page which has the auth token like following:
http://127.0.0.1:3000/#/signup/redeem/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJlOTRhYzY3MC1kYTNlLTQyYTUtODVkZS02NDU4ZjVmZGMwYjAiLCJzdWIiOiJ0ZXN0QGNvZWYuY28iLCJpYXQiOjE0Mjc0OTM5MDMsImV4cCI6MTQyODA5ODcwM30.
But how do I fetch that token so that I can build the url or how can I click that button in my email so that I can complete the flow ? I am using mailcatcher to simulate email.
This is something I've solved recently. Hope the solution would also apply for your use-case.
Prerequisites:
mail-listener2 package
understanding of the concept of promises
Step by step instructions:
Install mail-listener2:
npm install mail-listener2 --save-dev
In your protractor config initialize Mail Listener and make it available globally:
onPrepare: function () {
var MailListener = require("mail-listener2");
// here goes your email connection configuration
var mailListener = new MailListener({
username: "imap-username",
password: "imap-password",
host: "imap-host",
port: 993, // imap port
tls: true,
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments
});
mailListener.start();
mailListener.on("server:connected", function(){
console.log("Mail listener initialized");
});
global.mailListener = mailListener;
}),
onCleanUp: function () {
mailListener.stop();
},
Create a helper getLastEmail() function which would wait for an email to be retrieved:
function getLastEmail() {
var deferred = protractor.promise.defer();
console.log("Waiting for an email...");
mailListener.on("mail", function(mail){
deferred.fulfill(mail);
});
return deferred.promise;
};
Example test case:
describe("Sample test case", function () {
beforeEach(function () {
browser.get("/#login");
browser.waitForAngular();
});
it("should login with a registration code sent to an email", function () {
element(by.id("username")).sendKeys("MyUserName");
element(by.id("password")).sendKeys("MyPassword");
element(by.id("loginButton")).click();
browser.controlFlow().await(getLastEmail()).then(function (email) {
expect(email.subject).toEqual("New Registration Code");
expect(email.headers.to).toEqual("myemail#email.com");
// extract registration code from the email message
var pattern = /Registration code is: (\w+)/g;
var regCode = pattern.exec(email.text)[1];
console.log(regCode);
});
});
});
The solution I implemented was using mailcatcher API, if you scroll down a bit you'll find the following about the API:
A fairly RESTful URL schema means you can download a list of messages
in JSON from /messages, each message's metadata with
/messages/:id.json, and then the pertinent parts with
/messages/:id.html and /messages/:id.plain for the default HTML and
plain text version, /messages/:id/:cid for individual attachments by
CID, or the whole message with /messages/:id.source.
So we first fetched the whole json response, parse it and fetch the latest email id:
// Returns the last email id
function(emails, user) {
var email, recipient;
for(var i = emails.length - 1; i >= 0; i--) {
email = emails[i];
for(var j = 0; j < email.recipients.length ; j++) {
recipient = email.recipients[j];
if(recipient == "<"+user+">") {
return email.id;
}
}
}
};
using that email id we can get the body of the email by hitting /messages/:id.plain(of course there are more variants like fetching the email source code or email rendered html, we only needed the message) then we can just parse the body to fetch what we want, following is the code:
browser.driver.get(mailcatcherUrl+"/messages");
browser.driver.findElement(by.tagName('body')).getText().then(function(response) {
var emails, lastEmailId, partialTokens ;
emails = JSON.parse(response);
lastEmailId = getLastEmailId(emails, user);
browser.driver.get(mailcatcherUrl+"/messages/"+lastEmailId+".plain");
browser.driver.findElement(by.tagName('body')).getText().then(function(lastEmail) {
// use latestEmail to get what you want.
});
});
And Cheers!
I had to do the same thing but the mail testing server we were using did not have imap support.
So in case anyone runs into the same issue, I achieved a similar solution as alecxe using mailpop3 npm library.
The thing with the pop3 client, however, was that it doesn't act as a listener so we had to define a helper function that would connect, login and fetch the latest email when we needed to test the latest email.
Something like this:
function getLastEmail() {
var deferred = protractor.promise.defer();
var POP3Client = require("mailpop3");
var client = new POP3Client(port, host, {
tlserrs: false,
enabletls: true,
debug: false
});
client.on("connect", function() {
console.log("CONNECT success");
client.login(username, password);
});
client.on("login", function(status, rawdata) {
if (status) {
console.log("LOGIN/PASS success");
client.retr(1);
} else {
console.log("LOGIN/PASS failed");
client.quit();
}
});
client.on("retr", function(status, msgnumber, data, rawdata) {
if (status === true) {
console.log("RETR success for msgnumber " + msgnumber);
deferred.fulfill(data);
} else {
console.log("RETR failed for msgnumber " + msgnumber);
}
client.quit();
});
return deferred.promise;
}

Categories

Resources