Archive

Posts Tagged ‘promises protractor’

Avoiding false positives with Protractor and Javascript

Do you have false positives when automating with protractor ?

Asynchronous code can be really annoying sometimes, callback pyramids, error handling on every second line, promises swallowing errors by default…

When using ES6 promises any exception which is thrown within a then or a catch handler, will be silently disposed of unless manually handled.

If you are not getting the errors, you are probably coding the promise something like this:

Promise.resolve('promised value').then(function() {
 throw new Error('error'); 
});

Then the more obvious way  to print the error is adding a catch after the promise:

Promise.resolve('promised value').then(function() {
 throw new Error('error'); 
}).catch(function(error) {
 console.error(error.stack); 
});

Remember that you need to add this after all the promises you want to print the error. Do you really think this is reliable ? Are you going to repeat yourself adding a catch after all the promises ?

So, how can you print the error without need to repeat yourself when creating a promise ? You can use .done instead of .then. This will run the promise and after print the error that you want.

Here is how you can avoid them:

Promise.resolve('promised value').done(function() {
 throw new Error('error');
 });

But what do you do when you chain multiple thens ?

There is a library called Bluebird which has a fix integrating your existing code by extending the ES6 Promises API. You can make sure you know about unhandled rejections, so no more false positives.

Promise.onPossiblyUnhandledRejection(function(error){
 throw error; 
});

Then if you want to discard an exception not because it is been swallowed, but you don’t need to print it, you can do something like this:

Promise.reject('error value').catch(function() {
});

 

How to create your tests with Protractor and cucumber

July 7, 2016 1 comment

Hello guys, today I will post about how to do assertions when using Cucumber and protractor. If you don’t know what is a promise and how they work, I suggest read this link. WebDriver’s JavaScript API is entirely asynchronous and every command results in a promise. WebDriverJS uses a custom promise library with a promise manager called the ControlFlow. The ControlFlow implicitly synchronizes asynchronous actions, making it so you only have to register a promise callback when you want to catch an error or access a return value.

  • So, create a protractor.js in your project’s root folder:
'use strict';

var os = require('os');
var platform = os.platform();

var HOST = 'http://test.protractor.com';
var USER = 'protractor';
var PASS = 'test';

var chrome = {
 browserName: 'chrome'
};

var safari = {
 browserName: 'safari'
};

var firefox = {
 browserName: 'firefox'
};

var capabilities = [chrome];

// Enable Safari only on macos
if (platform === 'darwin') {
 capabilities.push(safari);
}

exports.config = {
 seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
 specs: [
 'src/features/*.feature'
 ],
 multiCapabilities: capabilities,
 noGlobals: true,
 baseUrl: 'http://localhost:5000',
 framework: 'custom',
 frameworkPath: require.resolve('protractor-cucumber-framework'),
 resultJsonOutputFile: 'test-reports/protractor/protractor.json',
 cucumberOpts: {
 require: [
 'src/js/test/common.js',
 'src/js/test/steps/**/*.js'
 ],
 format: 'pretty',
 strict: true,
 tags: [
 '@smoke',
 '~@pending'
 ]
 },
 params: {

 credentials: {
 username: USER,
 password: PASS
 },

 endpoints: {
 authenticate_url: HOST + '/auth'
 }
 },

 //promise to get browserName
 onPrepare: function() {
 var protractor = require('protractor');
 var browser = protractor.browser;
 return browser.getProcessedConfig().then(function(config) {
 browser.browserName = config.capabilities.browserName;
 });
 }
};

 

  • Create your Feature file in: your_project_folder/src/features/login/login.feature
Feature: Login Page


@smoke
 Scenario: Login success
 Given I am on the login page
 When I input valid username and password
 Then I should be redirected to the management page

 

  • Common.js file which will contain the common libraries (You need install them first with npm install command): your_project_folder/src/js/test/common.js
'use strict';

// Configure chai & sinon
global.expect = require('chai')
 .use(require('sinon-chai'))
 .use(require('chai-string'))
 .use(require('chai-as-promised'))
 .use(require('expect-to-be-a-promise'))
 .expect;

global.sinon = require('sinon');

 

  • Create your step definition file in: your_project_folder/src/js/test/steps/login_steps.js
'use strict';

var LoginActions = require('../login_page/login_actions');
var LoginAssertions = require('../login_page/login_assertions');
var protractor = require('protractor');
var browser = protractor.browser;

