# lib/webdriver/locators.js

    1// Copyright 2011 Software Freedom Conservancy. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @fileoverview Factory methods for the supported locator strategies.17 */1819goog.provide('webdriver.By');20goog.provide('webdriver.Locator');21goog.provide('webdriver.Locator.Strategy');2223goog.require('goog.array');24goog.require('goog.object');25goog.require('goog.string');26272829/**30 * An element locator.31 * @param {string} using The type of strategy to use for this locator.32 * @param {string} value The search target of this locator.33 * @constructor34 */35webdriver.Locator = function(using, value) {3637  /**38   * The search strategy to use when searching for an element.39   * @type {string}40   */41  this.using = using;4243  /**44   * The search target for this locator.45   * @type {string}46   */47  this.value = value;48};495051/**52 * Creates a factory function for a {@link webdriver.Locator}.53 * @param {string} type The type of locator for the factory.54 * @return {function(string): !webdriver.Locator} The new factory function.55 * @private56 */57webdriver.Locator.factory_ = function(type) {58  return function(value) {59    return new webdriver.Locator(type, value);60  };61};626364/**65 * A collection of factory functions for creating {@link webdriver.Locator}66 * instances.67 */68webdriver.By = {};69// Exported to the global scope for legacy reasons.70goog.exportSymbol('By', webdriver.By);717273/**74 * Short-hand expressions for the primary element locator strategies.75 * For example the following two statements are equivalent:76 * <code><pre>77 * var e1 = driver.findElement(webdriver.By.id('foo'));78 * var e2 = driver.findElement({id: 'foo'});79 * </pre></code>80 *81 * <p>Care should be taken when using JavaScript minifiers (such as the82 * Closure compiler), as locator hashes will always be parsed using83 * the un-obfuscated properties listed below.84 *85 * @typedef {(86 *     {className: string}|87 *     {css: string}|88 *     {id: string}|89 *     {js: string}|90 *     {linkText: string}|91 *     {name: string}|92 *     {partialLinkText: string}|93 *     {tagName: string}|94 *     {xpath: string})}95 */96webdriver.By.Hash;979899/**100 * Locates elements that have a specific class name. The returned locator101 * is equivalent to searching for elements with the CSS selector ".clazz".102 *103 * @param {string} className The class name to search for.104 * @return {!webdriver.Locator} The new locator.105 * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes106 * @see http://www.w3.org/TR/CSS2/selector.html#class-html107 */108webdriver.By.className = webdriver.Locator.factory_('class name');109110111/**112 * Locates elements using a CSS selector. For browsers that do not support113 * CSS selectors, WebDriver implementations may return an114 * {@link bot.Error.State.INVALID_SELECTOR invalid selector} error. An115 * implementation may, however, emulate the CSS selector API.116 *117 * @param {string} selector The CSS selector to use.118 * @return {!webdriver.Locator} The new locator.119 * @see http://www.w3.org/TR/CSS2/selector.html120 */121webdriver.By.css = webdriver.Locator.factory_('css selector');122123124/**125 * Locates an element by its ID.126 *127 * @param {string} id The ID to search for.128 * @return {!webdriver.Locator} The new locator.129 */130webdriver.By.id = webdriver.Locator.factory_('id');131132133/**134 * Locates link elements whose {@link webdriver.WebElement#getText visible135 * text} matches the given string.136 *137 * @param {string} text The link text to search for.138 * @return {!webdriver.Locator} The new locator.139 */140webdriver.By.linkText = webdriver.Locator.factory_('link text');141142143/**144 * Locates an elements by evaluating a145 * {@link webdriver.WebDriver#executeScript JavaScript expression}.146 * The result of this expression must be an element or list of elements.147 *148 * @param {!(string|Function)} script The script to execute.149 * @param {...*} var_args The arguments to pass to the script.150 * @return {function(!webdriver.WebDriver): !webdriver.promise.Promise} A new,151 *     JavaScript-based locator function.152 */153webdriver.By.js = function(script, var_args) {154  var args = goog.array.slice(arguments, 0);155  return function(driver) {156    return driver.executeScript.apply(driver, args);157  };158};159160161/**162 * Locates elements whose {@code name} attribute has the given value.163 *164 * @param {string} name The name attribute to search for.165 * @return {!webdriver.Locator} The new locator.166 */167webdriver.By.name = webdriver.Locator.factory_('name');168169170/**171 * Locates link elements whose {@link webdriver.WebElement#getText visible172 * text} contains the given substring.173 *174 * @param {string} text The substring to check for in a link's visible text.175 * @return {!webdriver.Locator} The new locator.176 */177webdriver.By.partialLinkText = webdriver.Locator.factory_(178    'partial link text');179180181/**182 * Locates elements with a given tag name. The returned locator is183 * equivalent to using the {@code getElementsByTagName} DOM function.184 *185 * @param {string} text The substring to check for in a link's visible text.186 * @return {!webdriver.Locator} The new locator.187 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html188 */189webdriver.By.tagName = webdriver.Locator.factory_('tag name');190191192/**193 * Locates elements matching a XPath selector. Care should be taken when194 * using an XPath selector with a {@link webdriver.WebElement} as WebDriver195 * will respect the context in the specified in the selector. For example,196 * given the selector {@code "//div"}, WebDriver will search from the197 * document root regardless of whether the locator was used with a198 * WebElement.199 *200 * @param {string} xpath The XPath selector to use.201 * @return {!webdriver.Locator} The new locator.202 * @see http://www.w3.org/TR/xpath/203 */204webdriver.By.xpath = webdriver.Locator.factory_('xpath');205206207/**208 * Maps {@link webdriver.By.Hash} keys to the appropriate factory function.209 * @type {!Object.<string, function(string): !(Function|webdriver.Locator)>}210 * @const211 */212webdriver.Locator.Strategy = {213  'className': webdriver.By.className,214  'css': webdriver.By.css,215  'id': webdriver.By.id,216  'js': webdriver.By.js,217  'linkText': webdriver.By.linkText,218  'name': webdriver.By.name,219  'partialLinkText': webdriver.By.partialLinkText,220  'tagName': webdriver.By.tagName,221  'xpath': webdriver.By.xpath222};223224225/**226 * Verifies that a {@code value} is a valid locator to use for searching for227 * elements on the page.228 *229 * @param {*} value The value to check is a valid locator.230 * @return {!(webdriver.Locator|Function)} A valid locator object or function.231 * @throws {TypeError} If the given value is an invalid locator.232 */233webdriver.Locator.checkLocator = function(value) {234  if (goog.isFunction(value) || value instanceof webdriver.Locator) {235    return value;236  }237  for (var key in value) {238    if (value.hasOwnProperty(key) &&239        webdriver.Locator.Strategy.hasOwnProperty(key)) {240      return webdriver.Locator.Strategy[key](value[key]);241    }242  }243  throw new TypeError('Invalid locator');244};245246247248/** @override */249webdriver.Locator.prototype.toString = function() {250  return 'By.' + this.using.replace(/ ([a-z])/g, function(all, match) {251    return match.toUpperCase();252  }) + '(' + goog.string.quote(this.value) + ')';253};