Skip to content

Commit 4957024

Browse files
[js] Add wheel support to actions
1 parent 70e54e6 commit 4957024

4 files changed

Lines changed: 236 additions & 140 deletions

File tree

javascript/node/selenium-webdriver/lib/input.js

Lines changed: 174 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ Action.Type = {
200200
POINTER_UP: 'pointerUp',
201201
POINTER_MOVE: 'pointerMove',
202202
POINTER_CANCEL: 'pointerCancel',
203+
SCROLL: 'scroll',
203204
}
204205

205206
/**
@@ -233,6 +234,7 @@ Device.Type = {
233234
KEY: 'key',
234235
NONE: 'none',
235236
POINTER: 'pointer',
237+
WHEEL: 'wheel',
236238
}
237239

238240
/**
@@ -392,6 +394,34 @@ Pointer.Type = {
392394
TOUCH: 'touch',
393395
}
394396

397+
class Wheel extends Device {
398+
/**
399+
* @param {string} id the device ID.
400+
* @param {Pointer.Type} type the pointer type.
401+
*/
402+
constructor(id, type) {
403+
super(Device.Type.WHEEL, id)
404+
}
405+
406+
/**
407+
* Scrolls a page via the coordinates given
408+
* @param {number} x starting x coordinate
409+
* @param {number} y starting y coordinate
410+
* @param {number} deltaX Delta X to scroll to target
411+
* @param {number} deltaY Delta Y to scroll to target
412+
* @param {number} duration duration ratio be the ratio of time delta and duration
413+
* @returns {!Action} An action to scroll with this device.
414+
*/
415+
scroll(x, y, deltaX, deltaY, origin, duration) {
416+
return {
417+
type: Action.Type.SCROLL,
418+
duration: duration, x: x, y: y,
419+
deltaX: deltaX, deltaY: deltaY,
420+
origin: origin
421+
}
422+
}
423+
}
424+
395425
/**
396426
* User facing API for generating complex user gestures. This class should not
397427
* be instantiated directly. Instead, users should create new instances by
@@ -509,10 +539,14 @@ class Actions {
509539
/** @private @const */
510540
this.mouse_ = new Pointer('default mouse', Pointer.Type.MOUSE)
511541

542+
/** @private @const */
543+
this.wheel_ = new Wheel('default wheel')
544+
512545
/** @private @const {!Map<!Device, !Array<!Action>>} */
513546
this.sequences_ = new Map([
514547
[this.keyboard_, []],
515548
[this.mouse_, []],
549+
[this.wheel_, []],
516550
])
517551
}
518552

@@ -526,6 +560,11 @@ class Actions {
526560
return this.mouse_
527561
}
528562

563+
/** @return {!Wheel} the wheel device handle. */
564+
wheel() {
565+
return this.wheel_
566+
}
567+
529568
/**
530569
* @param {!Device} device
531570
* @return {!Array<!Action>}
@@ -736,125 +775,139 @@ class Actions {
736775
}
737776

738777
/**
739-
* Inserts an action for moving the mouse `x` and `y` pixels relative to the
740-
* specified `origin`. The `origin` may be defined as the mouse's
741-
* {@linkplain ./input.Origin.POINTER current position}, the
742-
* {@linkplain ./input.Origin.VIEWPORT viewport}, or the center of a specific
743-
* {@linkplain ./webdriver.WebElement WebElement}.
744-
*
745-
* You may adjust how long the remote end should take, in milliseconds, to
746-
* perform the move using the `duration` parameter (defaults to 100 ms).
747-
* The number of incremental move events generated over this duration is an
748-
* implementation detail for the remote end.
749-
*
750-
* @param {{
751-
* x: (number|undefined),
752-
* y: (number|undefined),
753-
* duration: (number|undefined),
754-
* origin: (!Origin|!./webdriver.WebElement|undefined),
755-
* }=} options The move options. Defaults to moving the mouse to the top-left
756-
* corner of the viewport over 100ms.
757-
* @return {!Actions} a self reference.
778+
* scrolls a page via the coordinates given
779+
* @param {number} x starting x coordinate
780+
* @param {number} y starting y coordinate
781+
* @param {number} deltax delta x to scroll to target
782+
* @param {number} deltay delta y to scroll to target
783+
* @param {number} duration duration ratio be the ratio of time delta and duration
784+
* @returns {!Action} An action to scroll with this device.
758785
*/
759-
move({ x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT } = {}) {
760-
return this.insert(
761-
this.mouse_,
762-
this.mouse_.move({ x, y, duration, origin })
763-
)
764-
}
786+
*/
787+
scroll(x, y, targetDeltaX, targetDeltaY, origin, duration) {
788+
return this.insert(this.wheel_, this.wheel_.scroll(x, y, targetDeltaX, targetDeltaY, origin, duration))
789+
}
765790

