From de0acef52dea7671e181c2ea934fffd83532ba3f Mon Sep 17 00:00:00 2001 From: Matt Edelman Date: Tue, 2 Jun 2015 19:03:19 -0700 Subject: [PATCH 1/6] adding JIT locale functionality --- lib/locatex.js | 7 ++----- lib/locreator.js | 39 +++++++++++++++++++++------------------ lib/view.js | 8 ++++---- test/locale.js | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 test/locale.js diff --git a/lib/locatex.js b/lib/locatex.js index 5c77043..741ce3e 100644 --- a/lib/locatex.js +++ b/lib/locatex.js @@ -1,12 +1,9 @@ 'use strict'; module.exports = function (nemo) { - return function locatex(viewJSON, locatorArray) { + return function locatex(locatorJSON) { var locale = (nemo.data && nemo.data.locale) ? nemo.data.locale : 'default', - locatr = viewJSON; - locatorArray.forEach(function (level) { - locatr = locatr[level]; - }); + locatr = locatorJSON; return locatr[locale] || locatr['default'] || locatr; }; }; \ No newline at end of file diff --git a/lib/locreator.js b/lib/locreator.js index 58d0238..575bf5a 100644 --- a/lib/locreator.js +++ b/lib/locreator.js @@ -44,56 +44,59 @@ Locreator.prototype.addGenericMethods = function generics() { }; }; -Locreator.prototype.addStarMethods = function locreator(locatorId, _locator, parentWebElement) { +Locreator.prototype.addStarMethods = function locreator(locatorId, locatorJSON, parentWebElement) { var locatorObject = {}; var drivex = this.drivex; - var locator = this.normify(_locator); + var locreator = this; + var locator = function () { + return locreator.normify(locreator.locatex(locatorJSON)); + } + locatorObject[locatorId] = function () { - return drivex.find(locator, parentWebElement); + return drivex.find(locator(), parentWebElement); }; locatorObject[locatorId + 'By'] = function () { - return locator; + return locator(); }; locatorObject[locatorId + 'Present'] = function () { - return drivex.present(locator, parentWebElement); + return drivex.present(locator(), parentWebElement); }; locatorObject[locatorId + 'Wait'] = function (timeout, msg) { - return drivex.waitForElementPromise(locator, timeout || 5000, msg); + return drivex.waitForElementPromise(locator(), timeout || 5000, msg); }; locatorObject[locatorId + 'WaitVisible'] = function (timeout, msg) { - return drivex.waitForElementVisiblePromise(locator, timeout || 5000, msg); + return drivex.waitForElementVisiblePromise(locator(), timeout || 5000, msg); }; locatorObject[locatorId + 'Visible'] = function () { - return drivex.visible(locator, parentWebElement); + return drivex.visible(locator(), parentWebElement); }; locatorObject[locatorId + 'OptionText'] = function (optionText) { - return drivex.selectByOptionText(locator, optionText, parentWebElement); + return drivex.selectByOptionText(locator(), optionText, parentWebElement); }; locatorObject[locatorId + 'OptionValue'] = function (optionValue) { - return drivex.selectByOptionValue(locator, optionValue, parentWebElement); + return drivex.selectByOptionValue(locator(), optionValue, parentWebElement); }; locatorObject[locatorId + 'TextEquals'] = function (value) { - return drivex.validateText(locator, parentWebElement, value) ; + return drivex.validateText(locator(), parentWebElement, value) ; }; locatorObject[locatorId + 'AttrEquals'] = function (attribute, value) { - return drivex.validateAttributeValue(locator, parentWebElement, attribute, value); + return drivex.validateAttributeValue(locator(), parentWebElement, attribute, value); }; return locatorObject; }; -Locreator.prototype.addGroup = function (viewJSON, locator, locatorId) { +Locreator.prototype.addGroup = function (locatorId, locatorJSON) { var nemo = this.nemo; var drivex = this.drivex; - var locatex = this.locatex; var self = this; return function () { //give back the nemo.view.viewname.list() function - return drivex.finds(normalize(nemo, locator)).then(function (parentWebElements) { + return drivex.finds(normalize(nemo, locatorJSON)).then(function (parentWebElements) { return nemo.wd.promise.map(parentWebElements, function (parentWebElement) { var parentObject = {}; - Object.keys(viewJSON[locatorId].Elements).forEach(function (childLocatorId) { - var childLocator = locatex(viewJSON, [locatorId, 'Elements', childLocatorId]); - var starMethods = self.addStarMethods(childLocatorId, childLocator, parentWebElement); + Object.keys(locatorJSON.Elements).forEach(function (childLocatorId) { + var childLocatorJSON = locatorJSON.Elements[childLocatorId]; + var starMethods = self.addStarMethods(childLocatorId, childLocatorJSON, parentWebElement); _.merge(parentObject, starMethods); }); return parentObject; diff --git a/lib/view.js b/lib/view.js index ff63444..04d467a 100644 --- a/lib/view.js +++ b/lib/view.js @@ -28,11 +28,11 @@ var _ = require('lodash'); module.exports = function View(nemo, locreator, viewJSON) { return _.transform(viewJSON, function (_viewObject, n, locatorId) { - var locator = locreator.locatex(viewJSON, [locatorId]); - if (viewJSON[locatorId].Elements) { - _viewObject[locatorId] = locreator.addGroup(viewJSON, locator, locatorId); + var locatorJSON = viewJSON[locatorId]; + if (locatorJSON.Elements) { + _viewObject[locatorId] = locreator.addGroup(locatorId, locatorJSON); } else { - _.merge(_viewObject, locreator.addStarMethods(locatorId, locator)); + _.merge(_viewObject, locreator.addStarMethods(locatorId, locatorJSON)); } }); }; diff --git a/test/locale.js b/test/locale.js new file mode 100644 index 0000000..461c4c9 --- /dev/null +++ b/test/locale.js @@ -0,0 +1,38 @@ +/* global describe,before,after,beforeEach,it */ +'use strict'; + +var Nemo = require('nemo'), + nemo = {}, + path = require('path'), + //assert = require('assert'), + util = require(path.resolve(__dirname, 'util')); + +describe('nemo-view @locale@', 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 correct locale based locators while locale is reset in realtime', function (done) { + done(new Error('test TBD')); + //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)); + }); +}); \ No newline at end of file From dd1b10a456fab40f3db9538dbcc176a8d312ce8e Mon Sep 17 00:00:00 2001 From: Matt Edelman Date: Wed, 3 Jun 2015 18:09:38 -0700 Subject: [PATCH 2/6] adding unit tests and fixes to locale feature based on discovery --- CHANGELOG.md | 13 +++++++ lib/locatex.js | 6 +-- lib/locreator.js | 22 +++++++---- lib/normalize.js | 4 +- lib/view.js | 2 +- test/locale.js | 75 +++++++++++++++++++++++++++---------- test/mocks/locale/form.json | 38 +++++++++++++++++++ 7 files changed, 128 insertions(+), 32 deletions(-) create mode 100644 test/mocks/locale/form.json diff --git a/CHANGELOG.md b/CHANGELOG.md index db9ce6b..342a545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # nemo-view changelog +## v1.2.0 + +* add realtime locale switching support (see https://github.com/paypal/nemo-view/issues/31) + +## v1.1.0 + +* add new methods `[locatorName]TextEquals` and `[locatorName]AttrEquals` (see https://github.com/paypal/nemo-view/pull/29) +* add optional msg parameter to generic methods (see https://github.com/paypal/nemo-view/pull/28) + +## v1.0.3 + +Change API to match `nemo@1.0` + ## v0.3.2-beta Add support for module based locators and path based locators. I.e. you can put locators into commonjs modules as well as into other directory structure diff --git a/lib/locatex.js b/lib/locatex.js index 741ce3e..aeb7b8b 100644 --- a/lib/locatex.js +++ b/lib/locatex.js @@ -2,8 +2,8 @@ module.exports = function (nemo) { return function locatex(locatorJSON) { - var locale = (nemo.data && nemo.data.locale) ? nemo.data.locale : 'default', - locatr = locatorJSON; - return locatr[locale] || locatr['default'] || locatr; + var locale = nemo._config.get('data:locale') || 'default'; + var localizedLocatorJSON = locatorJSON[locale] || locatorJSON['default'] || locatorJSON; + return localizedLocatorJSON; }; }; \ No newline at end of file diff --git a/lib/locreator.js b/lib/locreator.js index 575bf5a..d85dd5c 100644 --- a/lib/locreator.js +++ b/lib/locreator.js @@ -3,8 +3,11 @@ var Drivex = require('selenium-drivex'); var Locatex = require('./locatex'); var _ = require('lodash'); var normalize = require('./normalize'); +var debug = require('debug'); +var log = debug('nemo-view:log'); var Locreator = function (nemo) { + log('creating Locreator instance'); this.nemo = nemo; this.drivex = Drivex(nemo.driver, nemo.wd); this.locatex = Locatex(nemo); @@ -13,6 +16,7 @@ var Locreator = function (nemo) { }; }; Locreator.prototype.addGenericMethods = function generics() { + log('adding generic methods'); var normify = this.normify; var drivex = this.drivex; var nemo = this.nemo; @@ -44,14 +48,17 @@ Locreator.prototype.addGenericMethods = function generics() { }; }; -Locreator.prototype.addStarMethods = function locreator(locatorId, locatorJSON, parentWebElement) { +Locreator.prototype.addStarMethods = function addStarMethods(locatorId, locatorJSON, parentWebElement) { + log('add star methods for %s', locatorId); var locatorObject = {}; var drivex = this.drivex; var locreator = this; var locator = function () { return locreator.normify(locreator.locatex(locatorJSON)); - } + }; + //this is an error check. if an error thrown, invalid locatorJSON. + locator(); locatorObject[locatorId] = function () { return drivex.find(locator(), parentWebElement); @@ -89,14 +96,15 @@ Locreator.prototype.addStarMethods = function locreator(locatorId, locatorJSON, Locreator.prototype.addGroup = function (locatorId, locatorJSON) { var nemo = this.nemo; var drivex = this.drivex; - var self = this; + var locreator = this; return function () { //give back the nemo.view.viewname.list() function - return drivex.finds(normalize(nemo, locatorJSON)).then(function (parentWebElements) { + var localizedJSON = locreator.locatex(locatorJSON); + return drivex.finds(locreator.normify(localizedJSON)).then(function (parentWebElements) { return nemo.wd.promise.map(parentWebElements, function (parentWebElement) { var parentObject = {}; - Object.keys(locatorJSON.Elements).forEach(function (childLocatorId) { - var childLocatorJSON = locatorJSON.Elements[childLocatorId]; - var starMethods = self.addStarMethods(childLocatorId, childLocatorJSON, parentWebElement); + Object.keys(localizedJSON.Elements).forEach(function (childLocatorId) { + var childLocatorJSON = localizedJSON.Elements[childLocatorId]; + var starMethods = locreator.addStarMethods(childLocatorId, childLocatorJSON, parentWebElement); _.merge(parentObject, starMethods); }); return parentObject; diff --git a/lib/normalize.js b/lib/normalize.js index 61f3ec0..c4f6e7b 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -31,8 +31,10 @@ var _splitLocator = function (nemo, locatorString) { */ module.exports = function normalize(nemo, _locator) { var locator = _locator; + var normalizedLocator; if (_locator.constructor === String) { locator = _splitLocator(nemo, _locator); } - return nemo.wd.By[locator.type](locator.locator); + normalizedLocator = nemo.wd.By[locator.type](locator.locator); + return normalizedLocator; }; \ No newline at end of file diff --git a/lib/view.js b/lib/view.js index 04d467a..f5389c3 100644 --- a/lib/view.js +++ b/lib/view.js @@ -29,7 +29,7 @@ var _ = require('lodash'); module.exports = function View(nemo, locreator, viewJSON) { return _.transform(viewJSON, function (_viewObject, n, locatorId) { var locatorJSON = viewJSON[locatorId]; - if (locatorJSON.Elements) { + if (locreator.locatex(locatorJSON).Elements) { _viewObject[locatorId] = locreator.addGroup(locatorId, locatorJSON); } else { _.merge(_viewObject, locreator.addStarMethods(locatorId, locatorJSON)); diff --git a/test/locale.js b/test/locale.js index 461c4c9..7538619 100644 --- a/test/locale.js +++ b/test/locale.js @@ -4,35 +4,70 @@ var Nemo = require('nemo'), nemo = {}, path = require('path'), - //assert = require('assert'), + assert = require('assert'), util = require(path.resolve(__dirname, 'util')); describe('nemo-view @locale@', function () { - before(function(done) { - nemo = Nemo(done); + before(function (done) { + nemo = Nemo({ + plugins: { + view: { + module: 'path:../', + arguments: ['path:mocks/locale'] + } + } + }, done); }); - after(function(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 correct locale based locators while locale is reset in realtime', function (done) { - done(new Error('test TBD')); - //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)); + it('works for standard locators', function (done) { + nemo.view.form.text().getAttribute('id').then(function (idValue) { + assert.equal(idValue, 'foo_text'); + nemo._config.set('data:locale', 'DE'); + }).then(function () { + return nemo.view.form.text().getAttribute('id'); + }).then(function (idValue) { + assert.equal(idValue, 'bar_text'); + nemo._config.set('data:locale', ''); + }).then(function () { + return nemo.view.form.text().getAttribute('id'); + }).then(function (idValue) { + assert.equal(idValue, 'foo_text'); + done(); + }); + }); + it('works for Elements with inner locale scope', function (done) { + nemo.view.form.boxInnerLocale().then(function (elts) { + return elts[0].elt().getAttribute('type').then(function (typeValue) { + assert.equal(typeValue, 'text'); + nemo._config.set('data:locale', 'DE'); + return elts[0].elt().getAttribute('type'); + }); + }).then(function (typeValue) { + assert.equal(typeValue, 'button'); + done(); + }); + }); + it('works for Elements with outer locale scope', function (done) { + nemo._config.set('data:locale', null); + nemo.view.form.boxOuterLocale().then(function (elts) { + elts[0].elt().getAttribute('id').then(function (idValue) { + assert.equal(idValue, 'foo_text'); + return true; + }); + }).then(function (typeValue) { + nemo._config.set('data:locale', 'DE'); + nemo.view.form.boxOuterLocale().then(function (elts) { + elts[0].elt().getAttribute('id').then(function (idValue) { + assert.equal(idValue, 'bar_text'); + done(); + }); + }); + }); }); }); \ No newline at end of file diff --git a/test/mocks/locale/form.json b/test/mocks/locale/form.json new file mode 100644 index 0000000..6a78f36 --- /dev/null +++ b/test/mocks/locale/form.json @@ -0,0 +1,38 @@ +{ + "text": { + "default": { + "locator": "#foo input.texty", + "type": "css" + }, + "DE": { + "locator": "#bar input.texty", + "type": "css" + } + }, + "boxInnerLocale": { + "locator": "#foo", + "type": "css", + "Elements": { + "elt": { + "default": "input.texty", + "DE": "[type=button]" + } + } + }, + "boxOuterLocale": { + "default": { + "locator": "#foo", + "type": "css", + "Elements": { + "elt": "input.texty" + } + }, + "DE": { + "locator": "#bar", + "type": "css", + "Elements": { + "elt": "input.texty" + } + } + } +} \ No newline at end of file From 600e245fa7f5df922ee053f3712fde4d3764ce26 Mon Sep 17 00:00:00 2001 From: Matt Edelman Date: Wed, 3 Jun 2015 18:09:48 -0700 Subject: [PATCH 3/6] 1.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4fc6a87..8f9b5ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nemo-view", - "version": "1.1.0", + "version": "1.2.0", "description": "View Interface for nemo views (requires nemo-drivex and nemo-locatex plugins)", "main": "index.js", "registerAs": "view", From 733c040d9408ca8e7874278661521468c2b75fc6 Mon Sep 17 00:00:00 2001 From: Matt Edelman Date: Wed, 3 Jun 2015 18:17:31 -0700 Subject: [PATCH 4/6] update README --- README.md | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 73203c8..116a197 100644 --- a/README.md +++ b/README.md @@ -381,7 +381,39 @@ Other than that, the nemo-view uses nemo-locatex internally, so if you change yo ## Using LOCALE specific locators -Please see these sections in the nemo-locatex README: -* https://github.com/paypal/nemo-locatex#changing-your-locator-files -* https://github.com/paypal/nemo-locatex#setting-locale +You can specify different locator strings/strategies based on locale. To do so, first modify the entry you wish to be locale-specific: +```js +{ + "myLocator": { + "type": "css", + "locator": ".myLocator" + } +} +``` + +changes to + +```js +{ + "myLocator": { + "default": { + "type": "css", + "locator": ".myLocator" + }, + "DE": { + "type": "css", + "locator": ".meinLocator" + } + } +} +``` + +In order to specify a locale, you need to set the `nemo.data.locale` property. This can either be done directly in the +configuration file, or it can be set "on the fly" with the following command: + +```js +nemo._config.set('data:locale', 'DE'); +``` + +You can also refer to the unit tests for the locale feature (found in `test/locale.js`) in this module. From 4493bf5f4dff19a8d6eb858b583e861e13d0850e Mon Sep 17 00:00:00 2001 From: Matt Edelman Date: Wed, 3 Jun 2015 18:19:13 -0700 Subject: [PATCH 5/6] slight change to documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 116a197..b98b23a 100644 --- a/README.md +++ b/README.md @@ -416,4 +416,4 @@ configuration file, or it can be set "on the fly" with the following command: nemo._config.set('data:locale', 'DE'); ``` -You can also refer to the unit tests for the locale feature (found in `test/locale.js`) in this module. +For a working example, refer to the unit tests for the locale feature (found in `test/locale.js`) in this module. From 9d9ea2bb46239bf7fbb2e688eefde43f39497c38 Mon Sep 17 00:00:00 2001 From: Matt Edelman Date: Wed, 10 Jun 2015 09:40:09 -0700 Subject: [PATCH 6/6] adding NOTE to locale feature describing issues which came up in code review --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b98b23a..012be9b 100644 --- a/README.md +++ b/README.md @@ -379,9 +379,10 @@ Other than that, the nemo-view uses nemo-locatex internally, so if you change yo * value: the expected value for the element * returns: Promise which resolves to true when the expected text matches -## Using LOCALE specific locators +## Using locator specialization -You can specify different locator strings/strategies based on locale. To do so, first modify the entry you wish to be locale-specific: +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: ```js { @@ -409,11 +410,15 @@ changes to } ``` -In order to specify a locale, you need to set the `nemo.data.locale` property. This can either be done directly in the -configuration file, or it can be set "on the fly" with the following command: +You can set the `data.locale` property as follows: ```js 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](https://github.com/paypal/nemo-view/pull/32) and issue [here](https://github.com/paypal/nemo-view/issues/33). +Follow along as we discuss a backwards compatible way to resolve this unfortunate nomenclature error. \ No newline at end of file