selenium-webdriver 3.3.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 +273 -0
- package/NOTICE +1 -1
- package/README.md +28 -36
- package/chrome.js +221 -146
- package/edge.js +43 -118
- package/example/chrome_android.js +8 -9
- package/example/chrome_mobile_emulation.js +9 -10
- package/example/firefox_channels.js +23 -19
- package/example/google_search.js +3 -3
- package/example/google_search_test.js +39 -27
- package/example/headless.js +55 -0
- package/example/logging.js +40 -7
- package/firefox.js +769 -0
- package/http/index.js +41 -4
- package/http/util.js +11 -6
- package/ie.js +57 -98
- package/index.js +182 -115
- package/io/index.js +59 -0
- package/io/zip.js +214 -0
- package/jasmine.json +11 -0
- package/lib/actions.js +68 -63
- package/lib/atoms/get-attribute.js +9 -0
- package/lib/atoms/is-displayed.js +73 -78
- package/lib/by.js +9 -1
- package/lib/capabilities.js +303 -201
- package/lib/command.js +28 -50
- package/lib/error.js +113 -42
- package/lib/http.js +167 -135
- package/lib/input.js +1044 -8
- package/lib/promise.js +222 -3331
- 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/click_tests/disabled_element.html +12 -0
- package/lib/test/data/content-editable.html +10 -0
- package/lib/test/data/firefox/webextension.xpi +0 -0
- package/lib/test/data/inputs.html +42 -0
- package/lib/test/data/key_logger.html +34 -0
- package/lib/test/data/nestedElements.html +10 -1
- package/lib/test/data/scrolling_tests/page_with_partially_hidden_element.html +14 -0
- package/lib/test/data/selectPage.html +12 -0
- package/lib/test/data/simpleTest.html +5 -0
- package/lib/test/data/single_text_input.html +12 -0
- package/lib/test/data/upload_invisible.html +45 -0
- package/lib/test/fileserver.js +23 -19
- package/lib/test/index.js +30 -244
- package/lib/test/resources.js +0 -1
- package/lib/webdriver.js +770 -939
- package/net/index.js +21 -25
- package/net/portprober.js +51 -68
- package/package.json +6 -4
- package/remote/index.js +18 -30
- package/safari.js +42 -129
- package/test/actions_test.js +173 -18
- package/test/builder_test.js +106 -0
- 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 +18 -1
- package/test/http/util_test.js +2 -2
- package/test/{io_test.js → io/io_test.js} +40 -1
- package/test/io/zip_test.js +127 -0
- package/test/lib/by_test.js +21 -0
- package/test/lib/error_test.js +55 -8
- package/test/lib/http_test.js +18 -130
- package/test/lib/input_test.js +1379 -0
- package/test/lib/promise_test.js +473 -908
- package/test/lib/webdriver_test.js +357 -825
- package/test/logging_test.js +37 -43
- package/test/page_loading_test.js +70 -86
- package/test/proxy_test.js +64 -72
- package/test/rect_test.js +14 -25
- package/test/remote_test.js +13 -32
- 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 -315
- package/.npmignore +0 -2
- package/example/async_await_test.js +0 -68
- package/example/google_search_generator.js +0 -50
- package/example/parallel_flows.js +0 -54
- package/firefox/binary.js +0 -344
- package/firefox/extension.js +0 -187
- package/firefox/index.js +0 -722
- package/firefox/profile.js +0 -430
- package/lib/atoms/getAttribute.js +0 -12
- package/lib/events.js +0 -210
- package/lib/firefox/webdriver.json +0 -70
- package/lib/firefox/webdriver.xpi +0 -0
- 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 -96
- package/test/firefox/firefox_test.js +0 -226
- package/test/firefox/profile_test.js +0 -185
- 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/session_test.js +0 -54
- 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();
|
|
304
|
-
*
|
|
305
|
-
* @return {!actions.ActionSequence} A new action sequence for this instance.
|
|
306
|
-
*/
|
|
307
|
-
actions() {}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Creates a new touch sequence using this driver. The sequence will not be
|
|
311
|
-
* scheduled for execution until {@link actions.TouchSequence#perform} is
|
|
312
|
-
* called. Example:
|
|
278
|
+
* submitted for execution until
|
|
279
|
+
* {@link ./input.Actions#perform Actions.perform()} is called.
|
|
313
280
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* @return {!actions.TouchSequence} A new touch sequence for this instance.
|
|
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.
|
|
320
286
|
*/
|
|
321
|
-
|
|
287
|
+
actions(options) {}
|
|
322
288
|
|
|
323
289
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
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
|
-
*
|
|
435
|
-
* @template T
|
|
436
|
-
*/
|
|
437
|
-
executeAsyncScript(script, var_args) {}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Schedules a command to execute a custom function.
|
|
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.
|
|
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.
|
|
446
400
|
* @template T
|
|
447
401
|
*/
|
|
448
|
-
|
|
402
|
+
executeAsyncScript(script, ...args) {}
|
|
449
403
|
|
|
450
404
|
/**
|
|
451
|
-
*
|
|
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,86 +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(new: WebDriver,
|
|
754
|
-
* !IThenable<!Session>,
|
|
755
|
-
* !command.Executor,
|
|
756
|
-
* promise.ControlFlow=))=} opt_ctor
|
|
757
|
-
* A reference to the constructor of the specific type of WebDriver client
|
|
758
|
-
* to instantiate. Will create a vanilla {@linkplain WebDriver} instance
|
|
759
|
-
* if a constructor is not provided.
|
|
760
|
-
* @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
|
|
761
668
|
* the newly created session is terminated. This should be used to clean
|
|
762
669
|
* up any resources associated with the session.
|
|
763
670
|
* @return {!WebDriver} The driver for the newly created session.
|
|
764
671
|
*/
|
|
765
|
-
static createSession(
|
|
766
|
-
executor, capabilities, opt_flow, opt_ctor, opt_onQuit) {
|
|
767
|
-
let flow = opt_flow || promise.controlFlow();
|
|
672
|
+
static createSession(executor, capabilities, onQuit = undefined) {
|
|
768
673
|
let cmd = new command.Command(command.Name.NEW_SESSION);
|
|
769
674
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
675
|
+
// For OSS remote ends.
|
|
676
|
+
cmd.setParameter('desiredCapabilities', capabilities);
|
|
677
|
+
// For W3C remote ends.
|
|
678
|
+
cmd.setParameter('capabilities', {
|
|
679
|
+
alwaysMatch: filterNonW3CCaps(capabilities),
|
|
680
|
+
});
|
|
776
681
|
|
|
777
|
-
let session =
|
|
778
|
-
|
|
779
|
-
'WebDriver.createSession()');
|
|
780
|
-
if (typeof opt_onQuit === 'function') {
|
|
682
|
+
let session = executeCommand(executor, cmd);
|
|
683
|
+
if (typeof onQuit === 'function') {
|
|
781
684
|
session = session.catch(err => {
|
|
782
|
-
return Promise.resolve(
|
|
685
|
+
return Promise.resolve(onQuit.call(void 0)).then(_ => {throw err;});
|
|
783
686
|
});
|
|
784
687
|
}
|
|
785
|
-
|
|
786
|
-
return new ctor(session, executor, flow, opt_onQuit);
|
|
688
|
+
return new this(session, executor, onQuit);
|
|
787
689
|
}
|
|
788
690
|
|
|
789
691
|
/** @override */
|
|
790
|
-
|
|
791
|
-
return this.flow_;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
/** @override */
|
|
795
|
-
schedule(command, description) {
|
|
692
|
+
async execute(command) {
|
|
796
693
|
command.setParameter('sessionId', this.session_);
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
// propagate through the command itself, we resolve all of the
|
|
802
|
-
// command parameters now, but suppress any errors until the ControlFlow
|
|
803
|
-
// actually executes the command. This addresses scenarios like catching
|
|
804
|
-
// an element not found error in:
|
|
805
|
-
//
|
|
806
|
-
// driver.findElement(By.id('foo')).click().catch(function(e) {
|
|
807
|
-
// if (e instanceof NoSuchElementError) {
|
|
808
|
-
// // Do something.
|
|
809
|
-
// }
|
|
810
|
-
// });
|
|
811
|
-
var prepCommand = toWireValue(command.getParameters());
|
|
812
|
-
prepCommand.catch(function() {});
|
|
813
|
-
|
|
814
|
-
var flow = this.flow_;
|
|
815
|
-
var executor = this.executor_;
|
|
816
|
-
return flow.execute(() => {
|
|
817
|
-
// Retrieve resolved command parameters; any previously suppressed errors
|
|
818
|
-
// will now propagate up through the control flow as part of the command
|
|
819
|
-
// execution.
|
|
820
|
-
return prepCommand.then(function(parameters) {
|
|
821
|
-
command.setParameters(parameters);
|
|
822
|
-
return executor.execute(command);
|
|
823
|
-
}).then(value => fromWireValue(this, value));
|
|
824
|
-
}, 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);
|
|
825
698
|
}
|
|
826
699
|
|
|
827
700
|
/** @override */
|
|
@@ -846,17 +719,13 @@ class WebDriver {
|
|
|
846
719
|
|
|
847
720
|
/** @override */
|
|
848
721
|
quit() {
|
|
849
|
-
|
|
850
|
-
new command.Command(command.Name.QUIT),
|
|
851
|
-
'WebDriver.quit()');
|
|
722
|
+
let result = this.execute(new command.Command(command.Name.QUIT));
|
|
852
723
|
// Delete our session ID when the quit command finishes; this will allow us
|
|
853
724
|
// to throw an error when attempting to use a driver post-quit.
|
|
854
|
-
return
|
|
855
|
-
this.session_ =
|
|
856
|
-
reject(new error.NoSuchSessionError(
|
|
725
|
+
return promise.finally(result, () => {
|
|
726
|
+
this.session_ = Promise.reject(new error.NoSuchSessionError(
|
|
857
727
|
'This driver instance does not have a valid session ID ' +
|
|
858
728
|
'(did you call WebDriver.quit()?) and may no longer be used.'));
|
|
859
|
-
});
|
|
860
729
|
|
|
861
730
|
// Only want the session rejection to bubble if accessed.
|
|
862
731
|
this.session_.catch(function() {});
|
|
@@ -864,70 +733,73 @@ class WebDriver {
|
|
|
864
733
|
if (this.onQuit_) {
|
|
865
734
|
return this.onQuit_.call(void 0);
|
|
866
735
|
}
|
|
867
|
-
})
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
/** @override */
|
|
871
|
-
actions() {
|
|
872
|
-
return new actions.ActionSequence(this);
|
|
736
|
+
});
|
|
873
737
|
}
|
|
874
738
|
|
|
875
739
|
/** @override */
|
|
876
|
-
|
|
877
|
-
return new
|
|
740
|
+
actions(options) {
|
|
741
|
+
return new input.Actions(this, options || undefined);
|
|
878
742
|
}
|
|
879
743
|
|
|
880
744
|
/** @override */
|
|
881
|
-
executeScript(script,
|
|
745
|
+
executeScript(script, ...args) {
|
|
882
746
|
if (typeof script === 'function') {
|
|
883
747
|
script = 'return (' + script + ').apply(null, arguments);';
|
|
884
748
|
}
|
|
885
|
-
|
|
886
|
-
arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];
|
|
887
|
-
return this.schedule(
|
|
749
|
+
return this.execute(
|
|
888
750
|
new command.Command(command.Name.EXECUTE_SCRIPT).
|
|
889
751
|
setParameter('script', script).
|
|
890
|
-
setParameter('args', args)
|
|
891
|
-
'WebDriver.executeScript()');
|
|
752
|
+
setParameter('args', args));
|
|
892
753
|
}
|
|
893
754
|
|
|
894
755
|
/** @override */
|
|
895
|
-
executeAsyncScript(script,
|
|
756
|
+
executeAsyncScript(script, ...args) {
|
|
896
757
|
if (typeof script === 'function') {
|
|
897
758
|
script = 'return (' + script + ').apply(null, arguments);';
|
|
898
759
|
}
|
|
899
|
-
|
|
900
|
-
return this.schedule(
|
|
760
|
+
return this.execute(
|
|
901
761
|
new command.Command(command.Name.EXECUTE_ASYNC_SCRIPT).
|
|
902
762
|
setParameter('script', script).
|
|
903
|
-
setParameter('args', args)
|
|
904
|
-
'WebDriver.executeScript()');
|
|
763
|
+
setParameter('args', args));
|
|
905
764
|
}
|
|
906
765
|
|
|
907
766
|
/** @override */
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
if (promise.isGenerator(fn)) {
|
|
913
|
-
args.unshift(fn, opt_scope);
|
|
914
|
-
return promise.consume.apply(null, args);
|
|
915
|
-
}
|
|
916
|
-
return fn.apply(opt_scope, args);
|
|
917
|
-
});
|
|
918
|
-
}, 'WebDriver.call(' + (fn.name || 'function') + ')');
|
|
919
|
-
}
|
|
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
|
+
}
|
|
920
771
|
|
|
921
|
-
/** @override */
|
|
922
|
-
wait(condition, opt_timeout, opt_message) {
|
|
923
772
|
if (promise.isPromise(condition)) {
|
|
924
|
-
return
|
|
925
|
-
|
|
926
|
-
|
|
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
|
+
});
|
|
927
800
|
}
|
|
928
801
|
|
|
929
|
-
|
|
930
|
-
var fn = /** @type {!Function} */(condition);
|
|
802
|
+
let fn = /** @type {!Function} */(condition);
|
|
931
803
|
if (condition instanceof Condition) {
|
|
932
804
|
message = message || condition.description();
|
|
933
805
|
fn = condition.fn;
|
|
@@ -939,13 +811,36 @@ class WebDriver {
|
|
|
939
811
|
+ 'Condition object');
|
|
940
812
|
}
|
|
941
813
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
+
});
|
|
949
844
|
|
|
950
845
|
if (condition instanceof WebElementCondition) {
|
|
951
846
|
result = new WebElementPromise(this, result.then(function(value) {
|
|
@@ -962,34 +857,30 @@ class WebDriver {
|
|
|
962
857
|
|
|
963
858
|
/** @override */
|
|
964
859
|
sleep(ms) {
|
|
965
|
-
return
|
|
860
|
+
return new Promise(resolve => setTimeout(() => resolve(), ms));
|
|
966
861
|
}
|
|
967
862
|
|
|
968
863
|
/** @override */
|
|
969
864
|
getWindowHandle() {
|
|
970
|
-
return this.
|
|
971
|
-
new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE)
|
|
972
|
-
'WebDriver.getWindowHandle()');
|
|
865
|
+
return this.execute(
|
|
866
|
+
new command.Command(command.Name.GET_CURRENT_WINDOW_HANDLE));
|
|
973
867
|
}
|
|
974
868
|
|
|
975
869
|
/** @override */
|
|
976
870
|
getAllWindowHandles() {
|
|
977
|
-
return this.
|
|
978
|
-
new command.Command(command.Name.GET_WINDOW_HANDLES)
|
|
979
|
-
'WebDriver.getAllWindowHandles()');
|
|
871
|
+
return this.execute(
|
|
872
|
+
new command.Command(command.Name.GET_WINDOW_HANDLES));
|
|
980
873
|
}
|
|
981
874
|
|
|
982
875
|
/** @override */
|
|
983
876
|
getPageSource() {
|
|
984
|
-
return this.
|
|
985
|
-
new command.Command(command.Name.GET_PAGE_SOURCE)
|
|
986
|
-
'WebDriver.getPageSource()');
|
|
877
|
+
return this.execute(
|
|
878
|
+
new command.Command(command.Name.GET_PAGE_SOURCE));
|
|
987
879
|
}
|
|
988
880
|
|
|
989
881
|
/** @override */
|
|
990
882
|
close() {
|
|
991
|
-
return this.
|
|
992
|
-
'WebDriver.close()');
|
|
883
|
+
return this.execute(new command.Command(command.Name.CLOSE));
|
|
993
884
|
}
|
|
994
885
|
|
|
995
886
|
/** @override */
|
|
@@ -999,15 +890,12 @@ class WebDriver {
|
|
|
999
890
|
|
|
1000
891
|
/** @override */
|
|
1001
892
|
getCurrentUrl() {
|
|
1002
|
-
return this.
|
|
1003
|
-
new command.Command(command.Name.GET_CURRENT_URL),
|
|
1004
|
-
'WebDriver.getCurrentUrl()');
|
|
893
|
+
return this.execute(new command.Command(command.Name.GET_CURRENT_URL));
|
|
1005
894
|
}
|
|
1006
895
|
|
|
1007
896
|
/** @override */
|
|
1008
897
|
getTitle() {
|
|
1009
|
-
return this.
|
|
1010
|
-
'WebDriver.getTitle()');
|
|
898
|
+
return this.execute(new command.Command(command.Name.GET_TITLE));
|
|
1011
899
|
}
|
|
1012
900
|
|
|
1013
901
|
/** @override */
|
|
@@ -1020,33 +908,31 @@ class WebDriver {
|
|
|
1020
908
|
let cmd = new command.Command(command.Name.FIND_ELEMENT).
|
|
1021
909
|
setParameter('using', locator.using).
|
|
1022
910
|
setParameter('value', locator.value);
|
|
1023
|
-
id = this.
|
|
911
|
+
id = this.execute(cmd);
|
|
1024
912
|
}
|
|
1025
913
|
return new WebElementPromise(this, id);
|
|
1026
914
|
}
|
|
1027
915
|
|
|
1028
916
|
/**
|
|
1029
917
|
* @param {!Function} locatorFn The locator function to use.
|
|
1030
|
-
* @param {!(WebDriver|WebElement)} context The search
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
* 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.
|
|
1034
921
|
* @private
|
|
1035
922
|
*/
|
|
1036
|
-
findElementInternal_(locatorFn, context) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
});
|
|
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;
|
|
1046
932
|
}
|
|
1047
933
|
|
|
1048
934
|
/** @override */
|
|
1049
|
-
findElements(locator) {
|
|
935
|
+
async findElements(locator) {
|
|
1050
936
|
locator = by.checkedLocator(locator);
|
|
1051
937
|
if (typeof locator === 'function') {
|
|
1052
938
|
return this.findElementsInternal_(locator, this);
|
|
@@ -1054,43 +940,43 @@ class WebDriver {
|
|
|
1054
940
|
let cmd = new command.Command(command.Name.FIND_ELEMENTS).
|
|
1055
941
|
setParameter('using', locator.using).
|
|
1056
942
|
setParameter('value', locator.value);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
943
|
+
try {
|
|
944
|
+
let res = await this.execute(cmd);
|
|
945
|
+
return Array.isArray(res) ? res : [];
|
|
946
|
+
} catch (ex) {
|
|
947
|
+
if (ex instanceof error.NoSuchElementError) {
|
|
1060
948
|
return [];
|
|
1061
949
|
}
|
|
1062
|
-
throw
|
|
1063
|
-
}
|
|
950
|
+
throw ex;
|
|
951
|
+
}
|
|
1064
952
|
}
|
|
1065
953
|
}
|
|
1066
954
|
|
|
1067
955
|
/**
|
|
1068
956
|
* @param {!Function} locatorFn The locator function to use.
|
|
1069
957
|
* @param {!(WebDriver|WebElement)} context The search context.
|
|
1070
|
-
* @return {!
|
|
1071
|
-
*
|
|
958
|
+
* @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
|
|
959
|
+
* array of WebElements.
|
|
1072
960
|
* @private
|
|
1073
961
|
*/
|
|
1074
|
-
findElementsInternal_(locatorFn, context) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
962
|
+
async findElementsInternal_(locatorFn, context) {
|
|
963
|
+
const result = await locatorFn(context);
|
|
964
|
+
if (result instanceof WebElement) {
|
|
965
|
+
return [result];
|
|
966
|
+
}
|
|
1079
967
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
968
|
+
if (!Array.isArray(result)) {
|
|
969
|
+
return [];
|
|
970
|
+
}
|
|
1083
971
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
});
|
|
972
|
+
return result.filter(function(item) {
|
|
973
|
+
return item instanceof WebElement;
|
|
1087
974
|
});
|
|
1088
975
|
}
|
|
1089
976
|
|
|
1090
977
|
/** @override */
|
|
1091
978
|
takeScreenshot() {
|
|
1092
|
-
return this.
|
|
1093
|
-
'WebDriver.takeScreenshot()');
|
|
979
|
+
return this.execute(new command.Command(command.Name.SCREENSHOT));
|
|
1094
980
|
}
|
|
1095
981
|
|
|
1096
982
|
/** @override */
|
|
@@ -1131,49 +1017,46 @@ class Navigation {
|
|
|
1131
1017
|
}
|
|
1132
1018
|
|
|
1133
1019
|
/**
|
|
1134
|
-
*
|
|
1020
|
+
* Navigates to a new URL.
|
|
1021
|
+
*
|
|
1135
1022
|
* @param {string} url The URL to navigate to.
|
|
1136
|
-
* @return {!
|
|
1137
|
-
*
|
|
1023
|
+
* @return {!Promise<void>} A promise that will be resolved when the URL
|
|
1024
|
+
* has been loaded.
|
|
1138
1025
|
*/
|
|
1139
1026
|
to(url) {
|
|
1140
|
-
return this.driver_.
|
|
1027
|
+
return this.driver_.execute(
|
|
1141
1028
|
new command.Command(command.Name.GET).
|
|
1142
|
-
setParameter('url', url)
|
|
1143
|
-
'WebDriver.navigate().to(' + url + ')');
|
|
1029
|
+
setParameter('url', url));
|
|
1144
1030
|
}
|
|
1145
1031
|
|
|
1146
1032
|
/**
|
|
1147
|
-
*
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
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.
|
|
1150
1037
|
*/
|
|
1151
1038
|
back() {
|
|
1152
|
-
return this.driver_.
|
|
1153
|
-
new command.Command(command.Name.GO_BACK),
|
|
1154
|
-
'WebDriver.navigate().back()');
|
|
1039
|
+
return this.driver_.execute(new command.Command(command.Name.GO_BACK));
|
|
1155
1040
|
}
|
|
1156
1041
|
|
|
1157
1042
|
/**
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
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.
|
|
1161
1047
|
*/
|
|
1162
1048
|
forward() {
|
|
1163
|
-
return this.driver_.
|
|
1164
|
-
new command.Command(command.Name.GO_FORWARD),
|
|
1165
|
-
'WebDriver.navigate().forward()');
|
|
1049
|
+
return this.driver_.execute(new command.Command(command.Name.GO_FORWARD));
|
|
1166
1050
|
}
|
|
1167
1051
|
|
|
1168
1052
|
/**
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1053
|
+
* Refreshes the current page.
|
|
1054
|
+
*
|
|
1055
|
+
* @return {!Promise<void>} A promise that will be resolved when the
|
|
1056
|
+
* navigation event has completed.
|
|
1172
1057
|
*/
|
|
1173
1058
|
refresh() {
|
|
1174
|
-
return this.driver_.
|
|
1175
|
-
new command.Command(command.Name.REFRESH),
|
|
1176
|
-
'WebDriver.navigate().refresh()');
|
|
1059
|
+
return this.driver_.execute(new command.Command(command.Name.REFRESH));
|
|
1177
1060
|
}
|
|
1178
1061
|
}
|
|
1179
1062
|
|
|
@@ -1195,150 +1078,219 @@ class Options {
|
|
|
1195
1078
|
}
|
|
1196
1079
|
|
|
1197
1080
|
/**
|
|
1198
|
-
*
|
|
1081
|
+
* Adds a cookie.
|
|
1199
1082
|
*
|
|
1200
1083
|
* __Sample Usage:__
|
|
1201
1084
|
*
|
|
1202
1085
|
* // Set a basic cookie.
|
|
1203
|
-
* driver.
|
|
1086
|
+
* driver.manage().addCookie({name: 'foo', value: 'bar'});
|
|
1204
1087
|
*
|
|
1205
1088
|
* // Set a cookie that expires in 10 minutes.
|
|
1206
1089
|
* let expiry = new Date(Date.now() + (10 * 60 * 1000));
|
|
1207
|
-
* driver.
|
|
1090
|
+
* driver.manage().addCookie({name: 'foo', value: 'bar', expiry});
|
|
1208
1091
|
*
|
|
1209
1092
|
* // The cookie expiration may also be specified in seconds since epoch.
|
|
1210
|
-
* driver.
|
|
1093
|
+
* driver.manage().addCookie({
|
|
1211
1094
|
* name: 'foo',
|
|
1212
1095
|
* value: 'bar',
|
|
1213
1096
|
* expiry: Math.floor(Date.now() / 1000)
|
|
1214
1097
|
* });
|
|
1215
1098
|
*
|
|
1216
1099
|
* @param {!Options.Cookie} spec Defines the cookie to add.
|
|
1217
|
-
* @return {!
|
|
1100
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1218
1101
|
* when the cookie has been added to the page.
|
|
1219
1102
|
* @throws {error.InvalidArgumentError} if any of the cookie parameters are
|
|
1220
1103
|
* invalid.
|
|
1221
1104
|
* @throws {TypeError} if `spec` is not a cookie object.
|
|
1222
1105
|
*/
|
|
1223
|
-
addCookie(
|
|
1224
|
-
if (!spec || typeof spec !== 'object') {
|
|
1225
|
-
throw TypeError('addCookie called with non-cookie parameter');
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1106
|
+
addCookie({name, value, path, domain, secure, httpOnly, expiry}) {
|
|
1228
1107
|
// We do not allow '=' or ';' in the name.
|
|
1229
|
-
let name = spec.name;
|
|
1230
1108
|
if (/[;=]/.test(name)) {
|
|
1231
1109
|
throw new error.InvalidArgumentError(
|
|
1232
1110
|
'Invalid cookie name "' + name + '"');
|
|
1233
1111
|
}
|
|
1234
1112
|
|
|
1235
1113
|
// We do not allow ';' in value.
|
|
1236
|
-
let value = spec.value;
|
|
1237
1114
|
if (/;/.test(value)) {
|
|
1238
1115
|
throw new error.InvalidArgumentError(
|
|
1239
1116
|
'Invalid cookie value "' + value + '"');
|
|
1240
1117
|
}
|
|
1241
1118
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
let expiry;
|
|
1248
|
-
if (typeof spec.expiry === 'number') {
|
|
1249
|
-
expiry = Math.floor(spec.expiry);
|
|
1250
|
-
cookieString += ';expires=' + new Date(spec.expiry * 1000).toUTCString();
|
|
1251
|
-
} else if (spec.expiry instanceof Date) {
|
|
1252
|
-
let date = /** @type {!Date} */(spec.expiry);
|
|
1119
|
+
if (typeof expiry === 'number') {
|
|
1120
|
+
expiry = Math.floor(expiry);
|
|
1121
|
+
} else if (expiry instanceof Date) {
|
|
1122
|
+
let date = /** @type {!Date} */(expiry);
|
|
1253
1123
|
expiry = Math.floor(date.getTime() / 1000);
|
|
1254
|
-
cookieString += ';expires=' + date.toUTCString();
|
|
1255
1124
|
}
|
|
1256
1125
|
|
|
1257
|
-
return this.driver_.
|
|
1126
|
+
return this.driver_.execute(
|
|
1258
1127
|
new command.Command(command.Name.ADD_COOKIE).
|
|
1259
1128
|
setParameter('cookie', {
|
|
1260
1129
|
'name': name,
|
|
1261
1130
|
'value': value,
|
|
1262
|
-
'path':
|
|
1263
|
-
'domain':
|
|
1264
|
-
'secure': !!
|
|
1131
|
+
'path': path,
|
|
1132
|
+
'domain': domain,
|
|
1133
|
+
'secure': !!secure,
|
|
1134
|
+
'httpOnly': !!httpOnly,
|
|
1265
1135
|
'expiry': expiry
|
|
1266
|
-
})
|
|
1267
|
-
'WebDriver.manage().addCookie(' + cookieString + ')');
|
|
1136
|
+
}));
|
|
1268
1137
|
}
|
|
1269
1138
|
|
|
1270
1139
|
/**
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1140
|
+
* Deletes all cookies visible to the current page.
|
|
1141
|
+
*
|
|
1142
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1273
1143
|
* when all cookies have been deleted.
|
|
1274
1144
|
*/
|
|
1275
1145
|
deleteAllCookies() {
|
|
1276
|
-
return this.driver_.
|
|
1277
|
-
new command.Command(command.Name.DELETE_ALL_COOKIES)
|
|
1278
|
-
'WebDriver.manage().deleteAllCookies()');
|
|
1146
|
+
return this.driver_.execute(
|
|
1147
|
+
new command.Command(command.Name.DELETE_ALL_COOKIES));
|
|
1279
1148
|
}
|
|
1280
1149
|
|
|
1281
1150
|
/**
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
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
|
+
*
|
|
1285
1154
|
* @param {string} name The name of the cookie to delete.
|
|
1286
|
-
* @return {!
|
|
1155
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1287
1156
|
* when the cookie has been deleted.
|
|
1288
1157
|
*/
|
|
1289
1158
|
deleteCookie(name) {
|
|
1290
|
-
return this.driver_.
|
|
1159
|
+
return this.driver_.execute(
|
|
1291
1160
|
new command.Command(command.Name.DELETE_COOKIE).
|
|
1292
|
-
setParameter('name', name)
|
|
1293
|
-
'WebDriver.manage().deleteCookie(' + name + ')');
|
|
1161
|
+
setParameter('name', name));
|
|
1294
1162
|
}
|
|
1295
1163
|
|
|
1296
1164
|
/**
|
|
1297
|
-
*
|
|
1298
|
-
*
|
|
1299
|
-
*
|
|
1300
|
-
* @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
|
|
1301
1169
|
* resolved with the cookies visible to the current browsing context.
|
|
1302
1170
|
*/
|
|
1303
1171
|
getCookies() {
|
|
1304
|
-
return this.driver_.
|
|
1305
|
-
new command.Command(command.Name.GET_ALL_COOKIES)
|
|
1306
|
-
'WebDriver.manage().getCookies()');
|
|
1172
|
+
return this.driver_.execute(
|
|
1173
|
+
new command.Command(command.Name.GET_ALL_COOKIES));
|
|
1307
1174
|
}
|
|
1308
1175
|
|
|
1309
1176
|
/**
|
|
1310
|
-
*
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
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.
|
|
1313
1180
|
*
|
|
1314
1181
|
* @param {string} name The name of the cookie to retrieve.
|
|
1315
|
-
* @return {!
|
|
1182
|
+
* @return {!Promise<?Options.Cookie>} A promise that will be resolved
|
|
1316
1183
|
* with the named cookie, or `null` if there is no such cookie.
|
|
1317
1184
|
*/
|
|
1318
|
-
getCookie(name) {
|
|
1319
|
-
|
|
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();
|
|
1320
1199
|
for (let cookie of cookies) {
|
|
1321
1200
|
if (cookie && cookie['name'] === name) {
|
|
1322
1201
|
return cookie;
|
|
1323
1202
|
}
|
|
1324
1203
|
}
|
|
1325
1204
|
return null;
|
|
1326
|
-
}
|
|
1205
|
+
}
|
|
1327
1206
|
}
|
|
1328
1207
|
|
|
1329
1208
|
/**
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
1209
|
+
* Fetches the timeouts currently configured for the current session.
|
|
1210
|
+
*
|
|
1211
|
+
* @return {!Promise<{script: number,
|
|
1212
|
+
* pageLoad: number,
|
|
1213
|
+
* implicit: number}>} A promise that will be
|
|
1214
|
+
* resolved with the timeouts currently configured for the current
|
|
1215
|
+
* session.
|
|
1216
|
+
* @see #setTimeouts()
|
|
1332
1217
|
*/
|
|
1333
|
-
|
|
1334
|
-
return new
|
|
1218
|
+
getTimeouts() {
|
|
1219
|
+
return this.driver_.execute(new command.Command(command.Name.GET_TIMEOUT));
|
|
1335
1220
|
}
|
|
1336
1221
|
|
|
1337
1222
|
/**
|
|
1338
|
-
*
|
|
1223
|
+
* Sets the timeout durations associated with the current session.
|
|
1224
|
+
*
|
|
1225
|
+
* The following timeouts are supported (all timeouts are specified in
|
|
1226
|
+
* milliseconds):
|
|
1227
|
+
*
|
|
1228
|
+
* - `implicit` specifies the maximum amount of time to wait for an element
|
|
1229
|
+
* locator to succeed when {@linkplain WebDriver#findElement locating}
|
|
1230
|
+
* {@linkplain WebDriver#findElements elements} on the page.
|
|
1231
|
+
* Defaults to 0 milliseconds.
|
|
1232
|
+
*
|
|
1233
|
+
* - `pageLoad` specifies the maximum amount of time to wait for a page to
|
|
1234
|
+
* finishing loading. Defaults to 300000 milliseconds.
|
|
1235
|
+
*
|
|
1236
|
+
* - `script` specifies the maximum amount of time to wait for an
|
|
1237
|
+
* {@linkplain WebDriver#executeScript evaluated script} to run. If set to
|
|
1238
|
+
* `null`, the script timeout will be indefinite.
|
|
1239
|
+
* Defaults to 30000 milliseconds.
|
|
1240
|
+
*
|
|
1241
|
+
* @param {{script: (number|null|undefined),
|
|
1242
|
+
* pageLoad: (number|null|undefined),
|
|
1243
|
+
* implicit: (number|null|undefined)}} conf
|
|
1244
|
+
* The desired timeout configuration.
|
|
1245
|
+
* @return {!Promise<void>} A promise that will be resolved when the timeouts
|
|
1246
|
+
* have been set.
|
|
1247
|
+
* @throws {!TypeError} if an invalid options object is provided.
|
|
1248
|
+
* @see #getTimeouts()
|
|
1249
|
+
* @see <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-set-timeouts>
|
|
1339
1250
|
*/
|
|
1340
|
-
|
|
1341
|
-
|
|
1251
|
+
setTimeouts({script, pageLoad, implicit} = {}) {
|
|
1252
|
+
let cmd = new command.Command(command.Name.SET_TIMEOUT);
|
|
1253
|
+
|
|
1254
|
+
let valid = false;
|
|
1255
|
+
function setParam(key, value) {
|
|
1256
|
+
if (value === null || typeof value === 'number') {
|
|
1257
|
+
valid = true;
|
|
1258
|
+
cmd.setParameter(key, value);
|
|
1259
|
+
} else if (typeof value !== 'undefined') {
|
|
1260
|
+
throw TypeError(
|
|
1261
|
+
'invalid timeouts configuration:'
|
|
1262
|
+
+ ` expected "${key}" to be a number, got ${typeof value}`);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
setParam('implicit', implicit);
|
|
1266
|
+
setParam('pageLoad', pageLoad);
|
|
1267
|
+
setParam('script', script);
|
|
1268
|
+
|
|
1269
|
+
if (valid) {
|
|
1270
|
+
return this.driver_.execute(cmd)
|
|
1271
|
+
.catch(() => {
|
|
1272
|
+
// Fallback to the legacy method.
|
|
1273
|
+
let cmds = [];
|
|
1274
|
+
if (typeof script === 'number') {
|
|
1275
|
+
cmds.push(legacyTimeout(this.driver_, 'script', script));
|
|
1276
|
+
}
|
|
1277
|
+
if (typeof implicit === 'number') {
|
|
1278
|
+
cmds.push(legacyTimeout(this.driver_, 'implicit', implicit));
|
|
1279
|
+
}
|
|
1280
|
+
if (typeof pageLoad === 'number') {
|
|
1281
|
+
cmds.push(legacyTimeout(this.driver_, 'page load', pageLoad));
|
|
1282
|
+
}
|
|
1283
|
+
return Promise.all(cmds);
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
throw TypeError('no timeouts specified');
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* @return {!Logs} The interface for managing driver logs.
|
|
1291
|
+
*/
|
|
1292
|
+
logs() {
|
|
1293
|
+
return new Logs(this.driver_);
|
|
1342
1294
|
}
|
|
1343
1295
|
|
|
1344
1296
|
/**
|
|
@@ -1350,6 +1302,21 @@ class Options {
|
|
|
1350
1302
|
}
|
|
1351
1303
|
|
|
1352
1304
|
|
|
1305
|
+
/**
|
|
1306
|
+
* @param {!WebDriver} driver
|
|
1307
|
+
* @param {string} type
|
|
1308
|
+
* @param {number} ms
|
|
1309
|
+
* @return {!Promise<void>}
|
|
1310
|
+
*/
|
|
1311
|
+
function legacyTimeout(driver, type, ms) {
|
|
1312
|
+
return driver.execute(
|
|
1313
|
+
new command.Command(command.Name.SET_TIMEOUT)
|
|
1314
|
+
.setParameter('type', type)
|
|
1315
|
+
.setParameter('ms', ms));
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
|
|
1353
1320
|
/**
|
|
1354
1321
|
* A record object describing a browser cookie.
|
|
1355
1322
|
*
|
|
@@ -1413,8 +1380,7 @@ Options.Cookie.prototype.httpOnly;
|
|
|
1413
1380
|
* When the cookie expires.
|
|
1414
1381
|
*
|
|
1415
1382
|
* When {@linkplain Options#addCookie() adding a cookie}, this may be specified
|
|
1416
|
-
* in _seconds_ since Unix epoch (January 1, 1970).
|
|
1417
|
-
* 20 years in the future if omitted.
|
|
1383
|
+
* as a {@link Date} object, or in _seconds_ since Unix epoch (January 1, 1970).
|
|
1418
1384
|
*
|
|
1419
1385
|
* The expiry is always returned in seconds since epoch when
|
|
1420
1386
|
* {@linkplain Options#getCookies() retrieving cookies} from the browser.
|
|
@@ -1424,88 +1390,6 @@ Options.Cookie.prototype.httpOnly;
|
|
|
1424
1390
|
Options.Cookie.prototype.expiry;
|
|
1425
1391
|
|
|
1426
1392
|
|
|
1427
|
-
/**
|
|
1428
|
-
* An interface for managing timeout behavior for WebDriver instances.
|
|
1429
|
-
*
|
|
1430
|
-
* This class should never be instantiated directly. Instead, obtain an instance
|
|
1431
|
-
* with
|
|
1432
|
-
*
|
|
1433
|
-
* webdriver.manage().timeouts()
|
|
1434
|
-
*
|
|
1435
|
-
* @see WebDriver#manage()
|
|
1436
|
-
* @see Options#timeouts()
|
|
1437
|
-
*/
|
|
1438
|
-
class Timeouts {
|
|
1439
|
-
/**
|
|
1440
|
-
* @param {!WebDriver} driver The parent driver.
|
|
1441
|
-
* @private
|
|
1442
|
-
*/
|
|
1443
|
-
constructor(driver) {
|
|
1444
|
-
/** @private {!WebDriver} */
|
|
1445
|
-
this.driver_ = driver;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
/**
|
|
1449
|
-
* Specifies the amount of time the driver should wait when searching for an
|
|
1450
|
-
* element if it is not immediately present.
|
|
1451
|
-
*
|
|
1452
|
-
* When searching for a single element, the driver should poll the page
|
|
1453
|
-
* until the element has been found, or this timeout expires before failing
|
|
1454
|
-
* with a {@link bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
|
|
1455
|
-
* for multiple elements, the driver should poll the page until at least one
|
|
1456
|
-
* element has been found or this timeout has expired.
|
|
1457
|
-
*
|
|
1458
|
-
* Setting the wait timeout to 0 (its default value), disables implicit
|
|
1459
|
-
* waiting.
|
|
1460
|
-
*
|
|
1461
|
-
* Increasing the implicit wait timeout should be used judiciously as it
|
|
1462
|
-
* will have an adverse effect on test run time, especially when used with
|
|
1463
|
-
* slower location strategies like XPath.
|
|
1464
|
-
*
|
|
1465
|
-
* @param {number} ms The amount of time to wait, in milliseconds.
|
|
1466
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
1467
|
-
* when the implicit wait timeout has been set.
|
|
1468
|
-
*/
|
|
1469
|
-
implicitlyWait(ms) {
|
|
1470
|
-
return this._scheduleCommand(ms, 'implicit', 'implicitlyWait');
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* Sets the amount of time to wait, in milliseconds, for an asynchronous
|
|
1475
|
-
* script to finish execution before returning an error. If the timeout is
|
|
1476
|
-
* less than or equal to 0, the script will be allowed to run indefinitely.
|
|
1477
|
-
*
|
|
1478
|
-
* @param {number} ms The amount of time to wait, in milliseconds.
|
|
1479
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
1480
|
-
* when the script timeout has been set.
|
|
1481
|
-
*/
|
|
1482
|
-
setScriptTimeout(ms) {
|
|
1483
|
-
return this._scheduleCommand(ms, 'script', 'setScriptTimeout');
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
/**
|
|
1487
|
-
* Sets the amount of time to wait for a page load to complete before
|
|
1488
|
-
* returning an error. If the timeout is negative, page loads may be
|
|
1489
|
-
* indefinite.
|
|
1490
|
-
*
|
|
1491
|
-
* @param {number} ms The amount of time to wait, in milliseconds.
|
|
1492
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
1493
|
-
* when the timeout has been set.
|
|
1494
|
-
*/
|
|
1495
|
-
pageLoadTimeout(ms) {
|
|
1496
|
-
return this._scheduleCommand(ms, 'page load', 'pageLoadTimeout');
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
_scheduleCommand(ms, timeoutIdentifier, timeoutName) {
|
|
1500
|
-
return this.driver_.schedule(
|
|
1501
|
-
new command.Command(command.Name.SET_TIMEOUT).
|
|
1502
|
-
setParameter('type', timeoutIdentifier).
|
|
1503
|
-
setParameter('ms', ms),
|
|
1504
|
-
`WebDriver.manage().timeouts().${timeoutName}(${ms})`);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
1393
|
/**
|
|
1510
1394
|
* An interface for managing the current window.
|
|
1511
1395
|
*
|
|
@@ -1528,76 +1412,114 @@ class Window {
|
|
|
1528
1412
|
}
|
|
1529
1413
|
|
|
1530
1414
|
/**
|
|
1531
|
-
* Retrieves the
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1536
|
-
*/
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
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
|
+
}
|
|
1542
1439
|
}
|
|
1543
1440
|
|
|
1544
1441
|
/**
|
|
1545
|
-
*
|
|
1546
|
-
*
|
|
1547
|
-
*
|
|
1548
|
-
*
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
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
|
+
}
|
|
1560
1481
|
}
|
|
1561
1482
|
|
|
1562
1483
|
/**
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
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.
|
|
1567
1490
|
*/
|
|
1568
|
-
|
|
1569
|
-
return this.driver_.
|
|
1570
|
-
new command.Command(command.Name.
|
|
1571
|
-
setParameter('windowHandle', 'current')
|
|
1572
|
-
'WebDriver.manage().window().getSize()');
|
|
1491
|
+
maximize() {
|
|
1492
|
+
return this.driver_.execute(
|
|
1493
|
+
new command.Command(command.Name.MAXIMIZE_WINDOW).
|
|
1494
|
+
setParameter('windowHandle', 'current'));
|
|
1573
1495
|
}
|
|
1574
1496
|
|
|
1575
1497
|
/**
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
1580
|
-
*
|
|
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.
|
|
1581
1504
|
*/
|
|
1582
|
-
|
|
1583
|
-
return this.driver_.
|
|
1584
|
-
new command.Command(command.Name.
|
|
1585
|
-
setParameter('windowHandle', 'current').
|
|
1586
|
-
setParameter('width', width).
|
|
1587
|
-
setParameter('height', height),
|
|
1588
|
-
'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
|
|
1505
|
+
minimize() {
|
|
1506
|
+
return this.driver_.execute(
|
|
1507
|
+
new command.Command(command.Name.MINIMIZE_WINDOW));
|
|
1589
1508
|
}
|
|
1590
1509
|
|
|
1591
1510
|
/**
|
|
1592
|
-
*
|
|
1593
|
-
*
|
|
1594
|
-
*
|
|
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>
|
|
1595
1519
|
*/
|
|
1596
|
-
|
|
1597
|
-
return this.driver_.
|
|
1598
|
-
new command.Command(command.Name.
|
|
1599
|
-
setParameter('windowHandle', 'current'),
|
|
1600
|
-
'WebDriver.manage().window().maximize()');
|
|
1520
|
+
fullscreen() {
|
|
1521
|
+
return this.driver_.execute(
|
|
1522
|
+
new command.Command(command.Name.FULLSCREEN_WINDOW));
|
|
1601
1523
|
}
|
|
1602
1524
|
}
|
|
1603
1525
|
|
|
@@ -1632,15 +1554,14 @@ class Logs {
|
|
|
1632
1554
|
* entries since the last call, or from the start of the session.
|
|
1633
1555
|
*
|
|
1634
1556
|
* @param {!logging.Type} type The desired log type.
|
|
1635
|
-
* @return {!
|
|
1557
|
+
* @return {!Promise<!Array.<!logging.Entry>>} A
|
|
1636
1558
|
* promise that will resolve to a list of log entries for the specified
|
|
1637
1559
|
* type.
|
|
1638
1560
|
*/
|
|
1639
1561
|
get(type) {
|
|
1640
1562
|
let cmd = new command.Command(command.Name.GET_LOG).
|
|
1641
1563
|
setParameter('type', type);
|
|
1642
|
-
return this.driver_.
|
|
1643
|
-
cmd, 'WebDriver.manage().logs().get(' + type + ')').
|
|
1564
|
+
return this.driver_.execute(cmd).
|
|
1644
1565
|
then(function(entries) {
|
|
1645
1566
|
return entries.map(function(entry) {
|
|
1646
1567
|
if (!(entry instanceof logging.Entry)) {
|
|
@@ -1655,13 +1576,12 @@ class Logs {
|
|
|
1655
1576
|
|
|
1656
1577
|
/**
|
|
1657
1578
|
* Retrieves the log types available to this driver.
|
|
1658
|
-
* @return {!
|
|
1579
|
+
* @return {!Promise<!Array<!logging.Type>>} A
|
|
1659
1580
|
* promise that will resolve to a list of available log types.
|
|
1660
1581
|
*/
|
|
1661
1582
|
getAvailableLogTypes() {
|
|
1662
|
-
return this.driver_.
|
|
1663
|
-
new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES)
|
|
1664
|
-
'WebDriver.manage().logs().getAvailableLogTypes()');
|
|
1583
|
+
return this.driver_.execute(
|
|
1584
|
+
new command.Command(command.Name.GET_AVAILABLE_LOG_TYPES));
|
|
1665
1585
|
}
|
|
1666
1586
|
}
|
|
1667
1587
|
|
|
@@ -1687,35 +1607,34 @@ class TargetLocator {
|
|
|
1687
1607
|
}
|
|
1688
1608
|
|
|
1689
1609
|
/**
|
|
1690
|
-
*
|
|
1691
|
-
*
|
|
1610
|
+
* Locates the DOM element on the current page that corresponds to
|
|
1611
|
+
* `document.activeElement` or `document.body` if the active element is not
|
|
1692
1612
|
* available.
|
|
1613
|
+
*
|
|
1693
1614
|
* @return {!WebElementPromise} The active element.
|
|
1694
1615
|
*/
|
|
1695
1616
|
activeElement() {
|
|
1696
|
-
var id = this.driver_.
|
|
1697
|
-
new command.Command(command.Name.GET_ACTIVE_ELEMENT)
|
|
1698
|
-
'WebDriver.switchTo().activeElement()');
|
|
1617
|
+
var id = this.driver_.execute(
|
|
1618
|
+
new command.Command(command.Name.GET_ACTIVE_ELEMENT));
|
|
1699
1619
|
return new WebElementPromise(this.driver_, id);
|
|
1700
1620
|
}
|
|
1701
1621
|
|
|
1702
1622
|
/**
|
|
1703
|
-
*
|
|
1704
|
-
*
|
|
1705
|
-
*
|
|
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
|
|
1706
1627
|
* when the driver has changed focus to the default content.
|
|
1707
1628
|
*/
|
|
1708
1629
|
defaultContent() {
|
|
1709
|
-
return this.driver_.
|
|
1630
|
+
return this.driver_.execute(
|
|
1710
1631
|
new command.Command(command.Name.SWITCH_TO_FRAME).
|
|
1711
|
-
setParameter('id', null)
|
|
1712
|
-
'WebDriver.switchTo().defaultContent()');
|
|
1632
|
+
setParameter('id', null));
|
|
1713
1633
|
}
|
|
1714
1634
|
|
|
1715
1635
|
/**
|
|
1716
|
-
*
|
|
1717
|
-
*
|
|
1718
|
-
* 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:
|
|
1719
1638
|
*
|
|
1720
1639
|
* - A number that specifies a (zero-based) index into [window.frames](
|
|
1721
1640
|
* https://developer.mozilla.org/en-US/docs/Web/API/Window.frames).
|
|
@@ -1728,51 +1647,61 @@ class TargetLocator {
|
|
|
1728
1647
|
* rejected with a {@linkplain error.NoSuchFrameError}.
|
|
1729
1648
|
*
|
|
1730
1649
|
* @param {(number|WebElement|null)} id The frame locator.
|
|
1731
|
-
* @return {!
|
|
1650
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1732
1651
|
* when the driver has changed focus to the specified frame.
|
|
1733
1652
|
*/
|
|
1734
1653
|
frame(id) {
|
|
1735
|
-
return this.driver_.
|
|
1654
|
+
return this.driver_.execute(
|
|
1736
1655
|
new command.Command(command.Name.SWITCH_TO_FRAME).
|
|
1737
|
-
setParameter('id', id)
|
|
1738
|
-
|
|
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));
|
|
1739
1670
|
}
|
|
1740
1671
|
|
|
1741
1672
|
/**
|
|
1742
|
-
*
|
|
1743
|
-
*
|
|
1744
|
-
*
|
|
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}).
|
|
1745
1676
|
*
|
|
1746
1677
|
* If the specified window cannot be found, the returned promise will be
|
|
1747
1678
|
* rejected with a {@linkplain error.NoSuchWindowError}.
|
|
1748
1679
|
*
|
|
1749
1680
|
* @param {string} nameOrHandle The name or window handle of the window to
|
|
1750
1681
|
* switch focus to.
|
|
1751
|
-
* @return {!
|
|
1682
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
1752
1683
|
* when the driver has changed focus to the specified window.
|
|
1753
1684
|
*/
|
|
1754
1685
|
window(nameOrHandle) {
|
|
1755
|
-
return this.driver_.
|
|
1686
|
+
return this.driver_.execute(
|
|
1756
1687
|
new command.Command(command.Name.SWITCH_TO_WINDOW).
|
|
1757
1688
|
// "name" supports the legacy drivers. "handle" is the W3C
|
|
1758
1689
|
// compliant parameter.
|
|
1759
1690
|
setParameter('name', nameOrHandle).
|
|
1760
|
-
setParameter('handle', nameOrHandle)
|
|
1761
|
-
'WebDriver.switchTo().window(' + nameOrHandle + ')');
|
|
1691
|
+
setParameter('handle', nameOrHandle));
|
|
1762
1692
|
}
|
|
1763
1693
|
|
|
1764
1694
|
/**
|
|
1765
|
-
*
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
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
|
|
1768
1698
|
* {@linkplain error.NoSuchAlertError} if there are no open alerts.
|
|
1769
1699
|
*
|
|
1770
1700
|
* @return {!AlertPromise} The open alert.
|
|
1771
1701
|
*/
|
|
1772
1702
|
alert() {
|
|
1773
|
-
var text = this.driver_.
|
|
1774
|
-
new command.Command(command.Name.GET_ALERT_TEXT)
|
|
1775
|
-
'WebDriver.switchTo().alert()');
|
|
1703
|
+
var text = this.driver_.execute(
|
|
1704
|
+
new command.Command(command.Name.GET_ALERT_TEXT));
|
|
1776
1705
|
var driver = this.driver_;
|
|
1777
1706
|
return new AlertPromise(driver, text.then(function(text) {
|
|
1778
1707
|
return new Alert(driver, text);
|
|
@@ -1812,17 +1741,17 @@ class WebElement {
|
|
|
1812
1741
|
/** @private {!WebDriver} */
|
|
1813
1742
|
this.driver_ = driver;
|
|
1814
1743
|
|
|
1815
|
-
/** @private {!
|
|
1816
|
-
this.id_ =
|
|
1744
|
+
/** @private {!Promise<string>} */
|
|
1745
|
+
this.id_ = Promise.resolve(id);
|
|
1817
1746
|
}
|
|
1818
1747
|
|
|
1819
1748
|
/**
|
|
1820
1749
|
* @param {string} id The raw ID.
|
|
1821
|
-
* @param {boolean=}
|
|
1750
|
+
* @param {boolean=} noLegacy Whether to exclude the legacy element key.
|
|
1822
1751
|
* @return {!Object} The element ID for use with WebDriver's wire protocol.
|
|
1823
1752
|
*/
|
|
1824
|
-
static buildId(id,
|
|
1825
|
-
return
|
|
1753
|
+
static buildId(id, noLegacy = false) {
|
|
1754
|
+
return noLegacy
|
|
1826
1755
|
? {[ELEMENT_ID_KEY]: id}
|
|
1827
1756
|
: {[ELEMENT_ID_KEY]: id, [LEGACY_ELEMENT_ID_KEY]: id};
|
|
1828
1757
|
}
|
|
@@ -1860,27 +1789,14 @@ class WebElement {
|
|
|
1860
1789
|
*
|
|
1861
1790
|
* @param {!WebElement} a A WebElement.
|
|
1862
1791
|
* @param {!WebElement} b A WebElement.
|
|
1863
|
-
* @return {!
|
|
1792
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
1864
1793
|
* resolved to whether the two WebElements are equal.
|
|
1865
1794
|
*/
|
|
1866
|
-
static equals(a, b) {
|
|
1795
|
+
static async equals(a, b) {
|
|
1867
1796
|
if (a === b) {
|
|
1868
|
-
return
|
|
1797
|
+
return true;
|
|
1869
1798
|
}
|
|
1870
|
-
|
|
1871
|
-
return promise.all(ids).then(function(ids) {
|
|
1872
|
-
// If the two element's have the same ID, they should be considered
|
|
1873
|
-
// equal. Otherwise, they may still be equivalent, but we'll need to
|
|
1874
|
-
// ask the server to check for us.
|
|
1875
|
-
if (ids[0] === ids[1]) {
|
|
1876
|
-
return true;
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
let cmd = new command.Command(command.Name.ELEMENT_EQUALS);
|
|
1880
|
-
cmd.setParameter('id', ids[0]);
|
|
1881
|
-
cmd.setParameter('other', ids[1]);
|
|
1882
|
-
return a.driver_.schedule(cmd, 'WebElement.equals()');
|
|
1883
|
-
});
|
|
1799
|
+
return a.driver_.executeScript('arguments[0] === arguments[1]', a, b);
|
|
1884
1800
|
}
|
|
1885
1801
|
|
|
1886
1802
|
/** @return {!WebDriver} The parent driver for this instance. */
|
|
@@ -1889,7 +1805,7 @@ class WebElement {
|
|
|
1889
1805
|
}
|
|
1890
1806
|
|
|
1891
1807
|
/**
|
|
1892
|
-
* @return {!
|
|
1808
|
+
* @return {!Promise<string>} A promise that resolves to
|
|
1893
1809
|
* the server-assigned opaque ID assigned to this element.
|
|
1894
1810
|
*/
|
|
1895
1811
|
getId() {
|
|
@@ -1909,16 +1825,14 @@ class WebElement {
|
|
|
1909
1825
|
* parameters under the "id" key.
|
|
1910
1826
|
*
|
|
1911
1827
|
* @param {!command.Command} command The command to schedule.
|
|
1912
|
-
* @
|
|
1913
|
-
* @return {!promise.Thenable<T>} A promise that will be resolved
|
|
1914
|
-
* with the command result.
|
|
1828
|
+
* @return {!Promise<T>} A promise that will be resolved with the result.
|
|
1915
1829
|
* @template T
|
|
1916
1830
|
* @see WebDriver#schedule
|
|
1917
1831
|
* @private
|
|
1918
1832
|
*/
|
|
1919
|
-
|
|
1833
|
+
execute_(command) {
|
|
1920
1834
|
command.setParameter('id', this);
|
|
1921
|
-
return this.driver_.
|
|
1835
|
+
return this.driver_.execute(command);
|
|
1922
1836
|
}
|
|
1923
1837
|
|
|
1924
1838
|
/**
|
|
@@ -1965,48 +1879,46 @@ class WebElement {
|
|
|
1965
1879
|
command.Name.FIND_CHILD_ELEMENT).
|
|
1966
1880
|
setParameter('using', locator.using).
|
|
1967
1881
|
setParameter('value', locator.value);
|
|
1968
|
-
id = this.
|
|
1882
|
+
id = this.execute_(cmd);
|
|
1969
1883
|
}
|
|
1970
1884
|
return new WebElementPromise(this.driver_, id);
|
|
1971
1885
|
}
|
|
1972
1886
|
|
|
1973
1887
|
/**
|
|
1974
|
-
*
|
|
1975
|
-
*
|
|
1888
|
+
* Locates all of the descendants of this element that match the given search
|
|
1889
|
+
* criteria.
|
|
1976
1890
|
*
|
|
1977
1891
|
* @param {!(by.By|Function)} locator The locator strategy to use when
|
|
1978
1892
|
* searching for the element.
|
|
1979
|
-
* @return {!
|
|
1980
|
-
*
|
|
1893
|
+
* @return {!Promise<!Array<!WebElement>>} A promise that will resolve to an
|
|
1894
|
+
* array of WebElements.
|
|
1981
1895
|
*/
|
|
1982
|
-
findElements(locator) {
|
|
1896
|
+
async findElements(locator) {
|
|
1983
1897
|
locator = by.checkedLocator(locator);
|
|
1984
1898
|
let id;
|
|
1985
1899
|
if (typeof locator === 'function') {
|
|
1986
1900
|
return this.driver_.findElementsInternal_(locator, this);
|
|
1987
1901
|
} else {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
setParameter('
|
|
1991
|
-
|
|
1992
|
-
return
|
|
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 : [];
|
|
1993
1907
|
}
|
|
1994
1908
|
}
|
|
1995
1909
|
|
|
1996
1910
|
/**
|
|
1997
|
-
*
|
|
1998
|
-
*
|
|
1999
|
-
*
|
|
1911
|
+
* Clicks on this element.
|
|
1912
|
+
*
|
|
1913
|
+
* @return {!Promise<void>} A promise that will be resolved when the click
|
|
1914
|
+
* command has completed.
|
|
2000
1915
|
*/
|
|
2001
1916
|
click() {
|
|
2002
|
-
return this.
|
|
2003
|
-
new command.Command(command.Name.CLICK_ELEMENT),
|
|
2004
|
-
'WebElement.click()');
|
|
1917
|
+
return this.execute_(new command.Command(command.Name.CLICK_ELEMENT));
|
|
2005
1918
|
}
|
|
2006
1919
|
|
|
2007
1920
|
/**
|
|
2008
|
-
*
|
|
2009
|
-
* this instance.
|
|
1921
|
+
* Types a key sequence on the DOM element represented by this instance.
|
|
2010
1922
|
*
|
|
2011
1923
|
* Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
|
|
2012
1924
|
* processed in the key sequence, that key state is toggled until one of the
|
|
@@ -2053,95 +1965,80 @@ class WebElement {
|
|
|
2053
1965
|
* punctuation keys will be synthesized according to a standard QWERTY en-us
|
|
2054
1966
|
* keyboard layout.
|
|
2055
1967
|
*
|
|
2056
|
-
* @param {...(number|string|!IThenable<(number|string)>)}
|
|
1968
|
+
* @param {...(number|string|!IThenable<(number|string)>)} args The
|
|
2057
1969
|
* sequence of keys to type. Number keys may be referenced numerically or
|
|
2058
1970
|
* by string (1 or '1'). All arguments will be joined into a single
|
|
2059
1971
|
* sequence.
|
|
2060
|
-
* @return {!
|
|
2061
|
-
*
|
|
2062
|
-
*/
|
|
2063
|
-
sendKeys(
|
|
2064
|
-
let keys =
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
throw TypeError(
|
|
2073
|
-
'each key must be a number of string; got ' + type);
|
|
2074
|
-
}
|
|
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
|
+
}
|
|
2075
1984
|
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
return ret;
|
|
2081
|
-
});
|
|
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
|
+
});
|
|
2082
1989
|
|
|
2083
1990
|
if (!this.driver_.fileDetector_) {
|
|
2084
|
-
return this.
|
|
2085
|
-
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
2086
|
-
setParameter('
|
|
2087
|
-
|
|
1991
|
+
return this.execute_(
|
|
1992
|
+
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT)
|
|
1993
|
+
.setParameter('text', keys.join(''))
|
|
1994
|
+
.setParameter('value', keys));
|
|
2088
1995
|
}
|
|
2089
1996
|
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
.handleFile(element.driver_, keys.join(''));
|
|
2098
|
-
}).then(function(keys) {
|
|
2099
|
-
return element.schedule_(
|
|
2100
|
-
new command.Command(command.Name.SEND_KEYS_TO_ELEMENT).
|
|
2101
|
-
setParameter('value', keys.split('')),
|
|
2102
|
-
'WebElement.sendKeys()');
|
|
2103
|
-
});
|
|
2104
|
-
}, '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('')));
|
|
2105
2004
|
}
|
|
2106
2005
|
|
|
2107
2006
|
/**
|
|
2108
|
-
*
|
|
2109
|
-
*
|
|
2110
|
-
*
|
|
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.
|
|
2111
2011
|
*/
|
|
2112
2012
|
getTagName() {
|
|
2113
|
-
return this.
|
|
2114
|
-
new command.Command(command.Name.GET_ELEMENT_TAG_NAME)
|
|
2115
|
-
'WebElement.getTagName()');
|
|
2013
|
+
return this.execute_(
|
|
2014
|
+
new command.Command(command.Name.GET_ELEMENT_TAG_NAME));
|
|
2116
2015
|
}
|
|
2117
2016
|
|
|
2118
2017
|
/**
|
|
2119
|
-
*
|
|
2120
|
-
*
|
|
2121
|
-
*
|
|
2122
|
-
*
|
|
2123
|
-
* 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)).
|
|
2124
2022
|
*
|
|
2125
2023
|
* _Warning:_ the value returned will be as the browser interprets it, so
|
|
2126
2024
|
* it may be tricky to form a proper assertion.
|
|
2127
2025
|
*
|
|
2128
2026
|
* @param {string} cssStyleProperty The name of the CSS style property to look
|
|
2129
2027
|
* up.
|
|
2130
|
-
* @return {!
|
|
2131
|
-
*
|
|
2028
|
+
* @return {!Promise<string>} A promise that will be resolved with the
|
|
2029
|
+
* requested CSS value.
|
|
2132
2030
|
*/
|
|
2133
2031
|
getCssValue(cssStyleProperty) {
|
|
2134
2032
|
var name = command.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
|
|
2135
|
-
return this.
|
|
2033
|
+
return this.execute_(
|
|
2136
2034
|
new command.Command(name).
|
|
2137
|
-
setParameter('propertyName', cssStyleProperty)
|
|
2138
|
-
'WebElement.getCssValue(' + cssStyleProperty + ')');
|
|
2035
|
+
setParameter('propertyName', cssStyleProperty));
|
|
2139
2036
|
}
|
|
2140
2037
|
|
|
2141
2038
|
/**
|
|
2142
|
-
*
|
|
2143
|
-
*
|
|
2144
|
-
*
|
|
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
|
|
2145
2042
|
* of the given attribute, unless that attribute is not present, in which case
|
|
2146
2043
|
* the value of the property with the same name is returned. If neither value
|
|
2147
2044
|
* is set, null is returned (for example, the "value" property of a textarea
|
|
@@ -2163,131 +2060,122 @@ class WebElement {
|
|
|
2163
2060
|
* - "readonly"
|
|
2164
2061
|
*
|
|
2165
2062
|
* @param {string} attributeName The name of the attribute to query.
|
|
2166
|
-
* @return {!
|
|
2063
|
+
* @return {!Promise<?string>} A promise that will be
|
|
2167
2064
|
* resolved with the attribute's value. The returned value will always be
|
|
2168
2065
|
* either a string or null.
|
|
2169
2066
|
*/
|
|
2170
2067
|
getAttribute(attributeName) {
|
|
2171
|
-
return this.
|
|
2068
|
+
return this.execute_(
|
|
2172
2069
|
new command.Command(command.Name.GET_ELEMENT_ATTRIBUTE).
|
|
2173
|
-
setParameter('name', attributeName)
|
|
2174
|
-
'WebElement.getAttribute(' + attributeName + ')');
|
|
2070
|
+
setParameter('name', attributeName));
|
|
2175
2071
|
}
|
|
2176
2072
|
|
|
2177
2073
|
/**
|
|
2178
2074
|
* Get the visible (i.e. not hidden by CSS) innerText of this element,
|
|
2179
2075
|
* including sub-elements, without any leading or trailing whitespace.
|
|
2180
2076
|
*
|
|
2181
|
-
* @return {!
|
|
2077
|
+
* @return {!Promise<string>} A promise that will be
|
|
2182
2078
|
* resolved with the element's visible text.
|
|
2183
2079
|
*/
|
|
2184
2080
|
getText() {
|
|
2185
|
-
return this.
|
|
2186
|
-
new command.Command(command.Name.GET_ELEMENT_TEXT),
|
|
2187
|
-
'WebElement.getText()');
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
/**
|
|
2191
|
-
* Schedules a command to compute the size of this element's bounding box, in
|
|
2192
|
-
* pixels.
|
|
2193
|
-
* @return {!promise.Thenable<{width: number, height: number}>} A
|
|
2194
|
-
* promise that will be resolved with the element's size as a
|
|
2195
|
-
* {@code {width:number, height:number}} object.
|
|
2196
|
-
*/
|
|
2197
|
-
getSize() {
|
|
2198
|
-
return this.schedule_(
|
|
2199
|
-
new command.Command(command.Name.GET_ELEMENT_SIZE),
|
|
2200
|
-
'WebElement.getSize()');
|
|
2081
|
+
return this.execute_(new command.Command(command.Name.GET_ELEMENT_TEXT));
|
|
2201
2082
|
}
|
|
2202
2083
|
|
|
2203
2084
|
/**
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
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.
|
|
2208
2090
|
*/
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
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
|
+
}
|
|
2213
2106
|
}
|
|
2214
2107
|
|
|
2215
2108
|
/**
|
|
2216
|
-
*
|
|
2217
|
-
*
|
|
2218
|
-
*
|
|
2109
|
+
* Tests whether this element is enabled, as dictated by the `disabled`
|
|
2110
|
+
* attribute.
|
|
2111
|
+
*
|
|
2112
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
2219
2113
|
* resolved with whether this element is currently enabled.
|
|
2220
2114
|
*/
|
|
2221
2115
|
isEnabled() {
|
|
2222
|
-
return this.
|
|
2223
|
-
new command.Command(command.Name.IS_ELEMENT_ENABLED),
|
|
2224
|
-
'WebElement.isEnabled()');
|
|
2116
|
+
return this.execute_(new command.Command(command.Name.IS_ELEMENT_ENABLED));
|
|
2225
2117
|
}
|
|
2226
2118
|
|
|
2227
2119
|
/**
|
|
2228
|
-
*
|
|
2229
|
-
*
|
|
2120
|
+
* Tests whether this element is selected.
|
|
2121
|
+
*
|
|
2122
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
2230
2123
|
* resolved with whether this element is currently selected.
|
|
2231
2124
|
*/
|
|
2232
2125
|
isSelected() {
|
|
2233
|
-
return this.
|
|
2234
|
-
new command.Command(command.Name.IS_ELEMENT_SELECTED)
|
|
2235
|
-
'WebElement.isSelected()');
|
|
2126
|
+
return this.execute_(
|
|
2127
|
+
new command.Command(command.Name.IS_ELEMENT_SELECTED));
|
|
2236
2128
|
}
|
|
2237
2129
|
|
|
2238
2130
|
/**
|
|
2239
|
-
*
|
|
2240
|
-
*
|
|
2241
|
-
*
|
|
2242
|
-
*
|
|
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
|
|
2243
2136
|
* when the form has been submitted.
|
|
2244
2137
|
*/
|
|
2245
2138
|
submit() {
|
|
2246
|
-
return this.
|
|
2247
|
-
new command.Command(command.Name.SUBMIT_ELEMENT),
|
|
2248
|
-
'WebElement.submit()');
|
|
2139
|
+
return this.execute_(new command.Command(command.Name.SUBMIT_ELEMENT));
|
|
2249
2140
|
}
|
|
2250
2141
|
|
|
2251
2142
|
/**
|
|
2252
|
-
*
|
|
2253
|
-
*
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
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
|
|
2256
2148
|
* when the element has been cleared.
|
|
2257
2149
|
*/
|
|
2258
2150
|
clear() {
|
|
2259
|
-
return this.
|
|
2260
|
-
new command.Command(command.Name.CLEAR_ELEMENT),
|
|
2261
|
-
'WebElement.clear()');
|
|
2151
|
+
return this.execute_(new command.Command(command.Name.CLEAR_ELEMENT));
|
|
2262
2152
|
}
|
|
2263
2153
|
|
|
2264
2154
|
/**
|
|
2265
|
-
*
|
|
2266
|
-
*
|
|
2155
|
+
* Test whether this element is currently displayed.
|
|
2156
|
+
*
|
|
2157
|
+
* @return {!Promise<boolean>} A promise that will be
|
|
2267
2158
|
* resolved with whether this element is currently visible on the page.
|
|
2268
2159
|
*/
|
|
2269
2160
|
isDisplayed() {
|
|
2270
|
-
return this.
|
|
2271
|
-
new command.Command(command.Name.IS_ELEMENT_DISPLAYED)
|
|
2272
|
-
'WebElement.isDisplayed()');
|
|
2161
|
+
return this.execute_(
|
|
2162
|
+
new command.Command(command.Name.IS_ELEMENT_DISPLAYED));
|
|
2273
2163
|
}
|
|
2274
2164
|
|
|
2275
2165
|
/**
|
|
2276
2166
|
* Take a screenshot of the visible region encompassed by this element's
|
|
2277
2167
|
* bounding rectangle.
|
|
2278
2168
|
*
|
|
2279
|
-
* @param {boolean=}
|
|
2169
|
+
* @param {boolean=} scroll Optional argument that indicates whether the
|
|
2280
2170
|
* element should be scrolled into view before taking a screenshot.
|
|
2281
2171
|
* Defaults to false.
|
|
2282
|
-
* @return {!
|
|
2172
|
+
* @return {!Promise<string>} A promise that will be
|
|
2283
2173
|
* resolved to the screenshot as a base-64 encoded PNG.
|
|
2284
2174
|
*/
|
|
2285
|
-
takeScreenshot(
|
|
2286
|
-
|
|
2287
|
-
return this.schedule_(
|
|
2175
|
+
takeScreenshot(scroll = false) {
|
|
2176
|
+
return this.execute_(
|
|
2288
2177
|
new command.Command(command.Name.TAKE_ELEMENT_SCREENSHOT)
|
|
2289
|
-
.setParameter('scroll', scroll)
|
|
2290
|
-
'WebElement.takeScreenshot(' + scroll + ')');
|
|
2178
|
+
.setParameter('scroll', scroll));
|
|
2291
2179
|
}
|
|
2292
2180
|
}
|
|
2293
2181
|
|
|
@@ -2304,31 +2192,19 @@ class WebElement {
|
|
|
2304
2192
|
* return el.click();
|
|
2305
2193
|
* });
|
|
2306
2194
|
*
|
|
2307
|
-
* @implements {
|
|
2195
|
+
* @implements {IThenable<!WebElement>}
|
|
2308
2196
|
* @final
|
|
2309
2197
|
*/
|
|
2310
2198
|
class WebElementPromise extends WebElement {
|
|
2311
2199
|
/**
|
|
2312
2200
|
* @param {!WebDriver} driver The parent WebDriver instance for this
|
|
2313
2201
|
* element.
|
|
2314
|
-
* @param {!
|
|
2202
|
+
* @param {!Promise<!WebElement>} el A promise
|
|
2315
2203
|
* that will resolve to the promised element.
|
|
2316
2204
|
*/
|
|
2317
2205
|
constructor(driver, el) {
|
|
2318
2206
|
super(driver, 'unused');
|
|
2319
2207
|
|
|
2320
|
-
/**
|
|
2321
|
-
* Cancel operation is only supported if the wrapped thenable is also
|
|
2322
|
-
* cancellable.
|
|
2323
|
-
* @param {(string|Error)=} opt_reason
|
|
2324
|
-
* @override
|
|
2325
|
-
*/
|
|
2326
|
-
this.cancel = function(opt_reason) {
|
|
2327
|
-
if (promise.CancellableThenable.isImplementation(el)) {
|
|
2328
|
-
/** @type {!promise.CancellableThenable} */(el).cancel(opt_reason);
|
|
2329
|
-
}
|
|
2330
|
-
};
|
|
2331
|
-
|
|
2332
2208
|
/** @override */
|
|
2333
2209
|
this.then = el.then.bind(el);
|
|
2334
2210
|
|
|
@@ -2347,7 +2223,6 @@ class WebElementPromise extends WebElement {
|
|
|
2347
2223
|
};
|
|
2348
2224
|
}
|
|
2349
2225
|
}
|
|
2350
|
-
promise.CancellableThenable.addImplementation(WebElementPromise);
|
|
2351
2226
|
|
|
2352
2227
|
|
|
2353
2228
|
//////////////////////////////////////////////////////////////////////////////
|
|
@@ -2373,60 +2248,41 @@ class Alert {
|
|
|
2373
2248
|
/** @private {!WebDriver} */
|
|
2374
2249
|
this.driver_ = driver;
|
|
2375
2250
|
|
|
2376
|
-
/** @private {!
|
|
2377
|
-
this.text_ =
|
|
2251
|
+
/** @private {!Promise<string>} */
|
|
2252
|
+
this.text_ = Promise.resolve(text);
|
|
2378
2253
|
}
|
|
2379
2254
|
|
|
2380
2255
|
/**
|
|
2381
2256
|
* Retrieves the message text displayed with this alert. For instance, if the
|
|
2382
2257
|
* alert were opened with alert("hello"), then this would return "hello".
|
|
2383
2258
|
*
|
|
2384
|
-
* @return {!
|
|
2259
|
+
* @return {!Promise<string>} A promise that will be
|
|
2385
2260
|
* resolved to the text displayed with this alert.
|
|
2386
2261
|
*/
|
|
2387
2262
|
getText() {
|
|
2388
2263
|
return this.text_;
|
|
2389
2264
|
}
|
|
2390
2265
|
|
|
2391
|
-
/**
|
|
2392
|
-
* Sets the username and password in an alert prompting for credentials (such
|
|
2393
|
-
* as a Basic HTTP Auth prompt). This method will implicitly
|
|
2394
|
-
* {@linkplain #accept() submit} the dialog.
|
|
2395
|
-
*
|
|
2396
|
-
* @param {string} username The username to send.
|
|
2397
|
-
* @param {string} password The password to send.
|
|
2398
|
-
* @return {!promise.Thenable<void>} A promise that will be resolved when this
|
|
2399
|
-
* command has completed.
|
|
2400
|
-
*/
|
|
2401
|
-
authenticateAs(username, password) {
|
|
2402
|
-
return this.driver_.schedule(
|
|
2403
|
-
new command.Command(command.Name.SET_ALERT_CREDENTIALS),
|
|
2404
|
-
'WebDriver.switchTo().alert()'
|
|
2405
|
-
+ `.authenticateAs("${username}", "${password}")`);
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
2266
|
/**
|
|
2409
2267
|
* Accepts this alert.
|
|
2410
2268
|
*
|
|
2411
|
-
* @return {!
|
|
2269
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2412
2270
|
* when this command has completed.
|
|
2413
2271
|
*/
|
|
2414
2272
|
accept() {
|
|
2415
|
-
return this.driver_.
|
|
2416
|
-
new command.Command(command.Name.ACCEPT_ALERT)
|
|
2417
|
-
'WebDriver.switchTo().alert().accept()');
|
|
2273
|
+
return this.driver_.execute(
|
|
2274
|
+
new command.Command(command.Name.ACCEPT_ALERT));
|
|
2418
2275
|
}
|
|
2419
2276
|
|
|
2420
2277
|
/**
|
|
2421
2278
|
* Dismisses this alert.
|
|
2422
2279
|
*
|
|
2423
|
-
* @return {!
|
|
2280
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2424
2281
|
* when this command has completed.
|
|
2425
2282
|
*/
|
|
2426
2283
|
dismiss() {
|
|
2427
|
-
return this.driver_.
|
|
2428
|
-
new command.Command(command.Name.DISMISS_ALERT)
|
|
2429
|
-
'WebDriver.switchTo().alert().dismiss()');
|
|
2284
|
+
return this.driver_.execute(
|
|
2285
|
+
new command.Command(command.Name.DISMISS_ALERT));
|
|
2430
2286
|
}
|
|
2431
2287
|
|
|
2432
2288
|
/**
|
|
@@ -2435,14 +2291,13 @@ class Alert {
|
|
|
2435
2291
|
* window.confirm).
|
|
2436
2292
|
*
|
|
2437
2293
|
* @param {string} text The text to set.
|
|
2438
|
-
* @return {!
|
|
2294
|
+
* @return {!Promise<void>} A promise that will be resolved
|
|
2439
2295
|
* when this command has completed.
|
|
2440
2296
|
*/
|
|
2441
2297
|
sendKeys(text) {
|
|
2442
|
-
return this.driver_.
|
|
2298
|
+
return this.driver_.execute(
|
|
2443
2299
|
new command.Command(command.Name.SET_ALERT_TEXT).
|
|
2444
|
-
setParameter('text', text)
|
|
2445
|
-
'WebDriver.switchTo().alert().sendKeys(' + text + ')');
|
|
2300
|
+
setParameter('text', text));
|
|
2446
2301
|
}
|
|
2447
2302
|
}
|
|
2448
2303
|
|
|
@@ -2458,31 +2313,19 @@ class Alert {
|
|
|
2458
2313
|
* return alert.dismiss();
|
|
2459
2314
|
* });
|
|
2460
2315
|
*
|
|
2461
|
-
* @implements {
|
|
2316
|
+
* @implements {IThenable<!Alert>}
|
|
2462
2317
|
* @final
|
|
2463
2318
|
*/
|
|
2464
2319
|
class AlertPromise extends Alert {
|
|
2465
2320
|
/**
|
|
2466
2321
|
* @param {!WebDriver} driver The driver controlling the browser this
|
|
2467
2322
|
* alert is attached to.
|
|
2468
|
-
* @param {!
|
|
2323
|
+
* @param {!Promise<!Alert>} alert A thenable
|
|
2469
2324
|
* that will be fulfilled with the promised alert.
|
|
2470
2325
|
*/
|
|
2471
2326
|
constructor(driver, alert) {
|
|
2472
2327
|
super(driver, 'unused');
|
|
2473
2328
|
|
|
2474
|
-
/**
|
|
2475
|
-
* Cancel operation is only supported if the wrapped thenable is also
|
|
2476
|
-
* cancellable.
|
|
2477
|
-
* @param {(string|Error)=} opt_reason
|
|
2478
|
-
* @override
|
|
2479
|
-
*/
|
|
2480
|
-
this.cancel = function(opt_reason) {
|
|
2481
|
-
if (promise.CancellableThenable.isImplementation(alert)) {
|
|
2482
|
-
/** @type {!promise.CancellableThenable} */(alert).cancel(opt_reason);
|
|
2483
|
-
}
|
|
2484
|
-
};
|
|
2485
|
-
|
|
2486
2329
|
/** @override */
|
|
2487
2330
|
this.then = alert.then.bind(alert);
|
|
2488
2331
|
|
|
@@ -2499,16 +2342,6 @@ class AlertPromise extends Alert {
|
|
|
2499
2342
|
});
|
|
2500
2343
|
};
|
|
2501
2344
|
|
|
2502
|
-
/**
|
|
2503
|
-
* Defers action until the alert has been located.
|
|
2504
|
-
* @override
|
|
2505
|
-
*/
|
|
2506
|
-
this.authenticateAs = function(username, password) {
|
|
2507
|
-
return alert.then(function(alert) {
|
|
2508
|
-
return alert.authenticateAs(username, password);
|
|
2509
|
-
});
|
|
2510
|
-
};
|
|
2511
|
-
|
|
2512
2345
|
/**
|
|
2513
2346
|
* Defers action until the alert has been located.
|
|
2514
2347
|
* @override
|
|
@@ -2540,25 +2373,23 @@ class AlertPromise extends Alert {
|
|
|
2540
2373
|
};
|
|
2541
2374
|
}
|
|
2542
2375
|
}
|
|
2543
|
-
promise.CancellableThenable.addImplementation(AlertPromise);
|
|
2544
2376
|
|
|
2545
2377
|
|
|
2546
2378
|
// PUBLIC API
|
|
2547
2379
|
|
|
2548
2380
|
|
|
2549
2381
|
module.exports = {
|
|
2550
|
-
Alert
|
|
2551
|
-
AlertPromise
|
|
2552
|
-
Condition
|
|
2553
|
-
Logs
|
|
2554
|
-
Navigation
|
|
2555
|
-
Options
|
|
2556
|
-
TargetLocator
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
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
|
|
2564
2395
|
};
|