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/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
+ * &lt;option value="foo"&gt;Bar&lt;/option&gt;
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
+ * &lt;option value="foo"&gt;Bar&lt;/option&gt;
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
+ * &lt;option value="foo"&gt;Bar&lt;/option&gt;
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
- exports.ableToSwitchToFrame = function ableToSwitchToFrame(frame) {
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
- exports.alertIsPresent = function alertIsPresent() {
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
- exports.titleIs = function titleIs(title) {
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
- exports.titleContains = function titleContains(substr) {
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
- exports.titleMatches = function titleMatches(regex) {
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
- exports.urlIs = function urlIs(url) {
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
- exports.urlContains = function urlContains(substrUrl) {
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.indexOf(substrUrl) !== -1
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
- exports.urlMatches = function urlMatches(regex) {
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
- exports.elementLocated = function elementLocated(locator) {
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
- exports.elementsLocated = function elementsLocated(locator) {
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
- exports.stalenessOf = function stalenessOf(element) {
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
- exports.elementIsVisible = function elementIsVisible(element) {
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
- exports.elementIsNotVisible = function elementIsNotVisible(element) {
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
- exports.elementIsEnabled = function elementIsEnabled(element) {
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
- exports.elementIsDisabled = function elementIsDisabled(element) {
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
- exports.elementIsSelected = function elementIsSelected(element) {
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
- exports.elementIsNotSelected = function elementIsNotSelected(element) {
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
- exports.elementTextIs = function elementTextIs(element, text) {
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
- exports.elementTextContains = function elementTextContains(element, substr) {
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
- exports.elementTextMatches = function elementTextMatches(element, regex) {
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
+ }