lib/webdriver/locators.js

1// Licensed to the Software Freedom Conservancy (SFC) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The SFC licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18/**
19 * @fileoverview Factory methods for the supported locator strategies.
20 */
21
22goog.provide('webdriver.By');
23goog.provide('webdriver.Locator');
24goog.provide('webdriver.Locator.Strategy');
25
26goog.require('goog.array');
27goog.require('goog.object');
28goog.require('goog.string');
29
30
31
32/**
33 * An element locator.
34 * @param {string} using The type of strategy to use for this locator.
35 * @param {string} value The search target of this locator.
36 * @constructor
37 */
38webdriver.Locator = function(using, value) {
39
40 /**
41 * The search strategy to use when searching for an element.
42 * @type {string}
43 */
44 this.using = using;
45
46 /**
47 * The search target for this locator.
48 * @type {string}
49 */
50 this.value = value;
51};
52
53
54/**
55 * Creates a factory function for a {@link webdriver.Locator}.
56 * @param {string} type The type of locator for the factory.
57 * @return {function(string): !webdriver.Locator} The new factory function.
58 * @private
59 */
60webdriver.Locator.factory_ = function(type) {
61 return function(value) {
62 return new webdriver.Locator(type, value);
63 };
64};
65
66
67/**
68 * A collection of factory functions for creating {@link webdriver.Locator}
69 * instances.
70 */
71webdriver.By = {};
72// Exported to the global scope for legacy reasons.
73goog.exportSymbol('By', webdriver.By);
74
75
76/**
77 * Short-hand expressions for the primary element locator strategies.
78 * For example the following two statements are equivalent:
79 *
80 * var e1 = driver.findElement(webdriver.By.id('foo'));
81 * var e2 = driver.findElement({id: 'foo'});
82 *
83 * Care should be taken when using JavaScript minifiers (such as the
84 * Closure compiler), as locator hashes will always be parsed using
85 * the un-obfuscated properties listed.
86 *
87 * @typedef {(
88 * {className: string}|
89 * {css: string}|
90 * {id: string}|
91 * {js: string}|
92 * {linkText: string}|
93 * {name: string}|
94 * {partialLinkText: string}|
95 * {tagName: string}|
96 * {xpath: string})}
97 */
98webdriver.By.Hash;
99
100
101/**
102 * Locates elements that have a specific class name. The returned locator
103 * is equivalent to searching for elements with the CSS selector ".clazz".
104 *
105 * @param {string} className The class name to search for.
106 * @return {!webdriver.Locator} The new locator.
107 * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
108 * @see http://www.w3.org/TR/CSS2/selector.html#class-html
109 */
110webdriver.By.className = webdriver.Locator.factory_('class name');
111
112
113/**
114 * Locates elements using a CSS selector. For browsers that do not support
115 * CSS selectors, WebDriver implementations may return an
116 * {@linkplain bot.Error.State.INVALID_SELECTOR invalid selector} error. An
117 * implementation may, however, emulate the CSS selector API.
118 *
119 * @param {string} selector The CSS selector to use.
120 * @return {!webdriver.Locator} The new locator.
121 * @see http://www.w3.org/TR/CSS2/selector.html
122 */
123webdriver.By.css = webdriver.Locator.factory_('css selector');
124
125
126/**
127 * Locates an element by its ID.
128 *
129 * @param {string} id The ID to search for.
130 * @return {!webdriver.Locator} The new locator.
131 */
132webdriver.By.id = webdriver.Locator.factory_('id');
133
134
135/**
136 * Locates link elements whose {@linkplain webdriver.WebElement#getText visible
137 * text} matches the given string.
138 *
139 * @param {string} text The link text to search for.
140 * @return {!webdriver.Locator} The new locator.
141 */
142webdriver.By.linkText = webdriver.Locator.factory_('link text');
143
144
145/**
146 * Locates an elements by evaluating a
147 * {@linkplain webdriver.WebDriver#executeScript JavaScript expression}.
148 * The result of this expression must be an element or list of elements.
149 *
150 * @param {!(string|Function)} script The script to execute.
151 * @param {...*} var_args The arguments to pass to the script.
152 * @return {function(!webdriver.WebDriver): !webdriver.promise.Promise} A new,
153 * JavaScript-based locator function.
154 */
155webdriver.By.js = function(script, var_args) {
156 var args = goog.array.slice(arguments, 0);
157 return function(driver) {
158 return driver.executeScript.apply(driver, args);
159 };
160};
161
162
163/**
164 * Locates elements whose {@code name} attribute has the given value.
165 *
166 * @param {string} name The name attribute to search for.
167 * @return {!webdriver.Locator} The new locator.
168 */
169webdriver.By.name = webdriver.Locator.factory_('name');
170
171
172/**
173 * Locates link elements whose {@linkplain webdriver.WebElement#getText visible
174 * text} contains the given substring.
175 *
176 * @param {string} text The substring to check for in a link's visible text.
177 * @return {!webdriver.Locator} The new locator.
178 */
179webdriver.By.partialLinkText = webdriver.Locator.factory_(
180 'partial link text');
181
182
183/**
184 * Locates elements with a given tag name. The returned locator is
185 * equivalent to using the
186 * [getElementsByTagName](https://developer.mozilla.org/en-US/docs/Web/API/Element.getElementsByTagName)
187 * DOM function.
188 *
189 * @param {string} text The substring to check for in a link's visible text.
190 * @return {!webdriver.Locator} The new locator.
191 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html
192 */
193webdriver.By.tagName = webdriver.Locator.factory_('tag name');
194
195
196/**
197 * Locates elements matching a XPath selector. Care should be taken when
198 * using an XPath selector with a {@link webdriver.WebElement} as WebDriver
199 * will respect the context in the specified in the selector. For example,
200 * given the selector {@code "//div"}, WebDriver will search from the
201 * document root regardless of whether the locator was used with a
202 * WebElement.
203 *
204 * @param {string} xpath The XPath selector to use.
205 * @return {!webdriver.Locator} The new locator.
206 * @see http://www.w3.org/TR/xpath/
207 */
208webdriver.By.xpath = webdriver.Locator.factory_('xpath');
209
210
211/**
212 * Maps {@link webdriver.By.Hash} keys to the appropriate factory function.
213 * @type {!Object.<string, function(string): !(Function|webdriver.Locator)>}
214 * @const
215 */
216webdriver.Locator.Strategy = {
217 'className': webdriver.By.className,
218 'css': webdriver.By.css,
219 'id': webdriver.By.id,
220 'js': webdriver.By.js,
221 'linkText': webdriver.By.linkText,
222 'name': webdriver.By.name,
223 'partialLinkText': webdriver.By.partialLinkText,
224 'tagName': webdriver.By.tagName,
225 'xpath': webdriver.By.xpath
226};
227
228
229/**
230 * Verifies that a {@code value} is a valid locator to use for searching for
231 * elements on the page.
232 *
233 * @param {*} value The value to check is a valid locator.
234 * @return {!(webdriver.Locator|Function)} A valid locator object or function.
235 * @throws {TypeError} If the given value is an invalid locator.
236 */
237webdriver.Locator.checkLocator = function(value) {
238 if (goog.isFunction(value) || value instanceof webdriver.Locator) {
239 return value;
240 }
241 for (var key in value) {
242 if (value.hasOwnProperty(key) &&
243 webdriver.Locator.Strategy.hasOwnProperty(key)) {
244 return webdriver.Locator.Strategy[key](value[key]);
245 }
246 }
247 throw new TypeError('Invalid locator');
248};
249
250
251
252/** @override */
253webdriver.Locator.prototype.toString = function() {
254 return 'By.' + this.using.replace(/ ([a-z])/g, function(all, match) {
255 return match.toUpperCase();
256 }) + '(' + goog.string.quote(this.value) + ')';
257};