fix: noWaitAfter option for hover (#17856)
Fixes https://github.com/microsoft/playwright/issues/17833
This commit is contained in:
parent
1b5c2f9aba
commit
639b28db3b
|
|
@ -555,6 +555,9 @@ When all steps combined have not finished during the specified [`option: timeout
|
||||||
### option: ElementHandle.hover.trial = %%-input-trial-%%
|
### option: ElementHandle.hover.trial = %%-input-trial-%%
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
|
|
||||||
|
### option: ElementHandle.hover.noWaitAfter = %%-input-no-wait-after-%%
|
||||||
|
* since: v1.28
|
||||||
|
|
||||||
## async method: ElementHandle.innerHTML
|
## async method: ElementHandle.innerHTML
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
- returns: <[string]>
|
- returns: <[string]>
|
||||||
|
|
|
||||||
|
|
@ -1059,6 +1059,8 @@ When all steps combined have not finished during the specified [`option: timeout
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
### option: Frame.hover.trial = %%-input-trial-%%
|
### option: Frame.hover.trial = %%-input-trial-%%
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
|
### option: Frame.hover.noWaitAfter = %%-input-no-wait-after-%%
|
||||||
|
* since: v1.28
|
||||||
|
|
||||||
## async method: Frame.innerHTML
|
## async method: Frame.innerHTML
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
|
|
|
||||||
|
|
@ -734,6 +734,8 @@ When all steps combined have not finished during the specified [`option: timeout
|
||||||
* since: v1.14
|
* since: v1.14
|
||||||
### option: Locator.hover.trial = %%-input-trial-%%
|
### option: Locator.hover.trial = %%-input-trial-%%
|
||||||
* since: v1.14
|
* since: v1.14
|
||||||
|
### option: Locator.hover.noWaitAfter = %%-input-no-wait-after-%%
|
||||||
|
* since: v1.28
|
||||||
|
|
||||||
## async method: Locator.innerHTML
|
## async method: Locator.innerHTML
|
||||||
* since: v1.14
|
* since: v1.14
|
||||||
|
|
|
||||||
|
|
@ -2369,6 +2369,8 @@ Shortcut for main frame's [`method: Frame.hover`].
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
### option: Page.hover.trial = %%-input-trial-%%
|
### option: Page.hover.trial = %%-input-trial-%%
|
||||||
* since: v1.11
|
* since: v1.11
|
||||||
|
### option: Page.hover.noWaitAfter = %%-input-no-wait-after-%%
|
||||||
|
* since: v1.28
|
||||||
|
|
||||||
## async method: Page.innerHTML
|
## async method: Page.innerHTML
|
||||||
* since: v1.8
|
* since: v1.8
|
||||||
|
|
|
||||||
|
|
@ -1331,6 +1331,7 @@ scheme.FrameHoverParams = tObject({
|
||||||
position: tOptional(tType('Point')),
|
position: tOptional(tType('Point')),
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
trial: tOptional(tBoolean),
|
trial: tOptional(tBoolean),
|
||||||
|
noWaitAfter: tOptional(tBoolean),
|
||||||
});
|
});
|
||||||
scheme.FrameHoverResult = tOptional(tObject({}));
|
scheme.FrameHoverResult = tOptional(tObject({}));
|
||||||
scheme.FrameInnerHTMLParams = tObject({
|
scheme.FrameInnerHTMLParams = tObject({
|
||||||
|
|
@ -1717,6 +1718,7 @@ scheme.ElementHandleHoverParams = tObject({
|
||||||
position: tOptional(tType('Point')),
|
position: tOptional(tType('Point')),
|
||||||
timeout: tOptional(tNumber),
|
timeout: tOptional(tNumber),
|
||||||
trial: tOptional(tBoolean),
|
trial: tOptional(tBoolean),
|
||||||
|
noWaitAfter: tOptional(tBoolean),
|
||||||
});
|
});
|
||||||
scheme.ElementHandleHoverResult = tOptional(tObject({}));
|
scheme.ElementHandleHoverResult = tOptional(tObject({}));
|
||||||
scheme.ElementHandleInnerHTMLParams = tOptional(tObject({}));
|
scheme.ElementHandleInnerHTMLParams = tOptional(tObject({}));
|
||||||
|
|
|
||||||
|
|
@ -499,7 +499,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
}, this._page._timeoutSettings.timeout(options));
|
}, this._page._timeoutSettings.timeout(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
_hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
_hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||||
return this._retryPointerAction(progress, 'hover', false /* waitForEnabled */, point => this._page.mouse.move(point.x, point.y), options);
|
return this._retryPointerAction(progress, 'hover', false /* waitForEnabled */, point => this._page.mouse.move(point.x, point.y), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1287,7 +1287,7 @@ export class Frame extends SdkObject {
|
||||||
return this._elementState(metadata, selector, 'checked', options);
|
return this._elementState(metadata, selector, 'checked', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async hover(metadata: CallMetadata, selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions = {}) {
|
async hover(metadata: CallMetadata, selector: string, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}) {
|
||||||
const controller = new ProgressController(metadata, this);
|
const controller = new ProgressController(metadata, this);
|
||||||
return controller.run(async progress => {
|
return controller.run(async progress => {
|
||||||
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._hover(progress, options)));
|
return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, handle => handle._hover(progress, options)));
|
||||||
|
|
|
||||||
28
packages/playwright-core/types/types.d.ts
vendored
28
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -2753,6 +2753,13 @@ export interface Page {
|
||||||
*/
|
*/
|
||||||
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||||
* element.
|
* element.
|
||||||
|
|
@ -5785,6 +5792,13 @@ export interface Frame {
|
||||||
*/
|
*/
|
||||||
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||||
* element.
|
* element.
|
||||||
|
|
@ -8710,6 +8724,13 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
|
||||||
*/
|
*/
|
||||||
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||||
* element.
|
* element.
|
||||||
|
|
@ -10171,6 +10192,13 @@ export interface Locator {
|
||||||
*/
|
*/
|
||||||
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
* A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
|
||||||
* element.
|
* element.
|
||||||
|
|
|
||||||
|
|
@ -2441,6 +2441,7 @@ export type FrameHoverParams = {
|
||||||
position?: Point,
|
position?: Point,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
trial?: boolean,
|
trial?: boolean,
|
||||||
|
noWaitAfter?: boolean,
|
||||||
};
|
};
|
||||||
export type FrameHoverOptions = {
|
export type FrameHoverOptions = {
|
||||||
strict?: boolean,
|
strict?: boolean,
|
||||||
|
|
@ -2449,6 +2450,7 @@ export type FrameHoverOptions = {
|
||||||
position?: Point,
|
position?: Point,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
trial?: boolean,
|
trial?: boolean,
|
||||||
|
noWaitAfter?: boolean,
|
||||||
};
|
};
|
||||||
export type FrameHoverResult = void;
|
export type FrameHoverResult = void;
|
||||||
export type FrameInnerHTMLParams = {
|
export type FrameInnerHTMLParams = {
|
||||||
|
|
@ -3095,6 +3097,7 @@ export type ElementHandleHoverParams = {
|
||||||
position?: Point,
|
position?: Point,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
trial?: boolean,
|
trial?: boolean,
|
||||||
|
noWaitAfter?: boolean,
|
||||||
};
|
};
|
||||||
export type ElementHandleHoverOptions = {
|
export type ElementHandleHoverOptions = {
|
||||||
force?: boolean,
|
force?: boolean,
|
||||||
|
|
@ -3102,6 +3105,7 @@ export type ElementHandleHoverOptions = {
|
||||||
position?: Point,
|
position?: Point,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
trial?: boolean,
|
trial?: boolean,
|
||||||
|
noWaitAfter?: boolean,
|
||||||
};
|
};
|
||||||
export type ElementHandleHoverResult = void;
|
export type ElementHandleHoverResult = void;
|
||||||
export type ElementHandleInnerHTMLParams = {};
|
export type ElementHandleInnerHTMLParams = {};
|
||||||
|
|
|
||||||
|
|
@ -1767,6 +1767,7 @@ Frame:
|
||||||
position: Point?
|
position: Point?
|
||||||
timeout: number?
|
timeout: number?
|
||||||
trial: boolean?
|
trial: boolean?
|
||||||
|
noWaitAfter: boolean?
|
||||||
tracing:
|
tracing:
|
||||||
snapshot: true
|
snapshot: true
|
||||||
pausesBeforeInput: true
|
pausesBeforeInput: true
|
||||||
|
|
@ -2332,6 +2333,7 @@ ElementHandle:
|
||||||
position: Point?
|
position: Point?
|
||||||
timeout: number?
|
timeout: number?
|
||||||
trial: boolean?
|
trial: boolean?
|
||||||
|
noWaitAfter: boolean?
|
||||||
tracing:
|
tracing:
|
||||||
snapshot: true
|
snapshot: true
|
||||||
pausesBeforeInput: true
|
pausesBeforeInput: true
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,16 @@ it('should hover when Node is removed', async ({ page, server }) => {
|
||||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hover should support noWaitAfter', async ({ page, server }) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.setContent(`<button onmouseover='location.href="${server.PREFIX}/next"'>GO</button>`);
|
||||||
|
await Promise.all([
|
||||||
|
new Promise(fulfill => server.setRoute('/next', fulfill)),
|
||||||
|
page.locator('button').hover({ noWaitAfter: true })
|
||||||
|
]);
|
||||||
|
expect(page.url()).toBe(server.EMPTY_PAGE);
|
||||||
|
});
|
||||||
|
|
||||||
it('should fill input', async ({ page, server }) => {
|
it('should fill input', async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||||
const handle = page.locator('input');
|
const handle = page.locator('input');
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,16 @@ it('should trigger hover state', async ({ page, server }) => {
|
||||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hover should support noWaitAfter', async ({ page, server }) => {
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.setContent(`<button onmouseover='location.href="${server.PREFIX}/next"'>GO</button>`);
|
||||||
|
await Promise.all([
|
||||||
|
new Promise(fulfill => server.setRoute('/next', fulfill)),
|
||||||
|
page.hover('button', { noWaitAfter: true })
|
||||||
|
]);
|
||||||
|
expect(page.url()).toBe(server.EMPTY_PAGE);
|
||||||
|
});
|
||||||
|
|
||||||
it('should trigger hover state on disabled button', async ({ page, server }) => {
|
it('should trigger hover state on disabled button', async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||||
await page.$eval('#button-6', (button: HTMLButtonElement) => button.disabled = true);
|
await page.$eval('#button-6', (button: HTMLButtonElement) => button.disabled = true);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue