selenium-webdriver 4.3.1 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +43 -0
- package/README.md +29 -29
- package/index.js +2 -0
- package/io/exec.js +7 -4
- package/lib/by.js +1 -2
- package/lib/command.js +3 -3
- package/lib/error.js +8 -8
- package/lib/http.js +11 -13
- package/lib/input.js +10 -1
- package/lib/promise.js +2 -11
- package/lib/select.js +466 -0
- package/lib/until.js +46 -21
- package/lib/util.js +43 -0
- package/lib/virtual_authenticator.js +43 -29
- package/lib/webdriver.js +31 -42
- package/lib/webelement.js +66 -0
- package/net/index.js +1 -5
- package/package.json +2 -2
- package/remote/index.js +1 -1
- package/testing/index.js +1 -1
package/lib/select.js
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
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
|
+
* Licensed to the Software Freedom Conservancy (SFC) under one
|
|
20
|
+
* or more contributor license agreements. See the NOTICE file
|
|
21
|
+
* distributed with this work for additional information
|
|
22
|
+
* regarding copyright ownership. The SFC licenses this file
|
|
23
|
+
* to you under the Apache License, Version 2.0 (the
|
|
24
|
+
* "License"); you may not use this file except in compliance
|
|
25
|
+
* with the License. You may obtain a copy of the License at
|
|
26
|
+
*
|
|
27
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
28
|
+
*
|
|
29
|
+
* Unless required by applicable law or agreed to in writing,
|
|
30
|
+
* software distributed under the License is distributed on an
|
|
31
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
32
|
+
* KIND, either express or implied. See the License for the
|
|
33
|
+
* specific language governing permissions and limitations
|
|
34
|
+
* under the License.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
'use strict'
|
|
38
|
+
|
|
39
|
+
const { By, escapeCss } = require('./by')
|
|
40
|
+
const error = require('./error')
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* ISelect interface makes a protocol for all kind of select elements (standard html and custom
|
|
44
|
+
* model)
|
|
45
|
+
*
|
|
46
|
+
* @interface
|
|
47
|
+
*/
|
|
48
|
+
// eslint-disable-next-line no-unused-vars
|
|
49
|
+
class ISelect {
|
|
50
|
+
/**
|
|
51
|
+
* @return {!Promise<boolean>} Whether this select element supports selecting multiple options at the same time? This
|
|
52
|
+
* is done by checking the value of the "multiple" attribute.
|
|
53
|
+
*/
|
|
54
|
+
isMultiple() {} // eslint-disable-line
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @return {!Promise<!Array<!WebElement>>} All options belonging to this select tag
|
|
58
|
+
*/
|
|
59
|
+
getOptions() {} // eslint-disable-line
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @return {!Promise<!Array<!WebElement>>} All selected options belonging to this select tag
|
|
63
|
+
*/
|
|
64
|
+
getAllSelectedOptions() {} // eslint-disable-line
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @return {!Promise<!WebElement>} The first selected option in this select tag (or the currently selected option in a
|
|
68
|
+
* normal select)
|
|
69
|
+
*/
|
|
70
|
+
getFirstSelectedOption() {} // eslint-disable-line
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Select all options that display text matching the argument. That is, when given "Bar" this
|
|
74
|
+
* would select an option like:
|
|
75
|
+
*
|
|
76
|
+
* <option value="foo">Bar</option>
|
|
77
|
+
*
|
|
78
|
+
* @param {string} text The visible text to match against
|
|
79
|
+
* @return {Promise<void>}
|
|
80
|
+
*/
|
|
81
|
+
selectByVisibleText(text) {} // eslint-disable-line
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Select all options that have a value matching the argument. That is, when given "foo" this
|
|
85
|
+
* would select an option like:
|
|
86
|
+
*
|
|
87
|
+
* <option value="foo">Bar</option>
|
|
88
|
+
*
|
|
89
|
+
* @param {string} value The value to match against
|
|
90
|
+
* @return {Promise<void>}
|
|
91
|
+
*/
|
|
92
|
+
selectByValue(value) {} // eslint-disable-line
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Select the option at the given index. This is done by examining the "index" attribute of an
|
|
96
|
+
* element, and not merely by counting.
|
|
97
|
+
*
|
|
98
|
+
* @param {Number} index The option at this index will be selected
|
|
99
|
+
* @return {Promise<void>}
|
|
100
|
+
*/
|
|
101
|
+
selectByIndex(index) {} // eslint-disable-line
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Clear all selected entries. This is only valid when the SELECT supports multiple selections.
|
|
105
|
+
*
|
|
106
|
+
* @return {Promise<void>}
|
|
107
|
+
*/
|
|
108
|
+
deselectAll() {} // eslint-disable-line
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Deselect all options that display text matching the argument. That is, when given "Bar" this
|
|
112
|
+
* would deselect an option like:
|
|
113
|
+
*
|
|
114
|
+
* <option value="foo">Bar</option>
|
|
115
|
+
*
|
|
116
|
+
* @param {string} text The visible text to match against
|
|
117
|
+
* @return {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
deselectByVisibleText(text) {} // eslint-disable-line
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Deselect all options that have a value matching the argument. That is, when given "foo" this
|
|
123
|
+
* would deselect an option like:
|
|
124
|
+
*
|
|
125
|
+
* @param {string} value The value to match against
|
|
126
|
+
* @return {Promise<void>}
|
|
127
|
+
*/
|
|
128
|
+
deselectByValue(value) {} // eslint-disable-line
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Deselect the option at the given index. This is done by examining the "index" attribute of an
|
|
132
|
+
* element, and not merely by counting.
|
|
133
|
+
*
|
|
134
|
+
* @param {Number} index The option at this index will be deselected
|
|
135
|
+
* @return {Promise<void>}
|
|
136
|
+
*/
|
|
137
|
+
deselectByIndex(index) {} // eslint-disable-line
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @implements ISelect
|
|
142
|
+
*/
|
|
143
|
+
class Select {
|
|
144
|
+
/**
|
|
145
|
+
* Create an Select Element
|
|
146
|
+
* @param {WebElement} element Select WebElement.
|
|
147
|
+
*/
|
|
148
|
+
constructor(element) {
|
|
149
|
+
this.element = element
|
|
150
|
+
|
|
151
|
+
this.element.getAttribute('tagName').then(function (tagName) {
|
|
152
|
+
if (tagName.toLowerCase() !== 'select') {
|
|
153
|
+
throw new Error(`Select only works on <select> elements`)
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
*
|
|
160
|
+
* Select option with specified index.
|
|
161
|
+
*
|
|
162
|
+
* <example>
|
|
163
|
+
<select id="selectbox">
|
|
164
|
+
<option value="1">Option 1</option>
|
|
165
|
+
<option value="2">Option 2</option>
|
|
166
|
+
<option value="3">Option 3</option>
|
|
167
|
+
</select>
|
|
168
|
+
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
169
|
+
await selectObject.selectByIndex(1);
|
|
170
|
+
* </example>
|
|
171
|
+
*
|
|
172
|
+
* @param index
|
|
173
|
+
*/
|
|
174
|
+
async selectByIndex(index) {
|
|
175
|
+
if (index < 0) {
|
|
176
|
+
throw new Error('Index needs to be 0 or any other positive number')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let options = await this.element.findElements(By.tagName('option'))
|
|
180
|
+
|
|
181
|
+
if (options.length === 0) {
|
|
182
|
+
throw new Error("Select element doesn't contain any option element")
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (options.length - 1 < index) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Option with index "${index}" not found. Select element only contains ${
|
|
188
|
+
options.length - 1
|
|
189
|
+
} option elements`
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (let option of options) {
|
|
194
|
+
if ((await option.getAttribute('index')) === index.toString()) {
|
|
195
|
+
await this.setSelected(option)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* Select option by specific value.
|
|
203
|
+
*
|
|
204
|
+
* <example>
|
|
205
|
+
<select id="selectbox">
|
|
206
|
+
<option value="1">Option 1</option>
|
|
207
|
+
<option value="2">Option 2</option>
|
|
208
|
+
<option value="3">Option 3</option>
|
|
209
|
+
</select>
|
|
210
|
+
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
211
|
+
await selectObject.selectByVisibleText("Option 2");
|
|
212
|
+
* </example>
|
|
213
|
+
*
|
|
214
|
+
*
|
|
215
|
+
* @param {string} value value of option element to be selected
|
|
216
|
+
*/
|
|
217
|
+
async selectByValue(value) {
|
|
218
|
+
let matched = false
|
|
219
|
+
let isMulti = await this.isMultiple()
|
|
220
|
+
|
|
221
|
+
let options = await this.element.findElements({
|
|
222
|
+
css: 'option[value =' + escapeCss(value) + ']',
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
for (let option of options) {
|
|
226
|
+
await this.setSelected(option)
|
|
227
|
+
|
|
228
|
+
if (!isMulti) {
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
matched = true
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!matched) {
|
|
235
|
+
throw new Error(`Cannot locate option with value: ${value}`)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
*
|
|
241
|
+
* Select option with displayed text matching the argument.
|
|
242
|
+
*
|
|
243
|
+
* <example>
|
|
244
|
+
<select id="selectbox">
|
|
245
|
+
<option value="1">Option 1</option>
|
|
246
|
+
<option value="2">Option 2</option>
|
|
247
|
+
<option value="3">Option 3</option>
|
|
248
|
+
</select>
|
|
249
|
+
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
250
|
+
await selectObject.selectByVisibleText("Option 2");
|
|
251
|
+
* </example>
|
|
252
|
+
*
|
|
253
|
+
* @param {String|Number} text text of option element to get selected
|
|
254
|
+
*
|
|
255
|
+
*/
|
|
256
|
+
async selectByVisibleText(text) {
|
|
257
|
+
text = typeof text === 'number' ? text.toString() : text
|
|
258
|
+
|
|
259
|
+
const normalized = text
|
|
260
|
+
.trim() // strip leading and trailing white-space characters
|
|
261
|
+
.replace(/\s+/, ' ') // replace sequences of whitespace characters by a single space
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* find option element using xpath
|
|
265
|
+
*/
|
|
266
|
+
const formatted = /"/.test(normalized)
|
|
267
|
+
? 'concat("' + normalized.split('"').join('", \'"\', "') + '")'
|
|
268
|
+
: `"${normalized}"`
|
|
269
|
+
const dotFormat = `[. = ${formatted}]`
|
|
270
|
+
const spaceFormat = `[normalize-space(text()) = ${formatted}]`
|
|
271
|
+
|
|
272
|
+
const selections = [
|
|
273
|
+
`./option${dotFormat}`,
|
|
274
|
+
`./option${spaceFormat}`,
|
|
275
|
+
`./optgroup/option${dotFormat}`,
|
|
276
|
+
`./optgroup/option${spaceFormat}`,
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
const optionElement = await this.element.findElement({
|
|
280
|
+
xpath: selections.join('|'),
|
|
281
|
+
})
|
|
282
|
+
await this.setSelected(optionElement)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Returns a list of all options belonging to this select tag
|
|
287
|
+
* @returns {!Promise<!Array<!WebElement>>}
|
|
288
|
+
*/
|
|
289
|
+
async getOptions() {
|
|
290
|
+
return await this.element.findElements({ tagName: 'option' })
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Retruns a boolean value if the select tag is multiple
|
|
295
|
+
* @returns {Promise<boolean>}
|
|
296
|
+
*/
|
|
297
|
+
async isMultiple() {
|
|
298
|
+
return (await this.element.getAttribute('multiple')) !== null
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Returns a list of all selected options belonging to this select tag
|
|
303
|
+
*
|
|
304
|
+
* @returns {Promise<void>}
|
|
305
|
+
*/
|
|
306
|
+
async getAllSelectedOptions() {
|
|
307
|
+
const opts = await this.getOptions()
|
|
308
|
+
const results = []
|
|
309
|
+
for (let options of opts) {
|
|
310
|
+
if (await options.isSelected()) {
|
|
311
|
+
results.push(options)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return results
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Returns first Selected Option
|
|
319
|
+
* @returns {Promise<Element>}
|
|
320
|
+
*/
|
|
321
|
+
async getFirstSelectedOption() {
|
|
322
|
+
return (await this.getAllSelectedOptions())[0]
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Deselects all selected options
|
|
327
|
+
* @returns {Promise<void>}
|
|
328
|
+
*/
|
|
329
|
+
async deselectAll() {
|
|
330
|
+
if (!this.isMultiple()) {
|
|
331
|
+
throw new Error('You may only deselect all options of a multi-select')
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const options = await this.getOptions()
|
|
335
|
+
|
|
336
|
+
for (let option of options) {
|
|
337
|
+
if (await option.isSelected()) {
|
|
338
|
+
await option.click()
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
*
|
|
345
|
+
* @param {string|Number}text text of option to deselect
|
|
346
|
+
* @returns {Promise<void>}
|
|
347
|
+
*/
|
|
348
|
+
async deselectByVisibleText(text) {
|
|
349
|
+
if (!(await this.isMultiple())) {
|
|
350
|
+
throw new Error('You may only deselect options of a multi-select')
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* convert value into string
|
|
355
|
+
*/
|
|
356
|
+
text = typeof text === 'number' ? text.toString() : text
|
|
357
|
+
|
|
358
|
+
const normalized = text
|
|
359
|
+
.trim() // strip leading and trailing white-space characters
|
|
360
|
+
.replace(/\s+/, ' ') // replace sequences of whitespace characters by a single space
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* find option element using xpath
|
|
364
|
+
*/
|
|
365
|
+
const formatted = /"/.test(normalized)
|
|
366
|
+
? 'concat("' + normalized.split('"').join('", \'"\', "') + '")'
|
|
367
|
+
: `"${normalized}"`
|
|
368
|
+
const dotFormat = `[. = ${formatted}]`
|
|
369
|
+
const spaceFormat = `[normalize-space(text()) = ${formatted}]`
|
|
370
|
+
|
|
371
|
+
const selections = [
|
|
372
|
+
`./option${dotFormat}`,
|
|
373
|
+
`./option${spaceFormat}`,
|
|
374
|
+
`./optgroup/option${dotFormat}`,
|
|
375
|
+
`./optgroup/option${spaceFormat}`,
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
const optionElement = await this.element.findElement({
|
|
379
|
+
xpath: selections.join('|'),
|
|
380
|
+
})
|
|
381
|
+
if (await optionElement.isSelected()) {
|
|
382
|
+
await optionElement.click()
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
*
|
|
388
|
+
* @param {Number} index index of option element to deselect
|
|
389
|
+
* Deselect the option at the given index.
|
|
390
|
+
* This is done by examining the "index"
|
|
391
|
+
* attribute of an element, and not merely by counting.
|
|
392
|
+
* @returns {Promise<void>}
|
|
393
|
+
*/
|
|
394
|
+
async deselectByIndex(index) {
|
|
395
|
+
if (!(await this.isMultiple())) {
|
|
396
|
+
throw new Error('You may only deselect options of a multi-select')
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (index < 0) {
|
|
400
|
+
throw new Error('Index needs to be 0 or any other positive number')
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let options = await this.element.findElements(By.tagName('option'))
|
|
404
|
+
|
|
405
|
+
if (options.length === 0) {
|
|
406
|
+
throw new Error("Select element doesn't contain any option element")
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (options.length - 1 < index) {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`Option with index "${index}" not found. Select element only contains ${
|
|
412
|
+
options.length - 1
|
|
413
|
+
} option elements`
|
|
414
|
+
)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (let option of options) {
|
|
418
|
+
if ((await option.getAttribute('index')) === index.toString()) {
|
|
419
|
+
if (await option.isSelected()) {
|
|
420
|
+
await option.click()
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
*
|
|
428
|
+
* @param {String} value value of an option to deselect
|
|
429
|
+
* @returns {Promise<void>}
|
|
430
|
+
*/
|
|
431
|
+
async deselectByValue(value) {
|
|
432
|
+
if (!(await this.isMultiple())) {
|
|
433
|
+
throw new Error('You may only deselect options of a multi-select')
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
let matched = false
|
|
437
|
+
|
|
438
|
+
let options = await this.element.findElements({
|
|
439
|
+
css: 'option[value =' + escapeCss(value) + ']',
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
for (let option of options) {
|
|
443
|
+
if (await option.isSelected()) {
|
|
444
|
+
await option.click()
|
|
445
|
+
}
|
|
446
|
+
matched = true
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (!matched) {
|
|
450
|
+
throw new Error(`Cannot locate option with value: ${value}`)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async setSelected(option) {
|
|
455
|
+
if (!(await option.isSelected())) {
|
|
456
|
+
if (!(await option.isEnabled())) {
|
|
457
|
+
throw new error.UnsupportedOperationError(
|
|
458
|
+
`You may not select a disabled option`
|
|
459
|
+
)
|
|
460
|
+
}
|
|
461
|
+
await option.click()
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
module.exports = { Select }
|
package/lib/until.js
CHANGED
|
@@ -67,7 +67,7 @@ const webdriver = require('./webdriver'),
|
|
|
67
67
|
* The frame identifier.
|
|
68
68
|
* @return {!Condition<boolean>} A new condition.
|
|
69
69
|
*/
|
|
70
|
-
|
|
70
|
+
function ableToSwitchToFrame(frame) {
|
|
71
71
|
let condition
|
|
72
72
|
if (typeof frame === 'number' || frame instanceof webdriver.WebElement) {
|
|
73
73
|
condition = (driver) => attemptToSwitchFrames(driver, frame)
|
|
@@ -107,7 +107,7 @@ exports.ableToSwitchToFrame = function ableToSwitchToFrame(frame) {
|
|
|
107
107
|
*
|
|
108
108
|
* @return {!Condition<!./webdriver.Alert>} The new condition.
|
|
109
109
|
*/
|
|
110
|
-
|
|
110
|
+
function alertIsPresent() {
|
|
111
111
|
return new Condition('for alert to be present', function (driver) {
|
|
112
112
|
return driver
|
|
113
113
|
.switchTo()
|
|
@@ -136,7 +136,7 @@ exports.alertIsPresent = function alertIsPresent() {
|
|
|
136
136
|
* @param {string} title The expected page title.
|
|
137
137
|
* @return {!Condition<boolean>} The new condition.
|
|
138
138
|
*/
|
|
139
|
-
|
|
139
|
+
function titleIs(title) {
|
|
140
140
|
return new Condition('for title to be ' + JSON.stringify(title), function (
|
|
141
141
|
driver
|
|
142
142
|
) {
|
|
@@ -154,7 +154,7 @@ exports.titleIs = function titleIs(title) {
|
|
|
154
154
|
* title.
|
|
155
155
|
* @return {!Condition<boolean>} The new condition.
|
|
156
156
|
*/
|
|
157
|
-
|
|
157
|
+
function titleContains(substr) {
|
|
158
158
|
return new Condition(
|
|
159
159
|
'for title to contain ' + JSON.stringify(substr),
|
|
160
160
|
function (driver) {
|
|
@@ -172,7 +172,7 @@ exports.titleContains = function titleContains(substr) {
|
|
|
172
172
|
* @param {!RegExp} regex The regular expression to test against.
|
|
173
173
|
* @return {!Condition<boolean>} The new condition.
|
|
174
174
|
*/
|
|
175
|
-
|
|
175
|
+
function titleMatches(regex) {
|
|
176
176
|
return new Condition('for title to match ' + regex, function (driver) {
|
|
177
177
|
return driver.getTitle().then(function (title) {
|
|
178
178
|
return regex.test(title)
|
|
@@ -187,7 +187,7 @@ exports.titleMatches = function titleMatches(regex) {
|
|
|
187
187
|
* @param {string} url The expected page url.
|
|
188
188
|
* @return {!Condition<boolean>} The new condition.
|
|
189
189
|
*/
|
|
190
|
-
|
|
190
|
+
function urlIs(url) {
|
|
191
191
|
return new Condition('for URL to be ' + JSON.stringify(url), function (
|
|
192
192
|
driver
|
|
193
193
|
) {
|
|
@@ -205,12 +205,12 @@ exports.urlIs = function urlIs(url) {
|
|
|
205
205
|
* URL.
|
|
206
206
|
* @return {!Condition<boolean>} The new condition.
|
|
207
207
|
*/
|
|
208
|
-
|
|
208
|
+
function urlContains(substrUrl) {
|
|
209
209
|
return new Condition(
|
|
210
210
|
'for URL to contain ' + JSON.stringify(substrUrl),
|
|
211
211
|
function (driver) {
|
|
212
212
|
return driver.getCurrentUrl().then(function (url) {
|
|
213
|
-
return url && url.
|
|
213
|
+
return url && url.includes(substrUrl)
|
|
214
214
|
})
|
|
215
215
|
}
|
|
216
216
|
)
|
|
@@ -223,7 +223,7 @@ exports.urlContains = function urlContains(substrUrl) {
|
|
|
223
223
|
* @param {!RegExp} regex The regular expression to test against.
|
|
224
224
|
* @return {!Condition<boolean>} The new condition.
|
|
225
225
|
*/
|
|
226
|
-
|
|
226
|
+
function urlMatches(regex) {
|
|
227
227
|
return new Condition('for URL to match ' + regex, function (driver) {
|
|
228
228
|
return driver.getCurrentUrl().then(function (url) {
|
|
229
229
|
return regex.test(url)
|
|
@@ -238,7 +238,7 @@ exports.urlMatches = function urlMatches(regex) {
|
|
|
238
238
|
* @param {!(By|Function)} locator The locator to use.
|
|
239
239
|
* @return {!WebElementCondition} The new condition.
|
|
240
240
|
*/
|
|
241
|
-
|
|
241
|
+
function elementLocated(locator) {
|
|
242
242
|
locator = by.checkedLocator(locator)
|
|
243
243
|
let locatorStr =
|
|
244
244
|
typeof locator === 'function' ? 'by function()' : locator + ''
|
|
@@ -260,7 +260,7 @@ exports.elementLocated = function elementLocated(locator) {
|
|
|
260
260
|
* @return {!Condition<!Array<!./webdriver.WebElement>>} The new
|
|
261
261
|
* condition.
|
|
262
262
|
*/
|
|
263
|
-
|
|
263
|
+
function elementsLocated(locator) {
|
|
264
264
|
locator = by.checkedLocator(locator)
|
|
265
265
|
let locatorStr =
|
|
266
266
|
typeof locator === 'function' ? 'by function()' : locator + ''
|
|
@@ -282,7 +282,7 @@ exports.elementsLocated = function elementsLocated(locator) {
|
|
|
282
282
|
* @param {!./webdriver.WebElement} element The element that should become stale.
|
|
283
283
|
* @return {!Condition<boolean>} The new condition.
|
|
284
284
|
*/
|
|
285
|
-
|
|
285
|
+
function stalenessOf(element) {
|
|
286
286
|
return new Condition('element to become stale', function () {
|
|
287
287
|
return element.getTagName().then(
|
|
288
288
|
function () {
|
|
@@ -305,7 +305,7 @@ exports.stalenessOf = function stalenessOf(element) {
|
|
|
305
305
|
* @return {!WebElementCondition} The new condition.
|
|
306
306
|
* @see ./webdriver.WebDriver#isDisplayed
|
|
307
307
|
*/
|
|
308
|
-
|
|
308
|
+
function elementIsVisible(element) {
|
|
309
309
|
return new WebElementCondition('until element is visible', function () {
|
|
310
310
|
return element.isDisplayed().then((v) => (v ? element : null))
|
|
311
311
|
})
|
|
@@ -319,7 +319,7 @@ exports.elementIsVisible = function elementIsVisible(element) {
|
|
|
319
319
|
* @return {!WebElementCondition} The new condition.
|
|
320
320
|
* @see ./webdriver.WebDriver#isDisplayed
|
|
321
321
|
*/
|
|
322
|
-
|
|
322
|
+
function elementIsNotVisible(element) {
|
|
323
323
|
return new WebElementCondition('until element is not visible', function () {
|
|
324
324
|
return element.isDisplayed().then((v) => (v ? null : element))
|
|
325
325
|
})
|
|
@@ -332,7 +332,7 @@ exports.elementIsNotVisible = function elementIsNotVisible(element) {
|
|
|
332
332
|
* @return {!WebElementCondition} The new condition.
|
|
333
333
|
* @see webdriver.WebDriver#isEnabled
|
|
334
334
|
*/
|
|
335
|
-
|
|
335
|
+
function elementIsEnabled(element) {
|
|
336
336
|
return new WebElementCondition('until element is enabled', function () {
|
|
337
337
|
return element.isEnabled().then((v) => (v ? element : null))
|
|
338
338
|
})
|
|
@@ -345,7 +345,7 @@ exports.elementIsEnabled = function elementIsEnabled(element) {
|
|
|
345
345
|
* @return {!WebElementCondition} The new condition.
|
|
346
346
|
* @see webdriver.WebDriver#isEnabled
|
|
347
347
|
*/
|
|
348
|
-
|
|
348
|
+
function elementIsDisabled(element) {
|
|
349
349
|
return new WebElementCondition('until element is disabled', function () {
|
|
350
350
|
return element.isEnabled().then((v) => (v ? null : element))
|
|
351
351
|
})
|
|
@@ -357,7 +357,7 @@ exports.elementIsDisabled = function elementIsDisabled(element) {
|
|
|
357
357
|
* @return {!WebElementCondition} The new condition.
|
|
358
358
|
* @see webdriver.WebDriver#isSelected
|
|
359
359
|
*/
|
|
360
|
-
|
|
360
|
+
function elementIsSelected(element) {
|
|
361
361
|
return new WebElementCondition('until element is selected', function () {
|
|
362
362
|
return element.isSelected().then((v) => (v ? element : null))
|
|
363
363
|
})
|
|
@@ -370,7 +370,7 @@ exports.elementIsSelected = function elementIsSelected(element) {
|
|
|
370
370
|
* @return {!WebElementCondition} The new condition.
|
|
371
371
|
* @see webdriver.WebDriver#isSelected
|
|
372
372
|
*/
|
|
373
|
-
|
|
373
|
+
function elementIsNotSelected(element) {
|
|
374
374
|
return new WebElementCondition('until element is not selected', function () {
|
|
375
375
|
return element.isSelected().then((v) => (v ? null : element))
|
|
376
376
|
})
|
|
@@ -386,7 +386,7 @@ exports.elementIsNotSelected = function elementIsNotSelected(element) {
|
|
|
386
386
|
* @return {!WebElementCondition} The new condition.
|
|
387
387
|
* @see webdriver.WebDriver#getText
|
|
388
388
|
*/
|
|
389
|
-
|
|
389
|
+
function elementTextIs(element, text) {
|
|
390
390
|
return new WebElementCondition('until element text is', function () {
|
|
391
391
|
return element.getText().then((t) => (t === text ? element : null))
|
|
392
392
|
})
|
|
@@ -402,7 +402,7 @@ exports.elementTextIs = function elementTextIs(element, text) {
|
|
|
402
402
|
* @return {!WebElementCondition} The new condition.
|
|
403
403
|
* @see webdriver.WebDriver#getText
|
|
404
404
|
*/
|
|
405
|
-
|
|
405
|
+
function elementTextContains(element, substr) {
|
|
406
406
|
return new WebElementCondition('until element text contains', function () {
|
|
407
407
|
return element
|
|
408
408
|
.getText()
|
|
@@ -420,8 +420,33 @@ exports.elementTextContains = function elementTextContains(element, substr) {
|
|
|
420
420
|
* @return {!WebElementCondition} The new condition.
|
|
421
421
|
* @see webdriver.WebDriver#getText
|
|
422
422
|
*/
|
|
423
|
-
|
|
423
|
+
function elementTextMatches(element, regex) {
|
|
424
424
|
return new WebElementCondition('until element text matches', function () {
|
|
425
425
|
return element.getText().then((t) => (regex.test(t) ? element : null))
|
|
426
426
|
})
|
|
427
427
|
}
|
|
428
|
+
|
|
429
|
+
// PUBLIC API
|
|
430
|
+
|
|
431
|
+
module.exports = {
|
|
432
|
+
elementTextMatches,
|
|
433
|
+
elementTextContains,
|
|
434
|
+
elementTextIs,
|
|
435
|
+
elementIsNotSelected,
|
|
436
|
+
elementIsSelected,
|
|
437
|
+
elementIsDisabled,
|
|
438
|
+
ableToSwitchToFrame,
|
|
439
|
+
elementIsEnabled,
|
|
440
|
+
elementIsNotVisible,
|
|
441
|
+
elementIsVisible,
|
|
442
|
+
stalenessOf,
|
|
443
|
+
elementsLocated,
|
|
444
|
+
elementLocated,
|
|
445
|
+
urlMatches,
|
|
446
|
+
urlContains,
|
|
447
|
+
urlIs,
|
|
448
|
+
titleMatches,
|
|
449
|
+
titleContains,
|
|
450
|
+
alertIsPresent,
|
|
451
|
+
titleIs,
|
|
452
|
+
}
|