Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/src/api/class-elementhandle.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ When all steps combined have not finished during the specified [`option: timeout
### option: ElementHandle.click.trial = %%-input-trial-%%
* since: v1.11

### option: ElementHandle.click.steps = %%-input-mousemove-steps-%%
* since: v1.57

## async method: ElementHandle.contentFrame
* since: v1.8
- returns: <[null]|[Frame]>
Expand Down Expand Up @@ -287,6 +290,9 @@ When all steps combined have not finished during the specified [`option: timeout
### option: ElementHandle.dblclick.trial = %%-input-trial-%%
* since: v1.11

### option: ElementHandle.dblclick.steps = %%-input-mousemove-steps-%%
* since: v1.57

## async method: ElementHandle.dispatchEvent
* since: v1.8
* discouraged: Use locator-based [`method: Locator.dispatchEvent`] instead. Read more about [locators](../locators.md).
Expand Down
6 changes: 6 additions & 0 deletions docs/src/api/class-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,9 @@ await page.Locator("canvas").ClickAsync(new() {
### option: Locator.click.trial = %%-input-trial-with-modifiers-%%
* since: v1.14

### option: Locator.click.steps = %%-input-mousemove-steps-%%
* since: v1.57

## async method: Locator.count
* since: v1.14
- returns: <[int]>
Expand Down Expand Up @@ -580,6 +583,9 @@ When all steps combined have not finished during the specified [`option: timeout
### option: Locator.dblclick.trial = %%-input-trial-with-modifiers-%%
* since: v1.14

### option: Locator.dblclick.steps = %%-input-mousemove-steps-%%
* since: v1.57

## method: Locator.describe
* since: v1.53
- returns: <[Locator]>
Expand Down
5 changes: 1 addition & 4 deletions docs/src/api/class-mouse.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,8 @@ X coordinate relative to the main frame's viewport in CSS pixels.

Y coordinate relative to the main frame's viewport in CSS pixels.

### option: Mouse.move.steps
### option: Mouse.move.steps = %%-input-mousemove-steps-%%
* since: v1.8
- `steps` <[int]>

Defaults to 1. Sends intermediate `mousemove` events.

## async method: Mouse.up
* since: v1.8
Expand Down
5 changes: 5 additions & 0 deletions docs/src/api/params.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ A selector to search for an element to drop onto. If there are multiple elements
A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
element.

## input-mousemove-steps
- `steps` <[int]>

Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.

## input-modifiers
- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">>>

Expand Down
27 changes: 26 additions & 1 deletion packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11211,6 +11211,12 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -11294,6 +11300,12 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -12788,6 +12800,12 @@ export interface Locator {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -12905,6 +12923,12 @@ export interface Locator {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -20293,7 +20317,8 @@ export interface Mouse {
*/
move(x: number, y: number, options?: {
/**
* Defaults to 1. Sends intermediate `mousemove` events.
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;
}): Promise<void>;
Expand Down
4 changes: 4 additions & 0 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,7 @@ scheme.FrameClickParams = tObject({
clickCount: tOptional(tInt),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt),
});
scheme.FrameClickResult = tOptional(tObject({}));
scheme.FrameContentParams = tOptional(tObject({}));
Expand Down Expand Up @@ -1629,6 +1630,7 @@ scheme.FrameDblclickParams = tObject({
button: tOptional(tEnum(['left', 'right', 'middle'])),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt),
});
scheme.FrameDblclickResult = tOptional(tObject({}));
scheme.FrameDispatchEventParams = tObject({
Expand Down Expand Up @@ -2044,6 +2046,7 @@ scheme.ElementHandleClickParams = tObject({
clickCount: tOptional(tInt),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt),
});
scheme.ElementHandleClickResult = tOptional(tObject({}));
scheme.ElementHandleContentFrameParams = tOptional(tObject({}));
Expand All @@ -2058,6 +2061,7 @@ scheme.ElementHandleDblclickParams = tObject({
button: tOptional(tEnum(['left', 'right', 'middle'])),
timeout: tFloat,
trial: tOptional(tBoolean),
steps: tOptional(tInt),
});
scheme.ElementHandleDblclickResult = tOptional(tObject({}));
scheme.ElementHandleDispatchEventParams = tObject({
Expand Down
12 changes: 8 additions & 4 deletions packages/playwright-core/src/server/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,10 @@ export class Mouse {
await this._raw.up(progress, this._x, this._y, button, this._buttons, this._keyboard._modifiers(), clickCount);
}

async click(progress: Progress, x: number, y: number, options: { delay?: number, button?: types.MouseButton, clickCount?: number } = {}) {
const { delay = null, clickCount = 1 } = options;
async click(progress: Progress, x: number, y: number, options: { delay?: number, button?: types.MouseButton, clickCount?: number, steps?: number } = {}) {
const { delay = null, clickCount = 1, steps } = options;
if (delay) {
this.move(progress, x, y, { forClick: true });
await this.move(progress, x, y, { forClick: true, steps });
for (let cc = 1; cc <= clickCount; ++cc) {
await this.down(progress, { ...options, clickCount: cc });
await progress.wait(delay);
Expand All @@ -228,7 +228,11 @@ export class Mouse {
}
} else {
const promises = [];
promises.push(this.move(progress, x, y, { forClick: true }));
const movePromise = this.move(progress, x, y, { forClick: true, steps });
if (steps !== undefined && steps > 1)
await movePromise;
else
promises.push(movePromise);
for (let cc = 1; cc <= clickCount; ++cc) {
promises.push(this.down(progress, { ...options, clickCount: cc }));
promises.push(this.up(progress, { ...options, clickCount: cc }));
Expand Down
27 changes: 26 additions & 1 deletion packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11211,6 +11211,12 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -11294,6 +11300,12 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -12788,6 +12800,12 @@ export interface Locator {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -12905,6 +12923,12 @@ export interface Locator {
y: number;
};

/**
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;

/**
* Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout`
* option in the config, or by using the
Expand Down Expand Up @@ -20293,7 +20317,8 @@ export interface Mouse {
*/
move(x: number, y: number, options?: {
/**
* Defaults to 1. Sends intermediate `mousemove` events.
* Defaults to 1. Sends `n` interpolated `mousemove` events to represent travel between Playwright's current cursor
* position and the provided destination. When set to 1, emits a single `mousemove` event at the destination location.
*/
steps?: number;
}): Promise<void>;
Expand Down
8 changes: 8 additions & 0 deletions packages/protocol/src/channels.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2803,6 +2803,7 @@ export type FrameClickParams = {
clickCount?: number,
timeout: number,
trial?: boolean,
steps?: number,
};
export type FrameClickOptions = {
strict?: boolean,
Expand All @@ -2814,6 +2815,7 @@ export type FrameClickOptions = {
button?: 'left' | 'right' | 'middle',
clickCount?: number,
trial?: boolean,
steps?: number,
};
export type FrameClickResult = void;
export type FrameContentParams = {};
Expand Down Expand Up @@ -2849,6 +2851,7 @@ export type FrameDblclickParams = {
button?: 'left' | 'right' | 'middle',
timeout: number,
trial?: boolean,
steps?: number,
};
export type FrameDblclickOptions = {
strict?: boolean,
Expand All @@ -2858,6 +2861,7 @@ export type FrameDblclickOptions = {
delay?: number,
button?: 'left' | 'right' | 'middle',
trial?: boolean,
steps?: number,
};
export type FrameDblclickResult = void;
export type FrameDispatchEventParams = {
Expand Down Expand Up @@ -3514,6 +3518,7 @@ export type ElementHandleClickParams = {
clickCount?: number,
timeout: number,
trial?: boolean,
steps?: number,
};
export type ElementHandleClickOptions = {
force?: boolean,
Expand All @@ -3524,6 +3529,7 @@ export type ElementHandleClickOptions = {
button?: 'left' | 'right' | 'middle',
clickCount?: number,
trial?: boolean,
steps?: number,
};
export type ElementHandleClickResult = void;
export type ElementHandleContentFrameParams = {};
Expand All @@ -3539,6 +3545,7 @@ export type ElementHandleDblclickParams = {
button?: 'left' | 'right' | 'middle',
timeout: number,
trial?: boolean,
steps?: number,
};
export type ElementHandleDblclickOptions = {
force?: boolean,
Expand All @@ -3547,6 +3554,7 @@ export type ElementHandleDblclickOptions = {
delay?: number,
button?: 'left' | 'right' | 'middle',
trial?: boolean,
steps?: number,
};
export type ElementHandleDblclickResult = void;
export type ElementHandleDispatchEventParams = {
Expand Down
4 changes: 4 additions & 0 deletions packages/protocol/src/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2256,6 +2256,7 @@ Frame:
clickCount: int?
timeout: float
trial: boolean?
steps: int?
flags:
slowMo: true
snapshot: true
Expand Down Expand Up @@ -2311,6 +2312,7 @@ Frame:
- middle
timeout: float
trial: boolean?
steps: int?
flags:
slowMo: true
snapshot: true
Expand Down Expand Up @@ -3012,6 +3014,7 @@ ElementHandle:
clickCount: int?
timeout: float
trial: boolean?
steps: int?
flags:
slowMo: true
snapshot: true
Expand Down Expand Up @@ -3047,6 +3050,7 @@ ElementHandle:
- middle
timeout: float
trial: boolean?
steps: int?
flags:
slowMo: true
snapshot: true
Expand Down
30 changes: 30 additions & 0 deletions tests/page/page-click.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1287,3 +1287,33 @@ it('should click shadow root button', { annotation: { type: 'issue', description

await page.locator('my-button').click();
});

it('should click with tweened mouse movement', async ({ page, browserName, isAndroid }) => {
it.skip(isAndroid, 'Bad rounding');

await page.setContent(`
<body style="margin: 0; padding: 0; height: 500px; width: 500px;">
<div style="position: relative; top: 280px; left: 150px; width: 100px; height: 40px">Click me</div>
</body>
`);

// The test becomes flaky on WebKit without next line.
if (browserName === 'webkit')
await page.evaluate(() => new Promise(requestAnimationFrame));
await page.mouse.move(100, 100);
await page.evaluate(() => {
window['result'] = [];
document.addEventListener('mousemove', event => {
window['result'].push([event.clientX, event.clientY]);
});
});
// Centerpoint at 150 + 100/2, 280 + 40/2 = 200, 300
await page.locator('div').click({ steps: 5 });
expect(await page.evaluate('result')).toEqual([
[120, 140],
[140, 180],
[160, 220],
[180, 260],
[200, 300]
]);
});
Loading