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 found

Waiting 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
    $ protractor

Final 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