diff --git a/lib/clientsidescripts.js b/lib/clientsidescripts.js index f666a7a15..9f2bfbb21 100644 --- a/lib/clientsidescripts.js +++ b/lib/clientsidescripts.js @@ -45,11 +45,13 @@ functions.waitForAngular = function(selector, callback) { * @param {string} binding The binding, e.g. {{cat.name}}. * @param {boolean} exactMatch Whether the binding needs to be matched exactly * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The elements containing the binding. */ -functions.findBindings = function(binding, exactMatch, using) { - using = using || document; +functions.findBindings = function(binding, exactMatch, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); if (angular.getTestability) { return angular.getTestability(using). findBindings(using, binding, exactMatch); @@ -83,13 +85,15 @@ functions.findBindings = function(binding, exactMatch, using) { * * @param {string} repeater The text of the repeater, e.g. 'cat in cats'. * @param {number} index The row index. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The row of the repeater, or an array of elements * in the first row in the case of ng-repeat-start. */ - functions.findRepeaterRows = function(repeater, index, using) { - using = using || document; + functions.findRepeaterRows = function(repeater, index, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; var rows = []; @@ -132,12 +136,14 @@ functions.findBindings = function(binding, exactMatch, using) { * Find all rows of an ng-repeat. * * @param {string} repeater The text of the repeater, e.g. 'cat in cats'. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} All rows of the repeater. */ - functions.findAllRepeaterRows = function(repeater, using) { - using = using || document; + functions.findAllRepeaterRows = function(repeater, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); var rows = []; var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; @@ -177,13 +183,15 @@ functions.findBindings = function(binding, exactMatch, using) { * @param {string} repeater The text of the repeater, e.g. 'cat in cats'. * @param {number} index The row index. * @param {string} binding The column binding, e.g. '{{cat.name}}'. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The element in an array. */ -functions.findRepeaterElement = function(repeater, index, binding, using) { +functions.findRepeaterElement = function(repeater, index, binding, using, rootSelector) { var matches = []; - using = using || document; + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); var rows = []; var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; @@ -272,13 +280,15 @@ functions.findRepeaterElement = function(repeater, index, binding, using) { * * @param {string} repeater The text of the repeater, e.g. 'cat in cats'. * @param {string} binding The column binding, e.g. '{{cat.name}}'. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The elements in the column. */ -functions.findRepeaterColumn = function(repeater, binding, using) { +functions.findRepeaterColumn = function(repeater, binding, using, rootSelector) { var matches = []; - using = using || document; + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); var rows = []; var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; @@ -364,12 +374,15 @@ functions.findRepeaterColumn = function(repeater, binding, using) { * Find elements by model name. * * @param {string} model The model name. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The matching elements. */ -functions.findByModel = function(model, using) { - using = using || document; +functions.findByModel = function(model, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); + if (angular.getTestability) { return angular.getTestability(using). findModels(using, model); @@ -389,12 +402,15 @@ functions.findByModel = function(model, using) { * * @param {string} optionsDescriptor The descriptor for the option * (i.e. fruit for fruit in fruits). - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The matching elements. */ -functions.findByOptions = function(optionsDescriptor, using) { - using = using || document; +functions.findByOptions = function(optionsDescriptor, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); + var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; for (var p = 0; p < prefixes.length; ++p) { var selector = '[' + prefixes[p] + 'options="' + optionsDescriptor + '"] option'; @@ -409,12 +425,15 @@ functions.findByOptions = function(optionsDescriptor, using) { * Find buttons by textual content. * * @param {string} searchText The exact text to match. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The matching elements. */ -functions.findByButtonText = function(searchText, using) { - using = using || document; +functions.findByButtonText = function(searchText, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); + var elements = using.querySelectorAll('button, input[type="button"], input[type="submit"]'); var matches = []; for (var i = 0; i < elements.length; ++i) { @@ -437,12 +456,15 @@ functions.findByButtonText = function(searchText, using) { * Find buttons by textual content. * * @param {string} searchText The exact text to match. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} The matching elements. */ -functions.findByPartialButtonText = function(searchText, using) { - using = using || document; +functions.findByPartialButtonText = function(searchText, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); + var elements = using.querySelectorAll('button, input[type="button"], input[type="submit"]'); var matches = []; for (var i = 0; i < elements.length; ++i) { @@ -466,12 +488,15 @@ functions.findByPartialButtonText = function(searchText, using) { * * @param {string} cssSelector The css selector to match. * @param {string} searchText The exact text to match. - * @param {Element} using The scope of the search. Defaults to 'document'. + * @param {Element} using The scope of the search. + * @param {string} rootSelector The selector to use for the root app element. * * @return {Array.} An array of matching elements. */ -functions.findByCssContainingText = function(cssSelector, searchText, using) { - var using = using || document; +functions.findByCssContainingText = function(cssSelector, searchText, using, rootSelector) { + rootSelector = rootSelector || 'body'; + using = using || document.querySelector(rootSelector); + var elements = using.querySelectorAll(cssSelector); var matches = []; for (var i = 0; i < elements.length; ++i) { @@ -528,7 +553,6 @@ functions.testForAngular = function(attempts, asyncCallback) { * @return {?Object} The result of the evaluation. */ functions.evaluate = function(element, expression) { - return angular.element(element).scope().$eval(expression); }; diff --git a/lib/locators.js b/lib/locators.js index fd59831e0..ff0813d84 100644 --- a/lib/locators.js +++ b/lib/locators.js @@ -28,11 +28,12 @@ util.inherits(ProtractorBy, WebdriverBy); * * @example * // Add the custom locator. - * by.addLocator('buttonTextSimple', function(buttonText, opt_parentElement) { + * by.addLocator('buttonTextSimple', + * function(buttonText, opt_parentElement, opt_rootSelector) { * // This function will be serialized as a string and will execute in the * // browser. The first argument is the text for the button. The second * // argument is the parent element, if any. - * var using = opt_parentElement || document, + * var using = opt_parentElement || document.querySelector(opt_rootSelector), * buttons = using.querySelectorAll('button'); * * // Return an array of buttons with the text. @@ -49,18 +50,20 @@ util.inherits(ProtractorBy, WebdriverBy); * @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 contains any args passed into the locator followed by the - * element scoping the search. It should return an array of elements. + * element scoping the search and the css selector for the root angular + * element. It should return an array of elements. */ ProtractorBy.prototype.addLocator = function(name, script) { this[name] = function() { var locatorArguments = arguments; return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { var findElementArguments = [script]; for (var i = 0; i < locatorArguments.length; i++) { findElementArguments.push(locatorArguments[i]); } findElementArguments.push(using); + findElementArguments.push(rootSelector); return driver.findElements( webdriver.By.js.apply(webdriver.By, findElementArguments)); @@ -93,10 +96,10 @@ ProtractorBy.prototype.addLocator = function(name, script) { */ ProtractorBy.prototype.binding = function(bindingDescriptor) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findBindings, - bindingDescriptor, false, using)); + bindingDescriptor, false, using, rootSelector)); }, toString: function toString() { return 'by.binding("' + bindingDescriptor + '")'; @@ -126,10 +129,10 @@ ProtractorBy.prototype.binding = function(bindingDescriptor) { */ ProtractorBy.prototype.exactBinding = function(bindingDescriptor) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findBindings, - bindingDescriptor, true, using)); + bindingDescriptor, true, using, rootSelector)); }, toString: function toString() { return 'by.exactBinding("' + bindingDescriptor + '")'; @@ -153,9 +156,10 @@ ProtractorBy.prototype.exactBinding = function(bindingDescriptor) { */ ProtractorBy.prototype.model = function(model) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( - webdriver.By.js(clientSideScripts.findByModel, model, using)); + webdriver.By.js( + clientSideScripts.findByModel, model, using, rootSelector)); }, toString: function toString() { return 'by.model("' + model + '")'; @@ -177,10 +181,10 @@ ProtractorBy.prototype.model = function(model) { */ ProtractorBy.prototype.buttonText = function(searchText) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findByButtonText, - searchText, using)); + searchText, using, rootSelector)); }, toString: function toString() { return 'by.buttonText("' + searchText + '")'; @@ -202,10 +206,10 @@ ProtractorBy.prototype.buttonText = function(searchText) { */ ProtractorBy.prototype.partialButtonText = function(searchText) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findByPartialButtonText, - searchText, using)); + searchText, using, rootSelector)); }, toString: function toString() { return 'by.partialButtonText("' + searchText + '")'; @@ -267,30 +271,30 @@ ProtractorBy.prototype.partialButtonText = function(searchText) { */ ProtractorBy.prototype.repeater = function(repeatDescriptor) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findAllRepeaterRows, - repeatDescriptor, using)); + repeatDescriptor, using, rootSelector)); }, toString: function toString() { return 'by.repeater("' + repeatDescriptor + '")'; }, row: function(index) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findRepeaterRows, - repeatDescriptor, index, using)); + repeatDescriptor, index, using, rootSelector)); }, toString: function toString() { return 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"'; }, column: function(binding) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findRepeaterElement, - repeatDescriptor, index, binding, using)); + repeatDescriptor, index, binding, using, rootSelector)); }, toString: function toString() { return 'by.repeater("' + repeatDescriptor + '").row("' + index + @@ -302,10 +306,10 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { }, column: function(binding) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findRepeaterColumn, - repeatDescriptor, binding, using)); + repeatDescriptor, binding, using, rootSelector)); }, toString: function toString() { return 'by.repeater("' + repeatDescriptor + '").column("' + @@ -313,10 +317,10 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { }, row: function(index) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findRepeaterElement, - repeatDescriptor, index, binding, using)); + repeatDescriptor, index, binding, using, rootSelector)); }, toString: function toString() { return 'by.repeater("' + repeatDescriptor + '").column("' + @@ -344,10 +348,10 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { */ ProtractorBy.prototype.cssContainingText = function(cssSelector, searchText) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( webdriver.By.js(clientSideScripts.findByCssContainingText, - cssSelector, searchText, using)); + cssSelector, searchText, using, rootSelector)); }, toString: function toString() { return 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")'; @@ -375,9 +379,10 @@ ProtractorBy.prototype.cssContainingText = function(cssSelector, searchText) { */ ProtractorBy.prototype.options = function(optionsDescriptor) { return { - findElementsOverride: function(driver, using) { + findElementsOverride: function(driver, using, rootSelector) { return driver.findElements( - webdriver.By.js(clientSideScripts.findByOptions, optionsDescriptor, using)); + webdriver.By.js(clientSideScripts.findByOptions, optionsDescriptor, + using, rootSelector)); }, toString: function toString() { return 'by.option("' + optionsDescriptor + '")'; diff --git a/lib/protractor.js b/lib/protractor.js index 0f937577e..3cb99a2a3 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -115,7 +115,7 @@ var buildElementHelper = function(ptor) { if (this.parentElementFinder_) { var parentWebElement = this.parentElementFinder_.getWebElement(); if (this.locator_.findElementsOverride) { - return this.locator_.findElementsOverride(ptor.driver, parentWebElement); + return this.locator_.findElementsOverride(ptor.driver, parentWebElement, ptor.rootEl); } else { return parentWebElement.findElements(this.locator_); } @@ -123,7 +123,7 @@ var buildElementHelper = function(ptor) { var self = this; return ptor.waitForAngular().then(function() { if (self.locator_.findElementsOverride) { - return self.locator_.findElementsOverride(ptor.driver); + return self.locator_.findElementsOverride(ptor.driver, null, ptor.rootEl); } else { return ptor.driver.findElements(self.locator_); } @@ -889,6 +889,8 @@ var Protractor = function(webdriverInstance, opt_baseUrl, opt_rootElement) { * @type {Array<{name: string, script: function|string, args: Array.}>} */ this.mockModules_ = []; + + this.addBaseMockModules_(); }; /** @@ -987,6 +989,7 @@ Protractor.prototype.addMockModule = function(name, script) { */ Protractor.prototype.clearMockModules = function() { this.mockModules_ = []; + this.addBaseMockModules_(); }; /** @@ -1001,6 +1004,22 @@ Protractor.prototype.removeMockModule = function(name) { } }; +/** + * Add the base mock modules used for all Protractor tests. + * + * @private + */ +Protractor.prototype.addBaseMockModules_ = function() { + this.addMockModule('protractorBaseModule_', function() { + angular.module('protractorBaseModule_', []). + config(['$compileProvider', function($compileProvider) { + if ($compileProvider.enableDebugInfo) { + $compileProvider.enableDebugInfo(true); + }; + }]); + }); +}; + /** * See webdriver.WebDriver.get *