766-
/**
767-
* Short-hand for performing a simple left-click (down/up) with the mouse.
768-
*
769-
* @param {./webdriver.WebElement=} element If specified, the mouse will
770-
* first be moved to the center of the element before performing the
771-
* click.
772-
* @return {!Actions} a self reference.
773-
*/
774-
click(element) {
775-
if (element) {
776-
this.move({ origin: element })
777-
}
778-
return this.press().release()
779-
}
791+
/**
792+
* Inserts an action for moving the mouse `x` and `y` pixels relative to the
793+
* specified `origin`. The `origin` may be defined as the mouse's
794+
* {@linkplain ./input.Origin.POINTER current position}, the
795+
* {@linkplain ./input.Origin.VIEWPORT viewport}, or the center of a specific
796+
* {@linkplain ./webdriver.WebElement WebElement}.
797+
*
798+
* You may adjust how long the remote end should take, in milliseconds, to
799+
* perform the move using the `duration` parameter (defaults to 100 ms).
800+
* The number of incremental move events generated over this duration is an
801+
* implementation detail for the remote end.
802+
*
803+
* @param {{
804+
* x: (number|undefined),
805+
* y: (number|undefined),
806+
* duration: (number|undefined),
807+
* origin: (!Origin|!./webdriver.WebElement|undefined),
808+
* }=} options The move options. Defaults to moving the mouse to the top-left
809+
* corner of the viewport over 100ms.
810+
* @return {!Actions} a self reference.
811+
*/
812+
move({ x = 0, y = 0, duration = 100, origin = Origin.VIEWPORT } = {}) {
813+
return this.insert(
814+
this.mouse_,
815+
this.mouse_.move({ x, y, duration, origin })
816+
)
817+
}
780818

