diff --git a/lib/globals.d.ts b/lib/globals.d.ts index 5cd9283d8..d2972a073 100644 --- a/lib/globals.d.ts +++ b/lib/globals.d.ts @@ -25,6 +25,7 @@ declare var DartObject: any; declare namespace webdriver { class WebDriver { + findElements: Function; getSession: Function; quit: Function; static attachToSession: Function; @@ -45,4 +46,8 @@ declare namespace webdriver { isEnabled: Function; findElements: Function; } + + interface Locator { + toString(): string; + } } diff --git a/lib/locators.js b/lib/locators.js deleted file mode 100644 index 8eac442cc..000000000 --- a/lib/locators.js +++ /dev/null @@ -1,456 +0,0 @@ -var util = require('util'); -var webdriver = require('selenium-webdriver'); - -var clientSideScripts = require('./clientsidescripts.js'); - -/** - * The Protractor Locators. These provide ways of finding elements in - * Angular applications by binding, model, etc. - * - * @alias by - * @extends {webdriver.By} - */ -var ProtractorBy = function() {}; -var WebdriverBy = function() {}; - -/** - * webdriver's By is an enum of locator functions, so we must set it to - * a prototype before inheriting from it. - */ -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.locatorName(args)). - * - * @view - * - * - * @example - * // Add the custom locator. - * 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, - * buttons = using.querySelectorAll('button'); - * - * // Return an array of buttons with the text. - * return Array.prototype.filter.call(buttons, function(button) { - * return button.textContent === buttonText; - * }); - * }); - * - * // Use the custom locator. - * element(by.buttonTextSimple('Go!')).click(); - * - * @alias by.addLocator(locatorName, functionOrScript) - * @param {string} name The name of the new locator. - * @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 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, 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)); - }, - toString: function toString() { - return 'by.' + name + '("' + Array.prototype. - join.call(locatorArguments, '", "') + '")'; - } - }; - }; -}; - -/** - * Find an element by text binding. Does a partial match, so any elements bound - * to variables containing the input string will be returned. - * - * Note: For AngularJS version 1.2, the interpolation brackets, (usually {{}}), - * are optionally allowed in the binding description string. For Angular version - * 1.3+, they are not allowed, and no elements will be found if they are used. - * - * @view - * {{person.name}} - * - * - * @example - * var span1 = element(by.binding('person.name')); - * expect(span1.getText()).toBe('Foo'); - * - * var span2 = element(by.binding('person.email')); - * expect(span2.getText()).toBe('foo@bar.com'); - * - * // You can also use a substring for a partial match - * var span1alt = element(by.binding('name')); - * expect(span1alt.getText()).toBe('Foo'); - * - * // This works for sites using Angular 1.2 but NOT 1.3 - * var deprecatedSyntax = element(by.binding('{{person.name}}')); - * - * @param {string} bindingDescriptor - * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} - */ -ProtractorBy.prototype.binding = function(bindingDescriptor) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findBindings, - bindingDescriptor, false, using, rootSelector)); - }, - toString: function toString() { - return 'by.binding("' + bindingDescriptor + '")'; - } - }; -}; - -/** - * Find an element by exact binding. - * - * @view - * {{ person.name }} - * - * {{person_phone|uppercase}} - * - * @example - * expect(element(by.exactBinding('person.name')).isPresent()).toBe(true); - * expect(element(by.exactBinding('person-email')).isPresent()).toBe(true); - * expect(element(by.exactBinding('person')).isPresent()).toBe(false); - * expect(element(by.exactBinding('person_phone')).isPresent()).toBe(true); - * expect(element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true); - * expect(element(by.exactBinding('phone')).isPresent()).toBe(false); - * - * @param {string} bindingDescriptor - * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} - */ -ProtractorBy.prototype.exactBinding = function(bindingDescriptor) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findBindings, - bindingDescriptor, true, using, rootSelector)); - }, - toString: function toString() { - return 'by.exactBinding("' + bindingDescriptor + '")'; - } - }; -}; - -/** - * Find an element by ng-model expression. - * - * @alias by.model(modelName) - * @view - * - * - * @example - * var input = element(by.model('person.name')); - * input.sendKeys('123'); - * expect(input.getAttribute('value')).toBe('Foo123'); - * - * @param {string} model ng-model expression. - */ -ProtractorBy.prototype.model = function(model) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js( - clientSideScripts.findByModel, model, using, rootSelector)); - }, - toString: function toString() { - return 'by.model("' + model + '")'; - } - }; -}; - -/** - * Find a button by text. - * - * @view - * - * - * @example - * element(by.buttonText('Save')); - * - * @param {string} searchText - * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} - */ -ProtractorBy.prototype.buttonText = function(searchText) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findByButtonText, - searchText, using, rootSelector)); - }, - toString: function toString() { - return 'by.buttonText("' + searchText + '")'; - } - }; -}; - -/** - * Find a button by partial text. - * - * @view - * - * - * @example - * element(by.partialButtonText('Save')); - * - * @param {string} searchText - * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} - */ -ProtractorBy.prototype.partialButtonText = function(searchText) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findByPartialButtonText, - searchText, using, rootSelector)); - }, - toString: function toString() { - return 'by.partialButtonText("' + searchText + '")'; - } - }; -}; - -// Generate either by.repeater or by.exactRepeater -function byRepeaterInner(exact) { - var name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater'; - return function(repeatDescriptor) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findAllRepeaterRows, - repeatDescriptor, exact, using, rootSelector)); - }, - toString: function toString() { - return name + '("' + repeatDescriptor + '")'; - }, - row: function(index) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findRepeaterRows, - repeatDescriptor, exact, index, using, rootSelector)); - }, - toString: function toString() { - return name + '(' + repeatDescriptor + '").row("' + index + '")"'; - }, - column: function(binding) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findRepeaterElement, - repeatDescriptor, exact, index, binding, using, - rootSelector)); - }, - toString: function toString() { - return name + '("' + repeatDescriptor + '").row("' + index + - '").column("' + binding + '")'; - } - }; - } - }; - }, - column: function(binding) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findRepeaterColumn, - repeatDescriptor, exact, binding, using, rootSelector)); - }, - toString: function toString() { - return name + '("' + repeatDescriptor + '").column("' + - binding + '")'; - }, - row: function(index) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findRepeaterElement, - repeatDescriptor, exact, index, binding, using, rootSelector)); - }, - toString: function toString() { - return name + '("' + repeatDescriptor + '").column("' + - binding + '").row("' + index + '")'; - } - }; - } - }; - } - }; - }; -} - -/** - * Find elements inside an ng-repeat. - * - * @view - *
- * {{cat.name}} - * {{cat.age}} - *
- * - *
- * {{$index}} - *
- *
- *

