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