selenium-webdriver 3.6.0 → 4.0.0-alpha.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 +181 -0
- package/README.md +25 -27
- package/chrome.js +113 -127
- package/edge.js +42 -117
- package/example/chrome_android.js +8 -8
- package/example/chrome_mobile_emulation.js +8 -8
- package/example/firefox_channels.js +22 -18
- package/example/google_search_test.js +39 -26
- package/example/logging.js +39 -6
- package/firefox.js +769 -0
- package/http/index.js +41 -4
- package/http/util.js +11 -6
- package/ie.js +56 -97
- package/index.js +178 -114
- package/jasmine.json +11 -0
- package/lib/actions.js +68 -63
- package/lib/by.js +1 -1
- package/lib/capabilities.js +255 -192
- package/lib/command.js +26 -50
- package/lib/error.js +85 -44
- package/lib/http.js +133 -110
- package/lib/input.js +1044 -8
- package/lib/promise.js +222 -3323
- package/lib/proxy.js +134 -39
- package/lib/test/bootstrap_jasmine.js +28 -0
- package/lib/test/data/actions/click.html +24 -0
- package/lib/test/data/actions/drag.html +77 -0
- package/lib/test/data/actions/record_click.html +21 -0
- package/lib/test/data/chrome/download.html +2 -0
- package/lib/test/data/inputs.html +42 -0
- package/lib/test/data/selectPage.html +12 -0
- package/lib/test/data/simpleTest.html +5 -0
- package/lib/test/data/upload_invisible.html +45 -0
- package/lib/test/fileserver.js +23 -19
- package/lib/test/index.js +30 -212
- package/lib/test/resources.js +0 -1
- package/lib/webdriver.js +675 -936
- package/net/index.js +21 -25
- package/net/portprober.js +51 -68
- package/package.json +5 -3
- package/remote/index.js +5 -19
- package/safari.js +41 -128
- package/test/actions_test.js +173 -18
- package/test/{session_test.js → builder_test.js} +44 -37
- package/test/chrome/devtools_test.js +93 -0
- package/test/chrome/options_test.js +48 -154
- package/test/chrome/service_test.js +7 -7
- package/test/cookie_test.js +77 -63
- package/test/element_finding_test.js +206 -201
- package/test/execute_script_test.js +115 -114
- package/test/fingerprint_test.js +16 -15
- package/test/firefox_test.js +251 -0
- package/test/http/http_test.js +0 -1
- package/test/http/util_test.js +2 -2
- package/test/io/zip_test.js +6 -7
- package/test/lib/error_test.js +34 -7
- package/test/lib/http_test.js +8 -128
- package/test/lib/input_test.js +1379 -0
- package/test/lib/promise_test.js +473 -908
- package/test/lib/webdriver_test.js +296 -868
- package/test/logging_test.js +37 -43
- package/test/page_loading_test.js +71 -81
- package/test/proxy_test.js +64 -72
- package/test/rect_test.js +14 -25
- package/test/remote_test.js +10 -29
- package/test/safari_test.js +7 -71
- package/test/stale_element_test.js +20 -23
- package/test/tag_name_test.js +10 -9
- package/test/upload_test.js +27 -35
- package/test/window_test.js +72 -85
- package/testing/index.js +386 -314
- package/.npmignore +0 -2
- package/example/async_await_test.js +0 -69
- package/example/google_search_generator.js +0 -47
- package/example/parallel_flows.js +0 -51
- package/firefox/binary.js +0 -347
- package/firefox/extension.js +0 -224
- package/firefox/index.js +0 -576
- package/firefox/profile.js +0 -311
- package/lib/events.js +0 -210
- package/lib/test/data/firefox/jetpack-sample.xpi +0 -0
- package/lib/test/data/firefox/sample.xpi +0 -0
- package/lib/test/promise.js +0 -79
- package/opera.js +0 -405
- package/phantomjs.js +0 -282
- package/test/firefox/extension_test.js +0 -120
- package/test/firefox/firefox_test.js +0 -244
- package/test/firefox/profile_test.js +0 -140
- package/test/lib/events_test.js +0 -177
- package/test/lib/promise_aplus_test.js +0 -78
- package/test/lib/promise_error_test.js +0 -884
- package/test/lib/promise_flow_test.js +0 -2288
- package/test/lib/promise_generator_test.js +0 -310
- package/test/phantomjs/execute_phantomjs_test.js +0 -59
- package/test/testing/assert_test.js +0 -373
- package/test/testing/index_test.js +0 -224
- package/testing/assert.js +0 -378
package/lib/webdriver.js
CHANGED
|
@@ -21,16 +21,29 @@
|
|
|
21
21
|
|
|
22
22
|
'use strict';
|
|
23
23
|
|
|
24
|
-
const actions = require('./actions');
|
|
25
24
|
const by = require('./by');
|
|
26
|
-
const Capabilities = require('./capabilities').Capabilities;
|
|
27
25
|
const command = require('./command');
|
|
28
26
|
const error = require('./error');
|
|
29
27
|
const input = require('./input');
|
|
30
28
|
const logging = require('./logging');
|
|
31
|
-
const {Session} = require('./session');
|
|
32
|
-
const Symbols = require('./symbols');
|
|
33
29
|
const promise = require('./promise');
|
|
30
|
+
const Symbols = require('./symbols');
|
|
31
|
+
const {Capabilities} = require('./capabilities');
|
|
32
|
+
const {Session} = require('./session');
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
// Capability names that are defined in the W3C spec.
|
|
36
|
+
const W3C_CAPABILITY_NAMES = new Set([
|
|
37
|
+
'acceptInsecureCerts',
|
|
38
|
+
'browserName',
|
|
39
|
+
'browserVersion',
|
|
40
|
+
'platformName',
|
|
41
|
+
'pageLoadStrategy',
|
|
42
|
+
'proxy',
|
|
43
|
+
'setWindowRect',
|
|
44
|
+
'timeouts',
|
|
45
|
+
'unhandledPromptBehavior',
|
|
46
|
+
]);
|
|
34
47
|
|
|
35
48
|
|
|
36
49
|
/**
|
|
@@ -121,15 +134,8 @@ function executeCommand(executor, command) {
|
|
|
121
134
|
* @return {!Promise<?>} A promise that will resolve to the input value's JSON
|
|
122
135
|
* representation.
|
|
123
136
|
*/
|
|
124
|
-
function toWireValue(obj) {
|
|
125
|
-
|
|
126
|
-
return Promise.resolve(obj).then(toWireValue);
|
|
127
|
-
}
|
|
128
|
-
return Promise.resolve(convertValue(obj));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
function convertValue(value) {
|
|
137
|
+
async function toWireValue(obj) {
|
|
138
|
+
let value = await Promise.resolve(obj);
|
|
133
139
|
if (value === void 0 || value === null) {
|
|
134
140
|
return value;
|
|
135
141
|
}
|
|
@@ -157,53 +163,33 @@ function convertValue(value) {
|
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
|
|
160
|
-
function convertKeys(obj) {
|
|
166
|
+
async function convertKeys(obj) {
|
|
161
167
|
const isArray = Array.isArray(obj);
|
|
162
168
|
const numKeys = isArray ? obj.length : Object.keys(obj).length;
|
|
163
169
|
const ret = isArray ? new Array(numKeys) : {};
|
|
164
170
|
if (!numKeys) {
|
|
165
|
-
return
|
|
171
|
+
return ret;
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
let numResolved = 0;
|
|
169
175
|
|
|
170
|
-
function forEachKey(obj, fn) {
|
|
176
|
+
async function forEachKey(obj, fn) {
|
|
171
177
|
if (Array.isArray(obj)) {
|
|
172
178
|
for (let i = 0, n = obj.length; i < n; i++) {
|
|
173
|
-
fn(obj[i], i);
|
|
179
|
+
await fn(obj[i], i);
|
|
174
180
|
}
|
|
175
181
|
} else {
|
|
176
182
|
for (let key in obj) {
|
|
177
|
-
fn(obj[key], key);
|
|
183
|
+
await fn(obj[key], key);
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (promise.isPromise(value)) {
|
|
185
|
-
value.then(toWireValue).then(setValue, reject);
|
|
186
|
-
} else {
|
|
187
|
-
value = convertValue(value);
|
|
188
|
-
if (promise.isPromise(value)) {
|
|
189
|
-
value.then(toWireValue).then(setValue, reject);
|
|
190
|
-
} else {
|
|
191
|
-
setValue(value);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function setValue(value) {
|
|
196
|
-
ret[key] = value;
|
|
197
|
-
maybeFulfill();
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
function maybeFulfill() {
|
|
202
|
-
if (++numResolved === numKeys) {
|
|
203
|
-
done(ret);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
188
|
+
await forEachKey(obj, async function(value, key) {
|
|
189
|
+
ret[key] = await toWireValue(value);
|
|
206
190
|
});
|
|
191
|
+
|
|
192
|
+
return ret;
|
|
207
193
|
}
|
|
208
194
|
|
|
209
195
|
|
|
@@ -243,25 +229,21 @@ function fromWireValue(driver, value) {
|
|
|
243
229
|
*/
|
|
244
230
|
class IWebDriver {
|
|
245
231
|
|
|
246
|
-
/** @return {!promise.ControlFlow} The control flow used by this instance. */
|
|
247
|
-
controlFlow() {}
|
|
248
|
-
|
|
249
232
|
/**
|
|
250
|
-
*
|
|
233
|
+
* Executes the provided {@link command.Command} using this driver's
|
|
251
234
|
* {@link command.Executor}.
|
|
252
235
|
*
|
|
253
236
|
* @param {!command.Command} command The command to schedule.
|
|
254
|
-
* @
|
|
255
|
-
*
|
|
256
|
-
* with the command result.
|
|
237
|
+
* @return {!Promise<T>} A promise that will be resolved with the command
|
|
238
|
+
* result.
|
|
257
239
|
* @template T
|
|
258
240
|
*/
|
|
259
|
-
|
|
241
|
+
execute(command) {}
|
|
260
242
|
|
|
261
243
|
/**
|
|
262
244
|
* Sets the {@linkplain input.FileDetector file detector} that should be
|
|
263
245
|
* used with this instance.
|
|
264
|
-
* @param {input.FileDetector} detector The detector to use or
|
|
246
|
+
* @param {input.FileDetector} detector The detector to use or `null`.
|
|
265
247
|
*/
|
|
266
248
|
setFileDetector(detector) {}
|
|
267
249
|
|
|
@@ -271,12 +253,12 @@ class IWebDriver {
|
|
|
271
253
|
getExecutor() {}
|
|
272
254
|
|
|
273
255
|
/**
|
|
274
|
-
* @return {!
|
|
256
|
+
* @return {!Promise<!Session>} A promise for this client's session.
|
|
275
257
|
*/
|
|
276
258
|
getSession() {}
|
|
277
259
|
|
|
278
260
|
/**
|
|
279
|
-
* @return {!
|
|
261
|
+
* @return {!Promise<!Capabilities>} A promise that will resolve with
|
|
280
262
|
* the this instance's capabilities.
|
|
281
263
|
*/
|
|
282
264
|
getCapabilities() {}
|
|
@@ -286,56 +268,40 @@ class IWebDriver {
|
|
|
286
268
|
* invalidated and may no longer be used to issue commands against the
|
|
287
269
|
* browser.
|
|
288
270
|
*
|
|
289
|
-
* @return {!
|
|
271
|
+
* @return {!Promise<void>} A promise that will be resolved when the
|
|
290
272
|
* command has completed.
|
|
291
273
|
*/
|
|
292
274
|
quit() {}
|
|
293
275
|
|
|
294
276
|
/**
|
|
295
277
|
* Creates a new action sequence using this driver. The sequence will not be
|
|
296
|
-
*
|
|
297
|
-
* called.
|
|
298
|
-
*
|
|
299
|
-
* driver.actions().
|
|
300
|
-
* mouseDown(element1).
|
|
301
|
-
* mouseMove(element2).
|
|
302
|
-
* mouseUp().
|
|
303
|
-
* perform();
|
|
278
|
+
* submitted for execution until
|
|
279
|
+
* {@link ./input.Actions#perform Actions.perform()} is called.
|
|
304
280
|
*
|
|
305
|
-
* @
|
|
281
|
+
* @param {{async: (boolean|undefined),
|
|
282
|
+
* bridge: (boolean|undefined)}=} options Configuration options for
|
|
283
|
+
* the action sequence (see {@link ./input.Actions Actions} documentation
|
|
284
|
+
* for details).
|
|
285
|
+
* @return {!input.Actions} A new action sequence for this instance.
|
|
306
286
|
*/
|
|
307
|
-
actions() {}
|
|
287
|
+
actions(options) {}
|
|
308
288
|
|
|
309
289
|
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* driver.touchActions().
|
|
315
|
-
* tap(element1).
|
|
316
|
-
* doubleTap(element2).
|
|
317
|
-
* perform();
|
|
318
|
-
*
|
|
319
|
-
* @return {!actions.TouchSequence} A new touch sequence for this instance.
|
|
320
|
-
*/
|
|
321
|
-
touchActions() {}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Schedules a command to execute JavaScript in the context of the currently
|
|
325
|
-
* selected frame or window. The script fragment will be executed as the body
|
|
326
|
-
* of an anonymous function. If the script is provided as a function object,
|
|
327
|
-
* that function will be converted to a string for injection into the target
|
|
290
|
+
* Executes a snippet of JavaScript in the context of the currently selected
|
|
291
|
+
* frame or window. The script fragment will be executed as the body of an
|
|
292
|
+
* anonymous function. If the script is provided as a function object, that
|
|
293
|
+
* function will be converted to a string for injection into the target
|
|
328
294
|
* window.
|
|
329
295
|
*
|
|
330
296
|
* Any arguments provided in addition to the script will be included as script
|
|
331
|
-
* arguments and may be referenced using the
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
297
|
+
* arguments and may be referenced using the `arguments` object. Arguments may
|
|
298
|
+
* be a boolean, number, string, or {@linkplain WebElement}. Arrays and
|
|
299
|
+
* objects may also be used as script arguments as long as each item adheres
|
|
300
|
+
* to the types previously mentioned.
|
|
335
301
|
*
|
|
336
302
|
* The script may refer to any variables accessible from the current window.
|
|
337
303
|
* Furthermore, the script will execute in the window's context, thus
|
|
338
|
-
*
|
|
304
|
+
* `document` may be used to refer to the current document. Any local
|
|
339
305
|
* variables will not be available once the script has finished executing,
|
|
340
306
|
* though global variables will persist.
|
|
341
307
|
*
|
|
@@ -351,36 +317,35 @@ class IWebDriver {
|
|
|
351
317
|
* the rules above
|
|
352
318
|
*
|
|
353
319
|
* @param {!(string|Function)} script The script to execute.
|
|
354
|
-
* @param {...*}
|
|
355
|
-
* @return {!
|
|
320
|
+
* @param {...*} args The arguments to pass to the script.
|
|
321
|
+
* @return {!IThenable<T>} A promise that will resolve to the
|
|
356
322
|
* scripts return value.
|
|
357
323
|
* @template T
|
|
358
324
|
*/
|
|
359
|
-
executeScript(script,
|
|
325
|
+
executeScript(script, ...args) {}
|
|
360
326
|
|
|
361
327
|
/**
|
|
362
|
-
*
|
|
328
|
+
* Executes a snippet of asynchronous JavaScript in the context of the
|
|
363
329
|
* currently selected frame or window. The script fragment will be executed as
|
|
364
330
|
* the body of an anonymous function. If the script is provided as a function
|
|
365
331
|
* object, that function will be converted to a string for injection into the
|
|
366
332
|
* target window.
|
|
367
333
|
*
|
|
368
334
|
* Any arguments provided in addition to the script will be included as script
|
|
369
|
-
* arguments and may be referenced using the
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
335
|
+
* arguments and may be referenced using the `arguments` object. Arguments may
|
|
336
|
+
* be a boolean, number, string, or {@linkplain WebElement}. Arrays and
|
|
337
|
+
* objects may also be used as script arguments as long as each item adheres
|
|
338
|
+
* to the types previously mentioned.
|
|
373
339
|
*
|
|
374
340
|
* Unlike executing synchronous JavaScript with {@link #executeScript},
|
|
375
|
-
* scripts executed with this function must explicitly signal they are
|
|
376
|
-
* by invoking the provided callback. This callback will always be
|
|
377
|
-
* into the executed function as the last argument, and thus may be
|
|
378
|
-
* with
|
|
379
|
-
* taken for resolving this functions return value against the first
|
|
380
|
-
* to the script's callback function:
|
|
381
|
-
*
|
|
382
|
-
* - For a HTML element, the value will resolve to a
|
|
383
|
-
* {@link WebElement}
|
|
341
|
+
* scripts executed with this function must explicitly signal they are
|
|
342
|
+
* finished by invoking the provided callback. This callback will always be
|
|
343
|
+
* injected into the executed function as the last argument, and thus may be
|
|
344
|
+
* referenced with `arguments[arguments.length - 1]`. The following steps
|
|
345
|
+
* will be taken for resolving this functions return value against the first
|
|
346
|
+
* argument to the script's callback function:
|
|
347
|
+
*
|
|
348
|
+
* - For a HTML element, the value will resolve to a {@link WebElement}
|
|
384
349
|
* - Null and undefined return values will resolve to null
|
|
385
350
|
* - Booleans, numbers, and strings will resolve as is
|
|
386
351
|
* - Functions will resolve to their string representation
|
|
@@ -410,9 +375,9 @@ class IWebDriver {
|
|
|
410
375
|
*
|
|
411
376
|
* __Example #3:__ Injecting a XMLHttpRequest and waiting for the result. In
|
|
412
377
|
* this example, the inject script is specified with a function literal. When
|
|
413
|
-
* using this format, the function is converted to a string for injection, so
|
|
414
|
-
* should not reference any symbols not defined in the scope of the page
|
|
415
|
-
* test.
|
|
378
|
+
* using this format, the function is converted to a string for injection, so
|
|
379
|
+
* it should not reference any symbols not defined in the scope of the page
|
|
380
|
+
* under test.
|
|
416
381
|
*
|
|
417
382
|
* driver.executeAsyncScript(function() {
|
|
418
383
|
* var callback = arguments[arguments.length - 1];
|
|
@@ -429,70 +394,47 @@ class IWebDriver {
|
|
|
429
394
|
* });
|
|
430
395
|
*
|
|
431
396
|
* @param {!(string|Function)} script The script to execute.
|
|
432
|
-
* @param {...*}
|
|
433
|
-
* @return {!
|
|
434
|
-
*
|
|
397
|
+
* @param {...*} args The arguments to pass to the script.
|
|
398
|
+
* @return {!IThenable<T>} A promise that will resolve to the scripts return
|
|
399
|
+
* value.
|
|
435
400
|
* @template T
|
|
436
401
|
*/
|
|
437
|
-
executeAsyncScript(script,
|
|
402
|
+
executeAsyncScript(script, ...args) {}
|
|
438
403
|
|
|
439
404
|
/**
|
|
440
|
-
*
|
|
441
|
-
* @param {function(...): (T|IThenable<T>)} fn The function to execute.
|
|
442
|
-
* @param {Object=} opt_scope The object in whose scope to execute the function.
|
|
443
|
-
* @param {...*} var_args Any arguments to pass to the function.
|
|
444
|
-
* @return {!promise.Thenable<T>} A promise that will be resolved'
|
|
445
|
-
* with the function's result.
|
|
446
|
-
* @template T
|
|
447
|
-
*/
|
|
448
|
-
call(fn, opt_scope, var_args) {}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Schedules a command to wait for a condition to hold. The condition may be
|
|
405
|
+
* Waits for a condition to evaluate to a "truthy" value. The condition may be
|
|
452
406
|
* specified by a {@link Condition}, as a custom function, or as any
|
|
453
407
|
* promise-like thenable.
|
|
454
408
|
*
|
|
455
409
|
* For a {@link Condition} or function, the wait will repeatedly
|
|
456
410
|
* evaluate the condition until it returns a truthy value. If any errors occur
|
|
457
411
|
* while evaluating the condition, they will be allowed to propagate. In the
|
|
458
|
-
* event a condition returns a {@
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
412
|
+
* event a condition returns a {@linkplain Promise}, the polling loop will
|
|
413
|
+
* wait for it to be resolved and use the resolved value for whether the
|
|
414
|
+
* condition has been satisfied. The resolution time for a promise is always
|
|
415
|
+
* factored into whether a wait has timed out.
|
|
462
416
|
*
|
|
463
|
-
*
|
|
417
|
+
* If the provided condition is a {@link WebElementCondition}, then
|
|
464
418
|
* the wait will return a {@link WebElementPromise} that will resolve to the
|
|
465
419
|
* element that satisfied the condition.
|
|
466
420
|
*
|
|
467
421
|
* _Example:_ waiting up to 10 seconds for an element to be present on the
|
|
468
422
|
* page.
|
|
469
423
|
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* simply wait for its resolution before completing. A timeout may be provided
|
|
476
|
-
* to fail the command if the promise does not resolve before the timeout
|
|
477
|
-
* expires.
|
|
478
|
-
*
|
|
479
|
-
* _Example:_ Suppose you have a function, `startTestServer`, that returns a
|
|
480
|
-
* promise for when a server is ready for requests. You can block a WebDriver
|
|
481
|
-
* client on this promise with:
|
|
482
|
-
*
|
|
483
|
-
* var started = startTestServer();
|
|
484
|
-
* driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
|
|
485
|
-
* driver.get(getServerUrl());
|
|
424
|
+
* async function example() {
|
|
425
|
+
* let button =
|
|
426
|
+
* await driver.wait(until.elementLocated(By.id('foo')), 10000);
|
|
427
|
+
* await button.click();
|
|
428
|
+
* }
|
|
486
429
|
*
|
|
487
430
|
* @param {!(IThenable<T>|
|
|
488
431
|
* Condition<T>|
|
|
489
432
|
* function(!WebDriver): T)} condition The condition to
|
|
490
433
|
* wait on, defined as a promise, condition object, or a function to
|
|
491
434
|
* evaluate as a condition.
|
|
492
|
-
* @param {number=}
|
|
493
|
-
* @param {string=}
|
|
494
|
-
*
|
|
495
|
-
* @return {!(promise.Thenable<T>|WebElementPromise)} A promise that will be
|
|
435
|
+
* @param {number=} timeout How long to wait for the condition to be true.
|
|
436
|
+
* @param {string=} message An optional message to use if the wait times out.
|
|
437
|
+
* @return {!(IThenable<T>|WebElementPromise)} A promise that will be
|
|
496
438
|
* resolved with the first truthy value returned by the condition
|
|
497
439
|
* function, or rejected if the condition times out. If the input
|
|
498
440
|
* input condition is an instance of a {@link WebElementCondition},
|
|
@@ -500,76 +442,82 @@ class IWebDriver {
|
|
|
500
442
|
* @throws {TypeError} if the provided `condition` is not a valid type.
|
|
501
443
|
* @template T
|
|
502
444
|
*/
|
|
503
|
-
wait(condition,
|
|
445
|
+
wait(condition, timeout = undefined, message = undefined) {}
|
|
504
446
|
|
|
505
447
|
/**
|
|
506
|
-
*
|
|
448
|
+
* Makes the driver sleep for the given amount of time.
|
|
449
|
+
*
|
|
507
450
|
* @param {number} ms The amount of time, in milliseconds, to sleep.
|
|
508
|
-
* @return {!
|
|
509
|
-
*
|
|
451
|
+
* @return {!Promise<void>} A promise that will be resolved when the sleep has
|
|
452
|
+
* finished.
|
|
510
453
|
*/
|
|
511
454
|
sleep(ms) {}
|
|
512
455
|
|
|
513
456
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
457
|
+
* Retrieves the current window handle.
|
|
458
|
+
*
|
|
459
|
+
* @return {!Promise<string>} A promise that will be resolved with the current
|
|
460
|
+
* window handle.
|
|
517
461
|
*/
|
|
518
462
|
getWindowHandle() {}
|
|
519
463
|
|
|
520
464
|
/**
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
465
|
+
* Retrieves a list of all available window handles.
|
|
466
|
+
*
|
|
467
|
+
* @return {!Promise<!Array<string>>} A promise that will be resolved with an
|
|
468
|
+
* array of window handles.
|
|
524
469
|
*/
|
|
525
470
|
getAllWindowHandles() {}
|
|
526
471
|
|
|
527
472
|
/**
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
* @return {!
|
|
533
|
-
*
|
|
473
|
+
* Retrieves the current page's source. The returned souce is a representation
|
|
474
|
+
* of the underlying DOM: do not expect it to be formatted or escaped in the
|
|
475
|
+
* same way as the raw response sent from the web server.
|
|
476
|
+
*
|
|
477
|
+
* @return {!Promise<string>} A promise that will be resolved with the current
|
|
478
|
+
* page source.
|
|
534
479
|
*/
|
|
535
480
|
getPageSource() {}
|
|
536
481
|
|
|
537
482
|
/**
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
483
|
+
* Closes the current window.
|
|
484
|
+
*
|
|
485
|
+
* @return {!Promise<void>} A promise that will be resolved when this command
|
|
486
|
+
* has completed.
|
|
541
487
|
*/
|
|
542
488
|
close() {}
|
|
543
489
|
|
|
544
490
|
/**
|
|
545
|
-
*
|
|
491
|
+
* Navigates to the given URL.
|
|
492
|
+
*
|
|
546
493
|
* @param {string} url The fully qualified URL to open.
|
|
547
|
-
* @return {!
|
|
548
|
-
*
|
|
494
|
+
* @return {!Promise<void>} A promise that will be resolved when the document
|
|
495
|
+
* has finished loading.
|
|
549
496
|
*/
|
|
550
497
|
get(url) {}
|
|
551
498
|
|
|
552
499
|
/**
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
500
|
+
* Retrieves the URL for the current page.
|
|
501
|
+
*
|
|
502
|
+
* @return {!Promise<string>} A promise that will be resolved with the
|
|
503
|
+
* current URL.
|
|
556
504
|
*/
|
|
557
505
|
getCurrentUrl() {}
|
|
558
506
|
|
|
559
507
|
/**
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
508
|
+
* Retrieves the current page title.
|
|
509
|
+
*
|
|
510
|
+
* @return {!Promise<string>} A promise that will be resolved with the current
|
|
511
|
+
* page's title.
|
|
563
512
|
*/
|
|
564
513
|
getTitle() {}
|
|
565
514
|
|
|
566
515
|
/**
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
* present on the page, use {@link #findElements}:
|
|
516
|
+
* Locates an element on the page. If the element cannot be found, a
|
|
517
|
+
* {@link error.NoSuchEementError} will be returned by the driver.
|
|
518
|
+
*
|
|
519
|
+
* This function should not be used to test whether an element is present on
|
|
520
|
+
* the page. Rather, you should use {@link #findElements}:
|
|
573
521
|
*
|
|
574
522
|
* driver.findElements(By.id('foo'))
|
|
575
523
|
* .then(found => console.log('Element found? %s', !!found.length));
|
|
@@ -605,16 +553,17 @@ class IWebDriver {
|
|
|
605
553
|
findElement(locator) {}
|
|
606
554
|
|
|
607
555
|
/**
|
|
608
|
-
*
|
|
556
|
+
* Search for multiple elements on the page. Refer to the documentation on
|
|
557
|
+
* {@link #findElement(by)} for information on element locator strategies.
|
|
609
558
|
*
|
|
610
559
|
* @param {!(by.By|Function)} locator The locator to use.
|
|
611
|
-
* @return {!
|
|
612
|
-
*
|
|
560
|
+
* @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
|
|
561
|
+
* array of WebElements.
|
|
613
562
|
*/
|
|
614
563
|
findElements(locator) {}
|
|
615
564
|
|
|
616
565
|
/**
|
|
617
|
-
*
|
|
566
|
+
* Takes a screenshot of the current page. The driver makes a best effort to
|
|
618
567
|
* return a screenshot of the following, in order of preference:
|
|
619
568
|
*
|
|
620
569
|
* 1. Entire page
|
|
@@ -622,8 +571,8 @@ class IWebDriver {
|
|
|
622
571
|
* 3. Visible portion of the current frame
|
|
623
572
|
* 4. The entire display containing the browser
|
|
624
573
|
*
|
|
625
|
-
* @return {!
|
|
626
|
-
*
|
|
574
|
+
* @return {!Promise<string>} A promise that will be resolved to the
|
|
575
|
+
* screenshot as a base-64 encoded PNG.
|
|
627
576
|
*/
|
|
628
577
|
takeScreenshot() {}
|
|
629
578
|
|
|
@@ -645,6 +594,23 @@ class IWebDriver {
|
|
|
645
594
|
}
|
|
646
595
|
|
|
647
596
|
|
|
597
|
+
/**
|
|
598
|
+
* @param {!Capabilities} capabilities A capabilities object.
|
|
599
|
+
* @return {!Capabilities} A copy of the parameter capabilities, omitting
|
|
600
|
+
* capability names that are not valid W3C names.
|
|
601
|
+
*/
|
|
602
|
+
function filterNonW3CCaps(capabilities) {
|
|
603
|
+
let newCaps = new Capabilities(capabilities);
|
|
604
|
+
for (let k of newCaps.keys()) {
|
|
605
|
+
// Any key containing a colon is a vendor-prefixed capability.
|
|
606
|
+
if (!(W3C_CAPABILITY_NAMES.has(k) || k.indexOf(':') >= 0)) {
|
|
607
|
+
newCaps.delete(k);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return newCaps;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
|
|
648
614
|
/**
|
|
649
615
|
* Each WebDriver instance provides automated control over a browser session.
|
|
650
616
|
*
|
|
@@ -656,17 +622,17 @@ class WebDriver {
|
|
|
656
622
|
* promise that will be resolved to a session.
|
|
657
623
|
* @param {!command.Executor} executor The executor to use when sending
|
|
658
624
|
* commands to the browser.
|
|
659
|
-
* @param {
|
|
660
|
-
* schedule commands through. Defaults to the active flow object.
|
|
661
|
-
* @param {(function(this: void): ?)=} opt_onQuit A function to call, if any,
|
|
625
|
+
* @param {(function(this: void): ?)=} onQuit A function to call, if any,
|
|
662
626
|
* when the session is terminated.
|
|
663
627
|
*/
|
|
664
|
-
constructor(session, executor,
|
|
665
|
-
/** @private {!
|
|
666
|
-
this.
|
|
628
|
+
constructor(session, executor, onQuit = undefined) {
|
|
629
|
+
/** @private {!Promise<!Session>} */
|
|
630
|
+
this.session_ = Promise.resolve(session);
|
|
667
631
|
|
|
668
|
-
|
|
669
|
-
|
|
632
|
+
// If session is a rejected promise, add a no-op rejection handler.
|
|
633
|
+
// This effectively hides setup errors until users attempt to interact
|
|
634
|
+
// with the session.
|
|
635
|
+
this.session_.catch(function() {});
|
|
670
636
|
|
|
671
637
|
/** @private {!command.Executor} */
|
|
672
638
|
this.executor_ = executor;
|
|
@@ -675,63 +641,17 @@ class WebDriver {
|
|
|
675
641
|
this.fileDetector_ = null;
|
|
676
642
|
|
|
677
643
|
/** @private @const {(function(this: void): ?|undefined)} */
|
|
678
|
-
this.onQuit_ =
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Creates a new WebDriver client for an existing session.
|
|
683
|
-
* @param {!command.Executor} executor Command executor to use when querying
|
|
684
|
-
* for session details.
|
|
685
|
-
* @param {string} sessionId ID of the session to attach to.
|
|
686
|
-
* @param {promise.ControlFlow=} opt_flow The control flow all
|
|
687
|
-
* driver commands should execute under. Defaults to the
|
|
688
|
-
* {@link promise.controlFlow() currently active} control flow.
|
|
689
|
-
* @return {!WebDriver} A new client for the specified session.
|
|
690
|
-
*/
|
|
691
|
-
static attachToSession(executor, sessionId, opt_flow) {
|
|
692
|
-
let flow = opt_flow || promise.controlFlow();
|
|
693
|
-
let cmd = new command.Command(command.Name.DESCRIBE_SESSION)
|
|
694
|
-
.setParameter('sessionId', sessionId);
|
|
695
|
-
let session = flow.execute(
|
|
696
|
-
() => executeCommand(executor, cmd).catch(err => {
|
|
697
|
-
// The DESCRIBE_SESSION command is not supported by the W3C spec, so
|
|
698
|
-
// if we get back an unknown command, just return a session with
|
|
699
|
-
// unknown capabilities.
|
|
700
|
-
if (err instanceof error.UnknownCommandError) {
|
|
701
|
-
return new Session(sessionId, new Capabilities);
|
|
702
|
-
}
|
|
703
|
-
throw err;
|
|
704
|
-
}),
|
|
705
|
-
'WebDriver.attachToSession()');
|
|
706
|
-
return new WebDriver(session, executor, flow);
|
|
644
|
+
this.onQuit_ = onQuit;
|
|
707
645
|
}
|
|
708
646
|
|
|
709
647
|
/**
|
|
710
648
|
* Creates a new WebDriver session.
|
|
711
649
|
*
|
|
712
|
-
* By default, the requested session `capabilities` are merely "desired" and
|
|
713
|
-
* the remote end will still create a new session even if it cannot satisfy
|
|
714
|
-
* all of the requested capabilities. You can query which capabilities a
|
|
715
|
-
* session actually has using the
|
|
716
|
-
* {@linkplain #getCapabilities() getCapabilities()} method on the returned
|
|
717
|
-
* WebDriver instance.
|
|
718
|
-
*
|
|
719
|
-
* To define _required capabilities_, provide the `capabilities` as an object
|
|
720
|
-
* literal with `required` and `desired` keys. The `desired` key may be
|
|
721
|
-
* omitted if all capabilities are required, and vice versa. If the server
|
|
722
|
-
* cannot create a session with all of the required capabilities, it will
|
|
723
|
-
* return an {@linkplain error.SessionNotCreatedError}.
|
|
724
|
-
*
|
|
725
|
-
* let required = new Capabilities().set('browserName', 'firefox');
|
|
726
|
-
* let desired = new Capabilities().set('version', '45');
|
|
727
|
-
* let driver = WebDriver.createSession(executor, {required, desired});
|
|
728
|
-
*
|
|
729
650
|
* This function will always return a WebDriver instance. If there is an error
|
|
730
651
|
* creating the session, such as the aforementioned SessionNotCreatedError,
|
|
731
652
|
* the driver will have a rejected {@linkplain #getSession session} promise.
|
|
732
|
-
*
|
|
733
|
-
*
|
|
734
|
-
* cause subsequent commands to fail.
|
|
653
|
+
* This rejection will propagate through any subsequent commands scheduled
|
|
654
|
+
* on the returned WebDriver instance.
|
|
735
655
|
*
|
|
736
656
|
* let required = Capabilities.firefox();
|
|
737
657
|
* let driver = WebDriver.createSession(executor, {required});
|
|
@@ -742,77 +662,39 @@ class WebDriver {
|
|
|
742
662
|
*
|
|
743
663
|
* @param {!command.Executor} executor The executor to create the new session
|
|
744
664
|
* with.
|
|
745
|
-
* @param {
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
* capabilities for the new session.
|
|
749
|
-
* @param {promise.ControlFlow=} opt_flow The control flow all driver
|
|
750
|
-
* commands should execute under, including the initial session creation.
|
|
751
|
-
* Defaults to the {@link promise.controlFlow() currently active}
|
|
752
|
-
* control flow.
|
|
753
|
-
* @param {(function(this: void): ?)=} opt_onQuit A callback to invoke when
|
|
665
|
+
* @param {!Capabilities} capabilities The desired capabilities for the new
|
|
666
|
+
* session.
|
|
667
|
+
* @param {(function(this: void): ?)=} onQuit A callback to invoke when
|
|
754
668
|
* the newly created session is terminated. This should be used to clean
|
|
755
669
|
* up any resources associated with the session.
|
|
756
670
|
* @return {!WebDriver} The driver for the newly created session.
|
|
757
671
|
*/
|
|
758
|
-
static createSession(executor, capabilities,
|
|
759
|
-
let flow = opt_flow || promise.controlFlow();
|
|
672
|
+
static createSession(executor, capabilities, onQuit = undefined) {
|
|
760
673
|
let cmd = new command.Command(command.Name.NEW_SESSION);
|
|
761
674
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
675
|
+
// For OSS remote ends.
|
|
676
|
+
cmd.setParameter('desiredCapabilities', capabilities);
|
|
677
|
+
// For W3C remote ends.
|
|
678
|
+
cmd.setParameter('capabilities', {
|
|
679
|
+
alwaysMatch: filterNonW3CCaps(capabilities),
|
|
680
|
+
});
|
|
768
681
|
|
|
769
|
-
let session =
|
|
770
|
-
|
|
771
|
-
'WebDriver.createSession()');
|
|
772
|
-
if (typeof opt_onQuit === 'function') {
|
|
682
|
+
let session = executeCommand(executor, cmd);
|
|
683
|
+
if (typeof onQuit === 'function') {
|
|
773
684
|
session = session.catch(err => {
|
|
774
|
-
return Promise.resolve(
|
|
685
|
+
return Promise.resolve(onQuit.call(void 0)).then(_ => {throw err;});
|
|
775
686
|
});
|
|
776
687
|
}
|
|
777
|
-
return new this(session, executor,
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/** @override */
|
|
781
|
-
controlFlow() {
|
|
782
|
-
return this.flow_;
|
|
688
|
+
return new this(session, executor, onQuit);
|
|
783
689
|
}
|
|
784
690
|
|
|
785
691
|
/** @override */
|
|
786
|
-
|
|
692
|
+
async execute(command) {
|
|
787
693
|
command.setParameter('sessionId', this.session_);
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
// propagate through the command itself, we resolve all of the
|
|
793
|
-
// command parameters now, but suppress any errors until the ControlFlow
|
|
794
|
-
// actually executes the command. This addresses scenarios like catching
|
|
795
|
-
// an element not found error in:
|
|
796
|
-
//
|
|
797
|
-
// driver.findElement(By.id('foo')).click().catch(function(e) {
|
|
798
|
-
// if (e instanceof NoSuchElementError) {
|
|
799
|
-
// // Do something.
|
|
800
|
-
// }
|
|
801
|
-
// });
|
|
802
|
-
var prepCommand = toWireValue(command.getParameters());
|
|
803
|
-
prepCommand.catch(function() {});
|
|
804
|
-
|
|
805
|
-
var flow = this.flow_;
|
|
806
|
-
var executor = this.executor_;
|
|
807
|
-
return flow.execute(() => {
|
|
808
|
-
// Retrieve resolved command parameters; any previously suppressed errors
|
|
809
|
-
// will now propagate up through the control flow as part of the command
|
|
810
|
-
// execution.
|
|
811
|
-
return prepCommand.then(function(parameters) {
|
|
812
|
-
command.setParameters(parameters);
|
|
813
|
-
return executor.execute(command);
|
|
814
|
-
}).then(value => fromWireValue(this, value));
|
|
815
|
-
}, description);
|
|
694
|
+
let parameters = await toWireValue(command.getParameters());
|
|
695
|
+
command.setParameters(parameters);
|
|
696
|
+
let value = await this.executor_.execute(command);
|
|
697
|
+
return fromWireValue(this, value);
|
|
816
698
|
}
|
|
817
699
|
|
|
818
700
|
/** @override */
|
|
@@ -837,17 +719,13 @@ class WebDriver {
|
|
|
837
719
|
|
|
838
720
|
/** @override */
|
|
839
721
|
quit() {
|
|
840
|
-
|
|
841
|
-
new command.Command(command.Name.QUIT),
|
|
842
|
-
'WebDriver.quit()');
|
|
722
|
+
let result = this.execute(new command.Command(command.Name.QUIT));
|
|
843
723
|
// Delete our session ID when the quit command finishes; this will allow us
|
|
844
724
|
// to throw an error when attempting to use a driver post-quit.
|
|
845
|
-
return
|
|
846
|
-
this.session_ =
|
|
847
|
-
reject(new error.NoSuchSessionError(
|
|
725
|
+
return promise.finally(result, () => {
|
|
726
|
+
this.session_ = Promise.reject(new error.NoSuchSessionError(
|
|
848
727
|
'This driver instance does not have a valid session ID ' +
|
|
849
728
|
'(did you call WebDriver.quit()?) and may no longer be used.'));
|
|
850
|
-
});
|
|
851
729
|
|
|
852
730
|
// Only want the session rejection to bubble if accessed.
|
|
853
731
|
this.session_.catch(function() {});
|
|
@@ -855,70 +733,73 @@ class WebDriver {
|
|
|
855
733
|
if (this.onQuit_) {
|
|
856
734
|
return this.onQuit_.call(void 0);
|
|
857
735
|
}
|
|
858
|
-
})
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
/** @override */
|
|
862
|
-
actions() {
|
|
863
|
-
return new actions.ActionSequence(this);
|
|
736
|
+
});
|
|
864
737
|
}
|
|
865
738
|
|
|
866
739
|
/** @override */
|
|
867
|
-
|
|
868
|
-
return new
|
|
740
|
+
actions(options) {
|
|
741
|
+
return new input.Actions(this, options || undefined);
|
|
869
742
|
}
|
|
870
743
|
|
|
871
744
|
/** @override */
|
|
872
|
-
executeScript(script,
|
|
745
|
+
executeScript(script, ...args) {
|
|
873
746
|
if (typeof script === 'function') {
|
|
874
747
|
script = 'return (' + script + ').apply(null, arguments);';
|
|
875
748
|
}
|
|
876
|
-
|
|
877
|
-
arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];
|
|
878
|
-
return this.schedule(
|
|
749
|
+
return this.execute(
|
|
879
750
|
new command.Command(command.Name.EXECUTE_SCRIPT).
|
|
880
751
|
setParameter('script', script).
|
|
881
|
-
setParameter('args', args)
|
|
882
|
-
'WebDriver.executeScript()');
|
|
752
|
+
setParameter('args', args));
|
|
883
753
|
}
|
|
884
754
|
|
|
885
755
|
/** @override */
|
|
886
|
-
executeAsyncScript(script,
|
|
756
|
+
executeAsyncScript(script, ...args) {
|
|
887
757
|
if (typeof script === 'function') {
|
|
888
758
|
script = 'return (' + script + ').apply(null, arguments);';
|
|
889
759
|
}
|
|
890
|
-
|
|
891
|
-
return this.schedule(
|
|
760
|
+
return this.execute(
|
|
892
761
|
new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT).
|
|
893
762
|
setParameter('script', script).
|
|
894
|
-
setParameter('args', args)
|
|
895
|
-
'WebDriver.executeScript()');
|
|
763
|
+
setParameter('args', args));
|
|
896
764
|
}
|
|
897
765
|
|
|
898
766
|
/** @override */
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (promise.isGenerator(fn)) {
|
|
904
|
-
args.unshift(fn, opt_scope);
|
|
905
|
-
return promise.consume.apply(null, args);
|
|
906
|
-
}
|
|
907
|
-
return fn.apply(opt_scope, args);
|
|
908
|
-
});
|
|
909
|
-
}, 'WebDriver.call(' + (fn.name || 'function') + ')');
|
|
910
|
-
}
|
|
767
|
+
wait(condition, timeout = 0, message = undefined) {
|
|
768
|
+
if (typeof timeout !== 'number' || timeout < 0) {
|
|
769
|
+
throw TypeError('timeout must be a number >= 0: ' + timeout);
|
|
770
|
+
}
|
|
911
771
|
|
|
912
|
-
/** @override */
|
|
913
|
-
wait(condition, opt_timeout, opt_message) {
|
|
914
772
|
if (promise.isPromise(condition)) {
|
|
915
|
-
return
|
|
916
|
-
|
|
917
|
-
|
|
773
|
+
return new Promise((resolve, reject) => {
|
|
774
|
+
if (!timeout) {
|
|
775
|
+
resolve(condition);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
let start = Date.now();
|
|
780
|
+
let timer = setTimeout(function() {
|
|
781
|
+
timer = null;
|
|
782
|
+
reject(
|
|
783
|
+
new error.TimeoutError(
|
|
784
|
+
(message ? `${message}\n` : '')
|
|
785
|
+
+ 'Timed out waiting for promise to resolve after '
|
|
786
|
+
+ (Date.now() - start) + 'ms'));
|
|
787
|
+
}, timeout);
|
|
788
|
+
const clearTimer = () => timer && clearTimeout(timer);
|
|
789
|
+
|
|
790
|
+
/** @type {!IThenable} */(condition).then(
|
|
791
|
+
function(value) {
|
|
792
|
+
clearTimer();
|
|
793
|
+
resolve(value);
|
|
794
|
+
},
|
|
795
|
+
function(error) {
|
|
796
|
+
clearTimer();
|
|
797
|
+
reject(error);
|
|
798
|
+
});
|
|
799
|
+
});
|
|
918
800
|
}
|
|
919
801
|
|
|
920
|
-
|
|
921
|
-
var fn = /** @type {!Function} */(condition);
|
|
802
|
+
let fn = /** @type {!Function} */(condition);
|
|
922
803
|
if (condition instanceof Condition) {
|
|
923
804
|
message = message || condition.description();
|
|
924
805
|
fn = condition.fn;
|
|
@@ -930,13 +811,36 @@ class WebDriver {
|
|
|
930
811
|
+ 'Condition object');
|
|
931
812
|
}
|
|
932
813
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
814
|
+
const driver = this;
|
|
815
|
+
function evaluateCondition() {
|
|
816
|
+
return new Promise((resolve, reject) => {
|
|
817
|
+
try {
|
|
818
|
+
resolve(fn(driver));
|
|
819
|
+
} catch (ex) {
|
|
820
|
+
reject(ex);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
let result = new Promise((resolve, reject) => {
|
|
826
|
+
const startTime = Date.now();
|
|
827
|
+
const pollCondition = async () => {
|
|
828
|
+
evaluateCondition().then(function(value) {
|
|
829
|
+
const elapsed = Date.now() - startTime;
|
|
830
|
+
if (!!value) {
|
|
831
|
+
resolve(value);
|
|
832
|
+
} else if (timeout && elapsed >= timeout) {
|
|
833
|
+
reject(
|
|
834
|
+
new error.TimeoutError(
|
|
835
|
+
(message ? `${message}\n` : '')
|
|
836
|
+
+ `Wait timed out after ${elapsed}ms`));
|
|
837
|
+
} else {
|
|
838
|
+
setTimeout(pollCondition, 0);
|
|
839
|
+
}
|
|
840
|
+
}, reject);
|
|
841
|
+
};
|
|
842
|
+
pollCondition();
|
|
843
|
+
});
|
|
940
844
|
|
|
941
845
|
if (condition instanceof WebElementCondition) {
|
|
942
846
|
result = new WebElementPromise(this, result.then(function(value) {
|
|
@@ -953,34 +857,30 @@ class WebDriver {
|
|
|
953
857
|
|
|
954
858
|
/** @override */
|
|
955
859
|
sleep(ms) {
|
|
956
|
-
return
|
|
860
|
+
return new Promise(resolve => setTimeout(() => resolve(), ms));
|
|
957
861
|
}
|
|
958
862
|
|
|
959
863
|
/** @override */
|
|
960
864
|
getWindowHandle() {
|
|
961
|
-
return this.
|
|
962
|
-
new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE)
|
|
963
|
-
'WebDriver.getWindowHandle()');
|
|
865
|
+
return this.execute(
|
|
866
|
+
new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE));
|
|
964
867
|
}
|
|
965
868
|
|
|
966
869
|
/** @override */
|
|
967
870
|
getAllWindowHandles() {
|
|
968
|
-
return this.
|
|
969
|
-
new command.Command(command.Name.GET_WINDOW_HANDLES)
|
|
970
|
-
'WebDriver.getAllWindowHandles()');
|
|
871
|
+
return this.execute(
|
|
872
|
+
new command.Command(command.Name.GET_WINDOW_HANDLES));
|
|
971
873
|
}
|
|
972
874
|
|
|
973
875
|
/** @override */
|
|
974
876
|
getPageSource() {
|
|
975
|
-
return this.
|
|
976
|
-
new command.Command(command.Name.GET_PAGE_SOURCE)
|
|
977
|
-
'WebDriver.getPageSource()');
|
|
877
|
+
return this.execute(
|
|
878
|
+
new command.Command(command.Name.GET_PAGE_SOURCE));
|
|
978
879
|
}
|
|
979
880
|
|
|
980
881
|
/** @override */
|
|
981
882
|
close() {
|
|
982
|
-
return this.
|
|
983
|
-
'WebDriver.close()');
|
|
883
|
+
return this.execute(new command.Command(command.Name.CLOSE));
|
|
984
884
|
}
|
|
985
885
|
|
|
986
886
|
/** @override */
|
|
@@ -990,15 +890,12 @@ class WebDriver {
|
|
|
990
890
|
|
|
991
891
|
/** @override */
|
|
992
892
|
getCurrentUrl() {
|
|
993
|
-
return this.
|
|
994
|
-
new command.Command(command.Name.GET_CURRENT_URL),
|
|
995
|
-
'WebDriver.getCurrentUrl()');
|
|
893
|
+
return this.execute(new command.Command(command.Name.GET_CURRENT_URL));
|
|
996
894
|
}
|
|
997
895
|
|
|
998
896
|
/** @override */
|
|
999
897
|
getTitle() {
|
|
1000
|
-
return this.
|
|
1001
|
-
'WebDriver.getTitle()');
|
|
898
|
+
return this.execute(new command.Command(command.Name.GET_TITLE));
|
|
1002
899
|
}
|
|
1003
900
|
|
|
1004
901
|
/** @override */
|
|
@@ -1011,33 +908,31 @@ class WebDriver {
|
|
|
1011
908
|
let cmd = new command.Command(command.Name.FIND_ELEMENT).
|
|
1012
909
|
setParameter('using', locator.using).
|
|
1013
910
|
setParameter('value', locator.value);
|
|
1014
|
-
id = this.
|
|
911
|
+
id = this.execute(cmd);
|
|
1015
912
|
}
|
|
1016
913
|
return new WebElementPromise(this, id);
|
|
1017
914
|
}
|
|
1018
915
|
|
|
1019
916
|
/**
|
|
1020
917
|
* @param {!Function} locatorFn The locator function to use.
|
|
1021
|
-
* @param {!(WebDriver|WebElement)} context The search
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
* promise that will resolve to a list of WebElements.
|
|
918
|
+
* @param {!(WebDriver|WebElement)} context The search context.
|
|
919
|
+
* @return {!Promise<!WebElement>} A promise that will resolve to a list of
|
|
920
|
+
* WebElements.
|
|
1025
921
|
* @private
|
|
1026
922
|
*/
|
|
1027
|
-
findElementInternal_(locatorFn, context) {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
});
|
|
923
|
+
async findElementInternal_(locatorFn, context) {
|
|
924
|
+
let result = await locatorFn(context);
|
|
925
|
+
if (Array.isArray(result)) {
|
|
926
|
+
result = result[0];
|
|
927
|
+
}
|
|
928
|
+
if (!(result instanceof WebElement)) {
|
|
929
|
+
throw new TypeError('Custom locator did not return a WebElement');
|
|
930
|
+
}
|
|
931
|
+
return result;
|
|
1037
932
|
}
|
|
1038
933
|
|
|
1039
934
|
/** @override */
|
|
1040
|
-
findElements(locator) {
|
|
935
|
+
async findElements(locator) {
|
|
1041
936
|
locator = by.checkedLocator(locator);
|
|
1042
937
|
if (typeof locator === 'function') {
|
|
1043
938
|
return this.findElementsInternal_(locator, this);
|
|
@@ -1045,45 +940,43 @@ class WebDriver {
|
|
|
1045
940
|
let cmd = new command.Command(command.Name.FIND_ELEMENTS).
|
|
1046
941
|
setParameter('using', locator.using).
|
|
1047
942
|
setParameter('value', locator.value);
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
943
|
+
try {
|
|
944
|
+
let res = await this.execute(cmd);
|
|
945
|
+
return Array.isArray(res) ? res : [];
|
|
946
|
+
} catch (ex) {
|
|
947
|
+
if (ex instanceof error.NoSuchElementError) {
|
|
948
|
+
return [];
|
|
949
|
+
}
|
|
950
|
+
throw ex;
|
|
951
|
+
}
|
|
1057
952
|
}
|
|
1058
953
|
}
|
|
1059
954
|
|
|
1060
955
|
/**
|
|
1061
956
|
* @param {!Function} locatorFn The locator function to use.
|
|
1062
957
|
* @param {!(WebDriver|WebElement)} context The search context.
|
|
1063
|
-
* @return {!
|
|
1064
|
-
*
|
|
958
|
+
* @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
|
|
959
|
+
* array of WebElements.
|
|
1065
960
|
* @private
|
|
1066
961
|
*/
|
|
1067
|
-
findElementsInternal_(locatorFn, context) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
962
|
+
async findElementsInternal_(locatorFn, context) {
|
|
963
|
+
const result = await locatorFn(context);
|
|
964
|
+
if (result instanceof WebElement) {
|
|
965
|
+
return [result];
|
|
966
|
+
}
|
|
1072
967
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
968
|
+
if (!Array.isArray(result)) {
|
|
969
|
+
return [];
|
|
970
|
+
}
|
|
1076
971
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
});
|
|
972
|
+
return result.filter(function(item) {
|
|
973
|
+
return item instanceof WebElement;
|
|
1080
974
|
});
|
|
1081
975
|
}
|
|
1082
976
|
|
|
1083
977
|
/** @override */
|
|
1084
978
|
takeScreenshot() {
|
|
1085
|
-
return this.
|
|
1086
|
-
'WebDriver.takeScreenshot()');
|
|
979
|
+
return this.execute(new command.Command(command.Name.SCREENSHOT));
|
|
1087
980
|
}
|
|
1088
981
|
|
|
1089
982
|
/** @override */
|
|
@@ -1124,49 +1017,46 @@ class Navigation {
|
|
|
1124
1017
|
}
|
|
1125
1018
|
|
|
1126
1019
|
/**
|
|
1127
|
-
*
|
|
1020
|
+
* Navigates to a new URL.
|
|
1021
|
+
*
|
|
1128
1022
|
* @param {string} url The URL to navigate to.
|
|
1129
|
-
* @return {!
|
|
1130
|
-
*
|
|
1023
|
+
* @return {!Promise<void>} A promise that will be resolved when the URL
|
|
1024
|
+
* has been loaded.
|
|
1131
1025
|
*/
|
|
1132
1026
|
to(url) {
|
|
1133
|
-
return this.driver_.
|
|
1027
|
+
return this.driver_.execute(
|
|
1134
1028
|
new command.Command(command.Name.GET).
|
|
1135
|
-
setParameter('url', url)
|
|
1136
|
-
'WebDriver.navigate().to(' + url + ')');
|
|
1029
|
+
setParameter('url', url));
|
|
1137
1030
|
}
|
|
1138
1031
|
|
|
1139
1032
|
/**
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1033
|
+
* Moves backwards in the browser history.
|
|
1034
|
+
*
|
|
1035
|
+
* @return {!Promise<void>} A promise that will be resolved when the
|
|
1036
|
+
* navigation event has completed.
|
|
1143
1037
|
*/
|
|
1144
1038
|
back() {
|
|
1145
|
-
return this.driver_.
|
|
1146
|
-
new command.Command(command.Name.GO_BACK),
|
|
1147
|
-
'WebDriver.navigate().back()');
|
|
1039
|
+
return this.driver_.execute(new command.Command(command.Name.GO_BACK));
|
|
1148
1040
|
}
|
|
1149
1041
|
|
|
1150
1042
|
/**
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1043
|
+
* Moves forwards in the browser history.
|
|
1044
|
+
*
|
|
1045
|
+
* @return {!Promise<void>} A promise that will be resolved when the
|
|
1046
|
+
* navigation event has completed.
|
|
1154
1047
|
*/
|
|
1155
1048
|
forward() {
|
|
1156
|
-
return this.driver_.
|
|
1157
|
-
new command.Command(command.Name.GO_FORWARD),
|
|
1158
|
-
'WebDriver.navigate().forward()');
|
|
1049
|
+
return this.driver_.execute(new command.Command(command.Name.GO_FORWARD));
|
|
1159
1050
|
}
|
|
1160
1051
|
|
|
1161
1052
|
/**
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1053
|
+
* Refreshes the current page.
|
|
1054
|
+
*
|
|
1055
|
+
* @return {!Promise<void>} A promise that will be resolved when the
|
|
1056
|
+
* navigation event has completed.
|
|
1165
1057
|
*/
|
|
1166
1058
|
refresh() {
|
|
1167
|
-
return this.driver_.
|
|
1168
|
-
new command.Command(command.Name.REFRESH),
|
|
1169
|
-
'WebDriver.navigate().refresh()');
|
|
1059
|
+
return this.driver_.execute(new command.Command(command.Name.REFRESH));
|
|
1170
1060
|
}
|
|
1171
1061
|
}
|
|
1172
1062
|
|
|
@@ -1188,7 +1078,7 @@ class Options {
|
|
|
1188
1078
|
}
|
|
1189
1079
|
|
|
1190
1080
|
/**
|
|
1191
|
-
*
|
|
1081
|
+
* Adds a cookie.
|
|
1192
1082
|
*
|
|
1193
1083
|
* __Sample Usage:__
|
|
1194
1084
|
*
|
|
@@ -1207,7 +1097,7 @@ class Options {
|
|
|
1207
1097
|
* });
|
|
1208
1098
|
*
|
|
1209
1099
|
* @param {!Options.Cookie} spec Defines the cookie to add.
|
|
1210
|
-
* @return {!
|
|
1100
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1211
1101
|
* when the cookie has been added to the page.
|
|
1212
1102
|
* @throws {error.InvalidArgumentError} if any of the cookie parameters are
|
|
1213
1103
|
* invalid.
|
|
@@ -1226,21 +1116,14 @@ class Options {
|
|
|
1226
1116
|
'Invalid cookie value "' + value + '"');
|
|
1227
1117
|
}
|
|
1228
1118
|
|
|
1229
|
-
let cookieString = name + '=' + value +
|
|
1230
|
-
(domain ? ';domain=' + domain : '') +
|
|
1231
|
-
(path ? ';path=' + path : '') +
|
|
1232
|
-
(secure ? ';secure' : '');
|
|
1233
|
-
|
|
1234
1119
|
if (typeof expiry === 'number') {
|
|
1235
1120
|
expiry = Math.floor(expiry);
|
|
1236
|
-
cookieString += ';expires=' + new Date(expiry * 1000).toUTCString();
|
|
1237
1121
|
} else if (expiry instanceof Date) {
|
|
1238
1122
|
let date = /** @type {!Date} */(expiry);
|
|
1239
1123
|
expiry = Math.floor(date.getTime() / 1000);
|
|
1240
|
-
cookieString += ';expires=' + date.toUTCString();
|
|
1241
1124
|
}
|
|
1242
1125
|
|
|
1243
|
-
return this.driver_.
|
|
1126
|
+
return this.driver_.execute(
|
|
1244
1127
|
new command.Command(command.Name.ADD_COOKIE).
|
|
1245
1128
|
setParameter('cookie', {
|
|
1246
1129
|
'name': name,
|
|
@@ -1250,74 +1133,82 @@ class Options {
|
|
|
1250
1133
|
'secure': !!secure,
|
|
1251
1134
|
'httpOnly': !!httpOnly,
|
|
1252
1135
|
'expiry': expiry
|
|
1253
|
-
})
|
|
1254
|
-
'WebDriver.manage().addCookie(' + cookieString + ')');
|
|
1136
|
+
}));
|
|
1255
1137
|
}
|
|
1256
1138
|
|
|
1257
1139
|
/**
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1140
|
+
* Deletes all cookies visible to the current page.
|
|
1141
|
+
*
|
|
1142
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1260
1143
|
* when all cookies have been deleted.
|
|
1261
1144
|
*/
|
|
1262
1145
|
deleteAllCookies() {
|
|
1263
|
-
return this.driver_.
|
|
1264
|
-
new command.Command(command.Name.DELETE_ALL_COOKIES)
|
|
1265
|
-
'WebDriver.manage().deleteAllCookies()');
|
|
1146
|
+
return this.driver_.execute(
|
|
1147
|
+
new command.Command(command.Name.DELETE_ALL_COOKIES));
|
|
1266
1148
|
}
|
|
1267
1149
|
|
|
1268
1150
|
/**
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1151
|
+
* Deletes the cookie with the given name. This command is a no-op if there is
|
|
1152
|
+
* no cookie with the given name visible to the current page.
|
|
1153
|
+
*
|
|
1272
1154
|
* @param {string} name The name of the cookie to delete.
|
|
1273
|
-
* @return {!
|
|
1155
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1274
1156
|
* when the cookie has been deleted.
|
|
1275
1157
|
*/
|
|
1276
1158
|
deleteCookie(name) {
|
|
1277
|
-
return this.driver_.
|
|
1159
|
+
return this.driver_.execute(
|
|
1278
1160
|
new command.Command(command.Name.DELETE_COOKIE).
|
|
1279
|
-
setParameter('name', name)
|
|
1280
|
-
'WebDriver.manage().deleteCookie(' + name + ')');
|
|
1161
|
+
setParameter('name', name));
|
|
1281
1162
|
}
|
|
1282
1163
|
|
|
1283
1164
|
/**
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
* @return {!
|
|
1165
|
+
* Retrieves all cookies visible to the current page. Each cookie will be
|
|
1166
|
+
* returned as a JSON object as described by the WebDriver wire protocol.
|
|
1167
|
+
*
|
|
1168
|
+
* @return {!Promise<!Array<!Options.Cookie>>} A promise that will be
|
|
1288
1169
|
* resolved with the cookies visible to the current browsing context.
|
|
1289
1170
|
*/
|
|
1290
1171
|
getCookies() {
|
|
1291
|
-
return this.driver_.
|
|
1292
|
-
new command.Command(command.Name.GET_ALL_COOKIES)
|
|
1293
|
-
'WebDriver.manage().getCookies()');
|
|
1172
|
+
return this.driver_.execute(
|
|
1173
|
+
new command.Command(command.Name.GET_ALL_COOKIES));
|
|
1294
1174
|
}
|
|
1295
1175
|
|
|
1296
1176
|
/**
|
|
1297
|
-
*
|
|
1298
|
-
*
|
|
1299
|
-
*
|
|
1177
|
+
* Retrieves the cookie with the given name. Returns null if there is no such
|
|
1178
|
+
* cookie. The cookie will be returned as a JSON object as described by the
|
|
1179
|
+
* WebDriver wire protocol.
|
|
1300
1180
|
*
|
|
1301
1181
|
* @param {string} name The name of the cookie to retrieve.
|
|
1302
|
-
* @return {!
|
|
1182
|
+
* @return {!Promise<?Options.Cookie>} A promise that will be resolved
|
|
1303
1183
|
* with the named cookie, or `null` if there is no such cookie.
|
|
1304
1184
|
*/
|
|
1305
|
-
getCookie(name) {
|
|
1306
|
-
|
|
1185
|
+
async getCookie(name) {
|
|
1186
|
+
try {
|
|
1187
|
+
const cookie =
|
|
1188
|
+
await this.driver_.execute(
|
|
1189
|
+
new command.Command(command.Name.GET_COOKIE)
|
|
1190
|
+
.setParameter('name', name));
|
|
1191
|
+
return cookie;
|
|
1192
|
+
} catch (err) {
|
|
1193
|
+
if (!(err instanceof error.UnknownCommandError)
|
|
1194
|
+
&& !(err instanceof error.UnsupportedOperationError)) {
|
|
1195
|
+
throw err;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
const cookies = await this.getCookies();
|
|
1307
1199
|
for (let cookie of cookies) {
|
|
1308
1200
|
if (cookie && cookie['name'] === name) {
|
|
1309
1201
|
return cookie;
|
|
1310
1202
|
}
|
|
1311
1203
|
}
|
|
1312
1204
|
return null;
|
|
1313
|
-
}
|
|
1205
|
+
}
|
|
1314
1206
|
}
|
|
1315
1207
|
|
|
1316
1208
|
/**
|
|
1317
|
-
*
|
|
1318
|
-
* current session.
|
|
1209
|
+
* Fetches the timeouts currently configured for the current session.
|
|
1319
1210
|
*
|
|
1320
|
-
* @return {!
|
|
1211
|
+
* @return {!Promise<{script: number,
|
|
1321
1212
|
* pageLoad: number,
|
|
1322
1213
|
* implicit: number}>} A promise that will be
|
|
1323
1214
|
* resolved with the timeouts currently configured for the current
|
|
@@ -1325,14 +1216,11 @@ class Options {
|
|
|
1325
1216
|
* @see #setTimeouts()
|
|
1326
1217
|
*/
|
|
1327
1218
|
getTimeouts() {
|
|
1328
|
-
return this.driver_.
|
|
1329
|
-
new command.Command(command.Name.GET_TIMEOUT),
|
|
1330
|
-
`WebDriver.manage().getTimeouts()`)
|
|
1219
|
+
return this.driver_.execute(new command.Command(command.Name.GET_TIMEOUT));
|
|
1331
1220
|
}
|
|
1332
1221
|
|
|
1333
1222
|
/**
|
|
1334
|
-
*
|
|
1335
|
-
* session.
|
|
1223
|
+
* Sets the timeout durations associated with the current session.
|
|
1336
1224
|
*
|
|
1337
1225
|
* The following timeouts are supported (all timeouts are specified in
|
|
1338
1226
|
* milliseconds):
|
|
@@ -1354,8 +1242,8 @@ class Options {
|
|
|
1354
1242
|
* pageLoad: (number|null|undefined),
|
|
1355
1243
|
* implicit: (number|null|undefined)}} conf
|
|
1356
1244
|
* The desired timeout configuration.
|
|
1357
|
-
* @return {!
|
|
1358
|
-
*
|
|
1245
|
+
* @return {!Promise<void>} A promise that will be resolved when the timeouts
|
|
1246
|
+
* have been set.
|
|
1359
1247
|
* @throws {!TypeError} if an invalid options object is provided.
|
|
1360
1248
|
* @see #getTimeouts()
|
|
1361
1249
|
* @see <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-set-timeouts>
|
|
@@ -1379,7 +1267,7 @@ class Options {
|
|
|
1379
1267
|
setParam('script', script);
|
|
1380
1268
|
|
|
1381
1269
|
if (valid) {
|
|
1382
|
-
return this.driver_.
|
|
1270
|
+
return this.driver_.execute(cmd)
|
|
1383
1271
|
.catch(() => {
|
|
1384
1272
|
// Fallback to the legacy method.
|
|
1385
1273
|
let cmds = [];
|
|
@@ -1399,21 +1287,12 @@ class Options {
|
|
|
1399
1287
|
}
|
|
1400
1288
|
|
|
1401
1289
|
/**
|
|
1402
|
-
* @return {!Logs} The interface for managing driver
|
|
1403
|
-
* logs.
|
|
1290
|
+
* @return {!Logs} The interface for managing driver logs.
|
|
1404
1291
|
*/
|
|
1405
1292
|
logs() {
|
|
1406
1293
|
return new Logs(this.driver_);
|
|
1407
1294
|
}
|
|
1408
1295
|
|
|
1409
|
-
/**
|
|
1410
|
-
* @return {!Timeouts} The interface for managing driver timeouts.
|
|
1411
|
-
* @deprecated Use {@link #setTimeouts()} instead.
|
|
1412
|
-
*/
|
|
1413
|
-
timeouts() {
|
|
1414
|
-
return new Timeouts(this.driver_);
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
1296
|
/**
|
|
1418
1297
|
* @return {!Window} The interface for managing the current window.
|
|
1419
1298
|
*/
|
|
@@ -1427,14 +1306,13 @@ class Options {
|
|
|
1427
1306
|
* @param {!WebDriver} driver
|
|
1428
1307
|
* @param {string} type
|
|
1429
1308
|
* @param {number} ms
|
|
1430
|
-
* @return {!
|
|
1309
|
+
* @return {!Promise<void>}
|
|
1431
1310
|
*/
|
|
1432
1311
|
function legacyTimeout(driver, type, ms) {
|
|
1433
|
-
return driver.
|
|
1312
|
+
return driver.execute(
|
|
1434
1313
|
new command.Command(command.Name.SET_TIMEOUT)
|
|
1435
1314
|
.setParameter('type', type)
|
|
1436
|
-
.setParameter('ms', ms)
|
|
1437
|
-
`WebDriver.manage().setTimeouts({${type}: ${ms}})`);
|
|
1315
|
+
.setParameter('ms', ms));
|
|
1438
1316
|
}
|
|
1439
1317
|
|
|
1440
1318
|
|
|
@@ -1512,89 +1390,6 @@ Options.Cookie.prototype.httpOnly;
|
|
|
1512
1390
|
Options.Cookie.prototype.expiry;
|
|
1513
1391
|
|
|
1514
1392
|
|
|
1515
|
-
/**
|
|
1516
|
-
* An interface for managing timeout behavior for WebDriver instances.
|
|
1517
|
-
*
|
|
1518
|
-
* This class should never be instantiated directly. Instead, obtain an instance
|
|
1519
|
-
* with
|
|
1520
|
-
*
|
|
1521
|
-
* webdriver.manage().timeouts()
|
|
1522
|
-
*
|
|
1523
|
-
* @deprecated This has been deprecated in favor of
|
|
1524
|
-
* {@link Options#setTimeouts()}, which supports setting multiple timeouts
|
|
1525
|
-
* at once.
|
|
1526
|
-
* @see WebDriver#manage()
|
|
1527
|
-
* @see Options#timeouts()
|
|
1528
|
-
*/
|
|
1529
|
-
class Timeouts {
|
|
1530
|
-
/**
|
|
1531
|
-
* @param {!WebDriver} driver The parent driver.
|
|
1532
|
-
* @private
|
|
1533
|
-
*/
|
|
1534
|
-
constructor(driver) {
|
|
1535
|
-
/** @private {!WebDriver} */
|
|
1536
|
-
this.driver_ = driver;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
/**
|
|
1540
|
-
* Specifies the amount of time the driver should wait when searching for an
|
|
1541
|
-
* element if it is not immediately present.
|
|
1542
|
-
*
|
|
1543
|
-
* When searching for a single element, the driver should poll the page
|
|
1544
|
-
* until the element has been found, or this timeout expires before failing
|
|
1545
|
-
* with a {@link bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
|
|
1546
|
-
* for multiple elements, the driver should poll the page until at least one
|
|
1547
|
-
* element has been found or this timeout has expired.
|
|
1548
|
-
*
|
|
1549
|
-
* Setting the wait timeout to 0 (its default value), disables implicit
|
|
1550
|
-
* waiting.
|
|
1551
|
-
*
|
|
1552
|
-
* Increasing the implicit wait timeout should be used judiciously as it
|
|
1553
|
-
* will have an adverse effect on test run time, especially when used with
|
|
1554
|
-
* slower location strategies like XPath.
|
|
1555
|
-
*
|
|
1556
|
-
* @param {number} ms The amount of time to wait, in milliseconds.
|
|
1557
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
1558
|
-
* when the implicit wait timeout has been set.
|
|
1559
|
-
* @deprecated Use {@link Options#setTimeouts()
|
|
1560
|
-
* driver.manage().setTimeouts({implicit: ms})}.
|
|
1561
|
-
*/
|
|
1562
|
-
implicitlyWait(ms) {
|
|
1563
|
-
return this.driver_.manage().setTimeouts({implicit: ms});
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
/**
|
|
1567
|
-
* Sets the amount of time to wait, in milliseconds, for an asynchronous
|
|
1568
|
-
* script to finish execution before returning an error. If the timeout is
|
|
1569
|
-
* less than or equal to 0, the script will be allowed to run indefinitely.
|
|
1570
|
-
*
|
|
1571
|
-
* @param {number} ms The amount of time to wait, in milliseconds.
|
|
1572
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
1573
|
-
* when the script timeout has been set.
|
|
1574
|
-
* @deprecated Use {@link Options#setTimeouts()
|
|
1575
|
-
* driver.manage().setTimeouts({script: ms})}.
|
|
1576
|
-
*/
|
|
1577
|
-
setScriptTimeout(ms) {
|
|
1578
|
-
return this.driver_.manage().setTimeouts({script: ms});
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
/**
|
|
1582
|
-
* Sets the amount of time to wait for a page load to complete before
|
|
1583
|
-
* returning an error. If the timeout is negative, page loads may be
|
|
1584
|
-
* indefinite.
|
|
1585
|
-
*
|
|
1586
|
-
* @param {number} ms The amount of time to wait, in milliseconds.
|
|
1587
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
1588
|
-
* when the timeout has been set.
|
|
1589
|
-
* @deprecated Use {@link Options#setTimeouts()
|
|
1590
|
-
* driver.manage().setTimeouts({pageLoad: ms})}.
|
|
1591
|
-
*/
|
|
1592
|
-
pageLoadTimeout(ms) {
|
|
1593
|
-
return this.driver_.manage().setTimeouts({pageLoad: ms});
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
1393
|
/**
|
|
1599
1394
|
* An interface for managing the current window.
|
|
1600
1395
|
*
|
|
@@ -1617,76 +1412,114 @@ class Window {
|
|
|
1617
1412
|
}
|
|
1618
1413
|
|
|
1619
1414
|
/**
|
|
1620
|
-
* Retrieves the
|
|
1621
|
-
*
|
|
1622
|
-
*
|
|
1623
|
-
*
|
|
1624
|
-
*
|
|
1625
|
-
*/
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1415
|
+
* Retrieves the a rect describing the current top-level window's size and
|
|
1416
|
+
* position.
|
|
1417
|
+
*
|
|
1418
|
+
* @return {!Promise<{x: number, y: number, width: number, height: number}>}
|
|
1419
|
+
* A promise that will resolve to the window rect of the current window.
|
|
1420
|
+
*/
|
|
1421
|
+
async getRect() {
|
|
1422
|
+
try {
|
|
1423
|
+
return await this.driver_.execute(
|
|
1424
|
+
new command.Command(command.Name.GET_WINDOW_RECT));
|
|
1425
|
+
} catch (ex) {
|
|
1426
|
+
if (ex instanceof error.UnknownCommandError) {
|
|
1427
|
+
let {width, height} =
|
|
1428
|
+
await this.driver_.execute(
|
|
1429
|
+
new command.Command(command.Name.GET_WINDOW_SIZE)
|
|
1430
|
+
.setParameter('windowHandle', 'current'));
|
|
1431
|
+
let {x, y} =
|
|
1432
|
+
await this.driver_.execute(
|
|
1433
|
+
new command.Command(command.Name.GET_WINDOW_POSITION)
|
|
1434
|
+
.setParameter('windowHandle', 'current'));
|
|
1435
|
+
return {x, y, width, height};
|
|
1436
|
+
}
|
|
1437
|
+
throw ex;
|
|
1438
|
+
}
|
|
1631
1439
|
}
|
|
1632
1440
|
|
|
1633
1441
|
/**
|
|
1634
|
-
*
|
|
1635
|
-
*
|
|
1636
|
-
*
|
|
1637
|
-
*
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1442
|
+
* Sets the current top-level window's size and position. You may update just
|
|
1443
|
+
* the size by omitting `width` & `height`, or just the position by omitting
|
|
1444
|
+
* `x` & `y` options.
|
|
1445
|
+
*
|
|
1446
|
+
* @param {{x: (number|undefined),
|
|
1447
|
+
* y: (number|undefined),
|
|
1448
|
+
* width: (number|undefined),
|
|
1449
|
+
* height: (number|undefined)}} options
|
|
1450
|
+
* The desired window size and position.
|
|
1451
|
+
* @return {!Promise<{x: number, y: number, width: number, height: number}>}
|
|
1452
|
+
* A promise that will resolve to the current widnow's updated window
|
|
1453
|
+
* rect.
|
|
1454
|
+
*/
|
|
1455
|
+
async setRect({x, y, width, height}) {
|
|
1456
|
+
try {
|
|
1457
|
+
return await this.driver_.execute(
|
|
1458
|
+
new command.Command(command.Name.SET_WINDOW_RECT)
|
|
1459
|
+
.setParameters({x, y, width, height}));
|
|
1460
|
+
} catch (ex) {
|
|
1461
|
+
if (ex instanceof error.UnknownCommandError) {
|
|
1462
|
+
if (typeof x === 'number' && typeof y === 'number') {
|
|
1463
|
+
await this.driver_.execute(
|
|
1464
|
+
new command.Command(command.Name.SET_WINDOW_POSITION)
|
|
1465
|
+
.setParameter('windowHandle', 'current')
|
|
1466
|
+
.setParameter('x', x)
|
|
1467
|
+
.setParameter('y', y));
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
if (typeof width === 'number' && typeof height === 'number') {
|
|
1471
|
+
await this.driver_.execute(
|
|
1472
|
+
new command.Command(command.Name.SET_WINDOW_SIZE)
|
|
1473
|
+
.setParameter('windowHandle', 'current')
|
|
1474
|
+
.setParameter('width', width)
|
|
1475
|
+
.setParameter('height', height));
|
|
1476
|
+
}
|
|
1477
|
+
return this.getRect();
|
|
1478
|
+
}
|
|
1479
|
+
throw ex;
|
|
1480
|
+
}
|
|
1649
1481
|
}
|
|
1650
1482
|
|
|
1651
1483
|
/**
|
|
1652
|
-
*
|
|
1653
|
-
*
|
|
1654
|
-
*
|
|
1655
|
-
*
|
|
1484
|
+
* Maximizes the current window. The exact behavior of this command is
|
|
1485
|
+
* specific to individual window managers, but typically involves increasing
|
|
1486
|
+
* the window to the maximum available size without going full-screen.
|
|
1487
|
+
*
|
|
1488
|
+
* @return {!Promise<void>} A promise that will be resolved when the command
|
|
1489
|
+
* has completed.
|
|
1656
1490
|
*/
|
|
1657
|
-
|
|
1658
|
-
return this.driver_.
|
|
1659
|
-
new command.Command(command.Name.
|
|
1660
|
-
setParameter('windowHandle', 'current')
|
|
1661
|
-
'WebDriver.manage().window().getSize()');
|
|
1491
|
+
maximize() {
|
|
1492
|
+
return this.driver_.execute(
|
|
1493
|
+
new command.Command(command.Name.MAXIMIZE_WINDOW).
|
|
1494
|
+
setParameter('windowHandle', 'current'));
|
|
1662
1495
|
}
|
|
1663
1496
|
|
|
1664
1497
|
/**
|
|
1665
|
-
*
|
|
1666
|
-
*
|
|
1667
|
-
*
|
|
1668
|
-
*
|
|
1669
|
-
*
|
|
1498
|
+
* Minimizes the current window. The exact behavior of this command is
|
|
1499
|
+
* specific to individual window managers, but typicallly involves hiding
|
|
1500
|
+
* the window in the system tray.
|
|
1501
|
+
*
|
|
1502
|
+
* @return {!Promise<void>} A promise that will be resolved when the command
|
|
1503
|
+
* has completed.
|
|
1670
1504
|
*/
|
|
1671
|
-
|
|
1672
|
-
return this.driver_.
|
|
1673
|
-
new command.Command(command.Name.
|
|
1674
|
-
setParameter('windowHandle', 'current').
|
|
1675
|
-
setParameter('width', width).
|
|
1676
|
-
setParameter('height', height),
|
|
1677
|
-
'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
|
|
1505
|
+
minimize() {
|
|
1506
|
+
return this.driver_.execute(
|
|
1507
|
+
new command.Command(command.Name.MINIMIZE_WINDOW));
|
|
1678
1508
|
}
|
|
1679
1509
|
|
|
1680
1510
|
/**
|
|
1681
|
-
*
|
|
1682
|
-
*
|
|
1683
|
-
*
|
|
1511
|
+
* Invokes the "full screen" operation on the current window. The exact
|
|
1512
|
+
* behavior of this command is specific to individual window managers, but
|
|
1513
|
+
* this will typically increase the window size to the size of the physical
|
|
1514
|
+
* display and hide the browser chrome.
|
|
1515
|
+
*
|
|
1516
|
+
* @return {!Promise<void>} A promise that will be resolved when the command
|
|
1517
|
+
* has completed.
|
|
1518
|
+
* @see <https://fullscreen.spec.whatwg.org/#fullscreen-an-element>
|
|
1684
1519
|
*/
|
|
1685
|
-
|
|
1686
|
-
return this.driver_.
|
|
1687
|
-
new command.Command(command.Name.
|
|
1688
|
-
setParameter('windowHandle', 'current'),
|
|
1689
|
-
'WebDriver.manage().window().maximize()');
|
|
1520
|
+
fullscreen() {
|
|
1521
|
+
return this.driver_.execute(
|
|
1522
|
+
new command.Command(command.Name.FULLSCREEN_WINDOW));
|
|
1690
1523
|
}
|
|
1691
1524
|
}
|
|
1692
1525
|
|
|
@@ -1721,15 +1554,14 @@ class Logs {
|
|
|
1721
1554
|
* entries since the last call, or from the start of the session.
|
|
1722
1555
|
*
|
|
1723
1556
|
* @param {!logging.Type} type The desired log type.
|
|
1724
|
-
* @return {!
|
|
1557
|
+
* @return {!Promise<!Array.<!logging.Entry>>} A
|
|
1725
1558
|
* promise that will resolve to a list of log entries for the specified
|
|
1726
1559
|
* type.
|
|
1727
1560
|
*/
|
|
1728
1561
|
get(type) {
|
|
1729
1562
|
let cmd = new command.Command(command.Name.GET_LOG).
|
|
1730
1563
|
setParameter('type', type);
|
|
1731
|
-
return this.driver_.
|
|
1732
|
-
cmd, 'WebDriver.manage().logs().get(' + type + ')').
|
|
1564
|
+
return this.driver_.execute(cmd).
|
|
1733
1565
|
then(function(entries) {
|
|
1734
1566
|
return entries.map(function(entry) {
|
|
1735
1567
|
if (!(entry instanceof logging.Entry)) {
|
|
@@ -1744,13 +1576,12 @@ class Logs {
|
|
|
1744
1576
|
|
|
1745
1577
|
/**
|
|
1746
1578
|
* Retrieves the log types available to this driver.
|
|
1747
|
-
* @return {!
|
|
1579
|
+
* @return {!Promise<!Array<!logging.Type>>} A
|
|
1748
1580
|
* promise that will resolve to a list of available log types.
|
|
1749
1581
|
*/
|
|
1750
1582
|
getAvailableLogTypes() {
|
|
1751
|
-
return this.driver_.
|
|
1752
|
-
new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES)
|
|
1753
|
-
'WebDriver.manage().logs().getAvailableLogTypes()');
|
|
1583
|
+
return this.driver_.execute(
|
|
1584
|
+
new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES));
|
|
1754
1585
|
}
|
|
1755
1586
|
}
|
|
1756
1587
|
|
|
@@ -1776,35 +1607,34 @@ class TargetLocator {
|
|
|
1776
1607
|
}
|
|
1777
1608
|
|
|
1778
1609
|
/**
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1610
|
+
* Locates the DOM element on the current page that corresponds to
|
|
1611
|
+
* `document.activeElement` or `document.body` if the active element is not
|
|
1781
1612
|
* available.
|
|
1613
|
+
*
|
|
1782
1614
|
* @return {!WebElementPromise} The active element.
|
|
1783
1615
|
*/
|
|
1784
1616
|
activeElement() {
|
|
1785
|
-
var id = this.driver_.
|
|
1786
|
-
new command.Command(command.Name.GET_ACTIVE_ELEMENT)
|
|
1787
|
-
'WebDriver.switchTo().activeElement()');
|
|
1617
|
+
var id = this.driver_.execute(
|
|
1618
|
+
new command.Command(command.Name.GET_ACTIVE_ELEMENT));
|
|
1788
1619
|
return new WebElementPromise(this.driver_, id);
|
|
1789
1620
|
}
|
|
1790
1621
|
|
|
1791
1622
|
/**
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1623
|
+
* Switches focus of all future commands to the topmost frame in the current
|
|
1624
|
+
* window.
|
|
1625
|
+
*
|
|
1626
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1795
1627
|
* when the driver has changed focus to the default content.
|
|
1796
1628
|
*/
|
|
1797
1629
|
defaultContent() {
|
|
1798
|
-
return this.driver_.
|
|
1630
|
+
return this.driver_.execute(
|
|
1799
1631
|
new command.Command(command.Name.SWITCH_TO_FRAME).
|
|
1800
|
-
setParameter('id', null)
|
|
1801
|
-
'WebDriver.switchTo().defaultContent()');
|
|
1632
|
+
setParameter('id', null));
|
|
1802
1633
|
}
|
|
1803
1634
|
|
|
1804
1635
|
/**
|
|
1805
|
-
*
|
|
1806
|
-
*
|
|
1807
|
-
* following:
|
|
1636
|
+
* Changes the focus of all future commands to another frame on the page. The
|
|
1637
|
+
* target frame may be specified as one of the following:
|
|
1808
1638
|
*
|
|
1809
1639
|
* - A number that specifies a (zero-based) index into [window.frames](
|
|
1810
1640
|
* https://developer.mozilla.org/en-US/docs/Web/API/Window.frames).
|
|
@@ -1817,51 +1647,61 @@ class TargetLocator {
|
|
|
1817
1647
|
* rejected with a {@linkplain error.NoSuchFrameError}.
|
|
1818
1648
|
*
|
|
1819
1649
|
* @param {(number|WebElement|null)} id The frame locator.
|
|
1820
|
-
* @return {!
|
|
1650
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1821
1651
|
* when the driver has changed focus to the specified frame.
|
|
1822
1652
|
*/
|
|
1823
1653
|
frame(id) {
|
|
1824
|
-
return this.driver_.
|
|
1654
|
+
return this.driver_.execute(
|
|
1825
1655
|
new command.Command(command.Name.SWITCH_TO_FRAME).
|
|
1826
|
-
setParameter('id', id)
|
|
1827
|
-
|
|
1656
|
+
setParameter('id', id));
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/**
|
|
1660
|
+
* Changes the focus of all future commands to the parent frame of the
|
|
1661
|
+
* currently selected frame. This command has no effect if the driver is
|
|
1662
|
+
* already focused on the top-level browsing context.
|
|
1663
|
+
*
|
|
1664
|
+
* @return {!Promise<void>} A promise that will be resolved when the command
|
|
1665
|
+
* has completed.
|
|
1666
|
+
*/
|
|
1667
|
+
parentFrame() {
|
|
1668
|
+
return this.driver_.execute(
|
|
1669
|
+
new command.Command(command.Name.SWITCH_TO_FRAME_PARENT));
|
|
1828
1670
|
}
|
|
1829
1671
|
|
|
1830
1672
|
/**
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1833
|
-
*
|
|
1673
|
+
* Changes the focus of all future commands to another window. Windows may be
|
|
1674
|
+
* specified by their {@code window.name} attribute or by its handle
|
|
1675
|
+
* (as returned by {@link WebDriver#getWindowHandles}).
|
|
1834
1676
|
*
|
|
1835
1677
|
* If the specified window cannot be found, the returned promise will be
|
|
1836
1678
|
* rejected with a {@linkplain error.NoSuchWindowError}.
|
|
1837
1679
|
*
|
|
1838
1680
|
* @param {string} nameOrHandle The name or window handle of the window to
|
|
1839
1681
|
* switch focus to.
|
|
1840
|
-
* @return {!
|
|
1682
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1841
1683
|
* when the driver has changed focus to the specified window.
|
|
1842
1684
|
*/
|
|
1843
1685
|
window(nameOrHandle) {
|
|
1844
|
-
return this.driver_.
|
|
1686
|
+
return this.driver_.execute(
|
|
1845
1687
|
new command.Command(command.Name.SWITCH_TO_WINDOW).
|
|
1846
1688
|
// "name" supports the legacy drivers. "handle" is the W3C
|
|
1847
1689
|
// compliant parameter.
|
|
1848
1690
|
setParameter('name', nameOrHandle).
|
|
1849
|
-
setParameter('handle', nameOrHandle)
|
|
1850
|
-
'WebDriver.switchTo().window(' + nameOrHandle + ')');
|
|
1691
|
+
setParameter('handle', nameOrHandle));
|
|
1851
1692
|
}
|
|
1852
1693
|
|
|
1853
1694
|
/**
|
|
1854
|
-
*
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
1695
|
+
* Changes focus to the active modal dialog, such as those opened by
|
|
1696
|
+
* `window.alert()`, `window.confirm()`, and `window.prompt()`. The returned
|
|
1697
|
+
* promise will be rejected with a
|
|
1857
1698
|
* {@linkplain error.NoSuchAlertError} if there are no open alerts.
|
|
1858
1699
|
*
|
|
1859
1700
|
* @return {!AlertPromise} The open alert.
|
|
1860
1701
|
*/
|
|
1861
1702
|
alert() {
|
|
1862
|
-
var text = this.driver_.
|
|
1863
|
-
new command.Command(command.Name.GET_ALERT_TEXT)
|
|
1864
|
-
'WebDriver.switchTo().alert()');
|
|
1703
|
+
var text = this.driver_.execute(
|
|
1704
|
+
new command.Command(command.Name.GET_ALERT_TEXT));
|
|
1865
1705
|
var driver = this.driver_;
|
|
1866
1706
|
return new AlertPromise(driver, text.then(function(text) {
|
|
1867
1707
|
return new Alert(driver, text);
|
|
@@ -1901,17 +1741,17 @@ class WebElement {
|
|
|
1901
1741
|
/** @private {!WebDriver} */
|
|
1902
1742
|
this.driver_ = driver;
|
|
1903
1743
|
|
|
1904
|
-
/** @private {!
|
|
1905
|
-
this.id_ =
|
|
1744
|
+
/** @private {!Promise<string>} */
|
|
1745
|
+
this.id_ = Promise.resolve(id);
|
|
1906
1746
|
}
|
|
1907
1747
|
|
|
1908
1748
|
/**
|
|
1909
1749
|
* @param {string} id The raw ID.
|
|
1910
|
-
* @param {boolean=}
|
|
1750
|
+
* @param {boolean=} noLegacy Whether to exclude the legacy element key.
|
|
1911
1751
|
* @return {!Object} The element ID for use with WebDriver's wire protocol.
|
|
1912
1752
|
*/
|
|
1913
|
-
static buildId(id,
|
|
1914
|
-
return
|
|
1753
|
+
static buildId(id, noLegacy = false) {
|
|
1754
|
+
return noLegacy
|
|
1915
1755
|
? {[ELEMENT_ID_KEY]: id}
|
|
1916
1756
|
: {[ELEMENT_ID_KEY]: id, [LEGACY_ELEMENT_ID_KEY]: id};
|
|
1917
1757
|
}
|
|
@@ -1949,27 +1789,14 @@ class WebElement {
|
|
|
1949
1789
|
*
|
|
1950
1790
|
* @param {!WebElement} a A WebElement.
|
|
1951
1791
|
* @param {!WebElement} b A WebElement.
|
|
1952
|
-
* @return {!
|
|
1792
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
1953
1793
|
* resolved to whether the two WebElements are equal.
|
|
1954
1794
|
*/
|
|
1955
|
-
static equals(a, b) {
|
|
1795
|
+
static async equals(a, b) {
|
|
1956
1796
|
if (a === b) {
|
|
1957
|
-
return
|
|
1797
|
+
return true;
|
|
1958
1798
|
}
|
|
1959
|
-
|
|
1960
|
-
return promise.all(ids).then(function(ids) {
|
|
1961
|
-
// If the two element's have the same ID, they should be considered
|
|
1962
|
-
// equal. Otherwise, they may still be equivalent, but we'll need to
|
|
1963
|
-
// ask the server to check for us.
|
|
1964
|
-
if (ids[0] === ids[1]) {
|
|
1965
|
-
return true;
|
|
1966
|
-
}
|
|
1967
|
-
|
|
1968
|
-
let cmd = new command.Command(command.Name.ELEMENT_EQUALS);
|
|
1969
|
-
cmd.setParameter('id', ids[0]);
|
|
1970
|
-
cmd.setParameter('other', ids[1]);
|
|
1971
|
-
return a.driver_.schedule(cmd, 'WebElement.equals()');
|
|
1972
|
-
});
|
|
1799
|
+
return a.driver_.executeScript('arguments[0] === arguments[1]', a, b);
|
|
1973
1800
|
}
|
|
1974
1801
|
|
|
1975
1802
|
/** @return {!WebDriver} The parent driver for this instance. */
|
|
@@ -1978,7 +1805,7 @@ class WebElement {
|
|
|
1978
1805
|
}
|
|
1979
1806
|
|
|
1980
1807
|
/**
|
|
1981
|
-
* @return {!
|
|
1808
|
+
* @return {!Promise<string>} A promise that resolves to
|
|
1982
1809
|
* the server-assigned opaque ID assigned to this element.
|
|
1983
1810
|
*/
|
|
1984
1811
|
getId() {
|
|
@@ -1998,16 +1825,14 @@ class WebElement {
|
|
|
1998
1825
|
* parameters under the "id" key.
|
|
1999
1826
|
*
|
|
2000
1827
|
* @param {!command.Command} command The command to schedule.
|
|
2001
|
-
* @
|
|
2002
|
-
* @return {!promise.Thenable<T>} A promise that will be resolved
|
|
2003
|
-
* with the command result.
|
|
1828
|
+
* @return {!Promise<T>} A promise that will be resolved with the result.
|
|
2004
1829
|
* @template T
|
|
2005
1830
|
* @see WebDriver#schedule
|
|
2006
1831
|
* @private
|
|
2007
1832
|
*/
|
|
2008
|
-
|
|
1833
|
+
execute_(command) {
|
|
2009
1834
|
command.setParameter('id', this);
|
|
2010
|
-
return this.driver_.
|
|
1835
|
+
return this.driver_.execute(command);
|
|
2011
1836
|
}
|
|
2012
1837
|
|
|
2013
1838
|
/**
|
|
@@ -2054,49 +1879,46 @@ class WebElement {
|
|
|
2054
1879
|
command.Name.FIND_CHILD_ELEMENT).
|
|
2055
1880
|
setParameter('using', locator.using).
|
|
2056
1881
|
setParameter('value', locator.value);
|
|
2057
|
-
id = this.
|
|
1882
|
+
id = this.execute_(cmd);
|
|
2058
1883
|
}
|
|
2059
1884
|
return new WebElementPromise(this.driver_, id);
|
|
2060
1885
|
}
|
|
2061
1886
|
|
|
2062
1887
|
/**
|
|
2063
|
-
*
|
|
2064
|
-
*
|
|
1888
|
+
* Locates all of the descendants of this element that match the given search
|
|
1889
|
+
* criteria.
|
|
2065
1890
|
*
|
|
2066
1891
|
* @param {!(by.By|Function)} locator The locator strategy to use when
|
|
2067
1892
|
* searching for the element.
|
|
2068
|
-
* @return {!
|
|
2069
|
-
*
|
|
1893
|
+
* @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
|
|
1894
|
+
* array of WebElements.
|
|
2070
1895
|
*/
|
|
2071
|
-
findElements(locator) {
|
|
1896
|
+
async findElements(locator) {
|
|
2072
1897
|
locator = by.checkedLocator(locator);
|
|
2073
1898
|
let id;
|
|
2074
1899
|
if (typeof locator === 'function') {
|
|
2075
1900
|
return this.driver_.findElementsInternal_(locator, this);
|
|
2076
1901
|
} else {
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
setParameter('
|
|
2080
|
-
|
|
2081
|
-
return
|
|
2082
|
-
.then(result => Array.isArray(result) ? result : []);
|
|
1902
|
+
let cmd = new command.Command(command.Name.FIND_CHILD_ELEMENTS)
|
|
1903
|
+
.setParameter('using', locator.using)
|
|
1904
|
+
.setParameter('value', locator.value);
|
|
1905
|
+
let result = await this.execute_(cmd);
|
|
1906
|
+
return Array.isArray(result) ? result : [];
|
|
2083
1907
|
}
|
|
2084
1908
|
}
|
|
2085
1909
|
|
|
2086
1910
|
/**
|
|
2087
|
-
*
|
|
2088
|
-
*
|
|
2089
|
-
*
|
|
1911
|
+
* Clicks on this element.
|
|
1912
|
+
*
|
|
1913
|
+
* @return {!Promise<void>} A promise that will be resolved when the click
|
|
1914
|
+
* command has completed.
|
|
2090
1915
|
*/
|
|
2091
1916
|
click() {
|
|
2092
|
-
return this.
|
|
2093
|
-
new command.Command(command.Name.CLICK_ELEMENT),
|
|
2094
|
-
'WebElement.click()');
|
|
1917
|
+
return this.execute_(new command.Command(command.Name.CLICK_ELEMENT));
|
|
2095
1918
|
}
|
|
2096
1919
|
|
|
2097
1920
|
/**
|
|
2098
|
-
*
|
|
2099
|
-
* this instance.
|
|
1921
|
+
* Types a key sequence on the DOM element represented by this instance.
|
|
2100
1922
|
*
|
|
2101
1923
|
* Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
|
|
2102
1924
|
* processed in the key sequence, that key state is toggled until one of the
|
|
@@ -2143,97 +1965,80 @@ class WebElement {
|
|
|
2143
1965
|
* punctuation keys will be synthesized according to a standard QWERTY en-us
|
|
2144
1966
|
* keyboard layout.
|
|
2145
1967
|
*
|
|
2146
|
-
* @param {...(number|string|!IThenable<(number|string)>)}
|
|
1968
|
+
* @param {...(number|string|!IThenable<(number|string)>)} args The
|
|
2147
1969
|
* sequence of keys to type. Number keys may be referenced numerically or
|
|
2148
1970
|
* by string (1 or '1'). All arguments will be joined into a single
|
|
2149
1971
|
* sequence.
|
|
2150
|
-
* @return {!
|
|
2151
|
-
*
|
|
2152
|
-
*/
|
|
2153
|
-
sendKeys(
|
|
2154
|
-
let keys =
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
throw TypeError(
|
|
2163
|
-
'each key must be a number of string; got ' + type);
|
|
2164
|
-
}
|
|
1972
|
+
* @return {!Promise<void>} A promise that will be resolved when all keys
|
|
1973
|
+
* have been typed.
|
|
1974
|
+
*/
|
|
1975
|
+
async sendKeys(...args) {
|
|
1976
|
+
let keys = [];
|
|
1977
|
+
(await Promise.all(args)).forEach(key => {
|
|
1978
|
+
let type = typeof key;
|
|
1979
|
+
if (type === 'number') {
|
|
1980
|
+
key = String(key);
|
|
1981
|
+
} else if (type !== 'string') {
|
|
1982
|
+
throw TypeError('each key must be a number of string; got ' + type);
|
|
1983
|
+
}
|
|
2165
1984
|
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
return ret;
|
|
2171
|
-
});
|
|
1985
|
+
// The W3C protocol requires keys to be specified as an array where
|
|
1986
|
+
// each element is a single key.
|
|
1987
|
+
keys.push(...key.split(''));
|
|
1988
|
+
});
|
|
2172
1989
|
|
|
2173
1990
|
if (!this.driver_.fileDetector_) {
|
|
2174
|
-
return this.
|
|
2175
|
-
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
2176
|
-
setParameter('text', keys.
|
|
2177
|
-
setParameter('value', keys)
|
|
2178
|
-
'WebElement.sendKeys()');
|
|
1991
|
+
return this.execute_(
|
|
1992
|
+
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
1993
|
+
.setParameter('text', keys.join(''))
|
|
1994
|
+
.setParameter('value', keys));
|
|
2179
1995
|
}
|
|
2180
1996
|
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
.handleFile(element.driver_, keys.join(''));
|
|
2189
|
-
}).then(function(keys) {
|
|
2190
|
-
return element.schedule_(
|
|
2191
|
-
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT).
|
|
2192
|
-
setParameter('text', keys).
|
|
2193
|
-
setParameter('value', keys.split('')),
|
|
2194
|
-
'WebElement.sendKeys()');
|
|
2195
|
-
});
|
|
2196
|
-
}, 'WebElement.sendKeys()');
|
|
1997
|
+
keys =
|
|
1998
|
+
await this.driver_.fileDetector_.handleFile(
|
|
1999
|
+
this.driver_, keys.join(''));
|
|
2000
|
+
return this.execute_(
|
|
2001
|
+
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
2002
|
+
.setParameter('text', keys)
|
|
2003
|
+
.setParameter('value', keys.split('')));
|
|
2197
2004
|
}
|
|
2198
2005
|
|
|
2199
2006
|
/**
|
|
2200
|
-
*
|
|
2201
|
-
*
|
|
2202
|
-
*
|
|
2007
|
+
* Retrieves the element's tag name.
|
|
2008
|
+
*
|
|
2009
|
+
* @return {!Promise<string>} A promise that will be resolved with the
|
|
2010
|
+
* element's tag name.
|
|
2203
2011
|
*/
|
|
2204
2012
|
getTagName() {
|
|
2205
|
-
return this.
|
|
2206
|
-
new command.Command(command.Name.GET_ELEMENT_TAG_NAME)
|
|
2207
|
-
'WebElement.getTagName()');
|
|
2013
|
+
return this.execute_(
|
|
2014
|
+
new command.Command(command.Name.GET_ELEMENT_TAG_NAME));
|
|
2208
2015
|
}
|
|
2209
2016
|
|
|
2210
2017
|
/**
|
|
2211
|
-
*
|
|
2212
|
-
*
|
|
2213
|
-
*
|
|
2214
|
-
*
|
|
2215
|
-
* of rgb(0, 255, 0)).
|
|
2018
|
+
* Retrieves the value of a computed style property for this instance. If
|
|
2019
|
+
* the element inherits the named style from its parent, the parent will be
|
|
2020
|
+
* queried for its value. Where possible, color values will be converted to
|
|
2021
|
+
* their hex representation (e.g. #00ff00 instead of rgb(0, 255, 0)).
|
|
2216
2022
|
*
|
|
2217
2023
|
* _Warning:_ the value returned will be as the browser interprets it, so
|
|
2218
2024
|
* it may be tricky to form a proper assertion.
|
|
2219
2025
|
*
|
|
2220
2026
|
* @param {string} cssStyleProperty The name of the CSS style property to look
|
|
2221
2027
|
* up.
|
|
2222
|
-
* @return {!
|
|
2223
|
-
*
|
|
2028
|
+
* @return {!Promise<string>} A promise that will be resolved with the
|
|
2029
|
+
* requested CSS value.
|
|
2224
2030
|
*/
|
|
2225
2031
|
getCssValue(cssStyleProperty) {
|
|
2226
2032
|
var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
|
|
2227
|
-
return this.
|
|
2033
|
+
return this.execute_(
|
|
2228
2034
|
new command.Command(name).
|
|
2229
|
-
setParameter('propertyName', cssStyleProperty)
|
|
2230
|
-
'WebElement.getCssValue(' + cssStyleProperty + ')');
|
|
2035
|
+
setParameter('propertyName', cssStyleProperty));
|
|
2231
2036
|
}
|
|
2232
2037
|
|
|
2233
2038
|
/**
|
|
2234
|
-
*
|
|
2235
|
-
*
|
|
2236
|
-
*
|
|
2039
|
+
* Retrieves the current value of the given attribute of this element.
|
|
2040
|
+
* Will return the current value, even if it has been modified after the page
|
|
2041
|
+
* has been loaded. More exactly, this method will return the value
|
|
2237
2042
|
* of the given attribute, unless that attribute is not present, in which case
|
|
2238
2043
|
* the value of the property with the same name is returned. If neither value
|
|
2239
2044
|
* is set, null is returned (for example, the "value" property of a textarea
|
|
@@ -2255,131 +2060,122 @@ class WebElement {
|
|
|
2255
2060
|
* - "readonly"
|
|
2256
2061
|
*
|
|
2257
2062
|
* @param {string} attributeName The name of the attribute to query.
|
|
2258
|
-
* @return {!
|
|
2063
|
+
* @return {!Promise<?string>} A promise that will be
|
|
2259
2064
|
* resolved with the attribute's value. The returned value will always be
|
|
2260
2065
|
* either a string or null.
|
|
2261
2066
|
*/
|
|
2262
2067
|
getAttribute(attributeName) {
|
|
2263
|
-
return this.
|
|
2068
|
+
return this.execute_(
|
|
2264
2069
|
new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).
|
|
2265
|
-
setParameter('name', attributeName)
|
|
2266
|
-
'WebElement.getAttribute(' + attributeName + ')');
|
|
2070
|
+
setParameter('name', attributeName));
|
|
2267
2071
|
}
|
|
2268
2072
|
|
|
2269
2073
|
/**
|
|
2270
2074
|
* Get the visible (i.e. not hidden by CSS) innerText of this element,
|
|
2271
2075
|
* including sub-elements, without any leading or trailing whitespace.
|
|
2272
2076
|
*
|
|
2273
|
-
* @return {!
|
|
2077
|
+
* @return {!Promise<string>} A promise that will be
|
|
2274
2078
|
* resolved with the element's visible text.
|
|
2275
2079
|
*/
|
|
2276
2080
|
getText() {
|
|
2277
|
-
return this.
|
|
2278
|
-
new command.Command(command.Name.GET_ELEMENT_TEXT),
|
|
2279
|
-
'WebElement.getText()');
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
/**
|
|
2283
|
-
* Schedules a command to compute the size of this element's bounding box, in
|
|
2284
|
-
* pixels.
|
|
2285
|
-
* @return {!promise.Thenable<{width: number, height: number}>} A
|
|
2286
|
-
* promise that will be resolved with the element's size as a
|
|
2287
|
-
* {@code {width:number, height:number}} object.
|
|
2288
|
-
*/
|
|
2289
|
-
getSize() {
|
|
2290
|
-
return this.schedule_(
|
|
2291
|
-
new command.Command(command.Name.GET_ELEMENT_SIZE),
|
|
2292
|
-
'WebElement.getSize()');
|
|
2081
|
+
return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT));
|
|
2293
2082
|
}
|
|
2294
2083
|
|
|
2295
2084
|
/**
|
|
2296
|
-
*
|
|
2297
|
-
*
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2085
|
+
* Returns an object describing an element's location, in pixels relative to
|
|
2086
|
+
* the document element, and the element's size in pixels.
|
|
2087
|
+
*
|
|
2088
|
+
* @return {!Promise<{width: number, height: number, x: number, y: number}>}
|
|
2089
|
+
* A promise that will resolve with the element's rect.
|
|
2300
2090
|
*/
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2091
|
+
async getRect() {
|
|
2092
|
+
try {
|
|
2093
|
+
return await this.execute_(
|
|
2094
|
+
new command.Command(command.Name.GET_ELEMENT_RECT));
|
|
2095
|
+
} catch (err) {
|
|
2096
|
+
if (err instanceof error.UnknownCommandError) {
|
|
2097
|
+
const {width, height} =
|
|
2098
|
+
await this.execute_(
|
|
2099
|
+
new command.Command(command.Name.GET_ELEMENT_SIZE));
|
|
2100
|
+
const {x, y} =
|
|
2101
|
+
await this.execute_(
|
|
2102
|
+
new command.Command(command.Name.GET_ELEMENT_LOCATION));
|
|
2103
|
+
return {x, y, width, height};
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2305
2106
|
}
|
|
2306
2107
|
|
|
2307
2108
|
/**
|
|
2308
|
-
*
|
|
2309
|
-
*
|
|
2310
|
-
*
|
|
2109
|
+
* Tests whether this element is enabled, as dictated by the `disabled`
|
|
2110
|
+
* attribute.
|
|
2111
|
+
*
|
|
2112
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
2311
2113
|
* resolved with whether this element is currently enabled.
|
|
2312
2114
|
*/
|
|
2313
2115
|
isEnabled() {
|
|
2314
|
-
return this.
|
|
2315
|
-
new command.Command(command.Name.IS_ELEMENT_ENABLED),
|
|
2316
|
-
'WebElement.isEnabled()');
|
|
2116
|
+
return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED));
|
|
2317
2117
|
}
|
|
2318
2118
|
|
|
2319
2119
|
/**
|
|
2320
|
-
*
|
|
2321
|
-
*
|
|
2120
|
+
* Tests whether this element is selected.
|
|
2121
|
+
*
|
|
2122
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
2322
2123
|
* resolved with whether this element is currently selected.
|
|
2323
2124
|
*/
|
|
2324
2125
|
isSelected() {
|
|
2325
|
-
return this.
|
|
2326
|
-
new command.Command(command.Name.IS_ELEMENT_SELECTED)
|
|
2327
|
-
'WebElement.isSelected()');
|
|
2126
|
+
return this.execute_(
|
|
2127
|
+
new command.Command(command.Name.IS_ELEMENT_SELECTED));
|
|
2328
2128
|
}
|
|
2329
2129
|
|
|
2330
2130
|
/**
|
|
2331
|
-
*
|
|
2332
|
-
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2131
|
+
* Submits the form containing this element (or this element if it is itself
|
|
2132
|
+
* a FORM element). his command is a no-op if the element is not contained in
|
|
2133
|
+
* a form.
|
|
2134
|
+
*
|
|
2135
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2335
2136
|
* when the form has been submitted.
|
|
2336
2137
|
*/
|
|
2337
2138
|
submit() {
|
|
2338
|
-
return this.
|
|
2339
|
-
new command.Command(command.Name.SUBMIT_ELEMENT),
|
|
2340
|
-
'WebElement.submit()');
|
|
2139
|
+
return this.execute_(new command.Command(command.Name.SUBMIT_ELEMENT));
|
|
2341
2140
|
}
|
|
2342
2141
|
|
|
2343
2142
|
/**
|
|
2344
|
-
*
|
|
2345
|
-
*
|
|
2346
|
-
*
|
|
2347
|
-
*
|
|
2143
|
+
* Clear the `value` of this element. This command has no effect if the
|
|
2144
|
+
* underlying DOM element is neither a text INPUT element nor a TEXTAREA
|
|
2145
|
+
* element.
|
|
2146
|
+
*
|
|
2147
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2348
2148
|
* when the element has been cleared.
|
|
2349
2149
|
*/
|
|
2350
2150
|
clear() {
|
|
2351
|
-
return this.
|
|
2352
|
-
new command.Command(command.Name.CLEAR_ELEMENT),
|
|
2353
|
-
'WebElement.clear()');
|
|
2151
|
+
return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT));
|
|
2354
2152
|
}
|
|
2355
2153
|
|
|
2356
2154
|
/**
|
|
2357
|
-
*
|
|
2358
|
-
*
|
|
2155
|
+
* Test whether this element is currently displayed.
|
|
2156
|
+
*
|
|
2157
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
2359
2158
|
* resolved with whether this element is currently visible on the page.
|
|
2360
2159
|
*/
|
|
2361
2160
|
isDisplayed() {
|
|
2362
|
-
return this.
|
|
2363
|
-
new command.Command(command.Name.IS_ELEMENT_DISPLAYED)
|
|
2364
|
-
'WebElement.isDisplayed()');
|
|
2161
|
+
return this.execute_(
|
|
2162
|
+
new command.Command(command.Name.IS_ELEMENT_DISPLAYED));
|
|
2365
2163
|
}
|
|
2366
2164
|
|
|
2367
2165
|
/**
|
|
2368
2166
|
* Take a screenshot of the visible region encompassed by this element's
|
|
2369
2167
|
* bounding rectangle.
|
|
2370
2168
|
*
|
|
2371
|
-
* @param {boolean=}
|
|
2169
|
+
* @param {boolean=} scroll Optional argument that indicates whether the
|
|
2372
2170
|
* element should be scrolled into view before taking a screenshot.
|
|
2373
2171
|
* Defaults to false.
|
|
2374
|
-
* @return {!
|
|
2172
|
+
* @return {!Promise<string>} A promise that will be
|
|
2375
2173
|
* resolved to the screenshot as a base-64 encoded PNG.
|
|
2376
2174
|
*/
|
|
2377
|
-
takeScreenshot(
|
|
2378
|
-
|
|
2379
|
-
return this.schedule_(
|
|
2175
|
+
takeScreenshot(scroll = false) {
|
|
2176
|
+
return this.execute_(
|
|
2380
2177
|
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
|
|
2381
|
-
.setParameter('scroll', scroll)
|
|
2382
|
-
'WebElement.takeScreenshot(' + scroll + ')');
|
|
2178
|
+
.setParameter('scroll', scroll));
|
|
2383
2179
|
}
|
|
2384
2180
|
}
|
|
2385
2181
|
|
|
@@ -2396,31 +2192,19 @@ class WebElement {
|
|
|
2396
2192
|
* return el.click();
|
|
2397
2193
|
* });
|
|
2398
2194
|
*
|
|
2399
|
-
* @implements {
|
|
2195
|
+
* @implements {IThenable<!WebElement>}
|
|
2400
2196
|
* @final
|
|
2401
2197
|
*/
|
|
2402
2198
|
class WebElementPromise extends WebElement {
|
|
2403
2199
|
/**
|
|
2404
2200
|
* @param {!WebDriver} driver The parent WebDriver instance for this
|
|
2405
2201
|
* element.
|
|
2406
|
-
* @param {!
|
|
2202
|
+
* @param {!Promise<!WebElement>} el A promise
|
|
2407
2203
|
* that will resolve to the promised element.
|
|
2408
2204
|
*/
|
|
2409
2205
|
constructor(driver, el) {
|
|
2410
2206
|
super(driver, 'unused');
|
|
2411
2207
|
|
|
2412
|
-
/**
|
|
2413
|
-
* Cancel operation is only supported if the wrapped thenable is also
|
|
2414
|
-
* cancellable.
|
|
2415
|
-
* @param {(string|Error)=} opt_reason
|
|
2416
|
-
* @override
|
|
2417
|
-
*/
|
|
2418
|
-
this.cancel = function(opt_reason) {
|
|
2419
|
-
if (promise.CancellableThenable.isImplementation(el)) {
|
|
2420
|
-
/** @type {!promise.CancellableThenable} */(el).cancel(opt_reason);
|
|
2421
|
-
}
|
|
2422
|
-
};
|
|
2423
|
-
|
|
2424
2208
|
/** @override */
|
|
2425
2209
|
this.then = el.then.bind(el);
|
|
2426
2210
|
|
|
@@ -2439,7 +2223,6 @@ class WebElementPromise extends WebElement {
|
|
|
2439
2223
|
};
|
|
2440
2224
|
}
|
|
2441
2225
|
}
|
|
2442
|
-
promise.CancellableThenable.addImplementation(WebElementPromise);
|
|
2443
2226
|
|
|
2444
2227
|
|
|
2445
2228
|
//////////////////////////////////////////////////////////////////////////////
|
|
@@ -2465,60 +2248,41 @@ class Alert {
|
|
|
2465
2248
|
/** @private {!WebDriver} */
|
|
2466
2249
|
this.driver_ = driver;
|
|
2467
2250
|
|
|
2468
|
-
/** @private {!
|
|
2469
|
-
this.text_ =
|
|
2251
|
+
/** @private {!Promise<string>} */
|
|
2252
|
+
this.text_ = Promise.resolve(text);
|
|
2470
2253
|
}
|
|
2471
2254
|
|
|
2472
2255
|
/**
|
|
2473
2256
|
* Retrieves the message text displayed with this alert. For instance, if the
|
|
2474
2257
|
* alert were opened with alert("hello"), then this would return "hello".
|
|
2475
2258
|
*
|
|
2476
|
-
* @return {!
|
|
2259
|
+
* @return {!Promise<string>} A promise that will be
|
|
2477
2260
|
* resolved to the text displayed with this alert.
|
|
2478
2261
|
*/
|
|
2479
2262
|
getText() {
|
|
2480
2263
|
return this.text_;
|
|
2481
2264
|
}
|
|
2482
2265
|
|
|
2483
|
-
/**
|
|
2484
|
-
* Sets the username and password in an alert prompting for credentials (such
|
|
2485
|
-
* as a Basic HTTP Auth prompt). This method will implicitly
|
|
2486
|
-
* {@linkplain #accept() submit} the dialog.
|
|
2487
|
-
*
|
|
2488
|
-
* @param {string} username The username to send.
|
|
2489
|
-
* @param {string} password The password to send.
|
|
2490
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved when this
|
|
2491
|
-
* command has completed.
|
|
2492
|
-
*/
|
|
2493
|
-
authenticateAs(username, password) {
|
|
2494
|
-
return this.driver_.schedule(
|
|
2495
|
-
new command.Command(command.Name.SET_ALERT_CREDENTIALS),
|
|
2496
|
-
'WebDriver.switchTo().alert()'
|
|
2497
|
-
+ `.authenticateAs("${username}", "${password}")`);
|
|
2498
|
-
}
|
|
2499
|
-
|
|
2500
2266
|
/**
|
|
2501
2267
|
* Accepts this alert.
|
|
2502
2268
|
*
|
|
2503
|
-
* @return {!
|
|
2269
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2504
2270
|
* when this command has completed.
|
|
2505
2271
|
*/
|
|
2506
2272
|
accept() {
|
|
2507
|
-
return this.driver_.
|
|
2508
|
-
new command.Command(command.Name.ACCEPT_ALERT)
|
|
2509
|
-
'WebDriver.switchTo().alert().accept()');
|
|
2273
|
+
return this.driver_.execute(
|
|
2274
|
+
new command.Command(command.Name.ACCEPT_ALERT));
|
|
2510
2275
|
}
|
|
2511
2276
|
|
|
2512
2277
|
/**
|
|
2513
2278
|
* Dismisses this alert.
|
|
2514
2279
|
*
|
|
2515
|
-
* @return {!
|
|
2280
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2516
2281
|
* when this command has completed.
|
|
2517
2282
|
*/
|
|
2518
2283
|
dismiss() {
|
|
2519
|
-
return this.driver_.
|
|
2520
|
-
new command.Command(command.Name.DISMISS_ALERT)
|
|
2521
|
-
'WebDriver.switchTo().alert().dismiss()');
|
|
2284
|
+
return this.driver_.execute(
|
|
2285
|
+
new command.Command(command.Name.DISMISS_ALERT));
|
|
2522
2286
|
}
|
|
2523
2287
|
|
|
2524
2288
|
/**
|
|
@@ -2527,14 +2291,13 @@ class Alert {
|
|
|
2527
2291
|
* window.confirm).
|
|
2528
2292
|
*
|
|
2529
2293
|
* @param {string} text The text to set.
|
|
2530
|
-
* @return {!
|
|
2294
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2531
2295
|
* when this command has completed.
|
|
2532
2296
|
*/
|
|
2533
2297
|
sendKeys(text) {
|
|
2534
|
-
return this.driver_.
|
|
2298
|
+
return this.driver_.execute(
|
|
2535
2299
|
new command.Command(command.Name.SET_ALERT_TEXT).
|
|
2536
|
-
setParameter('text', text)
|
|
2537
|
-
'WebDriver.switchTo().alert().sendKeys(' + text + ')');
|
|
2300
|
+
setParameter('text', text));
|
|
2538
2301
|
}
|
|
2539
2302
|
}
|
|
2540
2303
|
|
|
@@ -2550,31 +2313,19 @@ class Alert {
|
|
|
2550
2313
|
* return alert.dismiss();
|
|
2551
2314
|
* });
|
|
2552
2315
|
*
|
|
2553
|
-
* @implements {
|
|
2316
|
+
* @implements {IThenable<!Alert>}
|
|
2554
2317
|
* @final
|
|
2555
2318
|
*/
|
|
2556
2319
|
class AlertPromise extends Alert {
|
|
2557
2320
|
/**
|
|
2558
2321
|
* @param {!WebDriver} driver The driver controlling the browser this
|
|
2559
2322
|
* alert is attached to.
|
|
2560
|
-
* @param {!
|
|
2323
|
+
* @param {!Promise<!Alert>} alert A thenable
|
|
2561
2324
|
* that will be fulfilled with the promised alert.
|
|
2562
2325
|
*/
|
|
2563
2326
|
constructor(driver, alert) {
|
|
2564
2327
|
super(driver, 'unused');
|
|
2565
2328
|
|
|
2566
|
-
/**
|
|
2567
|
-
* Cancel operation is only supported if the wrapped thenable is also
|
|
2568
|
-
* cancellable.
|
|
2569
|
-
* @param {(string|Error)=} opt_reason
|
|
2570
|
-
* @override
|
|
2571
|
-
*/
|
|
2572
|
-
this.cancel = function(opt_reason) {
|
|
2573
|
-
if (promise.CancellableThenable.isImplementation(alert)) {
|
|
2574
|
-
/** @type {!promise.CancellableThenable} */(alert).cancel(opt_reason);
|
|
2575
|
-
}
|
|
2576
|
-
};
|
|
2577
|
-
|
|
2578
2329
|
/** @override */
|
|
2579
2330
|
this.then = alert.then.bind(alert);
|
|
2580
2331
|
|
|
@@ -2591,16 +2342,6 @@ class AlertPromise extends Alert {
|
|
|
2591
2342
|
});
|
|
2592
2343
|
};
|
|
2593
2344
|
|
|
2594
|
-
/**
|
|
2595
|
-
* Defers action until the alert has been located.
|
|
2596
|
-
* @override
|
|
2597
|
-
*/
|
|
2598
|
-
this.authenticateAs = function(username, password) {
|
|
2599
|
-
return alert.then(function(alert) {
|
|
2600
|
-
return alert.authenticateAs(username, password);
|
|
2601
|
-
});
|
|
2602
|
-
};
|
|
2603
|
-
|
|
2604
2345
|
/**
|
|
2605
2346
|
* Defers action until the alert has been located.
|
|
2606
2347
|
* @override
|
|
@@ -2632,25 +2373,23 @@ class AlertPromise extends Alert {
|
|
|
2632
2373
|
};
|
|
2633
2374
|
}
|
|
2634
2375
|
}
|
|
2635
|
-
promise.CancellableThenable.addImplementation(AlertPromise);
|
|
2636
2376
|
|
|
2637
2377
|
|
|
2638
2378
|
// PUBLIC API
|
|
2639
2379
|
|
|
2640
2380
|
|
|
2641
2381
|
module.exports = {
|
|
2642
|
-
Alert
|
|
2643
|
-
AlertPromise
|
|
2644
|
-
Condition
|
|
2645
|
-
Logs
|
|
2646
|
-
Navigation
|
|
2647
|
-
Options
|
|
2648
|
-
TargetLocator
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
Window: Window
|
|
2382
|
+
Alert,
|
|
2383
|
+
AlertPromise,
|
|
2384
|
+
Condition,
|
|
2385
|
+
Logs,
|
|
2386
|
+
Navigation,
|
|
2387
|
+
Options,
|
|
2388
|
+
TargetLocator,
|
|
2389
|
+
IWebDriver,
|
|
2390
|
+
WebDriver,
|
|
2391
|
+
WebElement,
|
|
2392
|
+
WebElementCondition,
|
|
2393
|
+
WebElementPromise,
|
|
2394
|
+
Window
|
|
2656
2395
|
};
|