Protractor Tips
Avoiding Sleep, Modules, params and much more
Jason Mavandi
Data Files
You should always start with a data file variable parameters in JSON (or something else if you prefer) .
{
"int":{
"url":"https://int.example.com",
"username":"un",
"password":"pw!"
}
}data.json
Things to store:
- credentials per environments
- smoke test details
Sleep
When you do not know how long it will take for a page to load, sleep is a quick way to get it working.
DO NOT USE SLEEP!
It does not ensure anything but long tests.
//Sleeps are given in microseconds
//Protractor sleep
browser.sleep(500);
//Selenium under protractor sleep
browser.driver.sleep(1000);Synchronization
Protractor has built-in synchronization that will wait for elements as long as it can find angular first.
If for some reason your application takes too long or cannot synchronize you can ignore synchronization.
NOTE: This is not recommended, but may be necessary
//Required every time a page changes
browser.ignoreSynchronization = true;Timeout
If you are not not ignoring Synchronization and you want to wait longer than the default timeout time of 11 seconds, add "allScriptsTimeout" to your config file.
...
//Change default syncronization time of 11 seconds
allScriptsTimeout: timeout_in_milliseconds
...protractor.conf.js
Waiting For Elements
When you need to do not know how long something will take, rather than using sleep, choose an element and wait for it.
el = element(by.css("#id"));
//Wait for it to be on the page
browser.wait(function() { return el.isPresent(); }, waitTimeoutMilliseconds);
//Then wait for it to visible
browser.wait(function() { return el.isDisplayed(); }, waitTimeoutMilliseconds);
//If you try to check just isDisplayed sometimes the element will not be foundWaiting Something
If you know text on a page is going to change you can wait till that happens. Example would be wait for "loading" to become useful text, on angular defaults {{<useful info> || "loading"}}.
var waitForTextToBecome = function(el, text){
waitForElement(el);
browser.wait(function() {
return el.getText().then(function(currentText){
return currentText === text;
});
},waitTimeoutMs);
};Protractor Control Flow
Protractor is built on javascript which is asynchronous. The way it is able to do ordered/synchronized things is because it adds each step to the control flow.
If you want any Javascript to be run in the correct place in your test, you need to put it into the control flow.
//Adding something to protractor control flow
browser.controlFlow().execute(function() {
console.log("This will be printed in order");
});Timing
If you want to know how long a test takes saves the time through the control flow.
var startTime, duration;
...
browser.controlFlow().execute(function() {
startTime = new Date().getTime();
});
...
browser.controlFlow().execute(function() {
duration = new Date().getTime() - startTime;
});
...
browser.controlFlow().execute(function() {
console.log("Duration:", duration);
});Timing with Functions
If you want to follow multiple times make this a function and stave it into an object.
var timing = {}, timingStarts = {};
var startTimer = function(name){
browser.controlFlow().execute(function() {
timingStarts[name] = new Date().getTime();
});
};
var endTimer = function(name){
browser.controlFlow().execute(function() {
timing[name] = new Date().getTime() - timingStarts[name];
});
};
var logDuration = function(name){
//There could be much better uses of this such as uploading somewhere
browser.controlFlow().execute(function() {
console.log("Timer " + name + ": " + timing[name] + "ms");
});
};
it("",function(){
...
startTimer("timer");
...
endTimer("timer");
..
logDuration("timer")
});Matchers
Jasmine, the testing framework below Protractor, has ways to check if tests pass.
expect(true).toBeTruthy();
var text = "text"
expect(text).toEqual("text");
Add your own Matcher
Sometimes the error messages from expect statements are cryptic. If it is helpful you can make your own.
describe('Matchers', function() {
beforeEach(function(){
this.addMatchers(matchers);
});
it('examples', function() {
expect(false).toBeTruthy();
expect(false).toEqualBecause(true,"things should be true");
});
});
var matchers = {
toEqualBecause: function( value, message ) {
this.message = function() {
return "Expected '" + this.actual + "' to equal '" +
value + "' because '" + message + "'.";
};
return this.actual === value;
},
};Node Modules
Since Protractor is built on NodeJS you can make your code easily shareable as node modules. They can be uploaded to an NPM repository such as npmjs.org
This is especially helpful with things like login automation that is be needed by everyone.
Creating a module
All that is needed is a file called "package.json" and then a ".js" file that exports your code. This is a basic setup.
{
"name": "shareable-demo-module",
"version": "0.0.1",
"main": "index.js"
}
var share = {};
share.configurable = function(object, name){
return browser.params[name] || process.env[name] || object[name];
};
module.exports = exports = share;index.json
package.json
Using the module
Install the module then require it.
# Install the package from file
$ npm i /path/to/local/package
# Install the package from a repo
$ npm i shareable-demo-module var share = require('shareable-demo-module');
var data = require('/path/to/json/file');
var env = data[share.configurable({},"env")];
var url = share.configurable(data,"url");User Configurization
Sometimes you need you tests to be variable. This is helpful when using the same tests in different environments.
This is how to better use the data files we created.
Environment Variables
Javascript can see use your environment variables. Use choose things like enviroments
var url = process.env.url;
browser.get(url); //Mac/Linux
$ export url="http://localhost:8080"
//Windows
$ SET url="http://localhost:8080"Protractor Params
Another way is to pass the parameters into your protractor call from the command line.
var url = browser.params.url;
browser.get(url); $protractor --params.url="http://localhost:8080"Default Params
You can set default protractor commands in the config file. This is helpful to have a default environment.
var data = require('/path/to/datafile')[browser.params.env]
browser.get(data.url); ...
params: {
env: "int"
},
...protractor.conf.js
Using all combinations
You can set default protractor commands in the config file. This is helpful to have a default environment.
var configurable = function(object, name){
//This will check for a params then an enviroment variable then the object
return browser.params[name] || process.env[name] || object[name];
};
var data = {
"local":{
"url":"https://locahost:8080"
}
}
var env = data[configurable({},"env")];
var url = configurable(data,"url");
Other thoughts
You can work with your test results.
afterEach(function(){
this.results_.description //Name of the test
this.results_.failedCount //Number of failures
this.results_.totalCount //Number of expect statements
});If protractor config file name is "protractor.conf.js" calling "protractor" in that directory finds the config.
#Specified
$ protractor protractor.conf.js
#Default
$ protractorFinal Thoughts
Understanding everything going on is important.
- Protractor is Javascript on NodeJS use it as such.
- The default test structure of Protractor is Jasmine learn what that means.
- Protractor has a control flow to make asynchronous Javascript seem synchronous. When calling regular Javascript make sure you add it to the control flow.
- Make tests variable to help you. Make credentials and environments easily changeable.
Protract Tips
By Jason Mavandi
Protract Tips
- 1,055