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 at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15/**
16 * @fileoverview Factory methods for the supported locator strategies.
17 */
18
19goog.provide('webdriver.By');
20goog.provide('webdriver.Locator');
21goog.provide('webdriver.Locator.Strategy');
22
23goog.require('goog.array');
24goog.require('goog.object');
25goog.require('goog.string');
26
27
28
29/**
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 * @constructor
34 */
35webdriver.Locator = function(using, value) {
36
37 /**
38 * The search strategy to use when searching for an element.
39 * @type {string}
40 */
41 this.using = using;
42
43 /**
44 * The search target for this locator.
45 * @type {string}
46 */
47 this.value = value;
48};
49
50
51/**
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 * @private
56 */
57webdriver.Locator.factory_ = function(type) {
58 return function(value) {
59 return new webdriver.Locator(type, value);
60 };
61};
62
63
64/**
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);
71
72
73/**
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 the
82 * Closure compiler), as locator hashes will always be parsed using
83 * 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;
97
98
99/**
100 * Locates elements that have a specific class name. The returned locator
101 * 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#classes
106 * @see http://www.w3.org/TR/CSS2/selector.html#class-html
107 */
108webdriver.By.className = webdriver.Locator.factory_('class name');
109
110
111/**
112 * Locates elements using a CSS selector. For browsers that do not support
113 * CSS selectors, WebDriver implementations may return an
114 * {@link bot.Error.State.INVALID_SELECTOR invalid selector} error. An
115 * 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.html
120 */
121webdriver.By.css = webdriver.Locator.factory_('css selector');
122
123
124/**
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');
131
132
133/**
134 * Locates link elements whose {@link webdriver.WebElement#getText visible
135 * 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');
141
142
143/**
144 * Locates an elements by evaluating a
145 * {@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};
159
160
161/**
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');
168
169
170/**
171 * Locates link elements whose {@link webdriver.WebElement#getText visible
172 * 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');
179
180
181/**
182 * Locates elements with a given tag name. The returned locator is
183 * 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.html
188 */
189webdriver.By.tagName = webdriver.Locator.factory_('tag name');
190
191
192/**
193 * Locates elements matching a XPath selector. Care should be taken when
194 * using an XPath selector with a {@link webdriver.WebElement} as WebDriver
195 * will respect the context in the specified in the selector. For example,
196 * given the selector {@code "//div"}, WebDriver will search from the
197 * document root regardless of whether the locator was used with a
198 * 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');
205
206
207/**
208 * Maps {@link webdriver.By.Hash} keys to the appropriate factory function.
209 * @type {!Object.<string, function(string): !(Function|webdriver.Locator)>}
210 * @const
211 */
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.xpath
222};
223
224
225/**
226 * Verifies that a {@code value} is a valid locator to use for searching for
227 * 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};
245
246
247
248/** @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};