ngpo

2.3.1 • Public • Published

ngpo

Create page objects with helper functions for AngularJs Protractor tests using a simple object template.

I want:

  • Protractor tests as quick to write, easy to read and ignorant of the page elements as possible.
  • Simple and consistent protractor page objects which can be nested.
  • Consistent Protractor test methods across applicable elements and html widgets including:
    • enterValue clientPo.name.enterValue('franky');
    • getValue expect(clientPo.name.getValue()).toBe('franky');
    • isVisible (true if both isPresent and isDisplayed) expect(clientPo.name.isVisible()).toBe(true);
    • No impact to existing protractor methods (unless explicitly intended)
  • Simple and consistent way of accessing repeating and nested page elements expect(clientPo.payments.getRow(1).amount.getValue()).toBe('423');
  • Ability to easily attach custom functions to page objects.
  • Ability to easily incorporate other html widgets into ngpo.
  • Page Object code that's not repeated.

Install with npm install ngpo.

ngpo v2.x: requires nodejs 6.x or greater (no breaking api changes from ngpo v1.x). Tested with angularJS v1.5.0 & 1.6.5, Protractor v5.1.2, Chrome v60.0, and chromedriver 2.31.

ngpo v1.x: Tested with Protractor versions 2.5 and 5.1.

Examples

See test\test.js and test\client.po.js for a full set of examples.

Create a page object of Input, Dropdown & Button elements using jsfiddle ngpo example.

var ngpo = require('ngpo'); 
 
var els = {
  nameInput: {
    locator: by.model('client.name'),
    po: ngpo.makeInputPo},
  name: {
    locator: by.binding('client.name'),
    po: ngpo.makeTextPo},
  clearNameButton: {
    locator: by.id('clear-name-button'),
    po: ngpo.makeButtonPo},
  typeDd: {
    locator: by.model('client.type'),
    po: ngpo.makeDdSelectPo}
};
 
var pos = ngpo.makePos(els); 
 

And run a protractor test with those elements.

var clientPo = require('client.po.js');
 
