Protractor Page Objects Inheritance

angularjs, angularjs-e2e, javascript, node.js, protractor

Solution

Here is link to a tutorial I set up for training some of my co-workers on create good Protractor test suites.

It's all live, with a demo site that you can visit, explore, etc.

https://github.com/Droogans/ProtractorPageObjects

This will set you up with installation, outlines, organizational techniques, and more.

Feel free to leave an issue if you have any trouble.

Problem

Given i'm building my angularjs protractor e2e tesing suite leveraging the page objects pattern. And i separate page object code in different files as much as reasonable. What would be a good approach to enable page objects inheritance? javascript classic inheritance? Object.create() based inheritance? Other? Should i keep expectations within the page object? Or favor Martin Fowler optinion by moving them to an assertion library? in which case how exactly would that look like in this javascript-nodejs technology stack? I've prepared a live jsfiddle playground here so you can try your improvements on. Or simply paste code within the answer, i'll paste the jsfiddle content below for clarity: loginPage.js ``` "use strict"; // A Page Object is a Singleton, so no need to constructors or classic js inheritance, // please tell me if I'm wrong or what's the utility of creating a (new LoginPage()) // every time a spec need to use this login page. var loginPage = { // Page Object Elements userElm: $('.user.loginPage'), // Page Object Assertions // Martin Fowler [doesn't favor](http://martinfowler.com/bliki/PageObject.html) // assertions in page objects, I'm open to suggestions on how to move // the assertions away from the Page Object and see how an assertion library // could like like in protractor. assertInputsDisplayed: function() { return ('Assertion: this.userElm: '+this.userElm); }, // Page Object Actions get: function () { return ('navigating to LoginPage with userElm: '+this.userElm); } }; module.exports.loginPage = loginPage; ``` loginDialog.js ``` "use strict"; var loginPage = require('./loginPage.js').loginPage; var helpers = require('./helpers.js'); // Inherit properties from another Page Object var loginDialog = helpers.extend({}, Object.create(loginPage), { // Page Object Elements userElm: $('.user.loginDialog'), // Page Object Actions get: function () { return ('navigating to LoginDialog with userElm: '+this.userElm); }, logout: function () { return ('logging out of Dialog. user was: '+this.userElm); } }); module.exports.loginDialog = loginDialog; ``` helpers.js ``` "use strict"; // some helper to avoid adding an external dependency for now var extend = function(target) { var sources = [].slice.call(arguments, 1); sources.forEach(function (source) { for (var prop in source) { target[prop] = source[prop]; } }); return target; }; ``` usage.js ``` "use strict"; // Mock $() for easy unit testing this on nodejs REPL global.$ = function(args) { return ('$BUILT '+args); }; var loginPage = require('./loginPage.js').loginPage; var loginDialog = require('./loginDialog.js').loginDialog; console.log(loginPage.userElm); //=> '$BUILT .user.loginPage' console.log(loginDialog.userElm); //=> '$BUILT .user.loginDialog' console.log(loginPage.get()); //=> 'navigating to LoginPage with userElm: $BUILT .user.loginPage' console.log(loginDialog.get()); //=> 'navigating to LoginPage with userElm: $BUILT .user.loginDialog' console.log(loginPage.assertInputsDisplayed()); //=> 'LoginPage assertion: this.userElm: $BUILT .user.loginPage' console.log(loginDialog.assertInputsDisplayed()); //=> 'LoginPage assertion: this.userElm: $BUILT .user.loginDialog' //loginPage.logout(); //=> TypeError: Object #<Object> has no method 'logout' console.log(loginDialog.logout()); //=> 'logging out of Dialog. user was: $BUILT .user.loginDialog' ```

Original source

Related problems