@@ -2410,9 +2410,30 @@ class AlertPromise extends Alert {
24102410 *
24112411 * This input sequence can be produced using the ActionSequence API with
24122412 * the code below. Actions must be defined individually for each device and
2413- * explicitly kept in sync by the user. The list of actions for the mouse is
2414- * far longer than the keyboard. Rather than keeping track of the number of
2415- * steps for the keyboard to pause, there is a single call to
2413+ * explicitly kept in sync by the user.
2414+ *
2415+ * let actions = driver.actions();
2416+ * actions.keyboard().keyDown(Key.SHIFT);
2417+ * actions.mouse().click(element1).click(element2);
2418+ *
2419+ * // Insert no-op pauses so the keyboard does not act again until the
2420+ * // mouse has finished it's sequence. Note, we allow the mouse to move
2421+ * // to element1 during tick 1.
2422+ * action.keyboard()
2423+ * .pause() // mouse down
2424+ * .pause() // mouse up
2425+ * .pause() // move to element 2
2426+ * .pause() // mouse down
2427+ * .pause() // mouse up
2428+ * .keyUp(Key.SHIFT);
2429+ *
2430+ * actions.perform();
2431+ *
2432+ * Notice that `mouse().click(element1)` is a compound action of
2433+ * "move to element 1", "mouse down", "mouse up". This generates a sequence
2434+ * far longer than the keyboard, so we have to insert pauses so the keyboard
2435+ * is idle until the mouse has finished. Rather than trying to keep track of
2436+ * the number of steps to pause, we can use a single call to
24162437 * {@link #synchronize()}. This instructs the ActionSequence to ensure the
24172438 * input sequence for all devices are the same length - inserting explicit
24182439 * {@linkplain input.Sequence#pause pauses} as necessary.
@@ -2427,31 +2448,50 @@ class AlertPromise extends Alert {
24272448 * actions.keyboard().keyUp(Key.SHIFT);
24282449 * actions.perform();
24292450 *
2451+ *
2452+ * > __N.B.__ not all browsers support parallel action sequences that define
2453+ * > actions for multiple devices at a single tick. Our example above is one
2454+ * > such sequence: the very first tick moves the mouse while press the SHIFT
2455+ * > key. For maximum portability, users must ensure only one action is defined
2456+ * > at each tick.
2457+ *
2458+ * > __TIP:__ Action sequences involving a single device can be defined and
2459+ * > executed in a single, fluent statement by calling
2460+ * > {@linkplain input.Sequence#perform perform} on the specific device you wish
2461+ * > to execute:
2462+ * >
2463+ * > driver.actions().keyboard().sendKeys('hello').perform();
2464+ * >
2465+ *
24302466 * @final
24312467 * @see <https://www.w3.org/TR/webdriver/#actions>
24322468 */
24332469class ActionSequence {
24342470 /**
2435- * @param {!IWebDriver } driver The driver instance to execute the configured
2471+ * @param {!command.Executor } executor The object to execute the configured
24362472 * actions with.
24372473 */
2438- constructor ( driver ) {
2474+ constructor ( executor ) {
24392475 /** @private @const */
2440- this . driver_ = driver ;
2476+ this . executor_ = executor ;
24412477
24422478 /** @private @const */
24432479 this . keyboard_ =
2444- new input . KeySequence ( new input . Keyboard ( 'default keyboard' ) ) ;
2480+ new input . KeySequence (
2481+ new input . Keyboard ( 'default keyboard' ) ,
2482+ ( ) => this . perform ( this . keyboard_ ) ) ;
24452483
24462484 /** @private @const */
24472485 this . mouse_ =
24482486 new input . PointerSequence (
2449- new input . Pointer ( 'default mouse' , input . Pointer . Type . MOUSE ) ) ;
2487+ new input . Pointer ( 'default mouse' , input . Pointer . Type . MOUSE ) ,
2488+ ( ) => this . perform ( this . mouse_ ) ) ;
24502489
24512490 /** @private @const */
24522491 this . touch_ =
24532492 new input . PointerSequence (
2454- new input . Pointer ( 'default touch' , input . Pointer . Type . TOUCH ) ) ;
2493+ new input . Pointer ( 'default touch' , input . Pointer . Type . TOUCH ) ,
2494+ ( ) => this . perform ( this . touch_ ) ) ;
24552495 }
24562496
24572497 /**
@@ -2505,36 +2545,41 @@ class ActionSequence {
25052545 this . keyboard_ . clear ( ) ;
25062546 this . mouse_ . clear ( ) ;
25072547 this . touch_ . clear ( ) ;
2508- return this . driver_ . execute (
2548+ return this . executor_ . execute (
25092549 new command . Command ( command . Name . CLEAR_ACTIONS ) ) ;
25102550 }
25112551
25122552 /**
25132553 * Performs the configured action sequence.
25142554 *
2555+ * @param {input.Sequence= } device the specific device sequence to execute.
2556+ * If unspecified, this will default to executing the sequence for every
2557+ * defined device.
25152558 * @return {!Promise<void> } a promise that will resolve when all actions have
25162559 * been completed.
25172560 */
2518- async perform ( ) {
2519- let actions = [
2520- this . keyboard_ ,
2521- this . mouse_ ,
2522- this . touch_
2523- ] . filter ( sequence => ! sequence . isIdle ( ) ) ;
2561+ async perform ( device ) {
2562+ let devices =
2563+ device ? [ device ] : [ this . keyboard_ , this . mouse_ , this . touch_ ] ;
2564+
2565+ devices = devices . filter ( d => ! d . isIdle ( ) ) ;
2566+ if ( devices . length === 0 ) {
2567+ return Promise . resolve ( ) ;
2568+ }
25242569
25252570 try {
2526- await this . driver_ . execute (
2571+ await this . executor_ . execute (
25272572 new command . Command ( command . Name . ACTIONS )
2528- . setParameter ( 'actions' , actions ) ) ;
2573+ . setParameter ( 'actions' , devices ) ) ;
25292574 } catch ( err ) {
25302575 if ( ! ( err instanceof error . UnknownCommandError )
25312576 && ! ( err instanceof error . UnsupportedOperationError ) ) {
25322577 throw err ;
25332578 }
25342579
2535- const commands = await translateInputSequences ( actions ) ;
2580+ const commands = await translateInputSequences ( devices ) ;
25362581 for ( let cmd of commands ) {
2537- await this . driver_ . execute ( cmd ) ;
2582+ await this . executor_ . execute ( cmd ) ;
25382583 }
25392584 }
25402585 }
0 commit comments