selenium-webdriver 3.6.0 → 4.0.0-alpha.1

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