ngpo
v2.3.1
Published
Simple template to create page objects for AngularJS Protractor tests
Downloads
22
Maintainers
Readme
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)
- enterValue
- 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:
makePos
- Make page object functions and the functions attached (in addition to all expected Protractoor functions):
makeDefaultPo
makeTextPo
makeInputPo
makeDateInputPo
- enterValue
- getValue
- getValueTrim
- isVisible
- getValueMmddyyyy
- getValueYyyymmdd
makeButtonPo
makeButtonWithPausePo
(deprecated)makeDdSelectPo
- enterValue
- getValue
- getValueTrim
- isVisible
- clear (selects first item in dd list)
makeParentPo
- getValue
- subPo.poFn()
makeListPo
- getRow
- getRow(n).subPo.poFn()
- getCount
- getValue
- poFns (optional fns that can be attached to page objects; see Append custom functions to page object elements)
hasClass
,makeHasClassFn
:expect(clientPo.deleteHobbyButton.hasClass('yada')).toBe(true)
clearByBs
,makeHasClearByBs
(use to clear date field in Chrome; Protractor issue #562) :clientPo.dobInput.clearByBs()
clickWithPause
(deprecated)getAttributeValue
- Other functions
- pause
- acceptAlert
- dismissAlert
How to
- Append custom functions to page object elements
- Clear a date field in Chrome (Protractor Issue #562) - Use
poFns.clearByBs
. See example in Append custom functions to page object elements. - Nest ngpo page objects.
- Create your own makePo functions: See
makeDefaultPo
. And github/npm it: See ngpo-ui-select.
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 foroptions.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('');
});