View Interface for nemo views.
package.json
file and install ...
"nemo": "^2.0.0",
"nemo-view": "^2.0.0",
...
nemo-view
to your plugins nemo config{
"driver": {
...
},
"plugins": {
"view": {
"module": "nemo-view",
"arguments": ["path:locator"]
}
},
"data": {
...
}
}
The locatorDefinition
can either be a JSON object like this:
{
"locator": ".myClass",
"type": "css"
}
Where type
is any of the selenium locator strategies. A locator
or type
CANNOT be empty/blank/absent in JSON object representation of locatorDefinition
. An error will be thrown during the setup of nemo-view. If type
under locatorDefinition
is invalid (not amongst allowed types) then an error is thrown as well.
Or locatorDefinition
can be a string like this:
"css:.myClass"
String of the form <type>:<locator>
or <locator>
(where <type>
will be assumed as css)
The locator JSON file describes elements and the locator strategy used for each one. The most common use case is to store
all your locator files in the nemoBaseDir
+ /locator directory. The below example uses the JSON style locatorDefinition
.
{
"fooText": {
"locator": "#foo input.texty",
"type": "css"
},
"fooButton": {
"locator": "#foo input[type='button']",
"type": "css"
},
"barText": {
"locator": "#bar input.texty",
"type": "css"
},
"barButton": {
"locator": "#bar input[type='button']",
"type": "css"
},
"bingText": {
"locator": "#bing input.texty",
"type": "css"
},
"bingButton": {
"locator": "#bing input[type='button']",
"type": "css"
},
"bangText": {
"locator": "#bang input.texty",
"type": "css"
},
"bangButton": {
"locator": "#bang input[type='button']",
"type": "css"
},
"outBox": {
"locator": "outy",
"type": "id"
}
}
nemo-view
supports for adding JavaScript-style comments in your json files as each file is processed by using shush
Note: code snippets are minus the require blocks for the sake of brevity. Please see unit tests for full files
If you don't have any locator files, or don't configure nemo-view to find locator files, you will still get back convenience methods on the nemo object:
nemo.view._find
nemo.view._finds
nemo.view._present
nemo.view._visible
nemo.view._wait
nemo.view._waitVisible
nemo.view._optionValue
nemo.view._optionText
nemo.view._firstVisible
Which can be used as follows:
describe('nemo-view @verySimple@', function () {
before(function (done) {
nemo = Nemo(done);
});
after(function (done) {
nemo.driver.quit().then(done);
});
beforeEach(function (done) {
nemo.driver.get(nemo.data.baseUrl);
util.waitForJSReady(nemo).then(util.doneSuccess(done), util.doneError(done));
});
it('should use the form view to enter values and write to outy div @useView@', function (done) {
nemo.view._find('css:#outy').getTagName().then(function (tn) {
assert.equal(tn.toLowerCase(), 'div');
});
nemo.view._finds('body').then(function (bodyArray) {
return bodyArray[0].getTagName();
}).then(function (tn) {
assert.equal(tn.toLowerCase(), 'body');
}).then(done, util.doneError(done));
});
});
These generic - or "underbar" - methods are defined below.
If you've configured nemo-view properly, and have the following locator files:
<nemoBaseDir>
|- locator
|- form.json
|- formElementList.json
|- select.json
|- simple.json
|- sub
|- form.json
You will get back the following views on the nemo object:
nemo.view.form
nemo.view.formElementList
nemo.view.select
nemo.view.simple
nemo.view.sub.form
Each including a set of helper methods for each locator as documented below. And usable as follows:
describe('nemo-view @simpleViewSuite@', function () {
before(function(done) {
nemo = Nemo(done);
});
after(function(done) {
nemo.driver.quit().then(done);
});
beforeEach(function (done) {
nemo.driver.get(nemo.data.baseUrl);
util.waitForJSReady(nemo).then(util.doneSuccess(done), util.doneError(done));
});
it('should use the form view to enter values and write to outy div @useView@', function (done) {
nemo.view.form.fooText().sendKeys('foo');
nemo.driver.sleep(300);
nemo.view.form.fooButton().click();
nemo.view.form.barText().sendKeys('bar');
nemo.view.form.barButton().click();
nemo.view.form.bingText().sendKeys('bing');
nemo.view.form.bingButton().click();
nemo.view.form.bangText().sendKeys('bang');
nemo.view.form.bangButton().click();
nemo.driver.sleep(3000);
nemo.view.form.outBox().getText().then(function (outText) {
assert.equal(outText, 'foobarbingbang');
done();
}, util.doneError(done));
});
});
You can pass locators from [locatorName]By
to underscore methods like _find
or _finds
etc. For example,
nemo.view._finds(nemo.view.paypal.languageBy()).then(function(languages){
languages.forEach(function(language){
//do stuff
});
});
Finding elements under another element
nemo.view._finds('div.fielder', nemo.view.simple.parentBanner()).then(function (divs) {
//do stuff
});
You may want to publish complete flows as a nemo plugin. That way you can import the functionality and access as a plugin. The following is an example of that.
Please see the test/contained-functionality.js
test file and test/plugin/shared-fn-plugin.js
plugin file for an example of this.
The following generic methods are added to nemo.view
@argument locatorDefinition {String|Object}
- Please see locatorDefinition
above
@argument parentWebElement {WebElement} (optional, default driver)
- parent WebElement to search elements underneath
@returns {Promise}
resolves to a WebElement or rejected
@argument locatorDefinition {String|Object}
- Please see locatorDefinition
above
@argument parentWebElement {WebElement} (optional, default driver)
- parent WebElement to search elements underneath
@returns {Promise}
resolves to an array of WebElements or rejected
@argument locatorDefinition {String|Object}
- Please see locatorDefinition
above
@argument parentWebElement {WebElement} (optional, default driver)
- parent WebElement to search elements underneath
@returns {Promise}
resolves to true or false
@argument locatorDefinition {String|Object}
- Please see locatorDefinition
above
@argument parentWebElement {WebElement} (optional, default driver)
- parent WebElement to search elements underneath
@returns {Promise}
resolves to true or false. Rejected if element is not found
@argument locatorDefinition {String|Object}
- Please see locatorDefinition
above
@argument timeout {Integer} (optional, default 5000)
- ms to wait until rejecting
@argument msg {String} (optional)
- Message to accompany error in failure case`
@returns {Promise}
resolves to true or rejected
@argument locatorDefinition {String|Object}
- Please see locatorDefinition
above
@argument timeout {Integer} (optional, default 5000)
- ms to wait until rejecting
@argument msg {String} (optional)
- Message to accompany error in failure case`
@returns {Promise}
resolves to true or rejected
@argument locatorDefinition {String|Object}}
- Please see locatorDefinition
above
@argument value {String}
- the value attribute of the option you wish to select
@argument parentWebElement {WebElement} (optional, default driver)
- parent WebElement to search elements underneath
@argument locatorDefinition {String|Object}}
- Please see locatorDefinition
above
@argument text {String}
- The text in the option you wish to select
@argument parentWebElement {WebElement} (optional, default driver)
- parent WebElement to search elements underneath
@argument locatorObject {Object}
- Object of key/value pairs where the key describes the element to find and the
value is a locatorDefinition
(see above). Example would be:
{
'loginerror': '.notification.notification-critical',
'profile': '#contents[role=main]'
}
@argument timeout {Integer} (optional, default 5000)
- ms to wait until rejecting
@returns {Promise}
resolves to the found key (e.g. 'loginerror' or 'profile' from above example) or rejected
The addView method will be added to the nemo.view namespace with the following signature:
nemo.view.addView(viewSpec, addToNamespace);
viewSpec {String|JSON} will be either a string, or a JSON object to define the view/locator.
addToNamespace {boolean} (optional, defaults to true) if false
nemo-view will not attach the view to the nemo.view
namespace
Using the addView method, you can add a view at any time using the same formats and conventions as if you are adding them in the Nemo.setup() method. Examples:
//add using a locator in the autoBaseDir/locator directory
var myPage = nemo.view.addView('myPage');
var login = nemo.view.addView({
"name": "login",
"locator": "path:locator/loggedOut/login"
});
var addCard = nemo.view.addView({
"name": "addCard",
"locator": "module:nemo-paypal-locators/addCard"
});
The addView method will return the view object. It will also dedupe to prevent extra cycles adding the same view multiple times, or overwriting of a view with another of the same name.
The view will create the following methods for each locator object:
This is a special locator, which let's you find lists of related elements. For example, given the locator file formElementList.json
:
{
"inputGroup": {
"locator": "div.fielder",
"type": "css",
"Elements": {
"text": "input.texty",
"button": "input[type='button']"
}
}
}
You will get a list of items, each with two properties text
and button
which themselves return WebElements when called.
nemo.view.formElementList
.inputGroup()
.then((elements) => {
elements.forEach(function (el) {
el.text().sendKeys('abcd');
el.button().click();
})
});
You can specify different locator strings/strategies based on the data.locale
configuration value.
To do so, first modify the entry you wish to be specialized:
{
"myLocator": {
"type": "css",
"locator": ".myLocator"
}
}
changes to
{
"myLocator": {
"default": {
"type": "css",
"locator": ".myLocator"
},
"DE": {
"type": "css",
"locator": ".meinLocator"
}
}
}
You can set the data.locale
property as follows:
nemo._config.set('data:locale', 'DE');
For a working example, refer to the unit tests for the locale feature (found in test/locale.js
) in this module.
_NOTE: This feature is a carry-over from earlier versions of nemo-view. It is understood now that the above feature does not actually represent "locale" specificity as defined by bcp47 (https://tools.ietf.org/html/bcp47). See discussion here and issue here. Follow along as we discuss a backwards compatible way to resolve this unfortunate nomenclature error.
Unit tests run by default using headless browser PhantomJS. To run unit tests out of box, You must have PhantomJS installed on your system and must be present in the path
If you want to run unit tests on your local browser, like lets say Firefox, you need to update browser in unit test
configuration, for example the browser section under test/config/config.json
like here
How to run unit tests?
npm run test:unit
will just run unit testsnpm test
- default grunt task will run linting as well as unit testsmocha -t 60s
mocha --grep @_visible@withParent@negative@ -t 60s
npm install
on nemo-view module, you can run node_modules/.bin/mocha --grep @_visible@withParent@negative@ -t 60s