var LoginPageSteps = function() {

var loginActions = new LoginActions();
var loginAssertions = new LoginAssertions();

this.Given(/^I am on the login page$/, function() {
    loginActions.getPage();
 });

this.When(/^I input valid username and password$/, function() {
    var credentials = browser.params.credentials;
    loginActions.login(credentials.username, credentials.password);
 });

this.Then(/^I should be redirected to the management page$/, function() {
    return loginAssertions.assertManagementPage();
 });

};

module.exports = LoginPageSteps;

 

  • Create your login elements (map repository for login page) file in: your_project_folder/src/js/test/login_page/login_elements.js
'use strict';

var protractor = require('protractor');
var element = protractor.element;
var by = protractor.by;

function LoginStateElements() {
  return {
    url: function() {
    return '/login';
  },
  username: function() {
    return element(by.model('credentials.username'));
  },
  password: function() {
    return element(by.model('credentials.password'));
  },
  submit: function() {
    return element(by.css('[type="submit"]'));
  },
  form: function() {
   return element(by.css('[ng-submit="form("test")"]'));
  }
 };
}

module.exports = LoginStateElements;

 

  • Create your login actions file in: your_project_folder/src/js/test/login_page/login_actions.js
'use strict';

var LoginElements = require('./login_elements');
var protractor = require('protractor');
var browser = protractor.browser;

function LoginStateActions() {

 var loginElements = new LoginElements();

 return {
  getPage: function() {
     browser.get(browser.baseUrl + loginElements.url());
  },
  login: function(username, password) {
     loginElements.username().click();
     loginElements.username().clear();
     loginElements.username().sendKeys(username);
     loginElements.password().click();
     loginElements.password().clear();
     loginElements.password().sendKeys(password);
     loginElements.submit().click();
  }
 };
}

module.exports = LoginStateActions;

 

  • Create your login assertions file in: your_project_folder/src/js/test/login_page/login_assertions.js
'use strict';

var LoginElements = require('./login_elements');

function LoginAssertions() {

 var loginElements = new LoginElements();

 return {
  assertManagementPage: function() {
     var form = loginElements.form();
     var submit = loginElements.submit();
     expect(submit.isEnabled()).to.eventually.be.false;
     return expect(form.isPresent()).to.eventually.be.true;
  }

 };
}

module.exports = LoginAssertions;

 

Chai Assertion Library to understand how to use expect to assert elements.

WebDriver API commands to understand what you can do with the elements, for example, check if is displayed, click, clear, get Title, etc.

 

Thanks To Raphael Bonnet, who has been working with me on this project. It’s great when you find such a good developer to work with and really work as a team trying to find the best for the project together (Even that I am still skeptical about protractor instead of Selenium xD ).

Promise Manager (Protractor – WebDriverJs)

Hey guys,

I have coded in Java, C# and Ruby since I’ve started work with Automation, so about 8 years ago. It’s my first time coding in Javascript for AngularJs pages and for those who are like me you need to know that if you are working with protractor or webdriverjs they return promises from all of its browser interactions, which could lead to some confusing behaviour.

 

What is the promise manager ?

Protractor and webdriverjs are asynchronous and every result returns a promise. They use a custom promise library called ControlFlow, which implicitly synchronizes asynchronous actions, making it so you only have to register a promise callback when you want to catch an error or access a return value. The promise module coordinates the flow of the asynchronous tasks.

This Promise Manager ensures that calls to the browser are run in sequence, and you only need to worry about dealing with the Promises when you wish to do something with data from the page.

In practice, this means that you can ignore the returned promises while interacting with the page.

 

So, instead of:

 

driver.get('https://azevedorafaela.wordpress.com')
 .then(function() {
   return driver.findElement({class: '.searchtip'});
 })
 .then(function(search) {
   return search.sendKeys('webdriver');
 })
 .then(function() {
   return driver.findElement({class: '.button'});
 })
 .then(function(button) {
   return button.click();
 });

 

You can have:

 driver.get('https://azevedorafaela.wordpress.com');
 driver.findElement({class: '.searchtip'}).sendKeys('webdriver');
 driver.findElement({class: '.button'}).click();

 

The trick part is when you want to extract the values from the page to do an assertion for example:

 driver.get("https://azevedorafaela.wordpress.com");
    driver.getTitle().then(function(pageTitle) {
        assert.equal(pageTitle, "Rafaela Azevedo");
 });

 

You can find more information on the links below. Thank you ! See you next weekend 🙂

 

Resources: http://blog.scottlogic.com/2015/03/04/webdriverjs-and-promises.html

https://github.com/SeleniumHQ/selenium/wiki/WebDriverJs

http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/promise.html

 

%d bloggers like this: