UNPKG

13.9 kBJavaScriptView Raw
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 Defines common conditions for use with
20 * {@link webdriver.WebDriver#wait WebDriver wait}.
21 *
22 * Sample usage:
23 *
24 * driver.get('http://www.google.com/ncr');
25 *
26 * var query = driver.wait(until.elementLocated(By.name('q')));
27 * query.sendKeys('webdriver\n');
28 *
29 * driver.wait(until.titleIs('webdriver - Google Search'));
30 *
31 * To define a custom condition, simply call WebDriver.wait with a function
32 * that will eventually return a truthy-value (neither null, undefined, false,
33 * 0, or the empty string):
34 *
35 * driver.wait(function() {
36 * return driver.getTitle().then(function(title) {
37 * return title === 'webdriver - Google Search';
38 * });
39 * }, 1000);
40 */
41
42'use strict';
43
44const by = require('./by');
45const By = require('./by').By;
46const error = require('./error');
47const webdriver = require('./webdriver'),
48 Condition = webdriver.Condition,
49 WebElementCondition = webdriver.WebElementCondition;
50
51
52/**
53 * Creates a condition that will wait until the input driver is able to switch
54 * to the designated frame. The target frame may be specified as
55 *
56 * 1. a numeric index into
57 * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames)
58 * for the currently selected frame.
59 * 2. a {@link ./webdriver.WebElement}, which must reference a FRAME or IFRAME
60 * element on the current page.
61 * 3. a locator which may be used to first locate a FRAME or IFRAME on the
62 * current page before attempting to switch to it.
63 *
64 * Upon successful resolution of this condition, the driver will be left
65 * focused on the new frame.
66 *
67 * @param {!(number|./webdriver.WebElement|By|
68 * function(!./webdriver.WebDriver): !./webdriver.WebElement)} frame
69 * The frame identifier.
70 * @return {!Condition<boolean>} A new condition.
71 */
72exports.ableToSwitchToFrame = function ableToSwitchToFrame(frame) {
73 var condition;
74 if (typeof frame === 'number' || frame instanceof webdriver.WebElement) {
75 condition = attemptToSwitchFrames;
76 } else {
77 condition = function(driver) {
78 let locator = /** @type {!(By|Function)} */(frame);
79 return driver.findElements(locator).then(function(els) {
80 if (els.length) {
81 return attemptToSwitchFrames(driver, els[0]);
82 }
83 });
84 };
85 }
86
87 return new Condition('to be able to switch to frame', condition);
88
89 function attemptToSwitchFrames(driver, frame) {
90 return driver.switchTo().frame(frame).then(
91 function() { return true; },
92 function(e) {
93 if (!(e instanceof error.NoSuchFrameError)) {
94 throw e;
95 }
96 });
97 }
98};
99
100
101/**
102 * Creates a condition that waits for an alert to be opened. Upon success, the
103 * returned promise will be fulfilled with the handle for the opened alert.
104 *
105 * @return {!Condition<!./webdriver.Alert>} The new condition.
106 */
107exports.alertIsPresent = function alertIsPresent() {
108 return new Condition('for alert to be present', function(driver) {
109 return driver.switchTo().alert().catch(function(e) {
110 if (!(e instanceof error.NoSuchAlertError
111 // XXX: Workaround for GeckoDriver error `TypeError: can't convert null
112 // to object`. For more details, see
113 // https://github.com/SeleniumHQ/selenium/pull/2137
114 || (e instanceof error.WebDriverError
115 && e.message === `can't convert null to object`)
116 )) {
117 throw e;
118 }
119 });
120 });
121};
122
123
124/**
125 * Creates a condition that will wait for the current page's title to match the
126 * given value.
127 *
128 * @param {string} title The expected page title.
129 * @return {!Condition<boolean>} The new condition.
130 */
131exports.titleIs = function titleIs(title) {
132 return new Condition(
133 'for title to be ' + JSON.stringify(title),
134 function(driver) {
135 return driver.getTitle().then(function(t) {
136 return t === title;
137 });
138 });
139};
140
141
142/**
143 * Creates a condition that will wait for the current page's title to contain
144 * the given substring.
145 *
146 * @param {string} substr The substring that should be present in the page
147 * title.
148 * @return {!Condition<boolean>} The new condition.
149 */
150exports.titleContains = function titleContains(substr) {
151 return new Condition(
152 'for title to contain ' + JSON.stringify(substr),
153 function(driver) {
154 return driver.getTitle().then(function(title) {
155 return title.indexOf(substr) !== -1;
156 });
157 });
158};
159
160
161/**
162 * Creates a condition that will wait for the current page's title to match the
163 * given regular expression.
164 *
165 * @param {!RegExp} regex The regular expression to test against.
166 * @return {!Condition<boolean>} The new condition.
167 */
168exports.titleMatches = function titleMatches(regex) {
169 return new Condition('for title to match ' + regex, function(driver) {
170 return driver.getTitle().then(function(title) {
171 return regex.test(title);
172 });
173 });
174};
175
176
177/**
178 * Creates a condition that will wait for the current page's url to match the
179 * given value.
180 *
181 * @param {string} url The expected page url.
182 * @return {!Condition<boolean>} The new condition.
183 */
184exports.urlIs = function urlIs(url) {
185 return new Condition(
186 'for URL to be ' + JSON.stringify(url),
187 function(driver) {
188 return driver.getCurrentUrl().then(function(u) {
189 return u === url;
190 });
191 });
192};
193
194
195/**
196 * Creates a condition that will wait for the current page's url to contain
197 * the given substring.
198 *
199 * @param {string} substrUrl The substring that should be present in the current
200 * URL.
201 * @return {!Condition<boolean>} The new condition.
202 */
203exports.urlContains = function urlContains(substrUrl) {
204 return new Condition(
205 'for URL to contain ' + JSON.stringify(substrUrl),
206 function(driver) {
207 return driver.getCurrentUrl().then(function(url) {
208 return url.indexOf(substrUrl) !== -1;
209 });
210 });
211};
212
213
214/**
215 * Creates a condition that will wait for the current page's url to match the
216 * given regular expression.
217 *
218 * @param {!RegExp} regex The regular expression to test against.
219 * @return {!Condition<boolean>} The new condition.
220 */
221exports.urlMatches = function urlMatches(regex) {
222 return new Condition('for URL to match ' + regex, function(driver) {
223 return driver.getCurrentUrl().then(function(url) {
224 return regex.test(url);
225 });
226 });
227};
228
229
230/**
231 * Creates a condition that will loop until an element is
232 * {@link ./webdriver.WebDriver#findElement found} with the given locator.
233 *
234 * @param {!(By|Function)} locator The locator to use.
235 * @return {!WebElementCondition} The new condition.
236 */
237exports.elementLocated = function elementLocated(locator) {
238 locator = by.checkedLocator(locator);
239 let locatorStr =
240 typeof locator === 'function' ? 'by function()' : locator + '';
241 return new WebElementCondition('for element to be located ' + locatorStr,
242 function(driver) {
243 return driver.findElements(locator).then(function(elements) {
244 return elements[0];
245 });
246 });
247};
248
249
250/**
251 * Creates a condition that will loop until at least one element is
252 * {@link ./webdriver.WebDriver#findElement found} with the given locator.
253 *
254 * @param {!(By|Function)} locator The locator to use.
255 * @return {!Condition<!Array<!./webdriver.WebElement>>} The new
256 * condition.
257 */
258exports.elementsLocated = function elementsLocated(locator) {
259 locator = by.checkedLocator(locator);
260 let locatorStr =
261 typeof locator === 'function' ? 'by function()' : locator + '';
262 return new Condition(
263 'for at least one element to be located ' + locatorStr,
264 function(driver) {
265 return driver.findElements(locator).then(function(elements) {
266 return elements.length > 0 ? elements : null;
267 });
268 });
269};
270
271
272/**
273 * Creates a condition that will wait for the given element to become stale. An
274 * element is considered stale once it is removed from the DOM, or a new page
275 * has loaded.
276 *
277 * @param {!./webdriver.WebElement} element The element that should become stale.
278 * @return {!Condition<boolean>} The new condition.
279 */
280exports.stalenessOf = function stalenessOf(element) {
281 return new Condition('element to become stale', function() {
282 return element.getTagName().then(
283 function() { return false; },
284 function(e) {
285 if (e instanceof error.StaleElementReferenceError) {
286 return true;
287 }
288 throw e;
289 });
290 });
291};
292
293
294/**
295 * Creates a condition that will wait for the given element to become visible.
296 *
297 * @param {!./webdriver.WebElement} element The element to test.
298 * @return {!WebElementCondition} The new condition.
299 * @see ./webdriver.WebDriver#isDisplayed
300 */
301exports.elementIsVisible = function elementIsVisible(element) {
302 return new WebElementCondition('until element is visible', function() {
303 return element.isDisplayed().then(v => v ? element : null);
304 });
305};
306
307
308/**
309 * Creates a condition that will wait for the given element to be in the DOM,
310 * yet not visible to the user.
311 *
312 * @param {!./webdriver.WebElement} element The element to test.
313 * @return {!WebElementCondition} The new condition.
314 * @see ./webdriver.WebDriver#isDisplayed
315 */
316exports.elementIsNotVisible = function elementIsNotVisible(element) {
317 return new WebElementCondition('until element is not visible', function() {
318 return element.isDisplayed().then(v => v ? null : element);
319 });
320};
321
322
323/**
324 * Creates a condition that will wait for the given element to be enabled.
325 *
326 * @param {!./webdriver.WebElement} element The element to test.
327 * @return {!WebElementCondition} The new condition.
328 * @see webdriver.WebDriver#isEnabled
329 */
330exports.elementIsEnabled = function elementIsEnabled(element) {
331 return new WebElementCondition('until element is enabled', function() {
332 return element.isEnabled().then(v => v ? element : null);
333 });
334};
335
336
337/**
338 * Creates a condition that will wait for the given element to be disabled.
339 *
340 * @param {!./webdriver.WebElement} element The element to test.
341 * @return {!WebElementCondition} The new condition.
342 * @see webdriver.WebDriver#isEnabled
343 */
344exports.elementIsDisabled = function elementIsDisabled(element) {
345 return new WebElementCondition('until element is disabled', function() {
346 return element.isEnabled().then(v => v ? null : element);
347 });
348};
349
350
351/**
352 * Creates a condition that will wait for the given element to be selected.
353 * @param {!./webdriver.WebElement} element The element to test.
354 * @return {!WebElementCondition} The new condition.
355 * @see webdriver.WebDriver#isSelected
356 */
357exports.elementIsSelected = function elementIsSelected(element) {
358 return new WebElementCondition('until element is selected', function() {
359 return element.isSelected().then(v => v ? element : null);
360 });
361};
362
363
364/**
365 * Creates a condition that will wait for the given element to be deselected.
366 *
367 * @param {!./webdriver.WebElement} element The element to test.
368 * @return {!WebElementCondition} The new condition.
369 * @see webdriver.WebDriver#isSelected
370 */
371exports.elementIsNotSelected = function elementIsNotSelected(element) {
372 return new WebElementCondition('until element is not selected', function() {
373 return element.isSelected().then(v => v ? null : element);
374 });
375};
376
377
378/**
379 * Creates a condition that will wait for the given element's
380 * {@link webdriver.WebDriver#getText visible text} to match the given
381 * {@code text} exactly.
382 *
383 * @param {!./webdriver.WebElement} element The element to test.
384 * @param {string} text The expected text.
385 * @return {!WebElementCondition} The new condition.
386 * @see webdriver.WebDriver#getText
387 */
388exports.elementTextIs = function elementTextIs(element, text) {
389 return new WebElementCondition('until element text is', function() {
390 return element.getText().then(t => t === text ? element : null);
391 });
392};
393
394
395/**
396 * Creates a condition that will wait for the given element's
397 * {@link webdriver.WebDriver#getText visible text} to contain the given
398 * substring.
399 *
400 * @param {!./webdriver.WebElement} element The element to test.
401 * @param {string} substr The substring to search for.
402 * @return {!WebElementCondition} The new condition.
403 * @see webdriver.WebDriver#getText
404 */
405exports.elementTextContains = function elementTextContains(element, substr) {
406 return new WebElementCondition('until element text contains', function() {
407 return element.getText()
408 .then(t => t.indexOf(substr) != -1 ? element : null);
409 });
410};
411
412
413/**
414 * Creates a condition that will wait for the given element's
415 * {@link webdriver.WebDriver#getText visible text} to match a regular
416 * expression.
417 *
418 * @param {!./webdriver.WebElement} element The element to test.
419 * @param {!RegExp} regex The regular expression to test against.
420 * @return {!WebElementCondition} The new condition.
421 * @see webdriver.WebDriver#getText
422 */
423exports.elementTextMatches = function elementTextMatches(element, regex) {
424 return new WebElementCondition('until element text matches', function() {
425 return element.getText().then(t => regex.test(t) ? element : null);
426 });
427};