{{book.name}}

- *

{{book.blurb}}

- *
- * - * @example - * // Returns the DIV for the second cat. - * var secondCat = element(by.repeater('cat in pets').row(1)); - * - * // Returns the SPAN for the first cat's name. - * var firstCatName = element(by.repeater('cat in pets'). - * row(0).column('cat.name')); - * - * // Returns a promise that resolves to an array of WebElements from a column - * var ages = element.all( - * by.repeater('cat in pets').column('cat.age')); - * - * // Returns a promise that resolves to an array of WebElements containing - * // all top level elements repeated by the repeater. For 2 pets rows resolves - * // to an array of 2 elements. - * var rows = element.all(by.repeater('cat in pets')); - * - * // Returns a promise that resolves to an array of WebElements containing all - * // the elements with a binding to the book's name. - * var divs = element.all(by.repeater('book in library').column('book.name')); - * - * // Returns a promise that resolves to an array of WebElements containing - * // the DIVs for the second book. - * var bookInfo = element.all(by.repeater('book in library').row(1)); - * - * // Returns the H4 for the first book's name. - * var firstBookName = element(by.repeater('book in library'). - * row(0).column('book.name')); - * - * // Returns a promise that resolves to an array of WebElements containing - * // all top level elements repeated by the repeater. For 2 books divs - * // resolves to an array of 4 elements. - * var divs = element.all(by.repeater('book in library')); - * - * @param {string} repeatDescriptor - * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} - */ -ProtractorBy.prototype.repeater = byRepeaterInner(false); - -/** - * Find an element by exact repeater. - * - * @view - *
  • - *
  • - * - * @example - * expect(element(by.exactRepeater('person in peopleWithRedHair')).isPresent()) - * .toBe(true); - * expect(element(by.exactRepeater('person in people')).isPresent()).toBe(false); - * expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true); - * - * @param {string} repeatDescriptor - * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} - */ -ProtractorBy.prototype.exactRepeater = byRepeaterInner(true); - - -/** - * Find elements by CSS which contain a certain string. - * - * @view - * - * - * @example - * // Returns the li for the dog, but not cat. - * var dog = element(by.cssContainingText('.pet', 'Dog')); - */ -ProtractorBy.prototype.cssContainingText = function(cssSelector, searchText) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findByCssContainingText, - cssSelector, searchText, using, rootSelector)); - }, - toString: function toString() { - return 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")'; - } - }; -}; - -/** - * Find an element by ng-options expression. - * - * @alias by.options(optionsDescriptor) - * @view - * - * - * @example - * var allOptions = element.all(by.options('c for c in colors')); - * expect(allOptions.count()).toEqual(2); - * var firstOption = allOptions.first(); - * expect(firstOption.getText()).toEqual('red'); - * - * @param {string} optionsDescriptor ng-options expression. - */ -ProtractorBy.prototype.options = function(optionsDescriptor) { - return { - findElementsOverride: function(driver, using, rootSelector) { - return driver.findElements( - webdriver.By.js(clientSideScripts.findByOptions, optionsDescriptor, - using, rootSelector)); - }, - toString: function toString() { - return 'by.option("' + optionsDescriptor + '")'; - } - }; -}; - -/** - * Find an element by css selector within the Shadow DOM. - * - * @alias by.deepCss(selector) - * @view - *
    - * - * <"shadow tree"> - * - * <"shadow tree"> - * - * - * - *
    - * @example - * var spans = element.all(by.deepCss('span')); - * expect(spans.count()).toEqual(3); - */ -ProtractorBy.prototype.deepCss = function(selector) { - // TODO(julie): syntax will change from /deep/ to >>> at some point. - // When that is supported, switch it here. - return webdriver.By.css('* /deep/ ' + selector); -}; - -exports.ProtractorBy = ProtractorBy; diff --git a/lib/locators.ts b/lib/locators.ts new file mode 100644 index 000000000..b6d768963 --- /dev/null +++ b/lib/locators.ts @@ -0,0 +1,460 @@ +import * as util from 'util'; +let webdriver: any = require('selenium-webdriver'); +let clientSideScripts: any = require('./clientsidescripts'); + +/** + * webdriver's By is an enum of locator functions, so we must set it to + * a prototype before inheriting from it. + */ +export class WebdriverBy {}; +WebdriverBy.prototype = webdriver.By; + +export interface Locator extends webdriver.Locator { + findElementsOverride?: + (driver: webdriver.WebDriver, using: webdriver.WebElement, + rootSelector: string) => webdriver.WebElement; + row?: (index: number) => Locator; + column?: (index: string) => Locator; +} +; + +/** + * The Protractor Locators. These provide ways of finding elements in + * Angular applications by binding, model, etc. + * + * @alias by + * @extends {webdriver.By} + */ +export class ProtractorBy extends WebdriverBy { + /** + * Add a locator to this instance of ProtractorBy. This locator can then be + * used with element(by.locatorName(args)). + * + * @view + * + * + * @example + * // Add the custom locator. + * 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, + * buttons = using.querySelectorAll('button'); + * + * // Return an array of buttons with the text. + * return Array.prototype.filter.call(buttons, function(button) { + * return button.textContent === buttonText; + * }); + * }); + * + * // Use the custom locator. + * element(by.buttonTextSimple('Go!')).click(); + * + * @alias by.addLocator(locatorName, functionOrScript) + * @param {string} name The name of the new locator. + * @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 and the css selector for the root angular + * element. It should return an array of elements. + */ + addLocator(name: string, script: Function|string) { + this[name] = function(): Locator { + var locatorArguments = arguments; + return { + findElementsOverride: function(driver, using, rootSelector) { + var findElementArguments: any[] = [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)); + }, + toString: function toString() { + return 'by.' + name + '("' + + Array.prototype.join.call(locatorArguments, '", "') + '")'; + } + }; + }; + }; + + /** + * Find an element by text binding. Does a partial match, so any elements bound + * to variables containing the input string will be returned. + * + * Note: For AngularJS version 1.2, the interpolation brackets, (usually {{}}), + * are optionally allowed in the binding description string. For Angular version + * 1.3+, they are not allowed, and no elements will be found if they are used. + * + * @view + * {{person.name}} + * + * + * @example + * var span1 = element(by.binding('person.name')); + * expect(span1.getText()).toBe('Foo'); + * + * var span2 = element(by.binding('person.email')); + * expect(span2.getText()).toBe('foo@bar.com'); + * + * // You can also use a substring for a partial match + * var span1alt = element(by.binding('name')); + * expect(span1alt.getText()).toBe('Foo'); + * + * // This works for sites using Angular 1.2 but NOT 1.3 + * var deprecatedSyntax = element(by.binding('{{person.name}}')); + * + * @param {string} bindingDescriptor + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} + */ + binding(bindingDescriptor: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findBindings, bindingDescriptor, false, using, + rootSelector)); + }, + toString: function toString() { + return 'by.binding("' + bindingDescriptor + '")'; + } + }; + }; + + /** + * Find an element by exact binding. + * + * @view + * {{ person.name }} + * + * {{person_phone|uppercase}} + * + * @example + * expect(element(by.exactBinding('person.name')).isPresent()).toBe(true); + * expect(element(by.exactBinding('person-email')).isPresent()).toBe(true); + * expect(element(by.exactBinding('person')).isPresent()).toBe(false); + * expect(element(by.exactBinding('person_phone')).isPresent()).toBe(true); + * expect(element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true); + * expect(element(by.exactBinding('phone')).isPresent()).toBe(false); + * + * @param {string} bindingDescriptor + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} + */ + exactBinding(bindingDescriptor: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findBindings, bindingDescriptor, true, using, + rootSelector)); + }, + toString: function toString() { + return 'by.exactBinding("' + bindingDescriptor + '")'; + } + }; + }; + + /** + * Find an element by ng-model expression. + * + * @alias by.model(modelName) + * @view + * + * + * @example + * var input = element(by.model('person.name')); + * input.sendKeys('123'); + * expect(input.getAttribute('value')).toBe('Foo123'); + * + * @param {string} model ng-model expression. + */ + model(model: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findByModel, model, using, rootSelector)); + }, + toString: function toString() { return 'by.model("' + model + '")'; } + }; + }; + + /** + * Find a button by text. + * + * @view + * + * + * @example + * element(by.buttonText('Save')); + * + * @param {string} searchText + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} + */ + buttonText(searchText: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findByButtonText, searchText, using, + rootSelector)); + }, + toString: function toString() { + return 'by.buttonText("' + searchText + '")'; + } + }; + }; + + /** + * Find a button by partial text. + * + * @view + * + * + * @example + * element(by.partialButtonText('Save')); + * + * @param {string} searchText + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} + */ + partialButtonText(searchText: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findByPartialButtonText, searchText, using, + rootSelector)); + }, + toString: function toString() { + return 'by.partialButtonText("' + searchText + '")'; + } + }; + }; + + // Generate either by.repeater or by.exactRepeater + private byRepeaterInner(exact: boolean, repeatDescriptor: string): Locator { + var name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater'; + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findAllRepeaterRows, repeatDescriptor, exact, + using, rootSelector)); + }, + toString: function toString() { + return name + '("' + repeatDescriptor + '")'; + }, + row: function(index) { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findRepeaterRows, repeatDescriptor, exact, + index, using, rootSelector)); + }, + toString: function toString() { + return name + '(' + repeatDescriptor + '").row("' + index + '")"'; + }, + column: function(binding) { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findRepeaterElement, repeatDescriptor, + exact, index, binding, using, rootSelector)); + }, + toString: function toString() { + return name + '("' + repeatDescriptor + '").row("' + index + + '").column("' + binding + '")'; + } + }; + } + }; + }, + column: function(binding) { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findRepeaterColumn, repeatDescriptor, exact, + binding, using, rootSelector)); + }, + toString: function toString() { + return name + '("' + repeatDescriptor + '").column("' + binding + + '")'; + }, + row: function(index) { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findRepeaterElement, repeatDescriptor, + exact, index, binding, using, rootSelector)); + }, + toString: function toString() { + return name + '("' + repeatDescriptor + '").column("' + + binding + '").row("' + index + '")'; + } + }; + } + }; + } + }; + } + + /** + * Find elements inside an ng-repeat. + * + * @view + *
    + * {{cat.name}} + * {{cat.age}} + *
    + * + *
    + * {{$index}} + *
    + *
    + *

    {{book.name}}

    + *

    {{book.blurb}}

    + *
    + * + * @example + * // Returns the DIV for the second cat. + * var secondCat = element(by.repeater('cat in pets').row(1)); + * + * // Returns the SPAN for the first cat's name. + * var firstCatName = element(by.repeater('cat in pets'). + * row(0).column('cat.name')); + * + * // Returns a promise that resolves to an array of WebElements from a column + * var ages = element.all( + * by.repeater('cat in pets').column('cat.age')); + * + * // Returns a promise that resolves to an array of WebElements containing + * // all top level elements repeated by the repeater. For 2 pets rows resolves + * // to an array of 2 elements. + * var rows = element.all(by.repeater('cat in pets')); + * + * // Returns a promise that resolves to an array of WebElements containing all + * // the elements with a binding to the book's name. + * var divs = element.all(by.repeater('book in library').column('book.name')); + * + * // Returns a promise that resolves to an array of WebElements containing + * // the DIVs for the second book. + * var bookInfo = element.all(by.repeater('book in library').row(1)); + * + * // Returns the H4 for the first book's name. + * var firstBookName = element(by.repeater('book in library'). + * row(0).column('book.name')); + * + * // Returns a promise that resolves to an array of WebElements containing + * // all top level elements repeated by the repeater. For 2 books divs + * // resolves to an array of 4 elements. + * var divs = element.all(by.repeater('book in library')); + * + * @param {string} repeatDescriptor + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} + */ + repeater(repeatDescriptor: string): Locator { + return this.byRepeaterInner(false, repeatDescriptor); + } + + /** + * Find an element by exact repeater. + * + * @view + *
  • + *
  • + * + * @example + * expect(element(by.exactRepeater('person in peopleWithRedHair')).isPresent()) + * .toBe(true); + * expect(element(by.exactRepeater('person in people')).isPresent()).toBe(false); + * expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true); + * + * @param {string} repeatDescriptor + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} + */ + exactRepeater(repeatDescriptor: string): Locator { + return this.byRepeaterInner(true, repeatDescriptor); + } + + + /** + * Find elements by CSS which contain a certain string. + * + * @view + * + * + * @example + * // Returns the li for the dog, but not cat. + * var dog = element(by.cssContainingText('.pet', 'Dog')); + */ + cssContainingText(cssSelector: string, searchText: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findByCssContainingText, cssSelector, searchText, + using, rootSelector)); + }, + toString: function toString() { + return 'by.cssContainingText("' + cssSelector + '", "' + searchText + + '")'; + } + }; + }; + + /** + * Find an element by ng-options expression. + * + * @alias by.options(optionsDescriptor) + * @view + * + * + * @example + * var allOptions = element.all(by.options('c for c in colors')); + * expect(allOptions.count()).toEqual(2); + * var firstOption = allOptions.first(); + * expect(firstOption.getText()).toEqual('red'); + * + * @param {string} optionsDescriptor ng-options expression. + */ + options(optionsDescriptor: string): Locator { + return { + findElementsOverride: function(driver, using, rootSelector) { + return driver.findElements(webdriver.By.js( + clientSideScripts.findByOptions, optionsDescriptor, using, + rootSelector)); + }, + toString: function toString() { + return 'by.option("' + optionsDescriptor + '")'; + } + }; + }; + + /** + * Find an element by css selector within the Shadow DOM. + * + * @alias by.deepCss(selector) + * @view + *
    + * + * <"shadow tree"> + * + * <"shadow tree"> + * + * + * + *
    + * @example + * var spans = element.all(by.deepCss('span')); + * expect(spans.count()).toEqual(3); + */ + deepCss(selector: string): Locator { + // TODO(julie): syntax will change from /deep/ to >>> at some point. + // When that is supported, switch it here. + return webdriver.By.css('* /deep/ ' + selector); + }; +}