I am automating an angular js website which has a login functionality. All I want to Click on sign in link and enter username and password. But Somehow my script are executing really fast than the page load. Please advice me on how I can handle this:
My Login Page object is:
'use strict'
// Normal Login
require('../page-objects/loginPage.js');
var customerPortalHome = function () {
this.clickSignIn = function () {
browser.sleep(5000);
element(by.linkText('Sign In')).click();
browser.waitForAngular();
browser.sleep(2000);
return require('./loginPage.js');
}
}
module.exports = new customerPortalHome();
My Test Spec is;
var co = require('co');
var UI = require('./ui.js');
var ui = new UI();
var CustomerPage = require('../page-objects/customerPortalHome.js')
describe(" Smoke Test Login to the application", function () {
it("test", co.wrap(function* () {
var EC = protractor.ExpectedConditions;
browser.get(ui.createStartLink());
expect(browser.getTitle()).toContain("Portal");
// Verify if user is able to Login into the application
var loginPage = CustomerPage.clickSignIn();
loginPage.switchToFrame('account-sdk');
var reportPage = loginPage.clickLogin('$$$$$#gmail.com', '$$$$$');
expect(browser.getCurrentUrl()).toContain('reports');
reportPage.clickSignOut();
expect(browser.getCurrentUrl()).toContain("?signout");
browser.sleep(800);
}));
});
Whenever I execute the test The browser opens for a sec and then closes.
My Onprepare method looks like this:
beforeLaunch: function () {
return new Promise(function (resolve) {
reporter.beforeLaunch(resolve);
});
},
onPrepare: function () {
browser.manage().timeouts().implicitlyWait(5000);
afterEach(co.wrap(function* () {
var remote = require('protractor/node_modules/selenium-webdriver/remote');
browser.driver.setFileDetector(new remote.FileDetector());
}));
Using browser sleep is never a good idea the best thing to do is to wait for an element and use the then function to do so.
element(by.xpath("xpath")).click().then(function(){
var list = element(by.id('id'));
var until = protractor.ExpectedConditions;
browser.wait(until.presenceOf(list), 80000, 'Message: took too long');
});
browser.wait(protractor.ExpectedConditions.visibilityOf($$('.desk-sidebar > li').get(num-1)), 60000);
Usually,I use this wait.
Are you using an ignoreSynchronisation without putting the false somewhere in your page objects helpers?
Be careful, the login can sometimes break the waitForAngular when there are a lot of redirections. I ended up using a dirty sleep to wait the page to be loaded when logging (no other solution were found, ignoreSync, EC for a change of url and wait for an element were not working solutions).
You should also share the error you get.
Related
I'm trying to get page objects to work for Protractor and Jasmine, and I'm getting "Failed: Cannot read property 'sendKeys' of undefined" when I try to use page objects.
I have read similar posts but they do not provide any answers for me or the situation is too different to be of any help to me.
I've checked every character of the two files. It appears that the test cannot see the page object file for some reason. BTW Protractor is installed Globally so I don't need to include it in the script.
Everything was working prior to converting to using the "test_page.js" file instead of the hard-coded values.
This is from a tutorial where his code was working fine.
/superhero-tests/test/test_spec.js file
//require() is a notjs function - https://nodejs.org/en/knowledge/getting-started/what-is-require/
const TestPage = require("../page-objects/test.page")
//This file runs the browser and executes the script.
describe('Super Hero Page',function(){
var testPage
//Jasmine: Before each test case, do something
beforeEach(function(){
testPage = new TestPage()
//Protractor: For non-angular sites
browser.ignoreSynchronization = true
//Open URL
browser.get('file:///C:/projects/Protractor/Superhero/index.html')
})
//Jasmine: After each test case, do something
afterEach(function(){
//Timer to prevent immediate closure of the window
//browser.sleep(5000)
})
it('should load the correct URL',function(){
//Enter text into form fields
testPage.emailFld.sendKeys('a#b.com')
testPage.passwordFld.sendKeys('Test.123')
//Check some text in an element
expect(testPage.loginTitleTxt.getText()).toEqual('Welcome. Please Log In.')
//Check a HTML element's attribute
expect( testPage.emailFld.getAttribute('value')).toEqual('a#b.com')
//Non-precise matching - don't use with an empty string, will pass
//expect( testPage.loginTitleTxt.getText() ).toContain('Welcome.')
})
})
/superhero-tests/page-objects/test_page.js file
var TestPage = function(){}
TestPage.prototype = Object.create({},{
emailFld: { get: function(){ return element( by.id('loginEmail') ) } }, //this is a page object
passswordFld: { get: function(){ return element( by.id('loginPassword') ) } }, //this is a page object
loginTitleTxt: { get: function(){ return element( by.id('login-title') ) } } //this is a page object
})
module.exports = TestPage
I don't know what kind of tutorial would overcomplicate the logic so much. Try this
/superhero-tests/page-objects/test_page.js file
module.exports = function() {
this.emailFld = element( by.id('loginEmail') )
this.passswordFld = element( by.id('loginPassword') )
this.loginTitleTxt = element( by.id('login-title') )
}
Also keep in mind, the tutorial teaches you to outdated syntax of protractor 3 or 4, which is no longer used since protractor 7 was released a few years ago
In the end I found alternative Page Object code which works (tutorial linked below):
The page object:
var TestPage = (function () {
function TestPage() {
this.emailFld = element( by.id('loginEmail') )
this.passswordFld = element( by.id('loginPassword') )
this.loginTitleTxt = element( by.id('login-title') )
}
return TestPage;
})();
module.exports = TestPage;
The spec(test) file:
//Test for Login page
var TestPage = require("../page-objects/test.page")
//Appears in command line
describe('Login page tests', function(){
var test = new TestPage();
//Executed before the tests
beforeEach(function(){
//Protractor: For non-angular sites
browser.ignoreSynchronization = true
//Open URL
browser.get('file:///C:/projects/Protractor/Superhero/index.html')
})
//Executed after the tests
afterEach(function(){
//Timer so we can see what is going on
//browser.sleep(5000)
})
//The test statements themselves
it('should display all the Login page elements',function(){
//With page object
expect( test.emailFld.getAttribute('placeholder') ).toEqual('Enter email')
})
})
It seems to make better use of the initial function and then do the module.exports at the end. The only other difference I can see is that they used var not const in the test file, but I don't know this would change much.
https://teamgaslight.com/blog/getting-started-with-protractor-and-page-objects-for-angularjs-e2e-testing
I'm using node-typekit to create a new Typekit empty font set in my Yeoman generator for use in my project. I am able to successfully create the kit, but cannot figure out how to return the kit id value back to the Yeoman generator so I can add the necessary Typekit script tag values to my web pages. Here is the part of my index.js generator script in question:
At the top of index.js:
var kit = require('typekit');
var typekitID = '';
function setTypekitID(theid) {
typekitID = theid;
};
And the app section:
app: function () {
var token = 'xxxxxxxxxxxxxxxxxxxxxxxx';
var split = this.domainname.split('.');
split.pop();
var localdomain = split.join('.') + '.dev';
kit.create(token, {
name: this.appname,
badge: false,
domains: [this.domainname, localdomain],
families: []
}, function (err, data) {
setTypekitID(data.kit.id);
});
}
If instead of:
setTypekitID(data.kit.id);
I use:
console.log(data.kit.id);
The correct kit ID is displayed in the console. However, I can't quite figure out how to pass the data.kit.id value in the callback back to the generator for further use. Given the current code above, it comes back as "undefined".
Any ideas? Thanks!
Without any experience with typekit, I would guess that kit.create is an asynchronous call. When you make such a call, the generator needs to know about it, so it can delay invocation of further methods until it knows your asynchronous callback has had a chance to execute.
Try doing this:
app: function () {
var done = this.async(); // this tells the generator, "hang on, yo."
// ...
kit.create(token, { /* ... */ }, function (err, data) {
setTypekitID(data.kit.id);
done(); // calling this resumes the generator.
});
}
An example of how the default generator-generator uses this can be found here.
I am trying to initialize a Breeze manager inside a 'Web Worker'.
RequireJs, knockout, q, breeze are being imported inside the worker.
After a call to:EntityQuery.from('name').using(manager).execute(),
the following error appears:
Uncaught Error: Q is undefined. Are you missing Q.js? See https://github.com/kriskowal/q.
A live preview is uploaded here http://plnkr.co/edit/meXjKa?p=preview
(plunk supports downloading for easier debug).
EDIT -- relevant code
Worker.js
importScripts('knockout.js', 'q.js', 'breeze.js', 'require.js');
define('jquery', function () { return jQuery; });
define('knockout', ko);
define('q', Q); //Just trying to assign q since breeze requests Q as q
require(function () {
var self = this;
this.q = this.Q; //Just trying to assign q since breeze requests Q as q
breeze.NamingConvention.camelCase.setAsDefault();
var manager = new breeze.EntityManager("breeze/Breeze");
var EntityQuery = breeze.EntityQuery;
// Q or q here is defined (TESTED)
var test = function (name) {
return EntityQuery.from(name)
.using(manager).execute() // <-- Here q/Q breaks (I think on execute)
};
var primeData = function () {
return test('Languages')
.then(test('Lala'))
.then(test('Lala2'))
};
primeData();
setTimeout(function () { postMessage("TestMan"); }, 500);
});
Worker will be initialized on main page as:
var myWorker = new Worker("worker.js");
Ok here it goes:
Create a new requireJs and edit the
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document)
to
isBrowser = false
Create a new Jquery so it uses nothing related to window and generally anything that a WebWorker cannot access. Unfortunatelly i can't remember where i got this Custom JQueryJs but i have uploaded it here "https://dl.dropboxusercontent.com/u/48132252/jqueydemo.js".
Please if you find the author or the original change link and give credit.
My workerJs file looks like:
importScripts('Scripts/test.js', 'Scripts/jqueydemo.js', 'Scripts/q.js', 'Scripts/breeze.debug.js', 'Scripts/require2.js');
define('jquery', function () { return jQuery; });
require(
{
baseUrl: "..",
},
function () {
var manager = new breeze.EntityManager("breeze/Breeze");
var EntityQuery = breeze.EntityQuery;
var primeData = function () {
return EntityQuery.from(name)
.using(manager).execute() // Get my Data
.then(function (data) {
console.log("fetced!\n" + ((new Date()).getTime()));
var exportData = manager.exportEntities(); // Export my constructed entities
console.log("created!\n" + ((new Date()).getTime()));
var lala = JSON.stringify(exportData)
postMessage(lala); // Send them as a string to the main thread
})
};
primeData();
});
Finally on my mainJs i have something like:
this.testWorker = function () {
var myWorker = new Worker("worker.js"); // Init Worker
myWorker.onmessage = function (oEvent) { // On worker job finished
toastr.success('Worker finished and returned');
var lala = JSON.parse(oEvent.data); // Reverse string to JSON
manager.importEntities(lala); // Import the pre-Constructed Entities to breezeManager
toastr.success('Import done');
myWorker.terminate();
};
};
So we have managed to use breeze on a WebWorker enviroment to fetch and create all of our entities, pass our exported entities to our main breeze manager on the main thread(import).
I have tested this with 9 tables fully related to each other and about 4MB of raw data.
PROFIT: UI stays fully responsive all the time.
No more long execution script, application not responding or out of memory errors) at least for chrome
*As it makes sense breeze import entities is way more faster than the creation a full 4MB raw data plus the association process following for these entities.
By having all the heavy work done on the back, and only use import entities on the front, breeze allows you to handle large datasets 'like a breeze'.
I'm new on protractor, and I'm trying to implement an e2e test.
I don't know if this is the right way to do this, but...
The page that I want to test is not a full angular page based, so... I'm having some trouble.
On my first spec I have:
describe('should open contact page', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
var Login = require('./util/Login');
new Login(ptor);
});
I have created this Login class, but after login I want to open the contact page, but protractor immediately try to find element before the page is fully loaded.
I've tried to use:
browser.driver.wait(function() {
expect(browser.findElement(by.xpath("//a[#href='#/contacts']")).isDisplayed());
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
But it doesn't work... it always try to find the element before the page loads.
I tried this one too:
browser.driver.wait(function() {
expect(ptor.isElementPresent(by.xpath("//a[#href='#/contacts']")));
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
I'm able to do that using browser.sleep(); but I don't think that is a good option. Any idea? On my login class I have:
ptor.ignoreSynchronization = true;
How can I wait for this #href='#/contacts before protractor tries to click on it?
Protractor 1.7.0 has also introduced a new feature: 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();
See also:
Expected conditions in protractor
I finally find out...
var waitLoading = by.css('#loading.loader-state-hidden');
browser.wait(function() {
return ptor.isElementPresent(waitLoading);
}, 8000);
expect(ptor.isElementPresent(waitLoading)).toBeTruthy();
var openContact = by.xpath("//a[#href='#/contacts']");
element(openContact).click();
With this protractor could wait for that element until it loading page disappears.
Thanks for those who tried to help XD.
I had the same problem you were having for the longest time while using protractor. In my e2e test I start in a non angular app, then get into an angular portion, then get back out to a non angular portion. Made things tricky. The key is to understand promises and how they work. Here's some examples of my real world code in a functioning e2e test. Hoping this gives you an idea of how to structure your tests. Probably some bad practice in this code, please feel free to improve upon this, but I know that it works, maybe not the best way.
To get to angular I use
var ptor;
var events = require('events');
var eventEmitter = new events.EventEmitter();
var secondClick = require('./second-click');
beforeEach(function () {
browser.driver.get('http://localhost:8080/');
},10000);
it("should start the test", function () {
describe("starting", function () {
it("should find the link and start the test", function(){
var elementToFind = by.linkText('Start'); //what element we are looking for
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
expect(isPresent).toBe(true); //the test, kind of redundant but it helps pass or fail
browser.driver.findElement(elementToFind).then(function(start){
start.click().then(function(){ //once we've found the element and its on the page click it!! :)
ptor = protractor.getInstance(); //pass down protractor and the events to other files so we can emit events
secondClick(eventEmitter, ptor); //this is your callback to keep going on to other actions or test in another file
});
});
});
});
});
},60000);
While in angular this code works
describe("type in a message ", function(){
it("should find and type in a random message", function(){
var elementToFind = by.css('form textarea.limited');
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
element(elementToFind).sendKeys(randomSentence).then(function(){
console.log("typed in random message");
continueOn();
});
});
});
},15000);
After exiting angular
browser.driver.wait(function(){
console.log("polling for a firstName to appear");
return browser.driver.isElementPresent(by.name('firstName')).then(function(el){
return el === true;
});
}).
then(function(){
somefunctionToExecute()
});
Hope that gives some guidance and helps you out!
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.xpath("//a[#href='#/contacts']"));
});
This works for me too (without the timeout param)..
for more information, see http://angular.github.io/protractor/#/api?view=webdriver.WebDriver.prototype.wait
Thanks to answers above, this was my simplified and updated usage
function waitFor (selector) {
return browser.wait(function () {
return browser.isElementPresent(by.css(selector));
}, 50000);
}
Have you tried putting the ng-app in the <html> tag (assuming this part of code is under your control)? This solved a lot of initialization timing problems for me.
Best way to use wait conditions in protractor that helps to show proper error message to particular element if test case failed
const EC = ExpectedConditions;
const ele = element(by.xpath(your xpath));
return browser.wait(EC.visibilityOf(ele),9000,'element not found').then(() => {
ele.click();
});
I'm surprised that nobody has added this solution. Basically, if you are using modal dialogues you often get an element visible and available to click but not being clickable due to the modal dialogue being in front of it. This happens because protractor moves faster than angular and is ready to click the next element while angular is still closing the modal.
I suggest using
public async clickElementBug(elementLocator: Locator) {
const elem = await element(elementLocator);
await browser.wait(
async function() {
try {
await elem.click();
return true;
} catch (error) {
return false;
}
},
this.TIMEOUT_MILLIS,
'Clicking of element failed: ' + elem
);
}
browser.wait may sound too ordinary, but it's not!
browser.wait is the way to go. Just pass a function to it that would have a condition which to wait for. For example wait until there is no loading animation on the page
let $animation = $$('.loading');
await browser.wait(
async () => (await animation.count()) === 0, // function; if returns true it stops waiting; can wait for anything in the world if you get creative with it
5000, // timeout
`message on timeout` // comment on error
);
Make sure to use await
You can also use existing library called ExpectedConditions that has lots of predefined conditions to wait for
You can't imagine what you can do with it...
A few of my favorite ones:
wait until the number of browser's tab's is 2
// wait until the number of browser's tab's is 2
await browser.wait(
async () => {
let tabCount = await browser.getAllWindowHandles();
return tabCount.length === 2;
},
5000,
'the url didnt open in a new window'
);
wait until the loading animation is gone for at last 750ms
// wait until the loading animation is gone for at last 750ms
await browser.wait(
async () => (await this.$$loadAnimations.count()) === 0 && !(await browser.sleep(750)) && (await this.$$loadAnimations.count()) === 0,
5000,
`waiting timeout`
);
wait for ANY number of elements to be present
// wait for any number of elements to be present
async waitForElements($elem, timeout = 120000, start = +new Date()) {
let conditions = [];
for (let i = 0; i < $elem.length; i++) {
conditions.push(ExpectedConditions.presenceOf($elem[i]));
}
await browser.wait(
ExpectedConditions.and(...conditions),
remainingTimeout(timeout, start),
`wait for all elements`
);
}
// and use
await waitForElements([
$usernameField,
$passwordFiend,
$submitButton
])
My goal is to execute PhantomJS by using:
// adding $op and $er for debugging purposes
exec('phantomjs script.js', $op, $er);
print_r($op);
echo $er;
And then inside script.js, I plan to use multiple page.open() to capture screenshots of different pages such as:
var url = 'some dynamic url goes here';
page = require('webpage').create();
page.open(url, function (status) {
console.log('opening page 1');
page.render('./slide1.png');
});
page = require('webpage').create();
page.open(url, function (status) {
console.log('opening page 2');
page.render('./slide2.png');
});
page = require('webpage').create();
page.open(url, function (status) {
console.log('opening page 3');
page.render('./slide3.png');
phantom.exit(); //<-- Exiting phantomJS only after opening all 3 pages
});
On running exec, I get the following output on page:
Array ( [0] => opening page 3 ) 0
As a result I only get the screenshot of the 3rd page. I'm not sure why PhantomJS is skipping the first and second blocks of code (evident from the missing console.log() messages that were supposed to be output from 1st and 2nd block) and only executing the third block of code.
The problem is that the second page.open is being invoked before the first one finishes, which can cause multiple problems. You want logic roughly like the following (assuming the filenames are given as command line arguments):
function handle_page(file){
page.open(file,function(){
...
page.evaluate(function(){
...do stuff...
});
page.render(...);
setTimeout(next_page,100);
});
}
function next_page(){
var file=args.shift();
if(!file){phantom.exit(0);}
handle_page(file);
}
next_page();
Right, it's recursive. This ensures that the processing of the function passed to page.open finishes, with a little 100ms grace period, before you go to the next file.
By the way, you don't need to keep repeating
page = require('webpage').create();
I've tried the accepted answer suggestions, but it doesn't work (at least not for v2.1.1).
To be accurate the accepted answer worked some of the time, but I still experienced sporadic failed page.open() calls, about 90% of the time on specific data sets.
The simplest answer I found is to instantiate a new page module for each url.
// first page
var urlA = "http://first/url"
var pageA = require('webpage').create()
pageA.open(urlA, function(status){
if (status){
setTimeout(openPageB, 100) // open second page call
} else{
phantom.exit(1)
}
})
// second page
var urlB = "http://second/url"
var pageB = require('webpage').create()
function openPageB(){
pageB.open(urlB, function(){
// ...
// ...
})
}
The following from the page module api documentation on the close method says:
close() {void}
Close the page and releases the memory heap associated with it. Do not use the page instance after calling this.
Due to some technical limitations, the web page object might not be completely garbage collected. This is often encountered when the same object is used over and over again. Calling this function may stop the increasing heap allocation.
Basically after I tested the close() method I decided using the same web page instance for different open() calls is too unreliable and it needed to be said.
You can use recursion:
var page = require('webpage').create();
// the urls to navigate to
var urls = [
'http://phantomjs.org/',
'https://twitter.com/sidanmor',
'https://github.com/sidanmor'
];
var i = 0;
// the recursion function
var genericCallback = function () {
return function (status) {
console.log("URL: " + urls[i]);
console.log("Status: " + status);
// exit if there was a problem with the navigation
if (!status || status === 'fail') phantom.exit();
i++;
if (status === "success") {
//-- YOUR STUFF HERE ----------------------
// do your stuff here... I'm taking a picture of the page
page.render('example' + i + '.png');
//-----------------------------------------
if (i < urls.length) {
// navigate to the next url and the callback is this function (recursion)
page.open(urls[i], genericCallback());
} else {
// try navigate to the next url (it is undefined because it is the last element) so the callback is exit
page.open(urls[i], function () {
phantom.exit();
});
}
}
};
};
// start from the first url
page.open(urls[i], genericCallback());
Using Queued Processes, sample:
var page = require('webpage').create();
// Queue Class Helper
var Queue = function() {
this._tasks = [];
};
Queue.prototype.add = function(fn, scope) {
this._tasks.push({fn: fn,scope: scope});
return this;
};
Queue.prototype.process = function() {
var proxy, self = this;
task = this._tasks.shift();
if(!task) {return;}
proxy = {end: function() {self.process();}};
task.fn.call(task.scope, proxy);
return this;
};
Queue.prototype.clear = function() {
this._tasks = []; return this;
};
// Init pages .....
var q = new Queue();
q.add(function(proxy) {
page.open(url1, function() {
// page.evaluate
proxy.end();
});
});
q.add(function(proxy) {
page.open(url2, function() {
// page.evaluate
proxy.end();
});
});
q.add(function(proxy) {
page.open(urln, function() {
// page.evaluate
proxy.end();
});
});
// .....
q.add(function(proxy) {
phantom.exit()
proxy.end();
});
q.process();
I hope this is useful, regards.