feat(drag): sourcePosition and targetPosition (#7803)

This commit is contained in:
Joel Einbinder 2021-08-03 13:12:34 -04:00 committed by GitHub
parent a2cbba9c1c
commit 4fabe5e6e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 114 additions and 1 deletions

View file

@ -371,6 +371,10 @@ Optional event-specific initialization properties.
### option: Frame.dragAndDrop.timeout = %%-input-timeout-%%
### option: Frame.dragAndDrop.trial = %%-input-trial-%%
### option: Frame.dragAndDrop.sourcePosition = %%-input-source-position-%%
### option: Frame.dragAndDrop.targetPosition = %%-input-target-position-%%
## async method: Frame.evalOnSelector
* langs:
- alias-python: eval_on_selector

View file

@ -807,6 +807,10 @@ Optional event-specific initialization properties.
### option: Page.dragAndDrop.timeout = %%-input-timeout-%%
### option: Page.dragAndDrop.trial = %%-input-trial-%%
### option: Page.dragAndDrop.sourcePosition = %%-input-source-position-%%
### option: Page.dragAndDrop.targetPosition = %%-input-target-position-%%
## async method: Page.emulateMedia
This method changes the `CSS media type` through the `media` argument, and/or the `'prefers-colors-scheme'` media feature, using the `colorScheme` argument.

View file

@ -103,6 +103,20 @@ defaults to 1. See [UIEvent.detail].
When set, this method only performs the [actionability](./actionability.md) checks and skips the action. Defaults to `false`. Useful to wait until the element is ready for the action without performing it.
## input-source-position
- `sourcePosition` <[Object]>
- `x` <[float]>
- `y` <[float]>
Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not specified, some visible point of the element is used.
## input-target-position
- `targetPosition` <[Object]>
- `x` <[float]>
- `y` <[float]>
Drops on the target element at this point relative to the top-left corner of the element's padding box. If not specified, some visible point of the element is used.
## query-selector
- `selector` <[string]>

View file

@ -1462,12 +1462,16 @@ export type FrameDragAndDropParams = {
noWaitAfter?: boolean,
timeout?: number,
trial?: boolean,
sourcePosition?: Point,
targetPosition?: Point,
};
export type FrameDragAndDropOptions = {
force?: boolean,
noWaitAfter?: boolean,
timeout?: number,
trial?: boolean,
sourcePosition?: Point,
targetPosition?: Point,
};
export type FrameDragAndDropResult = void;
export type FrameDblclickParams = {

View file

@ -1188,6 +1188,8 @@ Frame:
noWaitAfter: boolean?
timeout: number?
trial: boolean?
sourcePosition: Point?
targetPosition: Point?
dblclick:
parameters:

View file

@ -597,6 +597,8 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
noWaitAfter: tOptional(tBoolean),
timeout: tOptional(tNumber),
trial: tOptional(tBoolean),
sourcePosition: tOptional(tType('Point')),
targetPosition: tOptional(tType('Point')),
});
scheme.FrameDblclickParams = tObject({
selector: tString,

View file

@ -983,7 +983,7 @@ export class Frame extends SdkObject {
}, this._page._timeoutSettings.timeout(options));
}
async dragAndDrop(metadata: CallMetadata, source: string, target: string, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
async dragAndDrop(metadata: CallMetadata, source: string, target: string, options: types.DragActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
const controller = new ProgressController(metadata, this);
await controller.run(async progress => {
await dom.assertDone(await this._retryWithProgressIfNotConnected(progress, source, !!options.strict, async handle => {
@ -992,6 +992,7 @@ export class Frame extends SdkObject {
await this._page.mouse.down();
}, {
...options,
position: options.sourcePosition,
timeout: progress.timeUntilDeadline(),
});
}));
@ -1001,6 +1002,7 @@ export class Frame extends SdkObject {
await this._page.mouse.up();
}, {
...options,
position: options.targetPosition,
timeout: progress.timeUntilDeadline(),
});
}));

View file

@ -171,6 +171,12 @@ export type PointerActionOptions = {
position?: Point;
};
export type DragActionOptions = {
sourcePosition?: Point;
targetPosition?: Point;
};
export type MouseClickOptions = PointerActionOptions & {
delay?: number;
button?: MouseButton;

View file

@ -234,6 +234,41 @@ it.describe('Drag and drop', () => {
expect(await page.$eval('#target', target => target.contains(document.querySelector('#source')))).toBe(true); // could not find source in target
});
it('should allow specifying the position', async ({page, server}) => {
await page.setContent(`
<div style="width:100px;height:100px;background:red;" id="red">
</div>
<div style="width:100px;height:100px;background:blue;" id="blue">
</div>
`);
const eventsHandle = await page.evaluateHandle(() => {
const events = [];
document.getElementById('red').addEventListener('mousedown', event => {
events.push({
type: 'mousedown',
x: event.offsetX,
y: event.offsetY,
});
});
document.getElementById('blue').addEventListener('mouseup', event => {
events.push({
type: 'mouseup',
x: event.offsetX,
y: event.offsetY,
});
});
return events;
});
await page.dragAndDrop('#red', '#blue', {
sourcePosition: {x: 34, y: 7},
targetPosition: {x: 10, y: 20},
});
expect(await eventsHandle.jsonValue()).toEqual([
{type: 'mousedown', x: 34, y: 7},
{type: 'mouseup', x: 10, y: 20},
]);
});
async function trackEvents(target: ElementHandle) {
const eventsHandle = await target.evaluateHandle(target => {
const events: string[] = [];

40
types/types.d.ts vendored
View file

@ -1441,12 +1441,32 @@ export interface Page {
*/
noWaitAfter?: boolean;
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
*/
sourcePosition?: {
x: number;
y: number;
};
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more then one
* element, the call throws an exception.
*/
strict?: boolean;
/**
* Drops on the target element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
*/
targetPosition?: {
x: number;
y: number;
};
/**
* Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
* using the
@ -3865,12 +3885,32 @@ export interface Frame {
*/
noWaitAfter?: boolean;
/**
* Clicks on the source element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
*/
sourcePosition?: {
x: number;
y: number;
};
/**
* When true, the call requires selector to resolve to a single element. If given selector resolves to more then one
* element, the call throws an exception.
*/
strict?: boolean;
/**
* Drops on the target element at this point relative to the top-left corner of the element's padding box. If not
* specified, some visible point of the element is used.
*/
targetPosition?: {
x: number;
y: number;
};
/**
* Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
* using the