mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
fix: improve mouse actions (#10021)
This commit is contained in:
parent
285c7912fc
commit
34db39e447
@ -97,6 +97,8 @@ sidebar_label: API
|
|||||||
| [LaunchOptions](./puppeteer.launchoptions.md) | Generic launch options that can be passed when launching any browser. |
|
| [LaunchOptions](./puppeteer.launchoptions.md) | Generic launch options that can be passed when launching any browser. |
|
||||||
| [MediaFeature](./puppeteer.mediafeature.md) | |
|
| [MediaFeature](./puppeteer.mediafeature.md) | |
|
||||||
| [Metrics](./puppeteer.metrics.md) | |
|
| [Metrics](./puppeteer.metrics.md) | |
|
||||||
|
| [MouseClickOptions](./puppeteer.mouseclickoptions.md) | |
|
||||||
|
| [MouseMoveOptions](./puppeteer.mousemoveoptions.md) | |
|
||||||
| [MouseOptions](./puppeteer.mouseoptions.md) | |
|
| [MouseOptions](./puppeteer.mouseoptions.md) | |
|
||||||
| [MouseWheelOptions](./puppeteer.mousewheeloptions.md) | |
|
| [MouseWheelOptions](./puppeteer.mousewheeloptions.md) | |
|
||||||
| [NetworkConditions](./puppeteer.networkconditions.md) | |
|
| [NetworkConditions](./puppeteer.networkconditions.md) | |
|
||||||
@ -135,6 +137,7 @@ sidebar_label: API
|
|||||||
| [executablePath](./puppeteer.executablepath.md) | |
|
| [executablePath](./puppeteer.executablepath.md) | |
|
||||||
| [KnownDevices](./puppeteer.knowndevices.md) | A list of devices to be used with [Page.emulate()](./puppeteer.page.emulate.md). |
|
| [KnownDevices](./puppeteer.knowndevices.md) | A list of devices to be used with [Page.emulate()](./puppeteer.page.emulate.md). |
|
||||||
| [launch](./puppeteer.launch.md) | |
|
| [launch](./puppeteer.launch.md) | |
|
||||||
|
| [MouseButton](./puppeteer.mousebutton.md) | Enum of valid mouse buttons. |
|
||||||
| [networkConditions](./puppeteer.networkconditions.md) | |
|
| [networkConditions](./puppeteer.networkconditions.md) | |
|
||||||
| [PredefinedNetworkConditions](./puppeteer.predefinednetworkconditions.md) | A list of network conditions to be used with [Page.emulateNetworkConditions()](./puppeteer.page.emulatenetworkconditions.md). |
|
| [PredefinedNetworkConditions](./puppeteer.predefinednetworkconditions.md) | A list of network conditions to be used with [Page.emulateNetworkConditions()](./puppeteer.page.emulatenetworkconditions.md). |
|
||||||
| [puppeteer](./puppeteer.puppeteer.md) | |
|
| [puppeteer](./puppeteer.puppeteer.md) | |
|
||||||
|
@ -10,23 +10,17 @@ Shortcut for `mouse.move`, `mouse.down` and `mouse.up`.
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Mouse {
|
class Mouse {
|
||||||
click(
|
click(x: number, y: number, options?: MouseClickOptions): Promise<void>;
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
options?: MouseOptions & {
|
|
||||||
delay?: number;
|
|
||||||
}
|
|
||||||
): Promise<void>;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | --------------------------------------------------------------------- | ------------------------------------------------ |
|
| --------- | ----------------------------------------------------- | ------------------------------------------- |
|
||||||
| x | number | Horizontal position of the mouse. |
|
| x | number | Horizontal position of the mouse. |
|
||||||
| y | number | Vertical position of the mouse. |
|
| y | number | Vertical position of the mouse. |
|
||||||
| options | [MouseOptions](./puppeteer.mouseoptions.md) & { delay?: number; } | _(Optional)_ Optional <code>MouseOptions</code>. |
|
| options | [MouseClickOptions](./puppeteer.mouseclickoptions.md) | _(Optional)_ Options to configure behavior. |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ sidebar_label: Mouse.down
|
|||||||
|
|
||||||
# Mouse.down() method
|
# Mouse.down() method
|
||||||
|
|
||||||
Dispatches a `mousedown` event.
|
Presses the mouse.
|
||||||
|
|
||||||
#### Signature:
|
#### Signature:
|
||||||
|
|
||||||
@ -16,9 +16,9 @@ class Mouse {
|
|||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------------------------------------------- | ------------------------------------------------ |
|
| --------- | ------------------------------------------- | ------------------------------------------- |
|
||||||
| options | [MouseOptions](./puppeteer.mouseoptions.md) | _(Optional)_ Optional <code>MouseOptions</code>. |
|
| options | [MouseOptions](./puppeteer.mouseoptions.md) | _(Optional)_ Options to configure behavior. |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -80,12 +80,12 @@ await browser
|
|||||||
| Method | Modifiers | Description |
|
| Method | Modifiers | Description |
|
||||||
| ----------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------- |
|
| ----------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------- |
|
||||||
| [click(x, y, options)](./puppeteer.mouse.click.md) | | Shortcut for <code>mouse.move</code>, <code>mouse.down</code> and <code>mouse.up</code>. |
|
| [click(x, y, options)](./puppeteer.mouse.click.md) | | Shortcut for <code>mouse.move</code>, <code>mouse.down</code> and <code>mouse.up</code>. |
|
||||||
| [down(options)](./puppeteer.mouse.down.md) | | Dispatches a <code>mousedown</code> event. |
|
| [down(options)](./puppeteer.mouse.down.md) | | Presses the mouse. |
|
||||||
| [drag(start, target)](./puppeteer.mouse.drag.md) | | Dispatches a <code>drag</code> event. |
|
| [drag(start, target)](./puppeteer.mouse.drag.md) | | Dispatches a <code>drag</code> event. |
|
||||||
| [dragAndDrop(start, target, options)](./puppeteer.mouse.draganddrop.md) | | Performs a drag, dragenter, dragover, and drop in sequence. |
|
| [dragAndDrop(start, target, options)](./puppeteer.mouse.draganddrop.md) | | Performs a drag, dragenter, dragover, and drop in sequence. |
|
||||||
| [dragEnter(target, data)](./puppeteer.mouse.dragenter.md) | | Dispatches a <code>dragenter</code> event. |
|
| [dragEnter(target, data)](./puppeteer.mouse.dragenter.md) | | Dispatches a <code>dragenter</code> event. |
|
||||||
| [dragOver(target, data)](./puppeteer.mouse.dragover.md) | | Dispatches a <code>dragover</code> event. |
|
| [dragOver(target, data)](./puppeteer.mouse.dragover.md) | | Dispatches a <code>dragover</code> event. |
|
||||||
| [drop(target, data)](./puppeteer.mouse.drop.md) | | Performs a dragenter, dragover, and drop in sequence. |
|
| [drop(target, data)](./puppeteer.mouse.drop.md) | | Performs a dragenter, dragover, and drop in sequence. |
|
||||||
| [move(x, y, options)](./puppeteer.mouse.move.md) | | Dispatches a <code>mousemove</code> event. |
|
| [move(x, y, options)](./puppeteer.mouse.move.md) | | Moves the mouse to the given coordinate. |
|
||||||
| [up(options)](./puppeteer.mouse.up.md) | | Dispatches a <code>mouseup</code> event. |
|
| [up(options)](./puppeteer.mouse.up.md) | | Releases the mouse. |
|
||||||
| [wheel(options)](./puppeteer.mouse.wheel.md) | | Dispatches a <code>mousewheel</code> event. |
|
| [wheel(options)](./puppeteer.mouse.wheel.md) | | Dispatches a <code>mousewheel</code> event. |
|
||||||
|
@ -4,29 +4,23 @@ sidebar_label: Mouse.move
|
|||||||
|
|
||||||
# Mouse.move() method
|
# Mouse.move() method
|
||||||
|
|
||||||
Dispatches a `mousemove` event.
|
Moves the mouse to the given coordinate.
|
||||||
|
|
||||||
#### Signature:
|
#### Signature:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Mouse {
|
class Mouse {
|
||||||
move(
|
move(x: number, y: number, options?: MouseMoveOptions): Promise<void>;
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
options?: {
|
|
||||||
steps?: number;
|
|
||||||
}
|
|
||||||
): Promise<void>;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| --------- | --------------------------------------------------- | ------------------------------------------- |
|
||||||
| x | number | Horizontal position of the mouse. |
|
| x | number | Horizontal position of the mouse. |
|
||||||
| y | number | Vertical position of the mouse. |
|
| y | number | Vertical position of the mouse. |
|
||||||
| options | { steps?: number; } | _(Optional)_ Optional object. If specified, the <code>steps</code> property sends intermediate <code>mousemove</code> events when set to <code>1</code> (default). |
|
| options | [MouseMoveOptions](./puppeteer.mousemoveoptions.md) | _(Optional)_ Options to configure behavior. |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ sidebar_label: Mouse.up
|
|||||||
|
|
||||||
# Mouse.up() method
|
# Mouse.up() method
|
||||||
|
|
||||||
Dispatches a `mouseup` event.
|
Releases the mouse.
|
||||||
|
|
||||||
#### Signature:
|
#### Signature:
|
||||||
|
|
||||||
@ -16,9 +16,9 @@ class Mouse {
|
|||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------------------------------------------- | ------------------------------------------------ |
|
| --------- | ------------------------------------------- | ------------------------------------------- |
|
||||||
| options | [MouseOptions](./puppeteer.mouseoptions.md) | _(Optional)_ Optional <code>MouseOptions</code>. |
|
| options | [MouseOptions](./puppeteer.mouseoptions.md) | _(Optional)_ Options to configure behavior. |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -2,10 +2,18 @@
|
|||||||
sidebar_label: MouseButton
|
sidebar_label: MouseButton
|
||||||
---
|
---
|
||||||
|
|
||||||
# MouseButton type
|
# MouseButton variable
|
||||||
|
|
||||||
|
Enum of valid mouse buttons.
|
||||||
|
|
||||||
#### Signature:
|
#### Signature:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export type MouseButton = 'left' | 'right' | 'middle' | 'back' | 'forward';
|
MouseButton: Readonly<{
|
||||||
|
Left: 'left';
|
||||||
|
Right: 'right';
|
||||||
|
Middle: 'middle';
|
||||||
|
Back: 'back';
|
||||||
|
Forward: 'forward';
|
||||||
|
}>;
|
||||||
```
|
```
|
||||||
|
19
docs/api/puppeteer.mouseclickoptions.md
Normal file
19
docs/api/puppeteer.mouseclickoptions.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: MouseClickOptions
|
||||||
|
---
|
||||||
|
|
||||||
|
# MouseClickOptions interface
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface MouseClickOptions extends MouseOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
**Extends:** [MouseOptions](./puppeteer.mouseoptions.md)
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Modifiers | Type | Description | Default |
|
||||||
|
| -------- | --------------------- | ------ | -------------------------------------------------------------- | ------- |
|
||||||
|
| delay | <code>optional</code> | number | Time (in ms) to delay the mouse release after the mouse press. | |
|
17
docs/api/puppeteer.mousemoveoptions.md
Normal file
17
docs/api/puppeteer.mousemoveoptions.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: MouseMoveOptions
|
||||||
|
---
|
||||||
|
|
||||||
|
# MouseMoveOptions interface
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface MouseMoveOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Modifiers | Type | Description | Default |
|
||||||
|
| -------- | --------------------- | ------ | ------------------------------------------------------------------------------------------ | -------------- |
|
||||||
|
| steps | <code>optional</code> | number | Determines the number of movements to make from the current mouse position to the new one. | <code>1</code> |
|
@ -12,7 +12,7 @@ export interface MouseOptions
|
|||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Modifiers | Type | Description | Default |
|
| Property | Modifiers | Type | Description | Default |
|
||||||
| ---------- | --------------------- | ----------------------------------------- | ----------- | ------- |
|
| ---------- | --------------------- | ----------------------------------------- | ----------------------------------------- | ------------------- |
|
||||||
| button | <code>optional</code> | [MouseButton](./puppeteer.mousebutton.md) | | |
|
| button | <code>optional</code> | [MouseButton](./puppeteer.mousebutton.md) | Determines which button will be pressed. | <code>'left'</code> |
|
||||||
| clickCount | <code>optional</code> | number | | |
|
| clickCount | <code>optional</code> | number | Determines the click count for the mouse. | <code>1</code> |
|
||||||
|
@ -334,14 +334,29 @@ export class Keyboard {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export type MouseButton = 'left' | 'right' | 'middle' | 'back' | 'forward';
|
export interface MouseOptions {
|
||||||
|
/**
|
||||||
|
* Determines which button will be pressed.
|
||||||
|
*
|
||||||
|
* @defaultValue `'left'`
|
||||||
|
*/
|
||||||
|
button?: MouseButton;
|
||||||
|
/**
|
||||||
|
* Determines the click count for the mouse.
|
||||||
|
*
|
||||||
|
* @defaultValue `1`
|
||||||
|
*/
|
||||||
|
clickCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface MouseOptions {
|
export interface MouseClickOptions extends MouseOptions {
|
||||||
button?: MouseButton;
|
/**
|
||||||
clickCount?: number;
|
* Time (in ms) to delay the mouse release after the mouse press.
|
||||||
|
*/
|
||||||
|
delay?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -352,6 +367,69 @@ export interface MouseWheelOptions {
|
|||||||
deltaY?: number;
|
deltaY?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface MouseMoveOptions {
|
||||||
|
/**
|
||||||
|
* Determines the number of movements to make from the current mouse position
|
||||||
|
* to the new one.
|
||||||
|
*
|
||||||
|
* @defaultValue `1`
|
||||||
|
*/
|
||||||
|
steps?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of valid mouse buttons.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export const MouseButton = Object.freeze({
|
||||||
|
Left: 'left',
|
||||||
|
Right: 'right',
|
||||||
|
Middle: 'middle',
|
||||||
|
Back: 'back',
|
||||||
|
Forward: 'forward',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export type MouseButton = (typeof MouseButton)[keyof typeof MouseButton];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This must follow {@link Protocol.Input.DispatchMouseEventRequest.buttons}.
|
||||||
|
*/
|
||||||
|
const enum MouseButtonFlag {
|
||||||
|
None = 0,
|
||||||
|
Left = 1,
|
||||||
|
Right = 1 << 1,
|
||||||
|
Middle = 1 << 2,
|
||||||
|
Back = 1 << 3,
|
||||||
|
Forward = 1 << 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFlag = (button: MouseButton): MouseButtonFlag => {
|
||||||
|
switch (button) {
|
||||||
|
case MouseButton.Left:
|
||||||
|
return MouseButtonFlag.Left;
|
||||||
|
case MouseButton.Right:
|
||||||
|
return MouseButtonFlag.Right;
|
||||||
|
case MouseButton.Middle:
|
||||||
|
return MouseButtonFlag.Middle;
|
||||||
|
case MouseButton.Back:
|
||||||
|
return MouseButtonFlag.Back;
|
||||||
|
case MouseButton.Forward:
|
||||||
|
return MouseButtonFlag.Forward;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MouseState {
|
||||||
|
position: Point;
|
||||||
|
buttons: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Mouse class operates in main-frame CSS pixels
|
* The Mouse class operates in main-frame CSS pixels
|
||||||
* relative to the top-left corner of the viewport.
|
* relative to the top-left corner of the viewport.
|
||||||
@ -426,9 +504,6 @@ export interface MouseWheelOptions {
|
|||||||
export class Mouse {
|
export class Mouse {
|
||||||
#client: CDPSession;
|
#client: CDPSession;
|
||||||
#keyboard: Keyboard;
|
#keyboard: Keyboard;
|
||||||
#x = 0;
|
|
||||||
#y = 0;
|
|
||||||
#button: MouseButton | 'none' = 'none';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -438,88 +513,176 @@ export class Mouse {
|
|||||||
this.#keyboard = keyboard;
|
this.#keyboard = keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#_state: Readonly<MouseState> = {
|
||||||
|
position: {x: 0, y: 0},
|
||||||
|
buttons: MouseButtonFlag.None,
|
||||||
|
};
|
||||||
|
get #state(): MouseState {
|
||||||
|
return Object.assign({...this.#_state}, ...this.#transactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transactions can run in parallel, so we store each of thme in this array.
|
||||||
|
#transactions: Array<Partial<MouseState>> = [];
|
||||||
|
#createTransaction(): {
|
||||||
|
update: (updates: Partial<MouseState>) => void;
|
||||||
|
commit: () => void;
|
||||||
|
rollback: () => void;
|
||||||
|
} {
|
||||||
|
const transaction: Partial<MouseState> = {};
|
||||||
|
this.#transactions.push(transaction);
|
||||||
|
const popTransaction = () => {
|
||||||
|
this.#transactions.splice(this.#transactions.indexOf(transaction), 1);
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
update: (updates: Partial<MouseState>) => {
|
||||||
|
Object.assign(transaction, updates);
|
||||||
|
},
|
||||||
|
commit: () => {
|
||||||
|
this.#_state = {...this.#_state, ...transaction};
|
||||||
|
popTransaction();
|
||||||
|
},
|
||||||
|
rollback: popTransaction,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches a `mousemove` event.
|
* This is a shortcut for a typical update, commit/rollback lifecycle based on
|
||||||
|
* the error of the action.
|
||||||
|
*/
|
||||||
|
async #withTransaction(
|
||||||
|
action: (update: (updates: Partial<MouseState>) => void) => Promise<unknown>
|
||||||
|
): Promise<void> {
|
||||||
|
const {update, commit, rollback} = this.#createTransaction();
|
||||||
|
try {
|
||||||
|
await action(update);
|
||||||
|
commit();
|
||||||
|
} catch (error) {
|
||||||
|
rollback();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the mouse to the given coordinate.
|
||||||
|
*
|
||||||
* @param x - Horizontal position of the mouse.
|
* @param x - Horizontal position of the mouse.
|
||||||
* @param y - Vertical position of the mouse.
|
* @param y - Vertical position of the mouse.
|
||||||
* @param options - Optional object. If specified, the `steps` property
|
* @param options - Options to configure behavior.
|
||||||
* sends intermediate `mousemove` events when set to `1` (default).
|
|
||||||
*/
|
*/
|
||||||
async move(
|
async move(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
options: {steps?: number} = {}
|
options: MouseMoveOptions = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {steps = 1} = options;
|
const {steps = 1} = options;
|
||||||
const fromX = this.#x,
|
const from = this.#state.position;
|
||||||
fromY = this.#y;
|
const to = {x, y};
|
||||||
this.#x = x;
|
|
||||||
this.#y = y;
|
|
||||||
for (let i = 1; i <= steps; i++) {
|
for (let i = 1; i <= steps; i++) {
|
||||||
await this.#client.send('Input.dispatchMouseEvent', {
|
await this.#withTransaction(updateState => {
|
||||||
type: 'mouseMoved',
|
updateState({
|
||||||
button: this.#button,
|
position: {
|
||||||
x: fromX + (this.#x - fromX) * (i / steps),
|
x: from.x + (to.x - from.x) * (i / steps),
|
||||||
y: fromY + (this.#y - fromY) * (i / steps),
|
y: from.y + (to.y - from.y) * (i / steps),
|
||||||
modifiers: this.#keyboard._modifiers,
|
},
|
||||||
|
});
|
||||||
|
const {buttons, position} = this.#state;
|
||||||
|
return this.#client.send('Input.dispatchMouseEvent', {
|
||||||
|
type: 'mouseMoved',
|
||||||
|
modifiers: this.#keyboard._modifiers,
|
||||||
|
buttons,
|
||||||
|
// This should always be 0 (i.e. 'left'). See
|
||||||
|
// https://w3c.github.io/uievents/#event-type-mousemove
|
||||||
|
button: MouseButton.Left,
|
||||||
|
...position,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Presses the mouse.
|
||||||
|
*
|
||||||
|
* @param options - Options to configure behavior.
|
||||||
|
*/
|
||||||
|
async down(options: MouseOptions = {}): Promise<void> {
|
||||||
|
const {button = MouseButton.Left, clickCount = 1} = options;
|
||||||
|
const flag = getFlag(button);
|
||||||
|
if (!flag) {
|
||||||
|
throw new Error(`Unsupported mouse button: ${button}`);
|
||||||
|
}
|
||||||
|
if (this.#state.buttons & flag) {
|
||||||
|
throw new Error(`'${button}' is already pressed.`);
|
||||||
|
}
|
||||||
|
await this.#withTransaction(updateState => {
|
||||||
|
updateState({buttons: this.#state.buttons | flag});
|
||||||
|
const {buttons, position} = this.#state;
|
||||||
|
return this.#client.send('Input.dispatchMouseEvent', {
|
||||||
|
type: 'mousePressed',
|
||||||
|
modifiers: this.#keyboard._modifiers,
|
||||||
|
clickCount,
|
||||||
|
buttons,
|
||||||
|
button,
|
||||||
|
...position,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the mouse.
|
||||||
|
*
|
||||||
|
* @param options - Options to configure behavior.
|
||||||
|
*/
|
||||||
|
async up(options: MouseOptions = {}): Promise<void> {
|
||||||
|
const {button = MouseButton.Left, clickCount = 1} = options;
|
||||||
|
const flag = getFlag(button);
|
||||||
|
if (!flag) {
|
||||||
|
throw new Error(`Unsupported mouse button: ${button}`);
|
||||||
|
}
|
||||||
|
if (!(this.#state.buttons & flag)) {
|
||||||
|
throw new Error(`'${button}' is not pressed.`);
|
||||||
|
}
|
||||||
|
await this.#withTransaction(updateState => {
|
||||||
|
updateState({buttons: this.#state.buttons & ~flag});
|
||||||
|
const {buttons, position} = this.#state;
|
||||||
|
return this.#client.send('Input.dispatchMouseEvent', {
|
||||||
|
type: 'mouseReleased',
|
||||||
|
modifiers: this.#keyboard._modifiers,
|
||||||
|
clickCount,
|
||||||
|
buttons,
|
||||||
|
button,
|
||||||
|
...position,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for `mouse.move`, `mouse.down` and `mouse.up`.
|
* Shortcut for `mouse.move`, `mouse.down` and `mouse.up`.
|
||||||
|
*
|
||||||
* @param x - Horizontal position of the mouse.
|
* @param x - Horizontal position of the mouse.
|
||||||
* @param y - Vertical position of the mouse.
|
* @param y - Vertical position of the mouse.
|
||||||
* @param options - Optional `MouseOptions`.
|
* @param options - Options to configure behavior.
|
||||||
*/
|
*/
|
||||||
async click(
|
async click(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
options: MouseOptions & {delay?: number} = {}
|
options: MouseClickOptions = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {delay = null} = options;
|
const {delay} = options;
|
||||||
await this.move(x, y);
|
const actions: Array<Promise<void>> = [];
|
||||||
await this.down(options);
|
const {position} = this.#state;
|
||||||
if (delay !== null) {
|
if (position.x !== x || position.y !== y) {
|
||||||
await new Promise(f => {
|
actions.push(this.move(x, y));
|
||||||
return setTimeout(f, delay);
|
}
|
||||||
|
actions.push(this.down(options));
|
||||||
|
if (typeof delay === 'number') {
|
||||||
|
await Promise.all(actions);
|
||||||
|
actions.length = 0;
|
||||||
|
await new Promise(resolve => {
|
||||||
|
setTimeout(resolve, delay);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await this.up(options);
|
actions.push(this.up(options));
|
||||||
}
|
await Promise.all(actions);
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches a `mousedown` event.
|
|
||||||
* @param options - Optional `MouseOptions`.
|
|
||||||
*/
|
|
||||||
async down(options: MouseOptions = {}): Promise<void> {
|
|
||||||
const {button = 'left', clickCount = 1} = options;
|
|
||||||
this.#button = button;
|
|
||||||
await this.#client.send('Input.dispatchMouseEvent', {
|
|
||||||
type: 'mousePressed',
|
|
||||||
button,
|
|
||||||
x: this.#x,
|
|
||||||
y: this.#y,
|
|
||||||
modifiers: this.#keyboard._modifiers,
|
|
||||||
clickCount,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches a `mouseup` event.
|
|
||||||
* @param options - Optional `MouseOptions`.
|
|
||||||
*/
|
|
||||||
async up(options: MouseOptions = {}): Promise<void> {
|
|
||||||
const {button = 'left', clickCount = 1} = options;
|
|
||||||
this.#button = 'none';
|
|
||||||
await this.#client.send('Input.dispatchMouseEvent', {
|
|
||||||
type: 'mouseReleased',
|
|
||||||
button,
|
|
||||||
x: this.#x,
|
|
||||||
y: this.#y,
|
|
||||||
modifiers: this.#keyboard._modifiers,
|
|
||||||
clickCount,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -546,14 +709,15 @@ export class Mouse {
|
|||||||
*/
|
*/
|
||||||
async wheel(options: MouseWheelOptions = {}): Promise<void> {
|
async wheel(options: MouseWheelOptions = {}): Promise<void> {
|
||||||
const {deltaX = 0, deltaY = 0} = options;
|
const {deltaX = 0, deltaY = 0} = options;
|
||||||
|
const {position, buttons} = this.#state;
|
||||||
await this.#client.send('Input.dispatchMouseEvent', {
|
await this.#client.send('Input.dispatchMouseEvent', {
|
||||||
type: 'mouseWheel',
|
type: 'mouseWheel',
|
||||||
x: this.#x,
|
|
||||||
y: this.#y,
|
|
||||||
deltaX,
|
|
||||||
deltaY,
|
|
||||||
modifiers: this.#keyboard._modifiers,
|
|
||||||
pointerType: 'mouse',
|
pointerType: 'mouse',
|
||||||
|
modifiers: this.#keyboard._modifiers,
|
||||||
|
deltaY,
|
||||||
|
deltaX,
|
||||||
|
buttons,
|
||||||
|
...position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,4 +259,96 @@ describe('Mouse', function () {
|
|||||||
|
|
||||||
expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
|
expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
|
||||||
});
|
});
|
||||||
|
it('should throw if buttons are pressed incorrectly', async () => {
|
||||||
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
|
||||||
|
await page.mouse.down();
|
||||||
|
await expect(page.mouse.down()).rejects.toBeInstanceOf(Error);
|
||||||
|
});
|
||||||
|
it('should not throw if clicking in parallel', async () => {
|
||||||
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
interface ClickData {
|
||||||
|
type: string;
|
||||||
|
detail: number;
|
||||||
|
clientX: number;
|
||||||
|
clientY: number;
|
||||||
|
isTrusted: boolean;
|
||||||
|
button: number;
|
||||||
|
buttons: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
const clicks: ClickData[] = [];
|
||||||
|
const mouseEventListener = (event: MouseEvent) => {
|
||||||
|
clicks.push({
|
||||||
|
type: event.type,
|
||||||
|
detail: event.detail,
|
||||||
|
clientX: event.clientX,
|
||||||
|
clientY: event.clientY,
|
||||||
|
isTrusted: event.isTrusted,
|
||||||
|
button: event.button,
|
||||||
|
buttons: event.buttons,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
document.addEventListener('mousedown', mouseEventListener);
|
||||||
|
document.addEventListener('mouseup', mouseEventListener);
|
||||||
|
document.addEventListener('click', mouseEventListener);
|
||||||
|
(window as unknown as {clicks: ClickData[]}).clicks = clicks;
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all([page.mouse.click(0, 5), page.mouse.click(6, 10)]);
|
||||||
|
|
||||||
|
const data = await page.evaluate(() => {
|
||||||
|
return (window as unknown as {clicks: ClickData[]}).clicks;
|
||||||
|
});
|
||||||
|
const commonAttrs = {
|
||||||
|
isTrusted: true,
|
||||||
|
detail: 1,
|
||||||
|
clientY: 5,
|
||||||
|
clientX: 0,
|
||||||
|
button: 0,
|
||||||
|
};
|
||||||
|
expect(data.splice(0, 3)).toMatchObject({
|
||||||
|
0: {
|
||||||
|
type: 'mousedown',
|
||||||
|
buttons: 1,
|
||||||
|
...commonAttrs,
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
type: 'mouseup',
|
||||||
|
buttons: 0,
|
||||||
|
...commonAttrs,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
type: 'click',
|
||||||
|
buttons: 0,
|
||||||
|
...commonAttrs,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Object.assign(commonAttrs, {
|
||||||
|
clientX: 6,
|
||||||
|
clientY: 10,
|
||||||
|
});
|
||||||
|
expect(data).toMatchObject({
|
||||||
|
0: {
|
||||||
|
type: 'mousedown',
|
||||||
|
buttons: 1,
|
||||||
|
...commonAttrs,
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
type: 'mouseup',
|
||||||
|
buttons: 0,
|
||||||
|
...commonAttrs,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
type: 'click',
|
||||||
|
buttons: 0,
|
||||||
|
...commonAttrs,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user