781-
/**
782-
* Short-hand for performing a simple right-click (down/up) with the mouse.
783-
*
784-
* @param {./webdriver.WebElement=} element If specified, the mouse will
785-
* first be moved to the center of the element before performing the
786-
* click.
787-
* @return {!Actions} a self reference.
788-
*/
789-
contextClick(element) {
790-
if (element) {
791-
this.move({ origin: element })
792-
}
793-
return this.press(Button.RIGHT).release(Button.RIGHT)
819+
/**
820+
* Short-hand for performing a simple left-click (down/up) with the mouse.
821+
*
822+
* @param {./webdriver.WebElement=} element If specified, the mouse will
823+
* first be moved to the center of the element before performing the
824+
* click.
825+
* @return {!Actions} a self reference.
826+
*/
827+
click(element) {
828+
if (element) {
829+
this.move({ origin: element })
794830
}
831+
return this.press().release()
832+
}
795833

796-
/**
797-
* Short-hand for performing a double left-click with the mouse.
798-
*
799-
* @param {./webdriver.WebElement=} element If specified, the mouse will
800-
* first be moved to the center of the element before performing the
801-
* click.
802-
* @return {!Actions} a self reference.
803-
*/
804-
doubleClick(element) {
805-
return this.click(element).press().release()
834+
/**
835+
* Short-hand for performing a simple right-click (down/up) with the mouse.
836+
*
837+
* @param {./webdriver.WebElement=} element If specified, the mouse will
838+
* first be moved to the center of the element before performing the
839+
* click.
840+
* @return {!Actions} a self reference.
841+
*/
842+
contextClick(element) {
843+
if (element) {
844+
this.move({ origin: element })
806845
}
846+
return this.press(Button.RIGHT).release(Button.RIGHT)
847+
}
807848

808-
/**
809-
* Configures a drag-and-drop action consisting of the following steps:
810-
*
811-
* 1. Move to the center of the `from` element (element to be dragged).
812-
* 2. Press the left mouse button.
813-
* 3. If the `to` target is a {@linkplain ./webdriver.WebElement WebElement},
814-
* move the mouse to its center. Otherwise, move the mouse by the
815-
* specified offset.
816-
* 4. Release the left mouse button.
817-
*
818-
* @param {!./webdriver.WebElement} from The element to press the left mouse
819-
* button on to start the drag.
820-
* @param {(!./webdriver.WebElement|{x: number, y: number})} to Either another
821-
* element to drag to (will drag to the center of the element), or an
822-
* object specifying the offset to drag by, in pixels.
823-
* @return {!Actions} a self reference.
824-
*/
825-
dragAndDrop(from, to) {
826-
// Do not require up top to avoid a cycle that breaks static analysis.
827-
const { WebElement } = require('./webdriver')
828-
if (
829-
!(to instanceof WebElement) &&
830-
(!to || typeof to.x !== 'number' || typeof to.y !== 'number')
831-
) {
832-
throw new InvalidArgumentError(
833-
'Invalid drag target; must specify a WebElement or {x, y} offset'
834-
)
835-
}
849+
/**
850+
* Short-hand for performing a double left-click with the mouse.
851+
*
852+
* @param {./webdriver.WebElement=} element If specified, the mouse will
853+
* first be moved to the center of the element before performing the
854+
* click.
855+
* @return {!Actions} a self reference.
856+
*/
857+
doubleClick(element) {
858+
return this.click(element).press().release()
859+
}
836860

837-
this.move({ origin: from }).press()
838-
if (to instanceof WebElement) {
839-
this.move({ origin: to })
840-
} else {
841-
this.move({ x: to.x, y: to.y, origin: Origin.POINTER })
842-
}
843-
return this.release()
861+
/**
862+
* Configures a drag-and-drop action consisting of the following steps:
863+
*
864+
* 1. Move to the center of the `from` element (element to be dragged).
865+
* 2. Press the left mouse button.
866+
* 3. If the `to` target is a {@linkplain ./webdriver.WebElement WebElement},
867+
* move the mouse to its center. Otherwise, move the mouse by the
868+
* specified offset.
869+
* 4. Release the left mouse button.
870+
*
871+
* @param {!./webdriver.WebElement} from The element to press the left mouse
872+
* button on to start the drag.
873+
* @param {(!./webdriver.WebElement|{x: number, y: number})} to Either another
874+
* element to drag to (will drag to the center of the element), or an
875+
* object specifying the offset to drag by, in pixels.
876+
* @return {!Actions} a self reference.
877+
*/
878+
dragAndDrop(from, to) {
879+
// Do not require up top to avoid a cycle that breaks static analysis.
880+
const { WebElement } = require('./webdriver')
881+
if (
882+
!(to instanceof WebElement) &&
883+
(!to || typeof to.x !== 'number' || typeof to.y !== 'number')
884+
) {
885+
throw new InvalidArgumentError(
886+
'Invalid drag target; must specify a WebElement or {x, y} offset'
887+
)
844888
}
845889

846-
/**
847-
* Releases all keys, pointers, and clears internal state.
848-
*
849-
* @return {!Promise<void>} a promise that will resolve when finished
850-
* clearing all action state.
851-
*/
852-
clear() {
853-
for (const s of this.sequences_.values()) {
854-
s.length = 0
855-
}
856-
return this.executor_.execute(new Command(Name.CLEAR_ACTIONS))
890+
this.move({ origin: from }).press()
891+
if (to instanceof WebElement) {
892+
this.move({ origin: to })
893+
} else {
894+
this.move({ x: to.x, y: to.y, origin: Origin.POINTER })
895+
}
896+
return this.release()
897+
}
898+
899+
/**
900+
* Releases all keys, pointers, and clears internal state.
901+
*
902+
* @return {!Promise<void>} a promise that will resolve when finished
903+
* clearing all action state.
904+
*/
905+
clear() {
906+
for (const s of this.sequences_.values()) {
907+
s.length = 0
857908
}
909+
return this.executor_.execute(new Command(Name.CLEAR_ACTIONS))
910+
}
858911

859912
/**
860913
* Performs the configured action sequence.
@@ -863,22 +916,22 @@ class Actions {
863916
* been completed.
864917
*/
865918
async perform() {
866-
const _actions = []
867-
this.sequences_.forEach((actions, device) => {
868-
if (!isIdle(actions)) {
869-
actions = actions.concat() // Defensive copy.
870-
_actions.push(Object.assign({ actions }, device.toJSON()))
871-
}
872-
})
873-
874-
if (_actions.length === 0) {
875-
return Promise.resolve()
919+
const _actions = []
920+
this.sequences_.forEach((actions, device) => {
921+
if (!isIdle(actions)) {
922+
actions = actions.concat() // Defensive copy.
923+
_actions.push(Object.assign({ actions }, device.toJSON()))
876924
}
925+
})
877926

878-
await this.executor_.execute(
879-
new Command(Name.ACTIONS).setParameter('actions', _actions)
880-
)
927+
if (_actions.length === 0) {
928+
return Promise.resolve()
881929
}
930+
931+
await this.executor_.execute(
932+
new Command(Name.ACTIONS).setParameter('actions', _actions)
933+
)
934+
}
882935
}
883936

884937
/**

javascript/node/selenium-webdriver/lib/test/fileserver.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const Pages = (function () {
8989
addPage('resultPage', 'resultPage.html')
9090
addPage('richTextPage', 'rich_text.html')
9191
addPage('printPage', 'printPage.html')
92+
addPage('scrollingPage', 'scrollingPage.html')
9293
addPage('selectableItemsPage', 'selectableItems.html')
9394
addPage('selectPage', 'selectPage.html')
9495
addPage('simpleTestPage', 'simpleTest.html')

0 commit comments

Comments
 (0)