diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 49d8f198eb..ec5d12efc0 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -380,6 +380,19 @@ Optional event-specific initialization properties. ### option: Locator.dispatchEvent.timeout = %%-input-timeout-%% +## async method: Locator.dragTo +### param: Locator.dragTo.target +- `target` <[Locator]> + +Locator of the element to drag to. + +### option: Locator.dragTo.force = %%-input-force-%% +### option: Locator.dragTo.noWaitAfter = %%-input-no-wait-after-%% +### option: Locator.dragTo.timeout = %%-input-timeout-%% +### option: Locator.dragTo.trial = %%-input-trial-%% +### option: Locator.dragTo.sourcePosition = %%-input-source-position-%% +### option: Locator.dragTo.targetPosition = %%-input-target-position-%% + ## async method: Locator.elementHandle - returns: <[ElementHandle]> diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index 2f78ce782f..22cf6f7ffa 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -70,6 +70,13 @@ export class Locator implements api.Locator { return this._frame.dispatchEvent(this._selector, type, eventInit, { strict: true, ...options }); } + async dragTo(target: Locator, options: channels.FrameDragAndDropOptions = {}) { + return this._frame.dragAndDrop(this._selector, target._selector, { + ...options, + strict: true, + }); + } + async evaluate(pageFunction: structs.PageFunctionOn, arg?: Arg, options?: TimeoutOptions): Promise { return this._withElement(h => h.evaluate(pageFunction, arg), options?.timeout); } diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index c4fb04f3da..8e45977d47 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -1892,6 +1892,7 @@ export type FrameDragAndDropParams = { trial?: boolean, sourcePosition?: Point, targetPosition?: Point, + strict?: boolean, }; export type FrameDragAndDropOptions = { force?: boolean, @@ -1900,6 +1901,7 @@ export type FrameDragAndDropOptions = { trial?: boolean, sourcePosition?: Point, targetPosition?: Point, + strict?: boolean, }; export type FrameDragAndDropResult = void; export type FrameDblclickParams = { diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index 17edaa4e00..b1e59bdec6 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -1367,6 +1367,7 @@ Frame: trial: boolean? sourcePosition: Point? targetPosition: Point? + strict: boolean? tracing: snapshot: true pausesBeforeInput: true diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 920b481d77..045bfdda89 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -695,6 +695,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { trial: tOptional(tBoolean), sourcePosition: tOptional(tType('Point')), targetPosition: tOptional(tType('Point')), + strict: tOptional(tBoolean), }); scheme.FrameDblclickParams = tObject({ selector: tString, diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 678d771635..5f2ec34307 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -8927,6 +8927,58 @@ export interface Locator { timeout?: number; }): Promise; + /** + * @param target Locator of the element to drag to. + * @param options + */ + dragTo(target: Locator, options?: { + /** + * Whether to bypass the [actionability](https://playwright.dev/docs/actionability) checks. Defaults to `false`. + */ + force?: boolean; + + /** + * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can + * opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to + * inaccessible pages. Defaults to `false`. + */ + 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; + }; + + /** + * 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 + * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) + * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. + */ + timeout?: number; + + /** + * When set, this method only performs the [actionability](https://playwright.dev/docs/actionability) checks and skips the action. Defaults to + * `false`. Useful to wait until the element is ready for the action without performing it. + */ + trial?: boolean; + }): Promise; + /** * Resolves given locator to all matching DOM elements. */ diff --git a/tests/page/page-drag.spec.ts b/tests/page/page-drag.spec.ts index 88e0d3e4ab..d687e18a04 100644 --- a/tests/page/page-drag.spec.ts +++ b/tests/page/page-drag.spec.ts @@ -270,6 +270,12 @@ it.describe('Drag and drop', () => { ]); }); + it('should work with locators', async ({ page, server }) => { + await page.goto(server.PREFIX + '/drag-n-drop.html'); + await page.locator('#source').dragTo(page.locator('#target')); + expect(await page.$eval('#target', target => target.contains(document.querySelector('#source')))).toBe(true); // could not find source in target + }); + async function trackEvents(target: ElementHandle) { const eventsHandle = await target.evaluateHandle(target => { const events: string[] = [];