describe('client', function() {
 
    it('should allow name to be modified', function() {
      clientPo.nameInput.enterValue('franky'); 
      expect(clientPo.nameInput.getValue()).toBe('franky'); 
      expect(clientPo.name.getValue()).toBe('franky');
 
    it('should clear the name', function() {
      clientPo.clearNameButton.click();
      expect(clientPo.name.getValue()).toBe('');
      expect(clientPo.nameInput.getValue()).toBe('');
    });
 
    it('should allow client type to be selected', function() {
      clientPo.typeDd.enterValue('cranky'); 
      expect(clientPo.typeDd.getValue()).toBe('cranky'); 
      expect(clientPo.type.getValue()).toBe('cranky'); 
    });
 
});
 

Create a list page object using jsfiddle ngpo example.

var ngpo = require('ngpo'); 
 
//Nest the list elements as another 'els' object in the list object ('payments' here): 
var els = {
  addPaymentButton: {
    locator: by.id('add-payment-button'),
    po: ngpo.makeButtonPo},
  payments: {
    locator: by.repeater('payment in payments'),
    po: ngpo.makeListPo,
    els: {
      amountInput: {
        locator: by.model('payment.amount'),
        po: ngpo.makeInputPo},
      amount: {
        locator: by.binding('payment.amount'),
        po: ngpo.makeTextPo},
    },
  }
};
 
var pos = ngpo.makePos(els); 
 

Run the protractor test.

var clientPo = require('client.po.js');
 
describe('client', function() {
 
    it('should have working payment inputs', function() {
      expect(clientPo.payments.count()).toBe(0);
 
      clientPo.addPaymentButton.click()
      .then(() => {
        expect(clientPo.payments.count()).toBe(1);
        // getRow() is the ngpo method to retrieve the row's nested elements.  
        // ngpo uses getRow() so as to not overwrite the get() method. 
        return clientPo.payments.getRow(0).amountInput.enterValue(5);
      })
      .then(() => {
        expect(clientPo.payments.getRow(0).amountInput.getValue()).toBe('5'); 
        expect(clientPo.payments.getRow(0).amount.getValue()).toBe('5'); 
      
        return clientPo.addPaymentButton.click();
      })
      .then(() => {
        expect(clientPo.payments.count()).toBe(2);
        expect(clientPo.payments.getRow(0).amount.getValue()).toBe('5'); 
        return clientPo.payments.getRow(1).amountInput.enterValue('423');
      })
      .then(() => {
        expect(clientPo.payments.getRow(1).amountInput.getValue()).toBe('423'); 
        expect(clientPo.payments.getRow(1).amount.getValue()).toBe('423'); 
      }); 
 
    });
 
});
 

Documentation

ngpo Functions available:

How to

ngpo functions attached to page objects

enterValue()

Enters a value into a page element and returns a Promise. How value is entered depends on the page element. Input elements use:

  return el
    .click()
    .clear()
    .sendKeys(value)
    .sendKeys(protractor.Key.TAB);
 

Example:

clientPo.nameInput.enterValue('franky'); 
expect(clientPo.nameInput.getValue()).toBe('franky'); 

getValue()

Returns the value of the pageObject as a Promise. How value is retrieved depends on the page element:

  • makeTextPo() uses Protractor getText()
  • input POs (makeInputPo, makeDateInputPo) use Protractor getAttribute('value')
  • makeDdSelectPo uses el.$('option:checked').getText()
clientPo.nameInput.enterValue('franky'); 
expect(clientPo.nameInput.getValue()).toBe('franky'); // input
expect(clientPo.name.getValue()).toBe('franky'); // text by.binding

getValueTrim()

Returns pageObject.getValue() to a string.trim() as a Promise.

clientPo.nameInput.enterValue('  b o bb y '); 
expect(clientPo.nameInput.getValueTrim()).toBe('b o bb y'); 
 

isVisible()

Returns true as a Promise if isPresent and isDisplayed.

expect(clientPo.showme.isPresent()).toBe(true);
expect(clientPo.showme.isDisplayed()).toBe(true);
expect(clientPo.showme.isVisible()).toBe(true);
// hide showme element
clientPo.showmeButton.click()
.then(function() {
  expect(clientPo.showme.isPresent()).toBe(true);
  expect(clientPo.showme.isDisplayed()).toBe(false);
  expect(clientPo.showme.isVisible()).toBe(false);
});

ngpo api functions

makePos(els)

Returns a Page Object: An object-literal of Protractor ElementFinder objects possibly with methods appended. Methods appended are based on the els object passed in.

makePos calls the function assigned to the po property for every object in the els object. The po function is called with (1) the locator and (2) the respective els object.

els is an object of the form :

var els {
  poName1: {
    locator: protractorLocator //  Req'd. eg; by.model('client.city')
    po: makePoFn  //Req'd. the function to append helper functions and return a protractor ElementFinder
    els: {...}    // optional nested object of same form els for list or parent pos
    fns: {fnName: function}  // optional object of custom functions that will be appended to this ElementFinder
    yourParam: value  // optional; any other parameter may be included for use in custom fns
  },
  poName2: ...
}

Example

var ngpo = require('ngpo'); 
 
var els = {
  nameInput: {
    locator: by.model('client.name'),
    po: ngpo.makeInputPo,
    myOption: 'abc'},
};
 
var pos = ngpo.makePos(els); 
 

In the above example, when makePos is called, makeInputPo will be called with

makeInputPo(
  by.model('clientName'),
  {locator: by.model('client.name'),
   po: ngpo.makeInputPo
  myOption: 'abc'}  
)

And return an object with the property nameInput which would be a protractor ElementFinder with getValue and enterValue methods appended (from makeInputPo).

makeDefaultPo(elOrLoc, options)

Returns a Protractor ElementFinder with only the isVisible ngpo function appended.

Determines if elOrLoc is a Protractor ElementFinder or locator. If it is a locator, creates an ElementFinder from it and returns that ElementFinder. Otherwise, returns ElementFinder as-is. options is the els object for the page object being created; eg, {locator: by.binding('client.name'), po: ngpo.makeTextPo}.

Every makeXxxPo function calls this function first. Custom makeXxxPo functions should also call this function first as well which will ensure that it will work as a 'sub' PO in list and parent elements.

Example

function myCustomPo(elOrLoc, options) {
  var el = makeDefaultPo(elOrLoc); 
 
  var yank = options && options.yank ? options.yank : '';   
 
  el.isBlada() = function() {
    return el.getAttribute('blada') === yank; 
  }
 
  return el; 
}
 
var els = {
  berl: {
    locator: by.model('something.what'),
    po: myCustomPo,
    yank: 'green'
  }
}
 

makeTextPo(elOrLoc, options)

Returns a Protractor ElementFinder with one appended function:

  • getValue - returns element.getText()

Arguments: See makeDefaultPo and makePos.

Would typically be used with by.binding.

var ngpo = require('ngpo'); 
 
var els = {
  name: {
    locator: by.binding('client.name'),
    po: ngpo.makeTextPo}
};
 
var pos = ngpo.makePos(els); 
 

makeInputPo(elOrLoc, options)

Returns a Protractor ElementFinder for html <input> with these appended functions:

  • getValue - returns element.getText()
  • enterValue(value) : returns element.click().clear().sendKeys(value).sendKeys(protractor.Key.TAB)

Arguments: See makeDefaultPo and makePos.

Example

var ngpo = require('ngpo'); 
 
var els = {
  nameInput: {
    locator: by.model('client.name'),
    po: ngpo.makeInputPo}
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  clientPo.nameInput.enterValue('franky'); 
  expect(clientPo.nameInput.getValue()).toBe('franky'); 
 

makeDateInputPo(elOrLoc, options)

Returns a Protractor ElementFinder for html <input type=date> tags with these appended functions:

  • getValue - See makeInputPo
  • enterValue(value) - See makeInputPo
  • getValueMmddyyyy - getValue() as string mm/dd/yyyy format
  • getValueYyyymmdd - getValue() as string yyyy-mm-dd format

Arguments: See makeDefaultPo and makePos.

Example

var ngpo = require('ngpo'); 
 
var els = {
  dobInput: {
    locator: by.model('client.dob'),
    po: ngpo.makeDateInputPo}
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  clientPo.dobInput.enterValue('01/01/1939'); 
  expect(clientPo.dobInput.getValue()).toBe('1939-01-01');
  expect(clientPo.dobInput.getValueMmddyyyy()).toBe('01/01/1939');
 

makeButtonPo(elOrLoc, options)

Returns a Protractor ElementFinder with no appended functions.

Arguments: See makeDefaultPo and makePos.

Example

var ngpo = require('ngpo'); 
 
var els = {
  deleteCityButton: {
    locator: by.id('delete-city-button'),
    po: ngpo.makeButtonPo}
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  clientPo.deleteCityButton.click(); 
 

makeButtonWithPausePo(elOrLoc, options)

Returns a Protractor ElementFinder with this amended function.

  • click() : sleeps for options.pause milleseconds after click() element.click().then(function() {pause(options.pause);});

Arguments: See makeDefaultPo and makePos.

Original ElementFinder click() method is available via element.p.click().

Example

var ngpo = require('ngpo'); 
 
var els = {
  deleteHobbyButton: {
    locator: by.id('delete-hobby-button'),
    po: ngpo.makeButtonWithPausePo,
    pause: 5000}
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  clientPo.deleteHobbyButton.click(); // pauses 5 seconds after click()
 

makeDdSelectPo(elOrLoc, options)

Returns a Protractor ElementFinder for html <select> tags with these appended functions :

  • getValue - returns element.$('option:checked').getText()
  • enterValue(value) : returns element.click().sendKeys(value).sendKeys(protractor.Key.TAB)

Arguments: See makeDefaultPo and makePos.

Example

var ngpo = require('ngpo'); 
 
var els = {
  nameInput: {
    locator: by.model('client.name'),
    po: ngpo.makeInputPo}
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  clientPo.nameInput.enterValue('franky'); 
  expect(clientPo.nameInput.getValue()).toBe('franky'); 
 

makeParentPo(elOrLoc, options)

Returns a Protractor ElementFinder which can have sub-ElementFinders on it.

Arguments: See makeDefaultPo and makePos.

Example

var ngpo = require('ngpo'); 
 
var els = {
  request: {
    locator: by.id('request'),
    po: ngpo.makeParentPo,
    els: {
      rInput: {
        locator: by.model('client.request'),
        po: ngpo.makeInputPo},
      rText: {
        locator: by.binding('client.request'),
        po: ngpo.makeTextPo},
    }
  },
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  clientPo.request.rInput.enterValue('be nice'); 
  expect(clientPo.request.rInput.getValue()).toBe('be nice'); 
  expect(clientPo.request.rText.getValue()).toBe('be nice'); 
 

makeListPo(elOrLoc, options)

Returns a Protractor element.all object with nested protractor ElementFinders. It has these appended functions:

  • getRow(n)
  • getRow(n).subPo.poFn()
  • getCount
  • getValue

Arguments: See makeDefaultPo and makePos.

Example

var ngpo = require('ngpo'); 
 
var els = {
  payments: {
    locator: by.repeater('payment in payments'),
    po: ngpo.makeListPo,
    els: {
      amountInput: {
        locator: by.model('payment.amount'),
        po: ngpo.makeInputPo},
      amount: {
        locator: by.binding('payment.amount'),
        po: ngpo.makeTextPo},
    }
};
 
var clientPo = ngpo.makePos(els); 
 
// test example
  expect(clientPo.payments.getCount()).toBe(1);
  clientPo.payments.getRow(0).amountInput.enterValue(5);
  expect(clientPo.payments.getRow(0).amountInput.getValue()).toBe('5'); 
  expect(clientPo.payments.getRow(0).amount.getValue()).toBe('5'); 
 

How to Nest Page Objects

Example to nest the transportation page object into a client page object.

// transportation.po.js
var ngpo = require('../lib/index.js'); 
 
var els = {
  transportationInput: {
    locator: by.model('client.transportation'),
    po: ngpo.makeInputPo},
  transportation: {
    locator: by.binding('client.transportation'),
    po: ngpo.makeTextPo},
 
}; 
 
var pos = ngpo.makePos(els); 
 
module.exports = pos; 
 

There are 2 ways to nest the above transportation page object.

// client.po.js
var ngpo = require('ngpo'); 
var transPo = require('./transportation.po.js');
 
// (1) directly in the client po els object
var els = {
  transportationParent: {
    locator: by.id('trans-parent'),
    po: ngpo.makeParentPo, 
    els: transPo.els},   
}; 
 
// (2) append the transportation page objects elements directly to the  client po object 
var pos = ngpo.makePos(els); 
pos = ngpo.makePos(transPo.els, pos);
 

The protractor tests would refer to these like this:

// #1 above would be called using transportationParent: 
  clientPo.transportationParent.transportationInput.enterValue('strides'); 
  expect(clientPo.transportationParent.transportationInput.getValue()).toBe('strides');
 
// #2 would allow the transportation page object elements to be called directly from the client po
  clientPo.transportationInput.enterValue('unicycle'); 
  expect(clientPo.transportationInput.getValue()).toBe('unicycle');
 
 

How to append custom functions to page object elements

Custom functions can be included in the els fns property object. Custom functions are called with the ElementFinder and the options object (see makeDefaultPo), so that you can refer to them in the custom function, and the arguments that you call the function with from the protractor script.

Example

 
var els = {
  hobbyInput: {
    locator: by.model('client.hobby'),
    po: ngpo.makeInputPo,
    pause: 5000,
    fns: {
      getClasses: function(el, options) {return el.getAttribute('class');},
      clickWithPause: function(el, options) {
        return el.click().then(function() {browser.sleep(options.pause);})
      }}
  },
  funnyInput: {
    locator: by.model('client.funny'),
    po: ngpo.makeInputPo,
    fns: {
      inputAddedVals: (funnyInputEl, options, val1, val2) => {
        var addedVals = val1 + val2; 
        return funnyInputEl.enterValue(addedVals);
      }
    }
  },
  dobInput: {
    locator: by.model('client.dob'),
    po: ngpo.makeDateInputPo,
    // override clear() fn with poFns.clearByBs b/c Protractor Issue #562
    fns: {clear: ngpo.poFns.clearByBs}},
}; 
 
// test example
  expect(clientPo.hobbyInput.getClasses()).toContain('yada');
  clientPo.hobbyInput.clickWithPause();  // pauses 5 seconds after click()
 
  clientPo.funnyInput.inputAddedVals(3,4)
  .then(function(){
    expect(clientPo.funnyInput.getValue()).toBe('7');
  });
 
  clientPo.dobInput.enterValue('01/02/1987')
  .then(() => {
    expect(clientPo.dobInput.getValue()).toBe('1987-01-02');
    clientPo.dobInput.clear();  // using poFns.clearByBs
    expect(clientPo.dobInput.getValue()).toBe(''); 
  }); 
 
 

Package Sidebar

Install

npm i ngpo

Weekly Downloads

7

Version

2.3.1

License

MIT

Last publish

Collaborators

  • tonybranfort