selenium-webdriver 4.0.0-alpha.5 → 4.0.0-beta.1
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 +35 -1
- package/LICENSE +1 -1
- package/NOTICE +1 -1
- package/README.md +4 -4
- package/chrome.js +72 -592
- package/chromium.js +829 -0
- package/devtools/CDPConnection.js +37 -0
- package/edge.js +124 -86
- package/example/chrome_android.js +16 -13
- package/example/chrome_mobile_emulation.js +18 -14
- package/example/firefox_channels.js +35 -28
- package/example/google_search.js +9 -9
- package/example/google_search_test.js +20 -22
- package/example/headless.js +27 -19
- package/example/logging.js +34 -38
- package/firefox.js +200 -200
- package/http/index.js +131 -115
- package/http/util.js +56 -64
- package/ie.js +180 -104
- package/index.js +246 -199
- package/io/exec.js +34 -39
- package/io/index.js +142 -152
- package/io/zip.js +75 -65
- package/lib/actions.js +140 -122
- package/lib/atoms/find-elements.js +124 -0
- package/lib/atoms/get-attribute.js +79 -59
- package/lib/atoms/is-displayed.js +72 -73
- package/lib/atoms/mutation-listener.js +38 -0
- package/lib/by.js +192 -65
- package/lib/capabilities.js +82 -78
- package/lib/command.js +20 -20
- package/lib/error.js +134 -168
- package/lib/http.js +337 -230
- package/lib/input.js +325 -302
- package/lib/logging.js +127 -138
- package/lib/promise.js +60 -66
- package/lib/proxy.js +31 -41
- package/lib/session.js +12 -15
- package/lib/symbols.js +3 -4
- package/lib/until.js +162 -160
- package/lib/webdriver.js +1024 -541
- package/net/index.js +30 -36
- package/net/portprober.js +31 -144
- package/opera.js +406 -0
- package/package.json +19 -12
- package/proxy.js +2 -2
- package/remote/index.js +190 -187
- package/safari.js +40 -49
- package/testing/index.js +166 -155
package/lib/webdriver.js
CHANGED
|
@@ -19,32 +19,36 @@
|
|
|
19
19
|
* @fileoverview The heart of the WebDriver JavaScript API.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
'use strict'
|
|
23
|
-
|
|
24
|
-
const by = require('./by')
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
22
|
+
'use strict'
|
|
23
|
+
|
|
24
|
+
const by = require('./by')
|
|
25
|
+
const { RelativeBy } = require('./by')
|
|
26
|
+
const command = require('./command')
|
|
27
|
+
const error = require('./error')
|
|
28
|
+
const input = require('./input')
|
|
29
|
+
const logging = require('./logging')
|
|
30
|
+
const promise = require('./promise')
|
|
31
|
+
const Symbols = require('./symbols')
|
|
32
|
+
const cdpTargets = ['page', 'browser']
|
|
33
|
+
const cdp = require('../devtools/CDPConnection')
|
|
34
|
+
const WebSocket = require('ws')
|
|
35
|
+
const http = require('../http/index')
|
|
36
|
+
const fs = require('fs')
|
|
37
|
+
const { Capabilities } = require('./capabilities')
|
|
34
38
|
|
|
35
39
|
// Capability names that are defined in the W3C spec.
|
|
36
40
|
const W3C_CAPABILITY_NAMES = new Set([
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
'acceptInsecureCerts',
|
|
42
|
+
'browserName',
|
|
43
|
+
'browserVersion',
|
|
44
|
+
'platformName',
|
|
45
|
+
'pageLoadStrategy',
|
|
46
|
+
'proxy',
|
|
47
|
+
'setWindowRect',
|
|
48
|
+
'timeouts',
|
|
49
|
+
'strictFileInteractability',
|
|
50
|
+
'unhandledPromptBehavior',
|
|
51
|
+
])
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
54
|
* Defines a condition for use with WebDriver's {@linkplain WebDriver#wait wait
|
|
@@ -61,19 +65,18 @@ class Condition {
|
|
|
61
65
|
*/
|
|
62
66
|
constructor(message, fn) {
|
|
63
67
|
/** @private {string} */
|
|
64
|
-
this.description_ = 'Waiting ' + message
|
|
68
|
+
this.description_ = 'Waiting ' + message
|
|
65
69
|
|
|
66
70
|
/** @type {function(!WebDriver): OUT} */
|
|
67
|
-
this.fn = fn
|
|
71
|
+
this.fn = fn
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
/** @return {string} A description of this condition. */
|
|
71
75
|
description() {
|
|
72
|
-
return this.description_
|
|
76
|
+
return this.description_
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
|
|
77
80
|
/**
|
|
78
81
|
* Defines a condition that will result in a {@link WebElement}.
|
|
79
82
|
*
|
|
@@ -88,18 +91,16 @@ class WebElementCondition extends Condition {
|
|
|
88
91
|
* loop.
|
|
89
92
|
*/
|
|
90
93
|
constructor(message, fn) {
|
|
91
|
-
super(message, fn)
|
|
94
|
+
super(message, fn)
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
|
|
96
98
|
//////////////////////////////////////////////////////////////////////////////
|
|
97
99
|
//
|
|
98
100
|
// WebDriver
|
|
99
101
|
//
|
|
100
102
|
//////////////////////////////////////////////////////////////////////////////
|
|
101
103
|
|
|
102
|
-
|
|
103
104
|
/**
|
|
104
105
|
* Translates a command to its wire-protocol representation before passing it
|
|
105
106
|
* to the given `executor` for execution.
|
|
@@ -108,14 +109,12 @@ class WebElementCondition extends Condition {
|
|
|
108
109
|
* @return {!Promise} A promise that will resolve with the command response.
|
|
109
110
|
*/
|
|
110
111
|
function executeCommand(executor, command) {
|
|
111
|
-
return toWireValue(command.getParameters()).
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
112
|
+
return toWireValue(command.getParameters()).then(function (parameters) {
|
|
113
|
+
command.setParameters(parameters)
|
|
114
|
+
return executor.execute(command)
|
|
115
|
+
})
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
119
118
|
/**
|
|
120
119
|
* Converts an object to its JSON representation in the WebDriver wire protocol.
|
|
121
120
|
* When converting values of type object, the following steps will be taken:
|
|
@@ -135,64 +134,62 @@ function executeCommand(executor, command) {
|
|
|
135
134
|
* representation.
|
|
136
135
|
*/
|
|
137
136
|
async function toWireValue(obj) {
|
|
138
|
-
let value = await Promise.resolve(obj)
|
|
137
|
+
let value = await Promise.resolve(obj)
|
|
139
138
|
if (value === void 0 || value === null) {
|
|
140
|
-
return value
|
|
139
|
+
return value
|
|
141
140
|
}
|
|
142
141
|
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
if (
|
|
143
|
+
typeof value === 'boolean' ||
|
|
144
|
+
typeof value === 'number' ||
|
|
145
|
+
typeof value === 'string'
|
|
146
|
+
) {
|
|
147
|
+
return value
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
if (Array.isArray(value)) {
|
|
150
|
-
return convertKeys(value)
|
|
151
|
+
return convertKeys(value)
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
if (typeof value === 'function') {
|
|
154
|
-
return '' + value
|
|
155
|
+
return '' + value
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
if (typeof value[Symbols.serialize] === 'function') {
|
|
158
|
-
return toWireValue(value[Symbols.serialize]())
|
|
159
|
+
return toWireValue(value[Symbols.serialize]())
|
|
159
160
|
} else if (typeof value.toJSON === 'function') {
|
|
160
|
-
return toWireValue(value.toJSON())
|
|
161
|
+
return toWireValue(value.toJSON())
|
|
161
162
|
}
|
|
162
|
-
return convertKeys(value)
|
|
163
|
+
return convertKeys(value)
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
|
|
166
166
|
async function convertKeys(obj) {
|
|
167
|
-
const isArray = Array.isArray(obj)
|
|
168
|
-
const numKeys = isArray ? obj.length : Object.keys(obj).length
|
|
169
|
-
const ret = isArray ? new Array(numKeys) : {}
|
|
167
|
+
const isArray = Array.isArray(obj)
|
|
168
|
+
const numKeys = isArray ? obj.length : Object.keys(obj).length
|
|
169
|
+
const ret = isArray ? new Array(numKeys) : {}
|
|
170
170
|
if (!numKeys) {
|
|
171
|
-
return ret
|
|
171
|
+
return ret
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
let numResolved = 0;
|
|
175
|
-
|
|
176
174
|
async function forEachKey(obj, fn) {
|
|
177
175
|
if (Array.isArray(obj)) {
|
|
178
176
|
for (let i = 0, n = obj.length; i < n; i++) {
|
|
179
|
-
await fn(obj[i], i)
|
|
177
|
+
await fn(obj[i], i)
|
|
180
178
|
}
|
|
181
179
|
} else {
|
|
182
180
|
for (let key in obj) {
|
|
183
|
-
await fn(obj[key], key)
|
|
181
|
+
await fn(obj[key], key)
|
|
184
182
|
}
|
|
185
183
|
}
|
|
186
184
|
}
|
|
187
185
|
|
|
188
|
-
await forEachKey(obj, async function(value, key) {
|
|
189
|
-
ret[key] = await toWireValue(value)
|
|
190
|
-
})
|
|
186
|
+
await forEachKey(obj, async function (value, key) {
|
|
187
|
+
ret[key] = await toWireValue(value)
|
|
188
|
+
})
|
|
191
189
|
|
|
192
|
-
return ret
|
|
190
|
+
return ret
|
|
193
191
|
}
|
|
194
192
|
|
|
195
|
-
|
|
196
193
|
/**
|
|
197
194
|
* Converts a value from its JSON representation according to the WebDriver wire
|
|
198
195
|
* protocol. Any JSON object that defines a WebElement ID will be decoded to a
|
|
@@ -205,22 +202,32 @@ async function convertKeys(obj) {
|
|
|
205
202
|
*/
|
|
206
203
|
function fromWireValue(driver, value) {
|
|
207
204
|
if (Array.isArray(value)) {
|
|
208
|
-
value = value.map(v => fromWireValue(driver, v))
|
|
205
|
+
value = value.map((v) => fromWireValue(driver, v))
|
|
209
206
|
} else if (WebElement.isId(value)) {
|
|
210
|
-
let id = WebElement.extractId(value)
|
|
211
|
-
value = new WebElement(driver, id)
|
|
207
|
+
let id = WebElement.extractId(value)
|
|
208
|
+
value = new WebElement(driver, id)
|
|
212
209
|
} else if (value && typeof value === 'object') {
|
|
213
|
-
let result = {}
|
|
210
|
+
let result = {}
|
|
214
211
|
for (let key in value) {
|
|
215
|
-
if (
|
|
216
|
-
result[key] = fromWireValue(driver, value[key])
|
|
212
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
213
|
+
result[key] = fromWireValue(driver, value[key])
|
|
217
214
|
}
|
|
218
215
|
}
|
|
219
|
-
value = result
|
|
216
|
+
value = result
|
|
220
217
|
}
|
|
221
|
-
return value
|
|
218
|
+
return value
|
|
222
219
|
}
|
|
223
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Resolves a wait message from either a function or a string.
|
|
223
|
+
* @param {(string|Function)=} message An optional message to use if the wait times out.
|
|
224
|
+
* @return {string} The resolved message
|
|
225
|
+
*/
|
|
226
|
+
function resolveWaitMessage(message) {
|
|
227
|
+
return message
|
|
228
|
+
? `${typeof message === 'function' ? message() : message}\n`
|
|
229
|
+
: ''
|
|
230
|
+
}
|
|
224
231
|
|
|
225
232
|
/**
|
|
226
233
|
* Structural interface for a WebDriver client.
|
|
@@ -228,7 +235,6 @@ function fromWireValue(driver, value) {
|
|
|
228
235
|
* @record
|
|
229
236
|
*/
|
|
230
237
|
class IWebDriver {
|
|
231
|
-
|
|
232
238
|
/**
|
|
233
239
|
* Executes the provided {@link command.Command} using this driver's
|
|
234
240
|
* {@link command.Executor}.
|
|
@@ -238,30 +244,30 @@ class IWebDriver {
|
|
|
238
244
|
* result.
|
|
239
245
|
* @template T
|
|
240
246
|
*/
|
|
241
|
-
execute(command) {}
|
|
247
|
+
execute(command) { } // eslint-disable-line
|
|
242
248
|
|
|
243
249
|
/**
|
|
244
250
|
* Sets the {@linkplain input.FileDetector file detector} that should be
|
|
245
251
|
* used with this instance.
|
|
246
252
|
* @param {input.FileDetector} detector The detector to use or `null`.
|
|
247
253
|
*/
|
|
248
|
-
setFileDetector(detector) {}
|
|
254
|
+
setFileDetector(detector) { } // eslint-disable-line
|
|
249
255
|
|
|
250
256
|
/**
|
|
251
257
|
* @return {!command.Executor} The command executor used by this instance.
|
|
252
258
|
*/
|
|
253
|
-
getExecutor() {}
|
|
259
|
+
getExecutor() { }
|
|
254
260
|
|
|
255
261
|
/**
|
|
256
262
|
* @return {!Promise<!Session>} A promise for this client's session.
|
|
257
263
|
*/
|
|
258
|
-
getSession() {}
|
|
264
|
+
getSession() { }
|
|
259
265
|
|
|
260
266
|
/**
|
|
261
267
|
* @return {!Promise<!Capabilities>} A promise that will resolve with
|
|
262
268
|
* the this instance's capabilities.
|
|
263
269
|
*/
|
|
264
|
-
getCapabilities() {}
|
|
270
|
+
getCapabilities() { }
|
|
265
271
|
|
|
266
272
|
/**
|
|
267
273
|
* Terminates the browser session. After calling quit, this instance will be
|
|
@@ -271,7 +277,7 @@ class IWebDriver {
|
|
|
271
277
|
* @return {!Promise<void>} A promise that will be resolved when the
|
|
272
278
|
* command has completed.
|
|
273
279
|
*/
|
|
274
|
-
quit() {}
|
|
280
|
+
quit() { }
|
|
275
281
|
|
|
276
282
|
/**
|
|
277
283
|
* Creates a new action sequence using this driver. The sequence will not be
|
|
@@ -284,7 +290,7 @@ class IWebDriver {
|
|
|
284
290
|
* for details).
|
|
285
291
|
* @return {!input.Actions} A new action sequence for this instance.
|
|
286
292
|
*/
|
|
287
|
-
actions(options) {}
|
|
293
|
+
actions(options) { } // eslint-disable-line
|
|
288
294
|
|
|
289
295
|
/**
|
|
290
296
|
* Executes a snippet of JavaScript in the context of the currently selected
|
|
@@ -322,7 +328,7 @@ class IWebDriver {
|
|
|
322
328
|
* scripts return value.
|
|
323
329
|
* @template T
|
|
324
330
|
*/
|
|
325
|
-
executeScript(script, ...args) {}
|
|
331
|
+
executeScript(script, ...args) { } // eslint-disable-line
|
|
326
332
|
|
|
327
333
|
/**
|
|
328
334
|
* Executes a snippet of asynchronous JavaScript in the context of the
|
|
@@ -399,7 +405,7 @@ class IWebDriver {
|
|
|
399
405
|
* value.
|
|
400
406
|
* @template T
|
|
401
407
|
*/
|
|
402
|
-
executeAsyncScript(script, ...args) {}
|
|
408
|
+
executeAsyncScript(script, ...args) { } // eslint-disable-line
|
|
403
409
|
|
|
404
410
|
/**
|
|
405
411
|
* Waits for a condition to evaluate to a "truthy" value. The condition may be
|
|
@@ -434,7 +440,7 @@ class IWebDriver {
|
|
|
434
440
|
* evaluate as a condition.
|
|
435
441
|
* @param {number=} timeout The duration in milliseconds, how long to wait
|
|
436
442
|
* for the condition to be true.
|
|
437
|
-
* @param {string=} message An optional message to use if the wait times out.
|
|
443
|
+
* @param {(string|Function)=} message An optional message to use if the wait times out.
|
|
438
444
|
* @param {number=} pollTimeout The duration in milliseconds, how long to
|
|
439
445
|
* wait between polling the condition.
|
|
440
446
|
* @return {!(IThenable<T>|WebElementPromise)} A promise that will be
|
|
@@ -445,7 +451,12 @@ class IWebDriver {
|
|
|
445
451
|
* @throws {TypeError} if the provided `condition` is not a valid type.
|
|
446
452
|
* @template T
|
|
447
453
|
*/
|
|
448
|
-
wait(
|
|
454
|
+
wait(
|
|
455
|
+
condition, // eslint-disable-line
|
|
456
|
+
timeout = undefined, // eslint-disable-line
|
|
457
|
+
message = undefined, // eslint-disable-line
|
|
458
|
+
pollTimeout = undefined // eslint-disable-line
|
|
459
|
+
) { }
|
|
449
460
|
|
|
450
461
|
/**
|
|
451
462
|
* Makes the driver sleep for the given amount of time.
|
|
@@ -454,7 +465,7 @@ class IWebDriver {
|
|
|
454
465
|
* @return {!Promise<void>} A promise that will be resolved when the sleep has
|
|
455
466
|
* finished.
|
|
456
467
|
*/
|
|
457
|
-
sleep(ms) {}
|
|
468
|
+
sleep(ms) { } // eslint-disable-line
|
|
458
469
|
|
|
459
470
|
/**
|
|
460
471
|
* Retrieves the current window handle.
|
|
@@ -462,7 +473,7 @@ class IWebDriver {
|
|
|
462
473
|
* @return {!Promise<string>} A promise that will be resolved with the current
|
|
463
474
|
* window handle.
|
|
464
475
|
*/
|
|
465
|
-
getWindowHandle() {}
|
|
476
|
+
getWindowHandle() { }
|
|
466
477
|
|
|
467
478
|
/**
|
|
468
479
|
* Retrieves a list of all available window handles.
|
|
@@ -470,17 +481,17 @@ class IWebDriver {
|
|
|
470
481
|
* @return {!Promise<!Array<string>>} A promise that will be resolved with an
|
|
471
482
|
* array of window handles.
|
|
472
483
|
*/
|
|
473
|
-
getAllWindowHandles() {}
|
|
484
|
+
getAllWindowHandles() { }
|
|
474
485
|
|
|
475
486
|
/**
|
|
476
|
-
* Retrieves the current page's source. The returned
|
|
487
|
+
* Retrieves the current page's source. The returned source is a representation
|
|
477
488
|
* of the underlying DOM: do not expect it to be formatted or escaped in the
|
|
478
489
|
* same way as the raw response sent from the web server.
|
|
479
490
|
*
|
|
480
491
|
* @return {!Promise<string>} A promise that will be resolved with the current
|
|
481
492
|
* page source.
|
|
482
493
|
*/
|
|
483
|
-
getPageSource() {}
|
|
494
|
+
getPageSource() { }
|
|
484
495
|
|
|
485
496
|
/**
|
|
486
497
|
* Closes the current window.
|
|
@@ -488,7 +499,7 @@ class IWebDriver {
|
|
|
488
499
|
* @return {!Promise<void>} A promise that will be resolved when this command
|
|
489
500
|
* has completed.
|
|
490
501
|
*/
|
|
491
|
-
close() {}
|
|
502
|
+
close() { }
|
|
492
503
|
|
|
493
504
|
/**
|
|
494
505
|
* Navigates to the given URL.
|
|
@@ -497,7 +508,7 @@ class IWebDriver {
|
|
|
497
508
|
* @return {!Promise<void>} A promise that will be resolved when the document
|
|
498
509
|
* has finished loading.
|
|
499
510
|
*/
|
|
500
|
-
get(url) {}
|
|
511
|
+
get(url) { } // eslint-disable-line
|
|
501
512
|
|
|
502
513
|
/**
|
|
503
514
|
* Retrieves the URL for the current page.
|
|
@@ -505,7 +516,7 @@ class IWebDriver {
|
|
|
505
516
|
* @return {!Promise<string>} A promise that will be resolved with the
|
|
506
517
|
* current URL.
|
|
507
518
|
*/
|
|
508
|
-
getCurrentUrl() {}
|
|
519
|
+
getCurrentUrl() { }
|
|
509
520
|
|
|
510
521
|
/**
|
|
511
522
|
* Retrieves the current page title.
|
|
@@ -513,11 +524,11 @@ class IWebDriver {
|
|
|
513
524
|
* @return {!Promise<string>} A promise that will be resolved with the current
|
|
514
525
|
* page's title.
|
|
515
526
|
*/
|
|
516
|
-
getTitle() {}
|
|
527
|
+
getTitle() { }
|
|
517
528
|
|
|
518
529
|
/**
|
|
519
530
|
* Locates an element on the page. If the element cannot be found, a
|
|
520
|
-
* {@link error.
|
|
531
|
+
* {@link error.NoSuchElementError} will be returned by the driver.
|
|
521
532
|
*
|
|
522
533
|
* This function should not be used to test whether an element is present on
|
|
523
534
|
* the page. Rather, you should use {@link #findElements}:
|
|
@@ -553,7 +564,7 @@ class IWebDriver {
|
|
|
553
564
|
* commands against the located element. If the element is not found, the
|
|
554
565
|
* element will be invalidated and all scheduled commands aborted.
|
|
555
566
|
*/
|
|
556
|
-
findElement(locator) {}
|
|
567
|
+
findElement(locator) { } // eslint-disable-line
|
|
557
568
|
|
|
558
569
|
/**
|
|
559
570
|
* Search for multiple elements on the page. Refer to the documentation on
|
|
@@ -563,7 +574,7 @@ class IWebDriver {
|
|
|
563
574
|
* @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
|
|
564
575
|
* array of WebElements.
|
|
565
576
|
*/
|
|
566
|
-
findElements(locator) {}
|
|
577
|
+
findElements(locator) { } // eslint-disable-line
|
|
567
578
|
|
|
568
579
|
/**
|
|
569
580
|
* Takes a screenshot of the current page. The driver makes a best effort to
|
|
@@ -577,25 +588,43 @@ class IWebDriver {
|
|
|
577
588
|
* @return {!Promise<string>} A promise that will be resolved to the
|
|
578
589
|
* screenshot as a base-64 encoded PNG.
|
|
579
590
|
*/
|
|
580
|
-
takeScreenshot() {}
|
|
591
|
+
takeScreenshot() { }
|
|
581
592
|
|
|
582
593
|
/**
|
|
583
594
|
* @return {!Options} The options interface for this instance.
|
|
584
595
|
*/
|
|
585
|
-
manage() {}
|
|
596
|
+
manage() { }
|
|
586
597
|
|
|
587
598
|
/**
|
|
588
599
|
* @return {!Navigation} The navigation interface for this instance.
|
|
589
600
|
*/
|
|
590
|
-
navigate() {}
|
|
601
|
+
navigate() { }
|
|
591
602
|
|
|
592
603
|
/**
|
|
593
604
|
* @return {!TargetLocator} The target locator interface for this
|
|
594
605
|
* instance.
|
|
595
606
|
*/
|
|
596
|
-
switchTo() {}
|
|
597
|
-
}
|
|
607
|
+
switchTo() { }
|
|
598
608
|
|
|
609
|
+
/**
|
|
610
|
+
*
|
|
611
|
+
* Takes a PDF of the current page. The driver makes a best effort to
|
|
612
|
+
* return a PDF based on the provided parameters.
|
|
613
|
+
*
|
|
614
|
+
* @param {{orientation: (string|undefined),
|
|
615
|
+
* scale: (number|undefined),
|
|
616
|
+
* background: (boolean|undefined)
|
|
617
|
+
* width: (number|undefined)
|
|
618
|
+
* height: (number|undefined)
|
|
619
|
+
* top: (number|undefined)
|
|
620
|
+
* bottom: (number|undefined)
|
|
621
|
+
* left: (number|undefined)
|
|
622
|
+
* right: (number|undefined)
|
|
623
|
+
* shrinkToFit: (boolean|undefined)
|
|
624
|
+
* pageRanges: (<Array>|undefined)}} options.
|
|
625
|
+
*/
|
|
626
|
+
printPage(options) { } // eslint-disable-line
|
|
627
|
+
}
|
|
599
628
|
|
|
600
629
|
/**
|
|
601
630
|
* @param {!Capabilities} capabilities A capabilities object.
|
|
@@ -603,17 +632,16 @@ class IWebDriver {
|
|
|
603
632
|
* capability names that are not valid W3C names.
|
|
604
633
|
*/
|
|
605
634
|
function filterNonW3CCaps(capabilities) {
|
|
606
|
-
let newCaps = new Capabilities(capabilities)
|
|
635
|
+
let newCaps = new Capabilities(capabilities)
|
|
607
636
|
for (let k of newCaps.keys()) {
|
|
608
637
|
// Any key containing a colon is a vendor-prefixed capability.
|
|
609
638
|
if (!(W3C_CAPABILITY_NAMES.has(k) || k.indexOf(':') >= 0)) {
|
|
610
|
-
newCaps.delete(k)
|
|
639
|
+
newCaps.delete(k)
|
|
611
640
|
}
|
|
612
641
|
}
|
|
613
|
-
return newCaps
|
|
642
|
+
return newCaps
|
|
614
643
|
}
|
|
615
644
|
|
|
616
|
-
|
|
617
645
|
/**
|
|
618
646
|
* Each WebDriver instance provides automated control over a browser session.
|
|
619
647
|
*
|
|
@@ -630,21 +658,21 @@ class WebDriver {
|
|
|
630
658
|
*/
|
|
631
659
|
constructor(session, executor, onQuit = undefined) {
|
|
632
660
|
/** @private {!Promise<!Session>} */
|
|
633
|
-
this.session_ = Promise.resolve(session)
|
|
661
|
+
this.session_ = Promise.resolve(session)
|
|
634
662
|
|
|
635
663
|
// If session is a rejected promise, add a no-op rejection handler.
|
|
636
664
|
// This effectively hides setup errors until users attempt to interact
|
|
637
665
|
// with the session.
|
|
638
|
-
this.session_.catch(function() {})
|
|
666
|
+
this.session_.catch(function () { })
|
|
639
667
|
|
|
640
668
|
/** @private {!command.Executor} */
|
|
641
|
-
this.executor_ = executor
|
|
669
|
+
this.executor_ = executor
|
|
642
670
|
|
|
643
671
|
/** @private {input.FileDetector} */
|
|
644
|
-
this.fileDetector_ = null
|
|
672
|
+
this.fileDetector_ = null
|
|
645
673
|
|
|
646
674
|
/** @private @const {(function(this: void): ?|undefined)} */
|
|
647
|
-
this.onQuit_ = onQuit
|
|
675
|
+
this.onQuit_ = onQuit
|
|
648
676
|
}
|
|
649
677
|
|
|
650
678
|
/**
|
|
@@ -673,251 +701,285 @@ class WebDriver {
|
|
|
673
701
|
* @return {!WebDriver} The driver for the newly created session.
|
|
674
702
|
*/
|
|
675
703
|
static createSession(executor, capabilities, onQuit = undefined) {
|
|
676
|
-
let cmd = new command.Command(command.Name.NEW_SESSION)
|
|
704
|
+
let cmd = new command.Command(command.Name.NEW_SESSION)
|
|
677
705
|
|
|
678
706
|
// For OSS remote ends.
|
|
679
|
-
cmd.setParameter('desiredCapabilities', capabilities)
|
|
707
|
+
cmd.setParameter('desiredCapabilities', capabilities)
|
|
680
708
|
// For W3C remote ends.
|
|
681
709
|
cmd.setParameter('capabilities', {
|
|
682
710
|
alwaysMatch: filterNonW3CCaps(capabilities),
|
|
683
|
-
})
|
|
711
|
+
})
|
|
684
712
|
|
|
685
|
-
let session = executeCommand(executor, cmd)
|
|
713
|
+
let session = executeCommand(executor, cmd)
|
|
686
714
|
if (typeof onQuit === 'function') {
|
|
687
|
-
session = session.catch(err => {
|
|
688
|
-
return Promise.resolve(onQuit.call(void 0)).then(_ => {
|
|
689
|
-
|
|
715
|
+
session = session.catch((err) => {
|
|
716
|
+
return Promise.resolve(onQuit.call(void 0)).then((_) => {
|
|
717
|
+
throw err
|
|
718
|
+
})
|
|
719
|
+
})
|
|
690
720
|
}
|
|
691
|
-
return new this(session, executor, onQuit)
|
|
721
|
+
return new this(session, executor, onQuit)
|
|
692
722
|
}
|
|
693
723
|
|
|
694
724
|
/** @override */
|
|
695
725
|
async execute(command) {
|
|
696
|
-
command.setParameter('sessionId', this.session_)
|
|
697
|
-
let parameters = await toWireValue(command.getParameters())
|
|
698
|
-
command.setParameters(parameters)
|
|
699
|
-
let value = await this.executor_.execute(command)
|
|
700
|
-
return fromWireValue(this, value)
|
|
726
|
+
command.setParameter('sessionId', this.session_)
|
|
727
|
+
let parameters = await toWireValue(command.getParameters())
|
|
728
|
+
command.setParameters(parameters)
|
|
729
|
+
let value = await this.executor_.execute(command)
|
|
730
|
+
return fromWireValue(this, value)
|
|
701
731
|
}
|
|
702
732
|
|
|
703
733
|
/** @override */
|
|
704
734
|
setFileDetector(detector) {
|
|
705
|
-
this.fileDetector_ = detector
|
|
735
|
+
this.fileDetector_ = detector
|
|
706
736
|
}
|
|
707
737
|
|
|
708
738
|
/** @override */
|
|
709
739
|
getExecutor() {
|
|
710
|
-
return this.executor_
|
|
740
|
+
return this.executor_
|
|
711
741
|
}
|
|
712
742
|
|
|
713
743
|
/** @override */
|
|
714
744
|
getSession() {
|
|
715
|
-
return this.session_
|
|
745
|
+
return this.session_
|
|
716
746
|
}
|
|
717
747
|
|
|
718
748
|
/** @override */
|
|
719
749
|
getCapabilities() {
|
|
720
|
-
return this.session_.then(s => s.getCapabilities())
|
|
750
|
+
return this.session_.then((s) => s.getCapabilities())
|
|
721
751
|
}
|
|
722
752
|
|
|
723
753
|
/** @override */
|
|
724
754
|
quit() {
|
|
725
|
-
let result = this.execute(new command.Command(command.Name.QUIT))
|
|
755
|
+
let result = this.execute(new command.Command(command.Name.QUIT))
|
|
726
756
|
// Delete our session ID when the quit command finishes; this will allow us
|
|
727
757
|
// to throw an error when attempting to use a driver post-quit.
|
|
728
758
|
return promise.finally(result, () => {
|
|
729
|
-
this.session_ = Promise.reject(
|
|
730
|
-
|
|
731
|
-
|
|
759
|
+
this.session_ = Promise.reject(
|
|
760
|
+
new error.NoSuchSessionError(
|
|
761
|
+
'This driver instance does not have a valid session ID ' +
|
|
762
|
+
'(did you call WebDriver.quit()?) and may no longer be used.'
|
|
763
|
+
)
|
|
764
|
+
)
|
|
732
765
|
|
|
733
766
|
// Only want the session rejection to bubble if accessed.
|
|
734
|
-
this.session_.catch(function() {})
|
|
767
|
+
this.session_.catch(function () { })
|
|
735
768
|
|
|
736
769
|
if (this.onQuit_) {
|
|
737
|
-
return this.onQuit_.call(void 0)
|
|
770
|
+
return this.onQuit_.call(void 0)
|
|
738
771
|
}
|
|
739
|
-
})
|
|
772
|
+
})
|
|
740
773
|
}
|
|
741
774
|
|
|
742
775
|
/** @override */
|
|
743
776
|
actions(options) {
|
|
744
|
-
return new input.Actions(this, options || undefined)
|
|
777
|
+
return new input.Actions(this, options || undefined)
|
|
745
778
|
}
|
|
746
779
|
|
|
747
780
|
/** @override */
|
|
748
781
|
executeScript(script, ...args) {
|
|
749
782
|
if (typeof script === 'function') {
|
|
750
|
-
script = 'return (' + script + ').apply(null, arguments);'
|
|
783
|
+
script = 'return (' + script + ').apply(null, arguments);'
|
|
751
784
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
785
|
+
return this.execute(
|
|
786
|
+
new command.Command(command.Name.EXECUTE_SCRIPT)
|
|
787
|
+
.setParameter('script', script)
|
|
788
|
+
.setParameter('args', args)
|
|
789
|
+
)
|
|
756
790
|
}
|
|
757
791
|
|
|
758
792
|
/** @override */
|
|
759
793
|
executeAsyncScript(script, ...args) {
|
|
760
794
|
if (typeof script === 'function') {
|
|
761
|
-
script = 'return (' + script + ').apply(null, arguments);'
|
|
795
|
+
script = 'return (' + script + ').apply(null, arguments);'
|
|
762
796
|
}
|
|
763
797
|
return this.execute(
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
798
|
+
new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT)
|
|
799
|
+
.setParameter('script', script)
|
|
800
|
+
.setParameter('args', args)
|
|
801
|
+
)
|
|
767
802
|
}
|
|
768
803
|
|
|
769
804
|
/** @override */
|
|
770
805
|
wait(condition, timeout = 0, message = undefined, pollTimeout = 200) {
|
|
771
806
|
if (typeof timeout !== 'number' || timeout < 0) {
|
|
772
|
-
throw TypeError('timeout must be a number >= 0: ' + timeout)
|
|
807
|
+
throw TypeError('timeout must be a number >= 0: ' + timeout)
|
|
773
808
|
}
|
|
774
809
|
|
|
775
810
|
if (typeof pollTimeout !== 'number' || pollTimeout < 0) {
|
|
776
|
-
throw TypeError('pollTimeout must be a number >= 0: ' + pollTimeout)
|
|
811
|
+
throw TypeError('pollTimeout must be a number >= 0: ' + pollTimeout)
|
|
777
812
|
}
|
|
778
813
|
|
|
779
814
|
if (promise.isPromise(condition)) {
|
|
780
815
|
return new Promise((resolve, reject) => {
|
|
781
816
|
if (!timeout) {
|
|
782
|
-
resolve(condition)
|
|
783
|
-
return
|
|
817
|
+
resolve(condition)
|
|
818
|
+
return
|
|
784
819
|
}
|
|
785
820
|
|
|
786
|
-
let start = Date.now()
|
|
787
|
-
let timer = setTimeout(function() {
|
|
788
|
-
timer = null
|
|
789
|
-
|
|
821
|
+
let start = Date.now()
|
|
822
|
+
let timer = setTimeout(function () {
|
|
823
|
+
timer = null
|
|
824
|
+
try {
|
|
825
|
+
let timeoutMessage = resolveWaitMessage(message)
|
|
826
|
+
reject(
|
|
790
827
|
new error.TimeoutError(
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
828
|
+
`${timeoutMessage}Timed out waiting for promise to resolve after ${Date.now() - start
|
|
829
|
+
}ms`
|
|
830
|
+
)
|
|
831
|
+
)
|
|
832
|
+
} catch (ex) {
|
|
833
|
+
reject(
|
|
834
|
+
new error.TimeoutError(
|
|
835
|
+
`${ex.message
|
|
836
|
+
}\nTimed out waiting for promise to resolve after ${Date.now() - start
|
|
837
|
+
}ms`
|
|
838
|
+
)
|
|
839
|
+
)
|
|
840
|
+
}
|
|
841
|
+
}, timeout)
|
|
842
|
+
const clearTimer = () => timer && clearTimeout(timer)
|
|
843
|
+
|
|
844
|
+
/** @type {!IThenable} */ condition.then(
|
|
845
|
+
function (value) {
|
|
846
|
+
clearTimer()
|
|
847
|
+
resolve(value)
|
|
848
|
+
},
|
|
849
|
+
function (error) {
|
|
850
|
+
clearTimer()
|
|
851
|
+
reject(error)
|
|
852
|
+
}
|
|
853
|
+
)
|
|
854
|
+
})
|
|
807
855
|
}
|
|
808
856
|
|
|
809
|
-
let fn = /** @type {!Function} */(condition)
|
|
857
|
+
let fn = /** @type {!Function} */ (condition)
|
|
810
858
|
if (condition instanceof Condition) {
|
|
811
|
-
message = message || condition.description()
|
|
812
|
-
fn = condition.fn
|
|
859
|
+
message = message || condition.description()
|
|
860
|
+
fn = condition.fn
|
|
813
861
|
}
|
|
814
862
|
|
|
815
863
|
if (typeof fn !== 'function') {
|
|
816
864
|
throw TypeError(
|
|
817
|
-
|
|
818
|
-
|
|
865
|
+
'Wait condition must be a promise-like object, function, or a ' +
|
|
866
|
+
'Condition object'
|
|
867
|
+
)
|
|
819
868
|
}
|
|
820
869
|
|
|
821
|
-
const driver = this
|
|
870
|
+
const driver = this
|
|
822
871
|
function evaluateCondition() {
|
|
823
872
|
return new Promise((resolve, reject) => {
|
|
824
873
|
try {
|
|
825
|
-
resolve(fn(driver))
|
|
874
|
+
resolve(fn(driver))
|
|
826
875
|
} catch (ex) {
|
|
827
|
-
reject(ex)
|
|
876
|
+
reject(ex)
|
|
828
877
|
}
|
|
829
|
-
})
|
|
878
|
+
})
|
|
830
879
|
}
|
|
831
880
|
|
|
832
881
|
let result = new Promise((resolve, reject) => {
|
|
833
|
-
const startTime = Date.now()
|
|
882
|
+
const startTime = Date.now()
|
|
834
883
|
const pollCondition = async () => {
|
|
835
|
-
evaluateCondition().then(function(value) {
|
|
836
|
-
const elapsed = Date.now() - startTime
|
|
837
|
-
if (
|
|
838
|
-
resolve(value)
|
|
884
|
+
evaluateCondition().then(function (value) {
|
|
885
|
+
const elapsed = Date.now() - startTime
|
|
886
|
+
if (value) {
|
|
887
|
+
resolve(value)
|
|
839
888
|
} else if (timeout && elapsed >= timeout) {
|
|
840
|
-
|
|
889
|
+
try {
|
|
890
|
+
let timeoutMessage = resolveWaitMessage(message)
|
|
891
|
+
reject(
|
|
841
892
|
new error.TimeoutError(
|
|
842
|
-
|
|
843
|
-
|
|
893
|
+
`${timeoutMessage}Wait timed out after ${elapsed}ms`
|
|
894
|
+
)
|
|
895
|
+
)
|
|
896
|
+
} catch (ex) {
|
|
897
|
+
reject(
|
|
898
|
+
new error.TimeoutError(
|
|
899
|
+
`${ex.message}\nWait timed out after ${elapsed}ms`
|
|
900
|
+
)
|
|
901
|
+
)
|
|
902
|
+
}
|
|
844
903
|
} else {
|
|
845
|
-
setTimeout(pollCondition, pollTimeout)
|
|
904
|
+
setTimeout(pollCondition, pollTimeout)
|
|
846
905
|
}
|
|
847
|
-
}, reject)
|
|
848
|
-
}
|
|
849
|
-
pollCondition()
|
|
850
|
-
})
|
|
906
|
+
}, reject)
|
|
907
|
+
}
|
|
908
|
+
pollCondition()
|
|
909
|
+
})
|
|
851
910
|
|
|
852
911
|
if (condition instanceof WebElementCondition) {
|
|
853
|
-
result = new WebElementPromise(
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
912
|
+
result = new WebElementPromise(
|
|
913
|
+
this,
|
|
914
|
+
result.then(function (value) {
|
|
915
|
+
if (!(value instanceof WebElement)) {
|
|
916
|
+
throw TypeError(
|
|
917
|
+
'WebElementCondition did not resolve to a WebElement: ' +
|
|
918
|
+
Object.prototype.toString.call(value)
|
|
919
|
+
)
|
|
920
|
+
}
|
|
921
|
+
return value
|
|
922
|
+
})
|
|
923
|
+
)
|
|
861
924
|
}
|
|
862
|
-
return result
|
|
925
|
+
return result
|
|
863
926
|
}
|
|
864
927
|
|
|
865
928
|
/** @override */
|
|
866
929
|
sleep(ms) {
|
|
867
|
-
return new Promise(resolve => setTimeout(
|
|
930
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
868
931
|
}
|
|
869
932
|
|
|
870
933
|
/** @override */
|
|
871
934
|
getWindowHandle() {
|
|
872
935
|
return this.execute(
|
|
873
|
-
|
|
936
|
+
new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE)
|
|
937
|
+
)
|
|
874
938
|
}
|
|
875
939
|
|
|
876
940
|
/** @override */
|
|
877
941
|
getAllWindowHandles() {
|
|
878
|
-
return this.execute(
|
|
879
|
-
new command.Command(command.Name.GET_WINDOW_HANDLES));
|
|
942
|
+
return this.execute(new command.Command(command.Name.GET_WINDOW_HANDLES))
|
|
880
943
|
}
|
|
881
944
|
|
|
882
945
|
/** @override */
|
|
883
946
|
getPageSource() {
|
|
884
|
-
return this.execute(
|
|
885
|
-
new command.Command(command.Name.GET_PAGE_SOURCE));
|
|
947
|
+
return this.execute(new command.Command(command.Name.GET_PAGE_SOURCE))
|
|
886
948
|
}
|
|
887
949
|
|
|
888
950
|
/** @override */
|
|
889
951
|
close() {
|
|
890
|
-
return this.execute(new command.Command(command.Name.CLOSE))
|
|
952
|
+
return this.execute(new command.Command(command.Name.CLOSE))
|
|
891
953
|
}
|
|
892
954
|
|
|
893
955
|
/** @override */
|
|
894
956
|
get(url) {
|
|
895
|
-
return this.navigate().to(url)
|
|
957
|
+
return this.navigate().to(url)
|
|
896
958
|
}
|
|
897
959
|
|
|
898
960
|
/** @override */
|
|
899
961
|
getCurrentUrl() {
|
|
900
|
-
return this.execute(new command.Command(command.Name.GET_CURRENT_URL))
|
|
962
|
+
return this.execute(new command.Command(command.Name.GET_CURRENT_URL))
|
|
901
963
|
}
|
|
902
964
|
|
|
903
965
|
/** @override */
|
|
904
966
|
getTitle() {
|
|
905
|
-
return this.execute(new command.Command(command.Name.GET_TITLE))
|
|
967
|
+
return this.execute(new command.Command(command.Name.GET_TITLE))
|
|
906
968
|
}
|
|
907
969
|
|
|
908
970
|
/** @override */
|
|
909
971
|
findElement(locator) {
|
|
910
|
-
let id
|
|
911
|
-
locator = by.checkedLocator(locator)
|
|
972
|
+
let id
|
|
973
|
+
locator = by.checkedLocator(locator)
|
|
912
974
|
if (typeof locator === 'function') {
|
|
913
|
-
id = this.findElementInternal_(locator, this)
|
|
975
|
+
id = this.findElementInternal_(locator, this)
|
|
914
976
|
} else {
|
|
915
|
-
let cmd = new command.Command(command.Name.FIND_ELEMENT)
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
id = this.execute(cmd)
|
|
977
|
+
let cmd = new command.Command(command.Name.FIND_ELEMENT)
|
|
978
|
+
.setParameter('using', locator.using)
|
|
979
|
+
.setParameter('value', locator.value)
|
|
980
|
+
id = this.execute(cmd)
|
|
919
981
|
}
|
|
920
|
-
return new WebElementPromise(this, id)
|
|
982
|
+
return new WebElementPromise(this, id)
|
|
921
983
|
}
|
|
922
984
|
|
|
923
985
|
/**
|
|
@@ -928,34 +990,42 @@ class WebDriver {
|
|
|
928
990
|
* @private
|
|
929
991
|
*/
|
|
930
992
|
async findElementInternal_(locatorFn, context) {
|
|
931
|
-
let result = await locatorFn(context)
|
|
993
|
+
let result = await locatorFn(context)
|
|
932
994
|
if (Array.isArray(result)) {
|
|
933
|
-
result = result[0]
|
|
995
|
+
result = result[0]
|
|
934
996
|
}
|
|
935
997
|
if (!(result instanceof WebElement)) {
|
|
936
|
-
throw new TypeError('Custom locator did not return a WebElement')
|
|
998
|
+
throw new TypeError('Custom locator did not return a WebElement')
|
|
937
999
|
}
|
|
938
|
-
return result
|
|
1000
|
+
return result
|
|
939
1001
|
}
|
|
940
1002
|
|
|
941
1003
|
/** @override */
|
|
942
1004
|
async findElements(locator) {
|
|
943
|
-
|
|
944
|
-
if (
|
|
945
|
-
|
|
1005
|
+
let cmd = null
|
|
1006
|
+
if (locator instanceof RelativeBy) {
|
|
1007
|
+
cmd = new command.Command(
|
|
1008
|
+
command.Name.FIND_ELEMENTS_RELATIVE
|
|
1009
|
+
).setParameter('args', locator.marshall())
|
|
946
1010
|
} else {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1011
|
+
locator = by.checkedLocator(locator)
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
if (typeof locator === 'function') {
|
|
1015
|
+
return this.findElementsInternal_(locator, this)
|
|
1016
|
+
} else if (cmd === null) {
|
|
1017
|
+
cmd = new command.Command(command.Name.FIND_ELEMENTS)
|
|
1018
|
+
.setParameter('using', locator.using)
|
|
1019
|
+
.setParameter('value', locator.value)
|
|
1020
|
+
}
|
|
1021
|
+
try {
|
|
1022
|
+
let res = await this.execute(cmd)
|
|
1023
|
+
return Array.isArray(res) ? res : []
|
|
1024
|
+
} catch (ex) {
|
|
1025
|
+
if (ex instanceof error.NoSuchElementError) {
|
|
1026
|
+
return []
|
|
958
1027
|
}
|
|
1028
|
+
throw ex
|
|
959
1029
|
}
|
|
960
1030
|
}
|
|
961
1031
|
|
|
@@ -967,41 +1037,373 @@ class WebDriver {
|
|
|
967
1037
|
* @private
|
|
968
1038
|
*/
|
|
969
1039
|
async findElementsInternal_(locatorFn, context) {
|
|
970
|
-
const result = await locatorFn(context)
|
|
1040
|
+
const result = await locatorFn(context)
|
|
971
1041
|
if (result instanceof WebElement) {
|
|
972
|
-
return [result]
|
|
1042
|
+
return [result]
|
|
973
1043
|
}
|
|
974
1044
|
|
|
975
1045
|
if (!Array.isArray(result)) {
|
|
976
|
-
return []
|
|
1046
|
+
return []
|
|
977
1047
|
}
|
|
978
1048
|
|
|
979
|
-
return result.filter(function(item) {
|
|
980
|
-
return item instanceof WebElement
|
|
981
|
-
})
|
|
1049
|
+
return result.filter(function (item) {
|
|
1050
|
+
return item instanceof WebElement
|
|
1051
|
+
})
|
|
982
1052
|
}
|
|
983
1053
|
|
|
984
1054
|
/** @override */
|
|
985
1055
|
takeScreenshot() {
|
|
986
|
-
return this.execute(new command.Command(command.Name.SCREENSHOT))
|
|
1056
|
+
return this.execute(new command.Command(command.Name.SCREENSHOT))
|
|
987
1057
|
}
|
|
988
1058
|
|
|
989
1059
|
/** @override */
|
|
990
1060
|
manage() {
|
|
991
|
-
return new Options(this)
|
|
1061
|
+
return new Options(this)
|
|
992
1062
|
}
|
|
993
1063
|
|
|
994
1064
|
/** @override */
|
|
995
1065
|
navigate() {
|
|
996
|
-
return new Navigation(this)
|
|
1066
|
+
return new Navigation(this)
|
|
997
1067
|
}
|
|
998
1068
|
|
|
999
1069
|
/** @override */
|
|
1000
1070
|
switchTo() {
|
|
1001
|
-
return new TargetLocator(this)
|
|
1071
|
+
return new TargetLocator(this)
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
validatePrintPageParams(keys, object) {
|
|
1075
|
+
let page = {}
|
|
1076
|
+
let margin = {}
|
|
1077
|
+
let data
|
|
1078
|
+
Object.keys(keys).forEach(function (key) {
|
|
1079
|
+
data = keys[key]
|
|
1080
|
+
let obj = {
|
|
1081
|
+
orientation: function () {
|
|
1082
|
+
object.orientation = data
|
|
1083
|
+
},
|
|
1084
|
+
|
|
1085
|
+
scale: function () {
|
|
1086
|
+
object.scale = data
|
|
1087
|
+
},
|
|
1088
|
+
|
|
1089
|
+
background: function () {
|
|
1090
|
+
object.background = data
|
|
1091
|
+
},
|
|
1092
|
+
|
|
1093
|
+
width: function () {
|
|
1094
|
+
page.width = data
|
|
1095
|
+
object.page = page
|
|
1096
|
+
},
|
|
1097
|
+
|
|
1098
|
+
height: function () {
|
|
1099
|
+
page.height = data
|
|
1100
|
+
object.page = page
|
|
1101
|
+
},
|
|
1102
|
+
|
|
1103
|
+
top: function () {
|
|
1104
|
+
margin.top = data
|
|
1105
|
+
object.margin = margin
|
|
1106
|
+
},
|
|
1107
|
+
|
|
1108
|
+
left: function () {
|
|
1109
|
+
margin.left = data
|
|
1110
|
+
object.margin = margin
|
|
1111
|
+
},
|
|
1112
|
+
|
|
1113
|
+
bottom: function () {
|
|
1114
|
+
margin.bottom = data
|
|
1115
|
+
object.margin = margin
|
|
1116
|
+
},
|
|
1117
|
+
|
|
1118
|
+
right: function () {
|
|
1119
|
+
margin.right = data
|
|
1120
|
+
object.margin = margin
|
|
1121
|
+
},
|
|
1122
|
+
|
|
1123
|
+
shrinkToFit: function () {
|
|
1124
|
+
object.shrinkToFit = data
|
|
1125
|
+
},
|
|
1126
|
+
|
|
1127
|
+
pageRanges: function () {
|
|
1128
|
+
object.pageRanges = data
|
|
1129
|
+
},
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
1133
|
+
throw new error.InvalidArgumentError(`Invalid Argument '${key}'`)
|
|
1134
|
+
} else {
|
|
1135
|
+
obj[key]()
|
|
1136
|
+
}
|
|
1137
|
+
})
|
|
1138
|
+
|
|
1139
|
+
return object
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/** @override */
|
|
1143
|
+
printPage(options = {}) {
|
|
1144
|
+
let keys = options
|
|
1145
|
+
let params = {}
|
|
1146
|
+
let resultObj
|
|
1147
|
+
|
|
1148
|
+
let self = this
|
|
1149
|
+
resultObj = self.validatePrintPageParams(keys, params)
|
|
1150
|
+
|
|
1151
|
+
return this.execute(
|
|
1152
|
+
new command.Command(command.Name.PRINT_PAGE).setParameters(resultObj)
|
|
1153
|
+
)
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Creates a new WebSocket connection.
|
|
1158
|
+
* @return {!Promise<resolved>} A new CDP instance.
|
|
1159
|
+
*/
|
|
1160
|
+
async createCDPConnection(target) {
|
|
1161
|
+
const caps = await this.getCapabilities()
|
|
1162
|
+
const seOptions = caps['map_'].get('se:options') || new Map()
|
|
1163
|
+
const vendorInfo =
|
|
1164
|
+
caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
|
|
1165
|
+
caps['map_'].get('moz:debuggerAddress') ||
|
|
1166
|
+
new Map()
|
|
1167
|
+
const debuggerUrl = seOptions['cdp'] || vendorInfo['debuggerAddress'] || vendorInfo
|
|
1168
|
+
this._wsUrl = await this.getWsUrl(debuggerUrl, target)
|
|
1169
|
+
|
|
1170
|
+
return new Promise((resolve, reject) => {
|
|
1171
|
+
try {
|
|
1172
|
+
this._wsConnection = new WebSocket(this._wsUrl)
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
reject(err)
|
|
1175
|
+
return
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
this._wsConnection.on('open', () => {
|
|
1179
|
+
this._cdpConnection = new cdp.CdpConnection(this._wsConnection)
|
|
1180
|
+
resolve(this._cdpConnection)
|
|
1181
|
+
})
|
|
1182
|
+
|
|
1183
|
+
this._wsConnection.on('error', (error) => {
|
|
1184
|
+
reject(error)
|
|
1185
|
+
})
|
|
1186
|
+
})
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Retrieves 'webSocketDebuggerUrl' by sending a http request using debugger address
|
|
1191
|
+
* @param {string} debuggerAddress
|
|
1192
|
+
* @param {string} target
|
|
1193
|
+
* @return {string} Returns parsed webSocketDebuggerUrl obtained from the http request
|
|
1194
|
+
*/
|
|
1195
|
+
async getWsUrl(debuggerAddress, target) {
|
|
1196
|
+
if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
|
|
1197
|
+
throw new error.InvalidArgumentError('invalid target value')
|
|
1198
|
+
}
|
|
1199
|
+
let path = '/json/version'
|
|
1200
|
+
|
|
1201
|
+
let request = new http.Request('GET', path)
|
|
1202
|
+
let client = new http.HttpClient('http://' + debuggerAddress)
|
|
1203
|
+
let response = await client.send(request)
|
|
1204
|
+
let url = JSON.parse(response.body)['webSocketDebuggerUrl']
|
|
1205
|
+
|
|
1206
|
+
return url
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Sets a listener for Fetch.authRequired event from CDP
|
|
1211
|
+
* If event is triggered, it enter username and password
|
|
1212
|
+
* and allows the test to move forward
|
|
1213
|
+
* @param {string} username
|
|
1214
|
+
* @param {string} password
|
|
1215
|
+
* @param connection CDP Connection
|
|
1216
|
+
*/
|
|
1217
|
+
async register(username, password, connection) {
|
|
1218
|
+
await connection.execute(
|
|
1219
|
+
'Network.setCacheDisabled',
|
|
1220
|
+
this.getRandomNumber(1, 10),
|
|
1221
|
+
{
|
|
1222
|
+
cacheDisabled: true,
|
|
1223
|
+
},
|
|
1224
|
+
null
|
|
1225
|
+
)
|
|
1226
|
+
|
|
1227
|
+
this._wsConnection.on('message', (message) => {
|
|
1228
|
+
const params = JSON.parse(message)
|
|
1229
|
+
|
|
1230
|
+
if (params.method === 'Fetch.authRequired') {
|
|
1231
|
+
const requestParams = params['params']
|
|
1232
|
+
connection.execute(
|
|
1233
|
+
'Fetch.continueWithAuth',
|
|
1234
|
+
this.getRandomNumber(1, 10),
|
|
1235
|
+
{
|
|
1236
|
+
requestId: requestParams['requestId'],
|
|
1237
|
+
authChallengeResponse: {
|
|
1238
|
+
response: 'ProvideCredentials',
|
|
1239
|
+
username: username,
|
|
1240
|
+
password: password,
|
|
1241
|
+
},
|
|
1242
|
+
}
|
|
1243
|
+
)
|
|
1244
|
+
} else if (params.method === 'Fetch.requestPaused') {
|
|
1245
|
+
const requestPausedParams = params['params']
|
|
1246
|
+
connection.execute(
|
|
1247
|
+
'Fetch.continueRequest',
|
|
1248
|
+
this.getRandomNumber(1, 10),
|
|
1249
|
+
{
|
|
1250
|
+
requestId: requestPausedParams['requestId'],
|
|
1251
|
+
}
|
|
1252
|
+
)
|
|
1253
|
+
}
|
|
1254
|
+
})
|
|
1255
|
+
|
|
1256
|
+
await connection.execute(
|
|
1257
|
+
'Fetch.enable',
|
|
1258
|
+
1,
|
|
1259
|
+
{
|
|
1260
|
+
handleAuthRequests: true,
|
|
1261
|
+
},
|
|
1262
|
+
null
|
|
1263
|
+
)
|
|
1002
1264
|
}
|
|
1003
|
-
}
|
|
1004
1265
|
|
|
1266
|
+
/**
|
|
1267
|
+
*
|
|
1268
|
+
* @param connection
|
|
1269
|
+
* @param callback
|
|
1270
|
+
* @returns {Promise<void>}
|
|
1271
|
+
*/
|
|
1272
|
+
async onLogEvent(connection, callback) {
|
|
1273
|
+
await connection.execute(
|
|
1274
|
+
'Runtime.enable',
|
|
1275
|
+
this.getRandomNumber(1, 10),
|
|
1276
|
+
{},
|
|
1277
|
+
null
|
|
1278
|
+
)
|
|
1279
|
+
|
|
1280
|
+
this._wsConnection.on('message', (message) => {
|
|
1281
|
+
const params = JSON.parse(message)
|
|
1282
|
+
|
|
1283
|
+
if (params.method === 'Runtime.consoleAPICalled') {
|
|
1284
|
+
const consoleEventParams = params['params']
|
|
1285
|
+
let event = {
|
|
1286
|
+
type: consoleEventParams['type'],
|
|
1287
|
+
timestamp: new Date(consoleEventParams['timestamp']),
|
|
1288
|
+
args: consoleEventParams['args'],
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
callback(event)
|
|
1292
|
+
}
|
|
1293
|
+
})
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
*
|
|
1298
|
+
* @param connection
|
|
1299
|
+
* @param callback
|
|
1300
|
+
* @returns {Promise<void>}
|
|
1301
|
+
*/
|
|
1302
|
+
async onLogException(connection, callback) {
|
|
1303
|
+
await connection.execute(
|
|
1304
|
+
'Runtime.enable',
|
|
1305
|
+
this.getRandomNumber(1, 10),
|
|
1306
|
+
{},
|
|
1307
|
+
null
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
this._wsConnection.on('message', (message) => {
|
|
1311
|
+
const params = JSON.parse(message)
|
|
1312
|
+
|
|
1313
|
+
if (params.method === 'Runtime.exceptionThrown') {
|
|
1314
|
+
const exceptionEventParams = params['params']
|
|
1315
|
+
let event = {
|
|
1316
|
+
exceptionDetails: exceptionEventParams['exceptionDetails'],
|
|
1317
|
+
timestamp: new Date(exceptionEventParams['timestamp']),
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
callback(event)
|
|
1321
|
+
}
|
|
1322
|
+
})
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* @param connection
|
|
1327
|
+
* @param callback
|
|
1328
|
+
* @returns {Promise<void>}
|
|
1329
|
+
*/
|
|
1330
|
+
async logMutationEvents(connection, callback) {
|
|
1331
|
+
await connection.execute(
|
|
1332
|
+
'Runtime.enable',
|
|
1333
|
+
this.getRandomNumber(1, 10),
|
|
1334
|
+
{},
|
|
1335
|
+
null
|
|
1336
|
+
)
|
|
1337
|
+
await connection.execute(
|
|
1338
|
+
'Page.enable',
|
|
1339
|
+
this.getRandomNumber(1, 10),
|
|
1340
|
+
{},
|
|
1341
|
+
null
|
|
1342
|
+
)
|
|
1343
|
+
|
|
1344
|
+
await connection.execute(
|
|
1345
|
+
'Runtime.addBinding',
|
|
1346
|
+
this.getRandomNumber(1, 10),
|
|
1347
|
+
{
|
|
1348
|
+
name: '__webdriver_attribute',
|
|
1349
|
+
},
|
|
1350
|
+
null
|
|
1351
|
+
)
|
|
1352
|
+
|
|
1353
|
+
let mutationListener = ''
|
|
1354
|
+
try {
|
|
1355
|
+
// Depending on what is running the code it could appear in 2 different places which is why we try
|
|
1356
|
+
// here and then the other location
|
|
1357
|
+
mutationListener = fs
|
|
1358
|
+
.readFileSync(
|
|
1359
|
+
'./javascript/node/selenium-webdriver/lib/atoms/mutation-listener.js',
|
|
1360
|
+
'utf-8'
|
|
1361
|
+
)
|
|
1362
|
+
.toString()
|
|
1363
|
+
} catch {
|
|
1364
|
+
mutationListener = fs
|
|
1365
|
+
.readFileSync('./atoms/mutation-listener.js', 'utf-8')
|
|
1366
|
+
.toString()
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
this.executeScript(mutationListener)
|
|
1370
|
+
|
|
1371
|
+
await connection.execute(
|
|
1372
|
+
'Page.addScriptToEvaluateOnNewDocument',
|
|
1373
|
+
this.getRandomNumber(1, 10),
|
|
1374
|
+
{
|
|
1375
|
+
source: mutationListener,
|
|
1376
|
+
},
|
|
1377
|
+
null
|
|
1378
|
+
)
|
|
1379
|
+
|
|
1380
|
+
this._wsConnection.on('message', async (message) => {
|
|
1381
|
+
const params = JSON.parse(message)
|
|
1382
|
+
if (params.method === 'Runtime.bindingCalled') {
|
|
1383
|
+
let payload = JSON.parse(params['params']['payload'])
|
|
1384
|
+
let elements = await this.findElements({
|
|
1385
|
+
css: '*[data-__webdriver_id=' + payload['target'],
|
|
1386
|
+
})
|
|
1387
|
+
|
|
1388
|
+
if (elements.length === 0) {
|
|
1389
|
+
return
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
let event = {
|
|
1393
|
+
element: elements[0],
|
|
1394
|
+
attribute_name: payload['name'],
|
|
1395
|
+
current_value: payload['value'],
|
|
1396
|
+
old_value: payload['oldValue'],
|
|
1397
|
+
}
|
|
1398
|
+
callback(event)
|
|
1399
|
+
}
|
|
1400
|
+
})
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
getRandomNumber(min, max) {
|
|
1404
|
+
return Math.floor(Math.random() * (max - min + 1) + min)
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1005
1407
|
|
|
1006
1408
|
/**
|
|
1007
1409
|
* Interface for navigating back and forth in the browser history.
|
|
@@ -1020,7 +1422,7 @@ class Navigation {
|
|
|
1020
1422
|
*/
|
|
1021
1423
|
constructor(driver) {
|
|
1022
1424
|
/** @private {!WebDriver} */
|
|
1023
|
-
this.driver_ = driver
|
|
1425
|
+
this.driver_ = driver
|
|
1024
1426
|
}
|
|
1025
1427
|
|
|
1026
1428
|
/**
|
|
@@ -1032,8 +1434,8 @@ class Navigation {
|
|
|
1032
1434
|
*/
|
|
1033
1435
|
to(url) {
|
|
1034
1436
|
return this.driver_.execute(
|
|
1035
|
-
|
|
1036
|
-
|
|
1437
|
+
new command.Command(command.Name.GET).setParameter('url', url)
|
|
1438
|
+
)
|
|
1037
1439
|
}
|
|
1038
1440
|
|
|
1039
1441
|
/**
|
|
@@ -1043,7 +1445,7 @@ class Navigation {
|
|
|
1043
1445
|
* navigation event has completed.
|
|
1044
1446
|
*/
|
|
1045
1447
|
back() {
|
|
1046
|
-
return this.driver_.execute(new command.Command(command.Name.GO_BACK))
|
|
1448
|
+
return this.driver_.execute(new command.Command(command.Name.GO_BACK))
|
|
1047
1449
|
}
|
|
1048
1450
|
|
|
1049
1451
|
/**
|
|
@@ -1053,7 +1455,7 @@ class Navigation {
|
|
|
1053
1455
|
* navigation event has completed.
|
|
1054
1456
|
*/
|
|
1055
1457
|
forward() {
|
|
1056
|
-
return this.driver_.execute(new command.Command(command.Name.GO_FORWARD))
|
|
1458
|
+
return this.driver_.execute(new command.Command(command.Name.GO_FORWARD))
|
|
1057
1459
|
}
|
|
1058
1460
|
|
|
1059
1461
|
/**
|
|
@@ -1063,11 +1465,10 @@ class Navigation {
|
|
|
1063
1465
|
* navigation event has completed.
|
|
1064
1466
|
*/
|
|
1065
1467
|
refresh() {
|
|
1066
|
-
return this.driver_.execute(new command.Command(command.Name.REFRESH))
|
|
1468
|
+
return this.driver_.execute(new command.Command(command.Name.REFRESH))
|
|
1067
1469
|
}
|
|
1068
1470
|
}
|
|
1069
1471
|
|
|
1070
|
-
|
|
1071
1472
|
/**
|
|
1072
1473
|
* Provides methods for managing browser and driver state.
|
|
1073
1474
|
*
|
|
@@ -1081,7 +1482,7 @@ class Options {
|
|
|
1081
1482
|
*/
|
|
1082
1483
|
constructor(driver) {
|
|
1083
1484
|
/** @private {!WebDriver} */
|
|
1084
|
-
this.driver_ = driver
|
|
1485
|
+
this.driver_ = driver
|
|
1085
1486
|
}
|
|
1086
1487
|
|
|
1087
1488
|
/**
|
|
@@ -1110,37 +1511,50 @@ class Options {
|
|
|
1110
1511
|
* invalid.
|
|
1111
1512
|
* @throws {TypeError} if `spec` is not a cookie object.
|
|
1112
1513
|
*/
|
|
1113
|
-
addCookie({name, value, path, domain, secure, httpOnly, expiry}) {
|
|
1514
|
+
addCookie({ name, value, path, domain, secure, httpOnly, expiry, sameSite }) {
|
|
1114
1515
|
// We do not allow '=' or ';' in the name.
|
|
1115
1516
|
if (/[;=]/.test(name)) {
|
|
1116
|
-
throw new error.InvalidArgumentError(
|
|
1117
|
-
'Invalid cookie name "' + name + '"');
|
|
1517
|
+
throw new error.InvalidArgumentError('Invalid cookie name "' + name + '"')
|
|
1118
1518
|
}
|
|
1119
1519
|
|
|
1120
1520
|
// We do not allow ';' in value.
|
|
1121
1521
|
if (/;/.test(value)) {
|
|
1122
1522
|
throw new error.InvalidArgumentError(
|
|
1123
|
-
|
|
1523
|
+
'Invalid cookie value "' + value + '"'
|
|
1524
|
+
)
|
|
1124
1525
|
}
|
|
1125
1526
|
|
|
1126
1527
|
if (typeof expiry === 'number') {
|
|
1127
|
-
expiry = Math.floor(expiry)
|
|
1528
|
+
expiry = Math.floor(expiry)
|
|
1128
1529
|
} else if (expiry instanceof Date) {
|
|
1129
|
-
let date = /** @type {!Date} */(expiry)
|
|
1130
|
-
expiry = Math.floor(date.getTime() / 1000)
|
|
1530
|
+
let date = /** @type {!Date} */ (expiry)
|
|
1531
|
+
expiry = Math.floor(date.getTime() / 1000)
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
if (sameSite && !['Strict', 'Lax', 'None'].includes(sameSite)) {
|
|
1535
|
+
throw new error.InvalidArgumentError(
|
|
1536
|
+
`Invalid sameSite cookie value '${sameSite}'. It should be one of "Lax", "Strict" or "None"`
|
|
1537
|
+
)
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
if (sameSite === 'None' && !secure) {
|
|
1541
|
+
throw new error.InvalidArgumentError(
|
|
1542
|
+
'Invalid cookie configuration: SameSite=None must be Secure'
|
|
1543
|
+
)
|
|
1131
1544
|
}
|
|
1132
1545
|
|
|
1133
1546
|
return this.driver_.execute(
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1547
|
+
new command.Command(command.Name.ADD_COOKIE).setParameter('cookie', {
|
|
1548
|
+
name: name,
|
|
1549
|
+
value: value,
|
|
1550
|
+
path: path,
|
|
1551
|
+
domain: domain,
|
|
1552
|
+
secure: !!secure,
|
|
1553
|
+
httpOnly: !!httpOnly,
|
|
1554
|
+
expiry: expiry,
|
|
1555
|
+
sameSite: sameSite,
|
|
1556
|
+
})
|
|
1557
|
+
)
|
|
1144
1558
|
}
|
|
1145
1559
|
|
|
1146
1560
|
/**
|
|
@@ -1151,7 +1565,8 @@ class Options {
|
|
|
1151
1565
|
*/
|
|
1152
1566
|
deleteAllCookies() {
|
|
1153
1567
|
return this.driver_.execute(
|
|
1154
|
-
|
|
1568
|
+
new command.Command(command.Name.DELETE_ALL_COOKIES)
|
|
1569
|
+
)
|
|
1155
1570
|
}
|
|
1156
1571
|
|
|
1157
1572
|
/**
|
|
@@ -1164,8 +1579,8 @@ class Options {
|
|
|
1164
1579
|
*/
|
|
1165
1580
|
deleteCookie(name) {
|
|
1166
1581
|
return this.driver_.execute(
|
|
1167
|
-
|
|
1168
|
-
|
|
1582
|
+
new command.Command(command.Name.DELETE_COOKIE).setParameter('name', name)
|
|
1583
|
+
)
|
|
1169
1584
|
}
|
|
1170
1585
|
|
|
1171
1586
|
/**
|
|
@@ -1177,7 +1592,8 @@ class Options {
|
|
|
1177
1592
|
*/
|
|
1178
1593
|
getCookies() {
|
|
1179
1594
|
return this.driver_.execute(
|
|
1180
|
-
|
|
1595
|
+
new command.Command(command.Name.GET_ALL_COOKIES)
|
|
1596
|
+
)
|
|
1181
1597
|
}
|
|
1182
1598
|
|
|
1183
1599
|
/**
|
|
@@ -1187,28 +1603,30 @@ class Options {
|
|
|
1187
1603
|
*
|
|
1188
1604
|
* @param {string} name The name of the cookie to retrieve.
|
|
1189
1605
|
* @return {!Promise<?Options.Cookie>} A promise that will be resolved
|
|
1190
|
-
* with the named cookie
|
|
1606
|
+
* with the named cookie
|
|
1607
|
+
* @throws {error.NoSuchCookieError} if there is no such cookie.
|
|
1191
1608
|
*/
|
|
1192
1609
|
async getCookie(name) {
|
|
1193
1610
|
try {
|
|
1194
|
-
const cookie =
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
return cookie;
|
|
1611
|
+
const cookie = await this.driver_.execute(
|
|
1612
|
+
new command.Command(command.Name.GET_COOKIE).setParameter('name', name)
|
|
1613
|
+
)
|
|
1614
|
+
return cookie
|
|
1199
1615
|
} catch (err) {
|
|
1200
|
-
if (
|
|
1201
|
-
|
|
1202
|
-
|
|
1616
|
+
if (
|
|
1617
|
+
!(err instanceof error.UnknownCommandError) &&
|
|
1618
|
+
!(err instanceof error.UnsupportedOperationError)
|
|
1619
|
+
) {
|
|
1620
|
+
throw err
|
|
1203
1621
|
}
|
|
1204
1622
|
|
|
1205
|
-
const cookies = await this.getCookies()
|
|
1623
|
+
const cookies = await this.getCookies()
|
|
1206
1624
|
for (let cookie of cookies) {
|
|
1207
1625
|
if (cookie && cookie['name'] === name) {
|
|
1208
|
-
return cookie
|
|
1626
|
+
return cookie
|
|
1209
1627
|
}
|
|
1210
1628
|
}
|
|
1211
|
-
return null
|
|
1629
|
+
return null
|
|
1212
1630
|
}
|
|
1213
1631
|
}
|
|
1214
1632
|
|
|
@@ -1223,7 +1641,7 @@ class Options {
|
|
|
1223
1641
|
* @see #setTimeouts()
|
|
1224
1642
|
*/
|
|
1225
1643
|
getTimeouts() {
|
|
1226
|
-
return this.driver_.execute(new command.Command(command.Name.GET_TIMEOUT))
|
|
1644
|
+
return this.driver_.execute(new command.Command(command.Name.GET_TIMEOUT))
|
|
1227
1645
|
}
|
|
1228
1646
|
|
|
1229
1647
|
/**
|
|
@@ -1255,60 +1673,59 @@ class Options {
|
|
|
1255
1673
|
* @see #getTimeouts()
|
|
1256
1674
|
* @see <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-set-timeouts>
|
|
1257
1675
|
*/
|
|
1258
|
-
setTimeouts({script, pageLoad, implicit} = {}) {
|
|
1259
|
-
let cmd = new command.Command(command.Name.SET_TIMEOUT)
|
|
1676
|
+
setTimeouts({ script, pageLoad, implicit } = {}) {
|
|
1677
|
+
let cmd = new command.Command(command.Name.SET_TIMEOUT)
|
|
1260
1678
|
|
|
1261
|
-
let valid = false
|
|
1679
|
+
let valid = false
|
|
1262
1680
|
function setParam(key, value) {
|
|
1263
1681
|
if (value === null || typeof value === 'number') {
|
|
1264
|
-
valid = true
|
|
1265
|
-
cmd.setParameter(key, value)
|
|
1682
|
+
valid = true
|
|
1683
|
+
cmd.setParameter(key, value)
|
|
1266
1684
|
} else if (typeof value !== 'undefined') {
|
|
1267
1685
|
throw TypeError(
|
|
1268
|
-
|
|
1269
|
-
|
|
1686
|
+
'invalid timeouts configuration:' +
|
|
1687
|
+
` expected "${key}" to be a number, got ${typeof value}`
|
|
1688
|
+
)
|
|
1270
1689
|
}
|
|
1271
1690
|
}
|
|
1272
|
-
setParam('implicit', implicit)
|
|
1273
|
-
setParam('pageLoad', pageLoad)
|
|
1274
|
-
setParam('script', script)
|
|
1691
|
+
setParam('implicit', implicit)
|
|
1692
|
+
setParam('pageLoad', pageLoad)
|
|
1693
|
+
setParam('script', script)
|
|
1275
1694
|
|
|
1276
1695
|
if (valid) {
|
|
1277
|
-
return this.driver_.execute(cmd)
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
});
|
|
1696
|
+
return this.driver_.execute(cmd).catch(() => {
|
|
1697
|
+
// Fallback to the legacy method.
|
|
1698
|
+
let cmds = []
|
|
1699
|
+
if (typeof script === 'number') {
|
|
1700
|
+
cmds.push(legacyTimeout(this.driver_, 'script', script))
|
|
1701
|
+
}
|
|
1702
|
+
if (typeof implicit === 'number') {
|
|
1703
|
+
cmds.push(legacyTimeout(this.driver_, 'implicit', implicit))
|
|
1704
|
+
}
|
|
1705
|
+
if (typeof pageLoad === 'number') {
|
|
1706
|
+
cmds.push(legacyTimeout(this.driver_, 'page load', pageLoad))
|
|
1707
|
+
}
|
|
1708
|
+
return Promise.all(cmds)
|
|
1709
|
+
})
|
|
1292
1710
|
}
|
|
1293
|
-
throw TypeError('no timeouts specified')
|
|
1711
|
+
throw TypeError('no timeouts specified')
|
|
1294
1712
|
}
|
|
1295
1713
|
|
|
1296
1714
|
/**
|
|
1297
1715
|
* @return {!Logs} The interface for managing driver logs.
|
|
1298
1716
|
*/
|
|
1299
1717
|
logs() {
|
|
1300
|
-
return new Logs(this.driver_)
|
|
1718
|
+
return new Logs(this.driver_)
|
|
1301
1719
|
}
|
|
1302
1720
|
|
|
1303
1721
|
/**
|
|
1304
1722
|
* @return {!Window} The interface for managing the current window.
|
|
1305
1723
|
*/
|
|
1306
1724
|
window() {
|
|
1307
|
-
return new Window(this.driver_)
|
|
1725
|
+
return new Window(this.driver_)
|
|
1308
1726
|
}
|
|
1309
1727
|
}
|
|
1310
1728
|
|
|
1311
|
-
|
|
1312
1729
|
/**
|
|
1313
1730
|
* @param {!WebDriver} driver
|
|
1314
1731
|
* @param {string} type
|
|
@@ -1317,44 +1734,39 @@ class Options {
|
|
|
1317
1734
|
*/
|
|
1318
1735
|
function legacyTimeout(driver, type, ms) {
|
|
1319
1736
|
return driver.execute(
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1737
|
+
new command.Command(command.Name.SET_TIMEOUT)
|
|
1738
|
+
.setParameter('type', type)
|
|
1739
|
+
.setParameter('ms', ms)
|
|
1740
|
+
)
|
|
1323
1741
|
}
|
|
1324
1742
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
1743
|
/**
|
|
1328
1744
|
* A record object describing a browser cookie.
|
|
1329
1745
|
*
|
|
1330
1746
|
* @record
|
|
1331
1747
|
*/
|
|
1332
|
-
Options.Cookie = function() {}
|
|
1333
|
-
|
|
1748
|
+
Options.Cookie = function () { }
|
|
1334
1749
|
|
|
1335
1750
|
/**
|
|
1336
1751
|
* The name of the cookie.
|
|
1337
1752
|
*
|
|
1338
1753
|
* @type {string}
|
|
1339
1754
|
*/
|
|
1340
|
-
Options.Cookie.prototype.name
|
|
1341
|
-
|
|
1755
|
+
Options.Cookie.prototype.name
|
|
1342
1756
|
|
|
1343
1757
|
/**
|
|
1344
1758
|
* The cookie value.
|
|
1345
1759
|
*
|
|
1346
1760
|
* @type {string}
|
|
1347
1761
|
*/
|
|
1348
|
-
Options.Cookie.prototype.value
|
|
1349
|
-
|
|
1762
|
+
Options.Cookie.prototype.value
|
|
1350
1763
|
|
|
1351
1764
|
/**
|
|
1352
1765
|
* The cookie path. Defaults to "/" when adding a cookie.
|
|
1353
1766
|
*
|
|
1354
1767
|
* @type {(string|undefined)}
|
|
1355
1768
|
*/
|
|
1356
|
-
Options.Cookie.prototype.path
|
|
1357
|
-
|
|
1769
|
+
Options.Cookie.prototype.path
|
|
1358
1770
|
|
|
1359
1771
|
/**
|
|
1360
1772
|
* The domain the cookie is visible to. Defaults to the current browsing
|
|
@@ -1362,8 +1774,7 @@ Options.Cookie.prototype.path;
|
|
|
1362
1774
|
*
|
|
1363
1775
|
* @type {(string|undefined)}
|
|
1364
1776
|
*/
|
|
1365
|
-
Options.Cookie.prototype.domain
|
|
1366
|
-
|
|
1777
|
+
Options.Cookie.prototype.domain
|
|
1367
1778
|
|
|
1368
1779
|
/**
|
|
1369
1780
|
* Whether the cookie is a secure cookie. Defaults to false when adding a new
|
|
@@ -1371,8 +1782,7 @@ Options.Cookie.prototype.domain;
|
|
|
1371
1782
|
*
|
|
1372
1783
|
* @type {(boolean|undefined)}
|
|
1373
1784
|
*/
|
|
1374
|
-
Options.Cookie.prototype.secure
|
|
1375
|
-
|
|
1785
|
+
Options.Cookie.prototype.secure
|
|
1376
1786
|
|
|
1377
1787
|
/**
|
|
1378
1788
|
* Whether the cookie is an HTTP only cookie. Defaults to false when adding a
|
|
@@ -1380,8 +1790,7 @@ Options.Cookie.prototype.secure;
|
|
|
1380
1790
|
*
|
|
1381
1791
|
* @type {(boolean|undefined)}
|
|
1382
1792
|
*/
|
|
1383
|
-
Options.Cookie.prototype.httpOnly
|
|
1384
|
-
|
|
1793
|
+
Options.Cookie.prototype.httpOnly
|
|
1385
1794
|
|
|
1386
1795
|
/**
|
|
1387
1796
|
* When the cookie expires.
|
|
@@ -1394,8 +1803,18 @@ Options.Cookie.prototype.httpOnly;
|
|
|
1394
1803
|
*
|
|
1395
1804
|
* @type {(!Date|number|undefined)}
|
|
1396
1805
|
*/
|
|
1397
|
-
Options.Cookie.prototype.expiry
|
|
1806
|
+
Options.Cookie.prototype.expiry
|
|
1398
1807
|
|
|
1808
|
+
/**
|
|
1809
|
+
* When the cookie applies to a SameSite policy.
|
|
1810
|
+
*
|
|
1811
|
+
* When {@linkplain Options#addCookie() adding a cookie}, this may be specified
|
|
1812
|
+
* as a {@link string} object which is one of 'Lax', 'Strict' or 'None'.
|
|
1813
|
+
*
|
|
1814
|
+
*
|
|
1815
|
+
* @type {(string|undefined)}
|
|
1816
|
+
*/
|
|
1817
|
+
Options.Cookie.prototype.sameSite
|
|
1399
1818
|
|
|
1400
1819
|
/**
|
|
1401
1820
|
* An interface for managing the current window.
|
|
@@ -1415,7 +1834,7 @@ class Window {
|
|
|
1415
1834
|
*/
|
|
1416
1835
|
constructor(driver) {
|
|
1417
1836
|
/** @private {!WebDriver} */
|
|
1418
|
-
this.driver_ = driver
|
|
1837
|
+
this.driver_ = driver
|
|
1419
1838
|
}
|
|
1420
1839
|
|
|
1421
1840
|
/**
|
|
@@ -1428,20 +1847,25 @@ class Window {
|
|
|
1428
1847
|
async getRect() {
|
|
1429
1848
|
try {
|
|
1430
1849
|
return await this.driver_.execute(
|
|
1431
|
-
|
|
1850
|
+
new command.Command(command.Name.GET_WINDOW_RECT)
|
|
1851
|
+
)
|
|
1432
1852
|
} catch (ex) {
|
|
1433
1853
|
if (ex instanceof error.UnknownCommandError) {
|
|
1434
|
-
let {width, height} =
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1854
|
+
let { width, height } = await this.driver_.execute(
|
|
1855
|
+
new command.Command(command.Name.GET_WINDOW_SIZE).setParameter(
|
|
1856
|
+
'windowHandle',
|
|
1857
|
+
'current'
|
|
1858
|
+
)
|
|
1859
|
+
)
|
|
1860
|
+
let { x, y } = await this.driver_.execute(
|
|
1861
|
+
new command.Command(command.Name.GET_WINDOW_POSITION).setParameter(
|
|
1862
|
+
'windowHandle',
|
|
1863
|
+
'current'
|
|
1864
|
+
)
|
|
1865
|
+
)
|
|
1866
|
+
return { x, y, width, height }
|
|
1443
1867
|
}
|
|
1444
|
-
throw ex
|
|
1868
|
+
throw ex
|
|
1445
1869
|
}
|
|
1446
1870
|
}
|
|
1447
1871
|
|
|
@@ -1456,34 +1880,41 @@ class Window {
|
|
|
1456
1880
|
* height: (number|undefined)}} options
|
|
1457
1881
|
* The desired window size and position.
|
|
1458
1882
|
* @return {!Promise<{x: number, y: number, width: number, height: number}>}
|
|
1459
|
-
* A promise that will resolve to the current
|
|
1883
|
+
* A promise that will resolve to the current window's updated window
|
|
1460
1884
|
* rect.
|
|
1461
1885
|
*/
|
|
1462
|
-
async setRect({x, y, width, height}) {
|
|
1886
|
+
async setRect({ x, y, width, height }) {
|
|
1463
1887
|
try {
|
|
1464
1888
|
return await this.driver_.execute(
|
|
1465
|
-
|
|
1466
|
-
|
|
1889
|
+
new command.Command(command.Name.SET_WINDOW_RECT).setParameters({
|
|
1890
|
+
x,
|
|
1891
|
+
y,
|
|
1892
|
+
width,
|
|
1893
|
+
height,
|
|
1894
|
+
})
|
|
1895
|
+
)
|
|
1467
1896
|
} catch (ex) {
|
|
1468
1897
|
if (ex instanceof error.UnknownCommandError) {
|
|
1469
1898
|
if (typeof x === 'number' && typeof y === 'number') {
|
|
1470
1899
|
await this.driver_.execute(
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1900
|
+
new command.Command(command.Name.SET_WINDOW_POSITION)
|
|
1901
|
+
.setParameter('windowHandle', 'current')
|
|
1902
|
+
.setParameter('x', x)
|
|
1903
|
+
.setParameter('y', y)
|
|
1904
|
+
)
|
|
1475
1905
|
}
|
|
1476
1906
|
|
|
1477
|
-
if (typeof
|
|
1907
|
+
if (typeof width === 'number' && typeof height === 'number') {
|
|
1478
1908
|
await this.driver_.execute(
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1909
|
+
new command.Command(command.Name.SET_WINDOW_SIZE)
|
|
1910
|
+
.setParameter('windowHandle', 'current')
|
|
1911
|
+
.setParameter('width', width)
|
|
1912
|
+
.setParameter('height', height)
|
|
1913
|
+
)
|
|
1483
1914
|
}
|
|
1484
|
-
return this.getRect()
|
|
1915
|
+
return this.getRect()
|
|
1485
1916
|
}
|
|
1486
|
-
throw ex
|
|
1917
|
+
throw ex
|
|
1487
1918
|
}
|
|
1488
1919
|
}
|
|
1489
1920
|
|
|
@@ -1497,13 +1928,16 @@ class Window {
|
|
|
1497
1928
|
*/
|
|
1498
1929
|
maximize() {
|
|
1499
1930
|
return this.driver_.execute(
|
|
1500
|
-
|
|
1501
|
-
|
|
1931
|
+
new command.Command(command.Name.MAXIMIZE_WINDOW).setParameter(
|
|
1932
|
+
'windowHandle',
|
|
1933
|
+
'current'
|
|
1934
|
+
)
|
|
1935
|
+
)
|
|
1502
1936
|
}
|
|
1503
1937
|
|
|
1504
1938
|
/**
|
|
1505
1939
|
* Minimizes the current window. The exact behavior of this command is
|
|
1506
|
-
* specific to individual window managers, but
|
|
1940
|
+
* specific to individual window managers, but typically involves hiding
|
|
1507
1941
|
* the window in the system tray.
|
|
1508
1942
|
*
|
|
1509
1943
|
* @return {!Promise<void>} A promise that will be resolved when the command
|
|
@@ -1511,7 +1945,8 @@ class Window {
|
|
|
1511
1945
|
*/
|
|
1512
1946
|
minimize() {
|
|
1513
1947
|
return this.driver_.execute(
|
|
1514
|
-
|
|
1948
|
+
new command.Command(command.Name.MINIMIZE_WINDOW)
|
|
1949
|
+
)
|
|
1515
1950
|
}
|
|
1516
1951
|
|
|
1517
1952
|
/**
|
|
@@ -1526,11 +1961,11 @@ class Window {
|
|
|
1526
1961
|
*/
|
|
1527
1962
|
fullscreen() {
|
|
1528
1963
|
return this.driver_.execute(
|
|
1529
|
-
|
|
1964
|
+
new command.Command(command.Name.FULLSCREEN_WINDOW)
|
|
1965
|
+
)
|
|
1530
1966
|
}
|
|
1531
1967
|
}
|
|
1532
1968
|
|
|
1533
|
-
|
|
1534
1969
|
/**
|
|
1535
1970
|
* Interface for managing WebDriver log records.
|
|
1536
1971
|
*
|
|
@@ -1549,7 +1984,7 @@ class Logs {
|
|
|
1549
1984
|
*/
|
|
1550
1985
|
constructor(driver) {
|
|
1551
1986
|
/** @private {!WebDriver} */
|
|
1552
|
-
this.driver_ = driver
|
|
1987
|
+
this.driver_ = driver
|
|
1553
1988
|
}
|
|
1554
1989
|
|
|
1555
1990
|
/**
|
|
@@ -1566,19 +2001,23 @@ class Logs {
|
|
|
1566
2001
|
* type.
|
|
1567
2002
|
*/
|
|
1568
2003
|
get(type) {
|
|
1569
|
-
let cmd = new command.Command(command.Name.GET_LOG).
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
2004
|
+
let cmd = new command.Command(command.Name.GET_LOG).setParameter(
|
|
2005
|
+
'type',
|
|
2006
|
+
type
|
|
2007
|
+
)
|
|
2008
|
+
return this.driver_.execute(cmd).then(function (entries) {
|
|
2009
|
+
return entries.map(function (entry) {
|
|
2010
|
+
if (!(entry instanceof logging.Entry)) {
|
|
2011
|
+
return new logging.Entry(
|
|
2012
|
+
entry['level'],
|
|
2013
|
+
entry['message'],
|
|
2014
|
+
entry['timestamp'],
|
|
2015
|
+
entry['type']
|
|
2016
|
+
)
|
|
2017
|
+
}
|
|
2018
|
+
return entry
|
|
2019
|
+
})
|
|
2020
|
+
})
|
|
1582
2021
|
}
|
|
1583
2022
|
|
|
1584
2023
|
/**
|
|
@@ -1588,11 +2027,11 @@ class Logs {
|
|
|
1588
2027
|
*/
|
|
1589
2028
|
getAvailableLogTypes() {
|
|
1590
2029
|
return this.driver_.execute(
|
|
1591
|
-
|
|
2030
|
+
new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES)
|
|
2031
|
+
)
|
|
1592
2032
|
}
|
|
1593
2033
|
}
|
|
1594
2034
|
|
|
1595
|
-
|
|
1596
2035
|
/**
|
|
1597
2036
|
* An interface for changing the focus of the driver to another frame or window.
|
|
1598
2037
|
*
|
|
@@ -1610,7 +2049,7 @@ class TargetLocator {
|
|
|
1610
2049
|
*/
|
|
1611
2050
|
constructor(driver) {
|
|
1612
2051
|
/** @private {!WebDriver} */
|
|
1613
|
-
this.driver_ = driver
|
|
2052
|
+
this.driver_ = driver
|
|
1614
2053
|
}
|
|
1615
2054
|
|
|
1616
2055
|
/**
|
|
@@ -1622,8 +2061,9 @@ class TargetLocator {
|
|
|
1622
2061
|
*/
|
|
1623
2062
|
activeElement() {
|
|
1624
2063
|
var id = this.driver_.execute(
|
|
1625
|
-
|
|
1626
|
-
|
|
2064
|
+
new command.Command(command.Name.GET_ACTIVE_ELEMENT)
|
|
2065
|
+
)
|
|
2066
|
+
return new WebElementPromise(this.driver_, id)
|
|
1627
2067
|
}
|
|
1628
2068
|
|
|
1629
2069
|
/**
|
|
@@ -1635,8 +2075,8 @@ class TargetLocator {
|
|
|
1635
2075
|
*/
|
|
1636
2076
|
defaultContent() {
|
|
1637
2077
|
return this.driver_.execute(
|
|
1638
|
-
|
|
1639
|
-
|
|
2078
|
+
new command.Command(command.Name.SWITCH_TO_FRAME).setParameter('id', null)
|
|
2079
|
+
)
|
|
1640
2080
|
}
|
|
1641
2081
|
|
|
1642
2082
|
/**
|
|
@@ -1659,8 +2099,8 @@ class TargetLocator {
|
|
|
1659
2099
|
*/
|
|
1660
2100
|
frame(id) {
|
|
1661
2101
|
return this.driver_.execute(
|
|
1662
|
-
|
|
1663
|
-
|
|
2102
|
+
new command.Command(command.Name.SWITCH_TO_FRAME).setParameter('id', id)
|
|
2103
|
+
)
|
|
1664
2104
|
}
|
|
1665
2105
|
|
|
1666
2106
|
/**
|
|
@@ -1673,7 +2113,8 @@ class TargetLocator {
|
|
|
1673
2113
|
*/
|
|
1674
2114
|
parentFrame() {
|
|
1675
2115
|
return this.driver_.execute(
|
|
1676
|
-
|
|
2116
|
+
new command.Command(command.Name.SWITCH_TO_FRAME_PARENT)
|
|
2117
|
+
)
|
|
1677
2118
|
}
|
|
1678
2119
|
|
|
1679
2120
|
/**
|
|
@@ -1691,11 +2132,12 @@ class TargetLocator {
|
|
|
1691
2132
|
*/
|
|
1692
2133
|
window(nameOrHandle) {
|
|
1693
2134
|
return this.driver_.execute(
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
2135
|
+
new command.Command(command.Name.SWITCH_TO_WINDOW)
|
|
2136
|
+
// "name" supports the legacy drivers. "handle" is the W3C
|
|
2137
|
+
// compliant parameter.
|
|
2138
|
+
.setParameter('name', nameOrHandle)
|
|
2139
|
+
.setParameter('handle', nameOrHandle)
|
|
2140
|
+
)
|
|
1699
2141
|
}
|
|
1700
2142
|
|
|
1701
2143
|
/**
|
|
@@ -1711,12 +2153,16 @@ class TargetLocator {
|
|
|
1711
2153
|
*/
|
|
1712
2154
|
newWindow(typeHint) {
|
|
1713
2155
|
var driver = this.driver_
|
|
1714
|
-
return this.driver_
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
2156
|
+
return this.driver_
|
|
2157
|
+
.execute(
|
|
2158
|
+
new command.Command(command.Name.SWITCH_TO_NEW_WINDOW).setParameter(
|
|
2159
|
+
'type',
|
|
2160
|
+
typeHint
|
|
2161
|
+
)
|
|
2162
|
+
)
|
|
2163
|
+
.then(function (response) {
|
|
2164
|
+
return driver.switchTo().window(response.handle)
|
|
2165
|
+
})
|
|
1720
2166
|
}
|
|
1721
2167
|
|
|
1722
2168
|
/**
|
|
@@ -1729,25 +2175,26 @@ class TargetLocator {
|
|
|
1729
2175
|
*/
|
|
1730
2176
|
alert() {
|
|
1731
2177
|
var text = this.driver_.execute(
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
2178
|
+
new command.Command(command.Name.GET_ALERT_TEXT)
|
|
2179
|
+
)
|
|
2180
|
+
var driver = this.driver_
|
|
2181
|
+
return new AlertPromise(
|
|
2182
|
+
driver,
|
|
2183
|
+
text.then(function (text) {
|
|
2184
|
+
return new Alert(driver, text)
|
|
2185
|
+
})
|
|
2186
|
+
)
|
|
1737
2187
|
}
|
|
1738
2188
|
}
|
|
1739
2189
|
|
|
1740
|
-
|
|
1741
2190
|
//////////////////////////////////////////////////////////////////////////////
|
|
1742
2191
|
//
|
|
1743
2192
|
// WebElement
|
|
1744
2193
|
//
|
|
1745
2194
|
//////////////////////////////////////////////////////////////////////////////
|
|
1746
2195
|
|
|
1747
|
-
|
|
1748
|
-
const
|
|
1749
|
-
const ELEMENT_ID_KEY = 'element-6066-11e4-a52e-4f735466cecf';
|
|
1750
|
-
|
|
2196
|
+
const LEGACY_ELEMENT_ID_KEY = 'ELEMENT'
|
|
2197
|
+
const ELEMENT_ID_KEY = 'element-6066-11e4-a52e-4f735466cecf'
|
|
1751
2198
|
|
|
1752
2199
|
/**
|
|
1753
2200
|
* Represents a DOM element. WebElements can be found by searching from the
|
|
@@ -1767,10 +2214,10 @@ class WebElement {
|
|
|
1767
2214
|
*/
|
|
1768
2215
|
constructor(driver, id) {
|
|
1769
2216
|
/** @private {!WebDriver} */
|
|
1770
|
-
this.driver_ = driver
|
|
2217
|
+
this.driver_ = driver
|
|
1771
2218
|
|
|
1772
2219
|
/** @private {!Promise<string>} */
|
|
1773
|
-
this.id_ = Promise.resolve(id)
|
|
2220
|
+
this.id_ = Promise.resolve(id)
|
|
1774
2221
|
}
|
|
1775
2222
|
|
|
1776
2223
|
/**
|
|
@@ -1780,8 +2227,8 @@ class WebElement {
|
|
|
1780
2227
|
*/
|
|
1781
2228
|
static buildId(id, noLegacy = false) {
|
|
1782
2229
|
return noLegacy
|
|
1783
|
-
|
|
1784
|
-
|
|
2230
|
+
? { [ELEMENT_ID_KEY]: id }
|
|
2231
|
+
: { [ELEMENT_ID_KEY]: id, [LEGACY_ELEMENT_ID_KEY]: id }
|
|
1785
2232
|
}
|
|
1786
2233
|
|
|
1787
2234
|
/**
|
|
@@ -1794,12 +2241,12 @@ class WebElement {
|
|
|
1794
2241
|
static extractId(obj) {
|
|
1795
2242
|
if (obj && typeof obj === 'object') {
|
|
1796
2243
|
if (typeof obj[ELEMENT_ID_KEY] === 'string') {
|
|
1797
|
-
return obj[ELEMENT_ID_KEY]
|
|
2244
|
+
return obj[ELEMENT_ID_KEY]
|
|
1798
2245
|
} else if (typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string') {
|
|
1799
|
-
return obj[LEGACY_ELEMENT_ID_KEY]
|
|
2246
|
+
return obj[LEGACY_ELEMENT_ID_KEY]
|
|
1800
2247
|
}
|
|
1801
2248
|
}
|
|
1802
|
-
throw new TypeError('object is not a WebElement ID')
|
|
2249
|
+
throw new TypeError('object is not a WebElement ID')
|
|
1803
2250
|
}
|
|
1804
2251
|
|
|
1805
2252
|
/**
|
|
@@ -1807,9 +2254,12 @@ class WebElement {
|
|
|
1807
2254
|
* @return {boolean} whether the object is a valid encoded WebElement ID.
|
|
1808
2255
|
*/
|
|
1809
2256
|
static isId(obj) {
|
|
1810
|
-
return
|
|
1811
|
-
|
|
1812
|
-
|
|
2257
|
+
return (
|
|
2258
|
+
obj &&
|
|
2259
|
+
typeof obj === 'object' &&
|
|
2260
|
+
(typeof obj[ELEMENT_ID_KEY] === 'string' ||
|
|
2261
|
+
typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string')
|
|
2262
|
+
)
|
|
1813
2263
|
}
|
|
1814
2264
|
|
|
1815
2265
|
/**
|
|
@@ -1822,15 +2272,14 @@ class WebElement {
|
|
|
1822
2272
|
*/
|
|
1823
2273
|
static async equals(a, b) {
|
|
1824
2274
|
if (a === b) {
|
|
1825
|
-
return true
|
|
2275
|
+
return true
|
|
1826
2276
|
}
|
|
1827
|
-
return a.driver_.executeScript(
|
|
1828
|
-
'return arguments[0] === arguments[1]', a, b);
|
|
2277
|
+
return a.driver_.executeScript('return arguments[0] === arguments[1]', a, b)
|
|
1829
2278
|
}
|
|
1830
2279
|
|
|
1831
2280
|
/** @return {!WebDriver} The parent driver for this instance. */
|
|
1832
2281
|
getDriver() {
|
|
1833
|
-
return this.driver_
|
|
2282
|
+
return this.driver_
|
|
1834
2283
|
}
|
|
1835
2284
|
|
|
1836
2285
|
/**
|
|
@@ -1838,14 +2287,14 @@ class WebElement {
|
|
|
1838
2287
|
* the server-assigned opaque ID assigned to this element.
|
|
1839
2288
|
*/
|
|
1840
2289
|
getId() {
|
|
1841
|
-
return this.id_
|
|
2290
|
+
return this.id_
|
|
1842
2291
|
}
|
|
1843
2292
|
|
|
1844
2293
|
/**
|
|
1845
2294
|
* @return {!Object} Returns the serialized representation of this WebElement.
|
|
1846
2295
|
*/
|
|
1847
2296
|
[Symbols.serialize]() {
|
|
1848
|
-
return this.getId().then(WebElement.buildId)
|
|
2297
|
+
return this.getId().then(WebElement.buildId)
|
|
1849
2298
|
}
|
|
1850
2299
|
|
|
1851
2300
|
/**
|
|
@@ -1860,8 +2309,8 @@ class WebElement {
|
|
|
1860
2309
|
* @private
|
|
1861
2310
|
*/
|
|
1862
2311
|
execute_(command) {
|
|
1863
|
-
command.setParameter('id', this)
|
|
1864
|
-
return this.driver_.execute(command)
|
|
2312
|
+
command.setParameter('id', this)
|
|
2313
|
+
return this.driver_.execute(command)
|
|
1865
2314
|
}
|
|
1866
2315
|
|
|
1867
2316
|
/**
|
|
@@ -1899,18 +2348,17 @@ class WebElement {
|
|
|
1899
2348
|
* element will be invalidated and all scheduled commands aborted.
|
|
1900
2349
|
*/
|
|
1901
2350
|
findElement(locator) {
|
|
1902
|
-
locator = by.checkedLocator(locator)
|
|
1903
|
-
let id
|
|
2351
|
+
locator = by.checkedLocator(locator)
|
|
2352
|
+
let id
|
|
1904
2353
|
if (typeof locator === 'function') {
|
|
1905
|
-
id = this.driver_.findElementInternal_(locator, this)
|
|
2354
|
+
id = this.driver_.findElementInternal_(locator, this)
|
|
1906
2355
|
} else {
|
|
1907
|
-
let cmd = new command.Command(
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
id = this.execute_(cmd);
|
|
2356
|
+
let cmd = new command.Command(command.Name.FIND_CHILD_ELEMENT)
|
|
2357
|
+
.setParameter('using', locator.using)
|
|
2358
|
+
.setParameter('value', locator.value)
|
|
2359
|
+
id = this.execute_(cmd)
|
|
1912
2360
|
}
|
|
1913
|
-
return new WebElementPromise(this.driver_, id)
|
|
2361
|
+
return new WebElementPromise(this.driver_, id)
|
|
1914
2362
|
}
|
|
1915
2363
|
|
|
1916
2364
|
/**
|
|
@@ -1923,16 +2371,15 @@ class WebElement {
|
|
|
1923
2371
|
* array of WebElements.
|
|
1924
2372
|
*/
|
|
1925
2373
|
async findElements(locator) {
|
|
1926
|
-
locator = by.checkedLocator(locator)
|
|
1927
|
-
let id;
|
|
2374
|
+
locator = by.checkedLocator(locator)
|
|
1928
2375
|
if (typeof locator === 'function') {
|
|
1929
|
-
return this.driver_.findElementsInternal_(locator, this)
|
|
2376
|
+
return this.driver_.findElementsInternal_(locator, this)
|
|
1930
2377
|
} else {
|
|
1931
2378
|
let cmd = new command.Command(command.Name.FIND_CHILD_ELEMENTS)
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
let result = await this.execute_(cmd)
|
|
1935
|
-
return Array.isArray(result) ? result : []
|
|
2379
|
+
.setParameter('using', locator.using)
|
|
2380
|
+
.setParameter('value', locator.value)
|
|
2381
|
+
let result = await this.execute_(cmd)
|
|
2382
|
+
return Array.isArray(result) ? result : []
|
|
1936
2383
|
}
|
|
1937
2384
|
}
|
|
1938
2385
|
|
|
@@ -1943,7 +2390,7 @@ class WebElement {
|
|
|
1943
2390
|
* command has completed.
|
|
1944
2391
|
*/
|
|
1945
2392
|
click() {
|
|
1946
|
-
return this.execute_(new command.Command(command.Name.CLICK_ELEMENT))
|
|
2393
|
+
return this.execute_(new command.Command(command.Name.CLICK_ELEMENT))
|
|
1947
2394
|
}
|
|
1948
2395
|
|
|
1949
2396
|
/**
|
|
@@ -2002,34 +2449,37 @@ class WebElement {
|
|
|
2002
2449
|
* have been typed.
|
|
2003
2450
|
*/
|
|
2004
2451
|
async sendKeys(...args) {
|
|
2005
|
-
let keys = []
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2452
|
+
let keys = []
|
|
2453
|
+
; (await Promise.all(args)).forEach((key) => {
|
|
2454
|
+
let type = typeof key
|
|
2455
|
+
if (type === 'number') {
|
|
2456
|
+
key = String(key)
|
|
2457
|
+
} else if (type !== 'string') {
|
|
2458
|
+
throw TypeError('each key must be a number of string; got ' + type)
|
|
2459
|
+
}
|
|
2013
2460
|
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2461
|
+
// The W3C protocol requires keys to be specified as an array where
|
|
2462
|
+
// each element is a single key.
|
|
2463
|
+
keys.push(...key.split(''))
|
|
2464
|
+
})
|
|
2018
2465
|
|
|
2019
2466
|
if (!this.driver_.fileDetector_) {
|
|
2020
2467
|
return this.execute_(
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2468
|
+
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
2469
|
+
.setParameter('text', keys.join(''))
|
|
2470
|
+
.setParameter('value', keys)
|
|
2471
|
+
)
|
|
2024
2472
|
}
|
|
2025
2473
|
|
|
2026
|
-
keys =
|
|
2027
|
-
|
|
2028
|
-
|
|
2474
|
+
keys = await this.driver_.fileDetector_.handleFile(
|
|
2475
|
+
this.driver_,
|
|
2476
|
+
keys.join('')
|
|
2477
|
+
)
|
|
2029
2478
|
return this.execute_(
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2479
|
+
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
2480
|
+
.setParameter('text', keys)
|
|
2481
|
+
.setParameter('value', keys.split(''))
|
|
2482
|
+
)
|
|
2033
2483
|
}
|
|
2034
2484
|
|
|
2035
2485
|
/**
|
|
@@ -2039,8 +2489,7 @@ class WebElement {
|
|
|
2039
2489
|
* element's tag name.
|
|
2040
2490
|
*/
|
|
2041
2491
|
getTagName() {
|
|
2042
|
-
return this.execute_(
|
|
2043
|
-
new command.Command(command.Name.GET_ELEMENT_TAG_NAME));
|
|
2492
|
+
return this.execute_(new command.Command(command.Name.GET_ELEMENT_TAG_NAME))
|
|
2044
2493
|
}
|
|
2045
2494
|
|
|
2046
2495
|
/**
|
|
@@ -2058,10 +2507,10 @@ class WebElement {
|
|
|
2058
2507
|
* requested CSS value.
|
|
2059
2508
|
*/
|
|
2060
2509
|
getCssValue(cssStyleProperty) {
|
|
2061
|
-
var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY
|
|
2510
|
+
var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY
|
|
2062
2511
|
return this.execute_(
|
|
2063
|
-
|
|
2064
|
-
|
|
2512
|
+
new command.Command(name).setParameter('propertyName', cssStyleProperty)
|
|
2513
|
+
)
|
|
2065
2514
|
}
|
|
2066
2515
|
|
|
2067
2516
|
/**
|
|
@@ -2095,8 +2544,26 @@ class WebElement {
|
|
|
2095
2544
|
*/
|
|
2096
2545
|
getAttribute(attributeName) {
|
|
2097
2546
|
return this.execute_(
|
|
2098
|
-
|
|
2099
|
-
|
|
2547
|
+
new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).setParameter(
|
|
2548
|
+
'name',
|
|
2549
|
+
attributeName
|
|
2550
|
+
)
|
|
2551
|
+
)
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
/**
|
|
2555
|
+
* Get the given property of the referenced web element
|
|
2556
|
+
* @param {string} propertyName The name of the attribute to query.
|
|
2557
|
+
* @return {!Promise<string>} A promise that will be
|
|
2558
|
+
* resolved with the element's property value
|
|
2559
|
+
*/
|
|
2560
|
+
getProperty(propertyName) {
|
|
2561
|
+
return this.execute_(
|
|
2562
|
+
new command.Command(command.Name.GET_ELEMENT_PROPERTY).setParameter(
|
|
2563
|
+
'name',
|
|
2564
|
+
propertyName
|
|
2565
|
+
)
|
|
2566
|
+
)
|
|
2100
2567
|
}
|
|
2101
2568
|
|
|
2102
2569
|
/**
|
|
@@ -2107,9 +2574,28 @@ class WebElement {
|
|
|
2107
2574
|
* resolved with the element's visible text.
|
|
2108
2575
|
*/
|
|
2109
2576
|
getText() {
|
|
2110
|
-
return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT))
|
|
2577
|
+
return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT))
|
|
2111
2578
|
}
|
|
2112
2579
|
|
|
2580
|
+
/**
|
|
2581
|
+
* Get the computed WAI-ARIA role of element.
|
|
2582
|
+
*
|
|
2583
|
+
* @return {!Promise<string>} A promise that will be
|
|
2584
|
+
* resolved with the element's computed role.
|
|
2585
|
+
*/
|
|
2586
|
+
getAriaRole() {
|
|
2587
|
+
return this.execute_(new command.Command(command.Name.GET_COMPUTED_ROLE))
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
/**
|
|
2591
|
+
* Get the computed WAI-ARIA label of element.
|
|
2592
|
+
*
|
|
2593
|
+
* @return {!Promise<string>} A promise that will be
|
|
2594
|
+
* resolved with the element's computed label.
|
|
2595
|
+
*/
|
|
2596
|
+
getAccessibleName() {
|
|
2597
|
+
return this.execute_(new command.Command(command.Name.GET_COMPUTED_LABEL))
|
|
2598
|
+
}
|
|
2113
2599
|
/**
|
|
2114
2600
|
* Returns an object describing an element's location, in pixels relative to
|
|
2115
2601
|
* the document element, and the element's size in pixels.
|
|
@@ -2120,16 +2606,17 @@ class WebElement {
|
|
|
2120
2606
|
async getRect() {
|
|
2121
2607
|
try {
|
|
2122
2608
|
return await this.execute_(
|
|
2123
|
-
|
|
2609
|
+
new command.Command(command.Name.GET_ELEMENT_RECT)
|
|
2610
|
+
)
|
|
2124
2611
|
} catch (err) {
|
|
2125
2612
|
if (err instanceof error.UnknownCommandError) {
|
|
2126
|
-
const {width, height} =
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
const {x, y} =
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
return {x, y, width, height}
|
|
2613
|
+
const { width, height } = await this.execute_(
|
|
2614
|
+
new command.Command(command.Name.GET_ELEMENT_SIZE)
|
|
2615
|
+
)
|
|
2616
|
+
const { x, y } = await this.execute_(
|
|
2617
|
+
new command.Command(command.Name.GET_ELEMENT_LOCATION)
|
|
2618
|
+
)
|
|
2619
|
+
return { x, y, width, height }
|
|
2133
2620
|
}
|
|
2134
2621
|
}
|
|
2135
2622
|
}
|
|
@@ -2142,7 +2629,7 @@ class WebElement {
|
|
|
2142
2629
|
* resolved with whether this element is currently enabled.
|
|
2143
2630
|
*/
|
|
2144
2631
|
isEnabled() {
|
|
2145
|
-
return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED))
|
|
2632
|
+
return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED))
|
|
2146
2633
|
}
|
|
2147
2634
|
|
|
2148
2635
|
/**
|
|
@@ -2152,8 +2639,7 @@ class WebElement {
|
|
|
2152
2639
|
* resolved with whether this element is currently selected.
|
|
2153
2640
|
*/
|
|
2154
2641
|
isSelected() {
|
|
2155
|
-
return this.execute_(
|
|
2156
|
-
new command.Command(command.Name.IS_ELEMENT_SELECTED));
|
|
2642
|
+
return this.execute_(new command.Command(command.Name.IS_ELEMENT_SELECTED))
|
|
2157
2643
|
}
|
|
2158
2644
|
|
|
2159
2645
|
/**
|
|
@@ -2165,7 +2651,7 @@ class WebElement {
|
|
|
2165
2651
|
* when the form has been submitted.
|
|
2166
2652
|
*/
|
|
2167
2653
|
submit() {
|
|
2168
|
-
return this.execute_(new command.Command(command.Name.SUBMIT_ELEMENT))
|
|
2654
|
+
return this.execute_(new command.Command(command.Name.SUBMIT_ELEMENT))
|
|
2169
2655
|
}
|
|
2170
2656
|
|
|
2171
2657
|
/**
|
|
@@ -2177,7 +2663,7 @@ class WebElement {
|
|
|
2177
2663
|
* when the element has been cleared.
|
|
2178
2664
|
*/
|
|
2179
2665
|
clear() {
|
|
2180
|
-
return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT))
|
|
2666
|
+
return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT))
|
|
2181
2667
|
}
|
|
2182
2668
|
|
|
2183
2669
|
/**
|
|
@@ -2187,8 +2673,7 @@ class WebElement {
|
|
|
2187
2673
|
* resolved with whether this element is currently visible on the page.
|
|
2188
2674
|
*/
|
|
2189
2675
|
isDisplayed() {
|
|
2190
|
-
return this.execute_(
|
|
2191
|
-
new command.Command(command.Name.IS_ELEMENT_DISPLAYED));
|
|
2676
|
+
return this.execute_(new command.Command(command.Name.IS_ELEMENT_DISPLAYED))
|
|
2192
2677
|
}
|
|
2193
2678
|
|
|
2194
2679
|
/**
|
|
@@ -2203,12 +2688,14 @@ class WebElement {
|
|
|
2203
2688
|
*/
|
|
2204
2689
|
takeScreenshot(scroll = false) {
|
|
2205
2690
|
return this.execute_(
|
|
2206
|
-
|
|
2207
|
-
|
|
2691
|
+
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT).setParameter(
|
|
2692
|
+
'scroll',
|
|
2693
|
+
scroll
|
|
2694
|
+
)
|
|
2695
|
+
)
|
|
2208
2696
|
}
|
|
2209
2697
|
}
|
|
2210
2698
|
|
|
2211
|
-
|
|
2212
2699
|
/**
|
|
2213
2700
|
* WebElementPromise is a promise that will be fulfilled with a WebElement.
|
|
2214
2701
|
* This serves as a forward proxy on WebElement, allowing calls to be
|
|
@@ -2232,35 +2719,33 @@ class WebElementPromise extends WebElement {
|
|
|
2232
2719
|
* that will resolve to the promised element.
|
|
2233
2720
|
*/
|
|
2234
2721
|
constructor(driver, el) {
|
|
2235
|
-
super(driver, 'unused')
|
|
2722
|
+
super(driver, 'unused')
|
|
2236
2723
|
|
|
2237
2724
|
/** @override */
|
|
2238
|
-
this.then = el.then.bind(el)
|
|
2725
|
+
this.then = el.then.bind(el)
|
|
2239
2726
|
|
|
2240
2727
|
/** @override */
|
|
2241
|
-
this.catch = el.catch.bind(el)
|
|
2728
|
+
this.catch = el.catch.bind(el)
|
|
2242
2729
|
|
|
2243
2730
|
/**
|
|
2244
2731
|
* Defers returning the element ID until the wrapped WebElement has been
|
|
2245
2732
|
* resolved.
|
|
2246
2733
|
* @override
|
|
2247
2734
|
*/
|
|
2248
|
-
this.getId = function() {
|
|
2249
|
-
return el.then(function(el) {
|
|
2250
|
-
return el.getId()
|
|
2251
|
-
})
|
|
2252
|
-
}
|
|
2735
|
+
this.getId = function () {
|
|
2736
|
+
return el.then(function (el) {
|
|
2737
|
+
return el.getId()
|
|
2738
|
+
})
|
|
2739
|
+
}
|
|
2253
2740
|
}
|
|
2254
2741
|
}
|
|
2255
2742
|
|
|
2256
|
-
|
|
2257
2743
|
//////////////////////////////////////////////////////////////////////////////
|
|
2258
2744
|
//
|
|
2259
2745
|
// Alert
|
|
2260
2746
|
//
|
|
2261
2747
|
//////////////////////////////////////////////////////////////////////////////
|
|
2262
2748
|
|
|
2263
|
-
|
|
2264
2749
|
/**
|
|
2265
2750
|
* Represents a modal dialog such as {@code alert}, {@code confirm}, or
|
|
2266
2751
|
* {@code prompt}. Provides functions to retrieve the message displayed with
|
|
@@ -2275,10 +2760,10 @@ class Alert {
|
|
|
2275
2760
|
*/
|
|
2276
2761
|
constructor(driver, text) {
|
|
2277
2762
|
/** @private {!WebDriver} */
|
|
2278
|
-
this.driver_ = driver
|
|
2763
|
+
this.driver_ = driver
|
|
2279
2764
|
|
|
2280
2765
|
/** @private {!Promise<string>} */
|
|
2281
|
-
this.text_ = Promise.resolve(text)
|
|
2766
|
+
this.text_ = Promise.resolve(text)
|
|
2282
2767
|
}
|
|
2283
2768
|
|
|
2284
2769
|
/**
|
|
@@ -2289,7 +2774,7 @@ class Alert {
|
|
|
2289
2774
|
* resolved to the text displayed with this alert.
|
|
2290
2775
|
*/
|
|
2291
2776
|
getText() {
|
|
2292
|
-
return this.text_
|
|
2777
|
+
return this.text_
|
|
2293
2778
|
}
|
|
2294
2779
|
|
|
2295
2780
|
/**
|
|
@@ -2299,8 +2784,7 @@ class Alert {
|
|
|
2299
2784
|
* when this command has completed.
|
|
2300
2785
|
*/
|
|
2301
2786
|
accept() {
|
|
2302
|
-
return this.driver_.execute(
|
|
2303
|
-
new command.Command(command.Name.ACCEPT_ALERT));
|
|
2787
|
+
return this.driver_.execute(new command.Command(command.Name.ACCEPT_ALERT))
|
|
2304
2788
|
}
|
|
2305
2789
|
|
|
2306
2790
|
/**
|
|
@@ -2310,8 +2794,7 @@ class Alert {
|
|
|
2310
2794
|
* when this command has completed.
|
|
2311
2795
|
*/
|
|
2312
2796
|
dismiss() {
|
|
2313
|
-
return this.driver_.execute(
|
|
2314
|
-
new command.Command(command.Name.DISMISS_ALERT));
|
|
2797
|
+
return this.driver_.execute(new command.Command(command.Name.DISMISS_ALERT))
|
|
2315
2798
|
}
|
|
2316
2799
|
|
|
2317
2800
|
/**
|
|
@@ -2325,12 +2808,14 @@ class Alert {
|
|
|
2325
2808
|
*/
|
|
2326
2809
|
sendKeys(text) {
|
|
2327
2810
|
return this.driver_.execute(
|
|
2328
|
-
|
|
2329
|
-
|
|
2811
|
+
new command.Command(command.Name.SET_ALERT_TEXT).setParameter(
|
|
2812
|
+
'text',
|
|
2813
|
+
text
|
|
2814
|
+
)
|
|
2815
|
+
)
|
|
2330
2816
|
}
|
|
2331
2817
|
}
|
|
2332
2818
|
|
|
2333
|
-
|
|
2334
2819
|
/**
|
|
2335
2820
|
* AlertPromise is a promise that will be fulfilled with an Alert. This promise
|
|
2336
2821
|
* serves as a forward proxy on an Alert, allowing calls to be scheduled
|
|
@@ -2353,60 +2838,58 @@ class AlertPromise extends Alert {
|
|
|
2353
2838
|
* that will be fulfilled with the promised alert.
|
|
2354
2839
|
*/
|
|
2355
2840
|
constructor(driver, alert) {
|
|
2356
|
-
super(driver, 'unused')
|
|
2841
|
+
super(driver, 'unused')
|
|
2357
2842
|
|
|
2358
2843
|
/** @override */
|
|
2359
|
-
this.then = alert.then.bind(alert)
|
|
2844
|
+
this.then = alert.then.bind(alert)
|
|
2360
2845
|
|
|
2361
2846
|
/** @override */
|
|
2362
|
-
this.catch = alert.catch.bind(alert)
|
|
2847
|
+
this.catch = alert.catch.bind(alert)
|
|
2363
2848
|
|
|
2364
2849
|
/**
|
|
2365
2850
|
* Defer returning text until the promised alert has been resolved.
|
|
2366
2851
|
* @override
|
|
2367
2852
|
*/
|
|
2368
|
-
this.getText = function() {
|
|
2369
|
-
return alert.then(function(alert) {
|
|
2370
|
-
return alert.getText()
|
|
2371
|
-
})
|
|
2372
|
-
}
|
|
2853
|
+
this.getText = function () {
|
|
2854
|
+
return alert.then(function (alert) {
|
|
2855
|
+
return alert.getText()
|
|
2856
|
+
})
|
|
2857
|
+
}
|
|
2373
2858
|
|
|
2374
2859
|
/**
|
|
2375
2860
|
* Defers action until the alert has been located.
|
|
2376
2861
|
* @override
|
|
2377
2862
|
*/
|
|
2378
|
-
this.accept = function() {
|
|
2379
|
-
return alert.then(function(alert) {
|
|
2380
|
-
return alert.accept()
|
|
2381
|
-
})
|
|
2382
|
-
}
|
|
2863
|
+
this.accept = function () {
|
|
2864
|
+
return alert.then(function (alert) {
|
|
2865
|
+
return alert.accept()
|
|
2866
|
+
})
|
|
2867
|
+
}
|
|
2383
2868
|
|
|
2384
2869
|
/**
|
|
2385
2870
|
* Defers action until the alert has been located.
|
|
2386
2871
|
* @override
|
|
2387
2872
|
*/
|
|
2388
|
-
this.dismiss = function() {
|
|
2389
|
-
return alert.then(function(alert) {
|
|
2390
|
-
return alert.dismiss()
|
|
2391
|
-
})
|
|
2392
|
-
}
|
|
2873
|
+
this.dismiss = function () {
|
|
2874
|
+
return alert.then(function (alert) {
|
|
2875
|
+
return alert.dismiss()
|
|
2876
|
+
})
|
|
2877
|
+
}
|
|
2393
2878
|
|
|
2394
2879
|
/**
|
|
2395
2880
|
* Defers action until the alert has been located.
|
|
2396
2881
|
* @override
|
|
2397
2882
|
*/
|
|
2398
|
-
this.sendKeys = function(text) {
|
|
2399
|
-
return alert.then(function(alert) {
|
|
2400
|
-
return alert.sendKeys(text)
|
|
2401
|
-
})
|
|
2402
|
-
}
|
|
2883
|
+
this.sendKeys = function (text) {
|
|
2884
|
+
return alert.then(function (alert) {
|
|
2885
|
+
return alert.sendKeys(text)
|
|
2886
|
+
})
|
|
2887
|
+
}
|
|
2403
2888
|
}
|
|
2404
2889
|
}
|
|
2405
2890
|
|
|
2406
|
-
|
|
2407
2891
|
// PUBLIC API
|
|
2408
2892
|
|
|
2409
|
-
|
|
2410
2893
|
module.exports = {
|
|
2411
2894
|
Alert,
|
|
2412
2895
|
AlertPromise,
|
|
@@ -2420,5 +2903,5 @@ module.exports = {
|
|
|
2420
2903
|
WebElement,
|
|
2421
2904
|
WebElementCondition,
|
|
2422
2905
|
WebElementPromise,
|
|
2423
|
-
Window
|
|
2424
|
-
}
|
|
2906
|
+
Window,
|
|
2907
|
+
}
|