diff --git a/docs/src/api/class-elementhandle.md b/docs/src/api/class-elementhandle.md index e87fe5cb96..a45bc8782f 100644 --- a/docs/src/api/class-elementhandle.md +++ b/docs/src/api/class-elementhandle.md @@ -138,6 +138,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: ElementHandle.check.timeout = %%-input-timeout-%% +### option: ElementHandle.check.trial = %%-input-trial-%% + ## async method: ElementHandle.click This method clicks the element by performing the following steps: @@ -167,6 +169,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: ElementHandle.click.timeout = %%-input-timeout-%% +### option: ElementHandle.click.trial = %%-input-trial-%% + ## async method: ElementHandle.contentFrame - returns: <[null]|[Frame]> @@ -206,6 +210,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: ElementHandle.dblclick.timeout = %%-input-timeout-%% +### option: ElementHandle.dblclick.trial = %%-input-trial-%% + ## async method: ElementHandle.dispatchEvent The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element, `click` @@ -434,6 +440,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: ElementHandle.hover.timeout = %%-input-timeout-%% +### option: ElementHandle.hover.trial = %%-input-trial-%% + ## async method: ElementHandle.innerHTML - returns: <[string]> @@ -693,6 +701,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: ElementHandle.tap.timeout = %%-input-timeout-%% +### option: ElementHandle.tap.trial = %%-input-trial-%% + ## async method: ElementHandle.textContent - returns: <[null]|[string]> @@ -788,6 +798,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: ElementHandle.uncheck.timeout = %%-input-timeout-%% +### option: ElementHandle.uncheck.trial = %%-input-trial-%% + ## async method: ElementHandle.waitForElementState Returns when the element satisfies the [`param: state`]. diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md index 1e95ef0714..783fc899af 100644 --- a/docs/src/api/class-frame.md +++ b/docs/src/api/class-frame.md @@ -177,6 +177,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.check.timeout = %%-input-timeout-%% +### option: Frame.check.trial = %%-input-trial-%% + ## method: Frame.childFrames - returns: <[Array]<[Frame]>> @@ -212,6 +214,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.click.timeout = %%-input-timeout-%% +### option: Frame.click.trial = %%-input-trial-%% + ## async method: Frame.content - returns: <[string]> @@ -254,6 +258,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.dblclick.timeout = %%-input-timeout-%% +### option: Frame.dblclick.trial = %%-input-trial-%% + ## async method: Frame.dispatchEvent The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element, `click` @@ -747,6 +753,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.hover.timeout = %%-input-timeout-%% +### option: Frame.hover.trial = %%-input-trial-%% + ## async method: Frame.innerHTML - returns: <[string]> @@ -1022,6 +1030,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.tap.timeout = %%-input-timeout-%% +### option: Frame.tap.trial = %%-input-trial-%% + ## async method: Frame.textContent - returns: <[null]|[string]> @@ -1108,6 +1118,8 @@ When all steps combined have not finished during the specified [`option: timeout ### option: Frame.uncheck.timeout = %%-input-timeout-%% +### option: Frame.uncheck.trial = %%-input-trial-%% + ## method: Frame.url - returns: <[string]> diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 7248d151b6..071a1b1ef8 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -535,6 +535,8 @@ Shortcut for main frame's [`method: Frame.check`]. ### option: Page.check.timeout = %%-input-timeout-%% +### option: Page.check.trial = %%-input-trial-%% + ## async method: Page.click This method clicks an element matching [`param: selector`] by performing the following steps: @@ -569,6 +571,8 @@ Shortcut for main frame's [`method: Frame.click`]. ### option: Page.click.timeout = %%-input-timeout-%% +### option: Page.click.trial = %%-input-trial-%% + ## async method: Page.close If [`option: runBeforeUnload`] is `false`, does not run any unload handlers and waits for the page to be closed. If @@ -646,6 +650,8 @@ Shortcut for main frame's [`method: Frame.dblclick`]. ### option: Page.dblclick.timeout = %%-input-timeout-%% +### option: Page.dblclick.trial = %%-input-trial-%% + ## async method: Page.dispatchEvent The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element, `click` @@ -1662,6 +1668,8 @@ Shortcut for main frame's [`method: Frame.hover`]. ### option: Page.hover.timeout = %%-input-timeout-%% +### option: Page.hover.trial = %%-input-trial-%% + ## async method: Page.innerHTML - returns: <[string]> @@ -2418,6 +2426,8 @@ Shortcut for main frame's [`method: Frame.tap`]. ### option: Page.tap.timeout = %%-input-timeout-%% +### option: Page.tap.trial = %%-input-trial-%% + ## async method: Page.textContent - returns: <[null]|[string]> @@ -2511,6 +2521,8 @@ Shortcut for main frame's [`method: Frame.uncheck`]. ### option: Page.uncheck.timeout = %%-input-timeout-%% +### option: Page.uncheck.trial = %%-input-trial-%% + ## async method: Page.unroute Removes a route created with [`method: Page.route`]. When [`param: handler`] is not specified, removes all routes for diff --git a/docs/src/api/params.md b/docs/src/api/params.md index bc689e2e86..27c1cfcc49 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -82,6 +82,11 @@ Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0. defaults to 1. See [UIEvent.detail]. +## input-trial +- `trial` <[boolean]> + +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. + ## query-selector - `selector` <[string]> diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index d6980d2f2d..35acdba962 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -1338,12 +1338,14 @@ export type FrameCheckParams = { noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type FrameCheckOptions = { force?: boolean, noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type FrameCheckResult = void; export type FrameClickParams = { @@ -1356,6 +1358,7 @@ export type FrameClickParams = { button?: 'left' | 'right' | 'middle', clickCount?: number, timeout?: number, + trial?: boolean, }; export type FrameClickOptions = { force?: boolean, @@ -1366,6 +1369,7 @@ export type FrameClickOptions = { button?: 'left' | 'right' | 'middle', clickCount?: number, timeout?: number, + trial?: boolean, }; export type FrameClickResult = void; export type FrameContentParams = {}; @@ -1382,6 +1386,7 @@ export type FrameDblclickParams = { delay?: number, button?: 'left' | 'right' | 'middle', timeout?: number, + trial?: boolean, }; export type FrameDblclickOptions = { force?: boolean, @@ -1391,6 +1396,7 @@ export type FrameDblclickOptions = { delay?: number, button?: 'left' | 'right' | 'middle', timeout?: number, + trial?: boolean, }; export type FrameDblclickResult = void; export type FrameDispatchEventParams = { @@ -1484,12 +1490,14 @@ export type FrameHoverParams = { modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type FrameHoverOptions = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type FrameHoverResult = void; export type FrameInnerHTMLParams = { @@ -1659,6 +1667,7 @@ export type FrameTapParams = { modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type FrameTapOptions = { force?: boolean, @@ -1666,6 +1675,7 @@ export type FrameTapOptions = { modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type FrameTapResult = void; export type FrameTextContentParams = { @@ -1702,12 +1712,14 @@ export type FrameUncheckParams = { noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type FrameUncheckOptions = { force?: boolean, noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type FrameUncheckResult = void; export type FrameWaitForFunctionParams = { @@ -1908,12 +1920,14 @@ export type ElementHandleCheckParams = { noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleCheckOptions = { force?: boolean, noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleCheckResult = void; export type ElementHandleClickParams = { @@ -1925,6 +1939,7 @@ export type ElementHandleClickParams = { button?: 'left' | 'right' | 'middle', clickCount?: number, timeout?: number, + trial?: boolean, }; export type ElementHandleClickOptions = { force?: boolean, @@ -1935,6 +1950,7 @@ export type ElementHandleClickOptions = { button?: 'left' | 'right' | 'middle', clickCount?: number, timeout?: number, + trial?: boolean, }; export type ElementHandleClickResult = void; export type ElementHandleContentFrameParams = {}; @@ -1950,6 +1966,7 @@ export type ElementHandleDblclickParams = { delay?: number, button?: 'left' | 'right' | 'middle', timeout?: number, + trial?: boolean, }; export type ElementHandleDblclickOptions = { force?: boolean, @@ -1959,6 +1976,7 @@ export type ElementHandleDblclickOptions = { delay?: number, button?: 'left' | 'right' | 'middle', timeout?: number, + trial?: boolean, }; export type ElementHandleDblclickResult = void; export type ElementHandleDispatchEventParams = { @@ -1996,12 +2014,14 @@ export type ElementHandleHoverParams = { modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleHoverOptions = { force?: boolean, modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleHoverResult = void; export type ElementHandleInnerHTMLParams = {}; @@ -2151,6 +2171,7 @@ export type ElementHandleTapParams = { modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleTapOptions = { force?: boolean, @@ -2158,6 +2179,7 @@ export type ElementHandleTapOptions = { modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleTapResult = void; export type ElementHandleTextContentParams = {}; @@ -2182,12 +2204,14 @@ export type ElementHandleUncheckParams = { noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleUncheckOptions = { force?: boolean, noWaitAfter?: boolean, position?: Point, timeout?: number, + trial?: boolean, }; export type ElementHandleUncheckResult = void; export type ElementHandleWaitForElementStateParams = { diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index ef766a9f5e..079b65a976 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -1048,6 +1048,7 @@ Frame: noWaitAfter: boolean? position: Point? timeout: number? + trial: boolean? click: parameters: @@ -1073,6 +1074,7 @@ Frame: - middle clickCount: number? timeout: number? + trial: boolean? content: returns: @@ -1101,6 +1103,7 @@ Frame: - right - middle timeout: number? + trial: boolean? dispatchEvent: parameters: @@ -1188,6 +1191,7 @@ Frame: - Shift position: Point? timeout: number? + trial: boolean? innerHTML: parameters: @@ -1329,6 +1333,7 @@ Frame: - Shift position: Point? timeout: number? + trial: boolean? textContent: parameters: @@ -1356,6 +1361,7 @@ Frame: noWaitAfter: boolean? position: Point? timeout: number? + trial: boolean? waitForFunction: parameters: @@ -1529,6 +1535,7 @@ ElementHandle: noWaitAfter: boolean? position: Point? timeout: number? + trial: boolean? click: parameters: @@ -1553,6 +1560,7 @@ ElementHandle: - middle clickCount: number? timeout: number? + trial: boolean? contentFrame: returns: @@ -1580,6 +1588,7 @@ ElementHandle: - right - middle timeout: number? + trial: boolean? dispatchEvent: parameters: @@ -1614,6 +1623,7 @@ ElementHandle: - Shift position: Point? timeout: number? + trial: boolean? innerHTML: returns: @@ -1741,6 +1751,7 @@ ElementHandle: - Shift position: Point? timeout: number? + trial: boolean? textContent: returns: @@ -1759,6 +1770,7 @@ ElementHandle: noWaitAfter: boolean? position: Point? timeout: number? + trial: boolean? waitForElementState: parameters: diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index 9194d0c1eb..e962509b04 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -541,6 +541,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { noWaitAfter: tOptional(tBoolean), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.FrameClickParams = tObject({ selector: tString, @@ -552,6 +553,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { button: tOptional(tEnum(['left', 'right', 'middle'])), clickCount: tOptional(tNumber), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.FrameContentParams = tOptional(tObject({})); scheme.FrameDblclickParams = tObject({ @@ -563,6 +565,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { delay: tOptional(tNumber), button: tOptional(tEnum(['left', 'right', 'middle'])), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.FrameDispatchEventParams = tObject({ selector: tString, @@ -610,6 +613,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.FrameInnerHTMLParams = tObject({ selector: tString, @@ -689,6 +693,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.FrameTextContentParams = tObject({ selector: tString, @@ -708,6 +713,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { noWaitAfter: tOptional(tBoolean), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.FrameWaitForFunctionParams = tObject({ expression: tString, @@ -771,6 +777,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { noWaitAfter: tOptional(tBoolean), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.ElementHandleClickParams = tObject({ force: tOptional(tBoolean), @@ -781,6 +788,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { button: tOptional(tEnum(['left', 'right', 'middle'])), clickCount: tOptional(tNumber), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.ElementHandleContentFrameParams = tOptional(tObject({})); scheme.ElementHandleDblclickParams = tObject({ @@ -791,6 +799,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { delay: tOptional(tNumber), button: tOptional(tEnum(['left', 'right', 'middle'])), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.ElementHandleDispatchEventParams = tObject({ type: tString, @@ -810,6 +819,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.ElementHandleInnerHTMLParams = tOptional(tObject({})); scheme.ElementHandleInnerTextParams = tOptional(tObject({})); @@ -869,6 +879,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.ElementHandleTextContentParams = tOptional(tObject({})); scheme.ElementHandleTypeParams = tObject({ @@ -882,6 +893,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { noWaitAfter: tOptional(tBoolean), position: tOptional(tType('Point')), timeout: tOptional(tNumber), + trial: tOptional(tBoolean), }); scheme.ElementHandleWaitForElementStateParams = tObject({ state: tEnum(['visible', 'hidden', 'stable', 'enabled', 'disabled', 'editable']), diff --git a/src/server/dom.ts b/src/server/dom.ts index 8f4be23ccd..9c3903361f 100644 --- a/src/server/dom.ts +++ b/src/server/dom.ts @@ -304,14 +304,14 @@ export class ElementHandle extends js.JSHandle { while (progress.isRunning()) { if (retry) { - progress.log(`retrying ${actionName} action, attempt #${retry}`); + progress.log(`retrying ${actionName} action${options.trial ? ' (trial run)' : ''}, attempt #${retry}`); const timeout = waitTime[Math.min(retry - 1, waitTime.length - 1)]; if (timeout) { progress.log(` waiting ${timeout}ms`); await this.evaluateInUtility(([injected, node, timeout]) => new Promise(f => setTimeout(f, timeout)), timeout); } } else { - progress.log(`attempting ${actionName} action`); + progress.log(`attempting ${actionName} action${options.trial ? ' (trial run)' : ''}`); } const forceScrollOptions = scrollOptions[retry % scrollOptions.length]; const result = await this._performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options); @@ -381,8 +381,12 @@ export class ElementHandle extends js.JSHandle { } progress.metadata.point = point; - await progress.beforeInputAction(this); + if (options.trial) { + progress.log(` trial ${actionName} has finished`); + return 'done'; + } + await progress.beforeInputAction(this); await this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { if ((options as any).__testHookBeforePointerAction) await (options as any).__testHookBeforePointerAction(); @@ -634,6 +638,8 @@ export class ElementHandle extends js.JSHandle { const result = await this._click(progress, options); if (result !== 'done') return result; + if (options.trial) + return 'done'; if (await isChecked() !== state) throw new Error('Clicking the checkbox did not change its state'); return 'done'; diff --git a/src/server/types.ts b/src/server/types.ts index 67de5365d2..abf81d1ea9 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -35,6 +35,7 @@ export type NavigatingActionWaitOptions = TimeoutOptions & { export type PointerActionWaitOptions = TimeoutOptions & { force?: boolean, + trial?: boolean; }; export type ElementScreenshotOptions = TimeoutOptions & { diff --git a/tests/page-check.spec.ts b/tests/page-check.spec.ts index 7f2a58a6a6..7e806c4dfb 100644 --- a/tests/page-check.spec.ts +++ b/tests/page-check.spec.ts @@ -119,3 +119,15 @@ it('should check the label with position', async ({page, server}) => { await page.check('text=Click me', { position: { x: box.width - 10, y: 2 } }); expect(await page.$eval('input', input => input.checked)).toBe(true); }); + +it('trial run should not check', async ({page}) => { + await page.setContent(``); + await page.check('input', { trial: true }); + expect(await page.evaluate(() => window['checkbox'].checked)).toBe(false); +}); + +it('trial run should not uncheck', async ({page}) => { + await page.setContent(``); + await page.uncheck('input', { trial: true }); + expect(await page.evaluate(() => window['checkbox'].checked)).toBe(true); +}); diff --git a/tests/page-click.spec.ts b/tests/page-click.spec.ts index f5bd7328cc..98c2bc34a3 100644 --- a/tests/page-click.spec.ts +++ b/tests/page-click.spec.ts @@ -456,6 +456,68 @@ it('should wait for becoming hit target', async ({page, server}) => { expect(await page.evaluate(() => window['result'])).toBe('Clicked'); }); +it('should wait for becoming hit target with trial run', async ({page, server}) => { + await page.goto(server.PREFIX + '/input/button.html'); + await page.$eval('button', button => { + button.style.borderWidth = '0'; + button.style.width = '200px'; + button.style.height = '20px'; + document.body.style.margin = '0'; + document.body.style.position = 'relative'; + const flyOver = document.createElement('div'); + flyOver.className = 'flyover'; + flyOver.style.position = 'absolute'; + flyOver.style.width = '400px'; + flyOver.style.height = '20px'; + flyOver.style.left = '-200px'; + flyOver.style.top = '0'; + flyOver.style.background = 'red'; + document.body.appendChild(flyOver); + }); + let clicked = false; + const clickPromise = page.click('button', { trial: true }).then(() => clicked = true); + expect(clicked).toBe(false); + + await page.$eval('.flyover', flyOver => flyOver.style.left = '0'); + await giveItAChanceToClick(page); + expect(clicked).toBe(false); + + await page.$eval('.flyover', flyOver => flyOver.style.left = '200px'); + await clickPromise; + expect(clicked).toBe(true); + + // Should not actually click. + expect(await page.evaluate(() => window['result'])).toBe('Was not clicked'); +}); + +it('trial run should work with short timeout', async ({page, server}) => { + await page.goto(server.PREFIX + '/input/button.html'); + await page.$eval('button', button => button.disabled = true); + const error = await page.click('button', { trial: true, timeout: 500 }).catch(e => e); + expect(error.message).toContain('click action (trial run)'); + expect(await page.evaluate(() => window['result'])).toBe('Was not clicked'); +}); + +it('trial run should not click', async ({page, server}) => { + await page.goto(server.PREFIX + '/input/button.html'); + await page.click('button', { trial: true }); + expect(await page.evaluate(() => window['result'])).toBe('Was not clicked'); +}); + +it('trial run should not double click', async ({page, server}) => { + await page.goto(server.PREFIX + '/input/button.html'); + await page.evaluate(() => { + window['double'] = false; + const button = document.querySelector('button'); + button.addEventListener('dblclick', event => { + window['double'] = true; + }); + }); + await page.dblclick('button', { trial: true }); + expect(await page.evaluate('double')).toBe(false); + expect(await page.evaluate('result')).toBe('Was not clicked'); +}); + it('should fail when obscured and not waiting for hit target', async ({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); diff --git a/tests/tap.spec.ts b/tests/tap.spec.ts index bfc3abf896..925bcca26f 100644 --- a/tests/tap.spec.ts +++ b/tests/tap.spec.ts @@ -50,6 +50,17 @@ it('should send all of the correct events', async ({}) => { ]); }); +it('trial run should not tap', async ({}) => { + await page.setContent(` +
a
+
b
+ `); + await page.tap('#a'); + const eventsHandle = await trackEvents(await page.$('#b')); + await page.tap('#b', { trial: true }); + expect(await eventsHandle.jsonValue()).toEqual([]); +}); + it('should not send mouse events touchstart is canceled', async ({}) => { await page.setContent(`
`); await page.evaluate(() => { diff --git a/types/types.d.ts b/types/types.d.ts index d0d23fb0aa..faa2d1f104 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -1392,6 +1392,12 @@ export interface Page { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -1463,6 +1469,12 @@ export interface Page { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -1567,6 +1579,12 @@ export interface Page { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -1950,6 +1968,12 @@ export interface Page { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -2723,6 +2747,12 @@ export interface Page { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -2839,6 +2869,12 @@ export interface Page { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -3604,6 +3640,12 @@ export interface Frame { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; childFrames(): Array; @@ -3674,6 +3716,12 @@ export interface Frame { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -3745,6 +3793,12 @@ export interface Frame { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -3963,6 +4017,12 @@ export interface Frame { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -4359,6 +4419,12 @@ export interface Frame { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -4467,6 +4533,12 @@ export interface Frame { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -5778,6 +5850,12 @@ export interface ElementHandle extends JSHandle { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -5845,6 +5923,12 @@ export interface ElementHandle extends JSHandle { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -5915,6 +5999,12 @@ export interface ElementHandle extends JSHandle { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -6031,6 +6121,12 @@ export interface ElementHandle extends JSHandle { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -6375,6 +6471,12 @@ export interface ElementHandle extends JSHandle { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /** @@ -6472,6 +6574,12 @@ export interface ElementHandle extends JSHandle { * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#pagesetdefaulttimeouttimeout) 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; /**