fix(hover): do not require the element to be enabled before hovering (#3445)
This commit is contained in:
Родитель
c1de95f91f
Коммит
dec8fb7890
|
@ -8,7 +8,8 @@ Some actions like `page.click()` support `{force: true}` option that disable non
|
|||
|
||||
| Actions | Performed checks |
|
||||
| ------ | ------- |
|
||||
| `check()`<br>`click()`<br>`dblclick()`<br>`hover()`<br>`uncheck()` | [Visible]<br>[Stable]<br>[Enabled]<br>[Receiving Events]<br>[Attached] |
|
||||
| `check()`<br>`click()`<br>`dblclick()`<br>`uncheck()` | [Visible]<br>[Stable]<br>[Enabled]<br>[Receiving Events]<br>[Attached] |
|
||||
| `hover()` | [Visible]<br>[Stable]<br>[Receiving Events]<br>[Attached] |
|
||||
| `fill()` | [Visible]<br>[Enabled]<br>[Editable]<br>[Attached] |
|
||||
| `dispatchEvent()`<br>`focus()`<br>`press()`<br>`setInputFiles()`<br>`selectOption()`<br>`type()` | [Attached] |
|
||||
| `scrollIntoViewIfNeeded()`<br>`screenshot()` | [Visible]<br>[Stable]<br>[Attached] |
|
||||
|
|
15
src/dom.ts
15
src/dom.ts
|
@ -287,11 +287,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
};
|
||||
}
|
||||
|
||||
async _retryPointerAction(progress: Progress, actionName: string, action: (point: types.Point) => Promise<void>, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||
async _retryPointerAction(progress: Progress, actionName: string, waitForEnabled: boolean, action: (point: types.Point) => Promise<void>,
|
||||
options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||
let first = true;
|
||||
while (progress.isRunning()) {
|
||||
progress.logger.info(`${first ? 'attempting' : 'retrying'} ${actionName} action`);
|
||||
const result = await this._performPointerAction(progress, actionName, action, options);
|
||||
const result = await this._performPointerAction(progress, actionName, waitForEnabled, action, options);
|
||||
first = false;
|
||||
if (result === 'error:notvisible') {
|
||||
if (options.force)
|
||||
|
@ -316,12 +317,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
return 'done';
|
||||
}
|
||||
|
||||
async _performPointerAction(progress: Progress, actionName: string, action: (point: types.Point) => Promise<void>, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notvisible' | 'error:notconnected' | 'error:notinviewport' | 'error:nothittarget' | 'done'> {
|
||||
async _performPointerAction(progress: Progress, actionName: string, waitForEnabled: boolean, action: (point: types.Point) => Promise<void>, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notvisible' | 'error:notconnected' | 'error:notinviewport' | 'error:nothittarget' | 'done'> {
|
||||
const { force = false, position } = options;
|
||||
if ((options as any).__testHookBeforeStable)
|
||||
await (options as any).__testHookBeforeStable();
|
||||
if (!force) {
|
||||
const result = await this._waitForDisplayedAtStablePosition(progress, true /* waitForEnabled */);
|
||||
const result = await this._waitForDisplayedAtStablePosition(progress, waitForEnabled);
|
||||
if (result !== 'done')
|
||||
return result;
|
||||
}
|
||||
|
@ -379,7 +380,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
_hover(progress: Progress, options: types.PointerActionOptions & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||
return this._retryPointerAction(progress, 'hover', 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);
|
||||
}
|
||||
|
||||
click(options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}): Promise<void> {
|
||||
|
@ -390,7 +391,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
_click(progress: Progress, options: types.MouseClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||
return this._retryPointerAction(progress, 'click', point => this._page.mouse.click(point.x, point.y, options), options);
|
||||
return this._retryPointerAction(progress, 'click', true /* waitForEnabled */, point => this._page.mouse.click(point.x, point.y, options), options);
|
||||
}
|
||||
|
||||
dblclick(options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions = {}): Promise<void> {
|
||||
|
@ -401,7 +402,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
|
||||
_dblclick(progress: Progress, options: types.MouseMultiClickOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||
return this._retryPointerAction(progress, 'dblclick', point => this._page.mouse.dblclick(point.x, point.y, options), options);
|
||||
return this._retryPointerAction(progress, 'dblclick', true /* waitForEnabled */, point => this._page.mouse.dblclick(point.x, point.y, options), options);
|
||||
}
|
||||
|
||||
async selectOption(values: string | ElementHandle | types.SelectOption | string[] | ElementHandle[] | types.SelectOption[] | null, options: types.NavigatingActionWaitOptions = {}): Promise<string[]> {
|
||||
|
|
|
@ -107,6 +107,13 @@ it('should trigger hover state', async({page, server}) => {
|
|||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
|
||||
});
|
||||
|
||||
it('should trigger hover state on disabled button', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.$eval('#button-6', (button: HTMLButtonElement) => button.disabled = true);
|
||||
await page.hover('#button-6', { timeout: 5000 });
|
||||
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
|
||||
});
|
||||
|
||||
it('should trigger hover state with removed window.Node', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
await page.evaluate(() => delete window.Node);
|
||||
|
|
Загрузка…
Ссылка в новой задаче