diff --git a/lib/locators.js b/lib/locators.js index 4ce25860c..71202170e 100644 --- a/lib/locators.js +++ b/lib/locators.js @@ -19,6 +19,29 @@ var WebdriverBy = function() {}; WebdriverBy.prototype = webdriver.By; util.inherits(ProtractorBy, WebdriverBy); +/** + * Add a locator to this instance of ProtractorBy. This locator can then be + * used with element(by.()). + * + * @param {string} name + * @param {function|string} script A script to be run in the context of + * the browser. This script will be passed an array of arguments + * that begins with the element scoping the search, and then + * contains any args passed into the locator. It should return + * an array of elements. + */ +ProtractorBy.prototype.addLocator = function(name, script) { + this[name] = function(varArgs) { + return { + findElementsOverride: function(driver, using) { + return driver.findElements( + webdriver.By.js(script), using, varArgs); + }, + message: 'by.' + name + '("' + varArgs + '")' + } + }; +}; + /** * Usage: * {{status}} @@ -31,7 +54,7 @@ ProtractorBy.prototype.binding = function(bindingDescriptor) { webdriver.By.js(clientSideScripts.findBindings), using, bindingDescriptor); }, - message: 'by.binding(' + bindingDescriptor + ')' + message: 'by.binding("' + bindingDescriptor + '")' }; }; @@ -46,7 +69,7 @@ ProtractorBy.prototype.select = function(model) { return driver.findElements( webdriver.By.js(clientSideScripts.findSelects), using, model); }, - message: 'by.select(' + model + ')' + message: 'by.select("' + model + '")' }; }; @@ -61,7 +84,7 @@ ProtractorBy.prototype.selectedOption = function(model) { return driver.findElements( webdriver.By.js(clientSideScripts.findSelectedOptions), using, model); }, - message: 'by.selectedOption(' + model + ')' + message: 'by.selectedOption("' + model + '")' }; }; @@ -77,7 +100,7 @@ ProtractorBy.prototype.input = function(model) { return driver.findElements( webdriver.By.js(clientSideScripts.findInputs), using, model); }, - message: 'by.input(' + model + ')' + message: 'by.input("' + model + '")' }; }; @@ -92,7 +115,7 @@ ProtractorBy.prototype.model = function(model) { return driver.findElements( webdriver.By.js(clientSideScripts.findInputs), using, model); }, - message: 'by.model(' + model + ')' + message: 'by.model("' + model + '")' }; }; @@ -107,7 +130,7 @@ ProtractorBy.prototype.textarea = function(model) { return driver.findElements( webdriver.By.js(clientSideScripts.findTextareas), using, model); }, - message: 'by.textarea(' + model + ')' + message: 'by.textarea("' + model + '")' }; }; @@ -137,7 +160,7 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findAllRepeaterRows), using, repeatDescriptor); }, - message: 'by.repeater(' + repeatDescriptor + ')', + message: 'by.repeater("' + repeatDescriptor + '")', row: function(index) { return { findElementsOverride: function(driver, using) { @@ -145,7 +168,7 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterRows), using, repeatDescriptor, index); }, - message: 'by.repeater(' + repeatDescriptor + ').row(' + index + ')', + message: 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"', column: function(binding) { return { findElementsOverride: function(driver, using) { @@ -153,8 +176,8 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterElement), using, repeatDescriptor, index, binding); }, - message: 'by.repeater(' + repeatDescriptor + ').row(' + index + - ').column(' + binding + ')' + message: 'by.repeater("' + repeatDescriptor + '").row("' + index + + '").column("' + binding + '")' }; } }; @@ -166,8 +189,8 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterColumn), using, repeatDescriptor, binding); }, - message: 'by.repeater(' + repeatDescriptor + ').column(' + binding + - ')', + message: 'by.repeater("' + repeatDescriptor + '").column("' + binding + + '")', row: function(index) { return { findElementsOverride: function(driver, using) { @@ -175,8 +198,8 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterElement), using, repeatDescriptor, index, binding); }, - message: 'by.repeater(' + repeatDescriptor + ').column(' + binding - + ').row(' + index + ')' + message: 'by.repeater("' + repeatDescriptor + '").column("' + + binding + '").row("' + index + '")' }; } }; diff --git a/spec/basic/lib_spec.js b/spec/basic/lib_spec.js index c839566af..6d85c1851 100644 --- a/spec/basic/lib_spec.js +++ b/spec/basic/lib_spec.js @@ -47,8 +47,23 @@ describe('protractor library', function() { expect(protractor.Key.RETURN).toEqual('\uE006'); }); - // it('should allow adding custom locators', function() { - // by.addLocator(); - - // }); + it('should allow adding custom locators', function() { + var findMenuItem = function() { + var using = arguments[0]; // unused + var itemName = arguments[1]; + var menu = document.querySelectorAll('.menu li'); + for (var i = 0; i < menu.length; ++i) { + if (menu[i].innerText == itemName) { + return [menu[i]]; + } + } + }; + + by.addLocator('menuItem', findMenuItem); + + expect(by.menuItem).toBeDefined(); + + expect(element(by.menuItem('repeater')).isPresent()); + expect(element(by.menuItem('repeater')).getText()).toEqual('repeater'); + }); });