mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 13:45:36 +03:00
fix(click): account for detached elements and iframe overlays (#10206)
This commit is contained in:
parent
1e38ec5fa4
commit
9ec3e7cd52
@ -413,7 +413,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||||||
return this._finishPointerActionDetectLayoutShift(progress, actionName, point, options, action);
|
return this._finishPointerActionDetectLayoutShift(progress, actionName, point, options, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _finishPointerAction(progress: Progress, actionName: string, point: types.Point, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions, action: (point: types.Point) => Promise<void>) {
|
private async _finishPointerAction(progress: Progress, actionName: string, point: types.Point, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions, action: (point: types.Point) => Promise<void>): Promise<'error:notconnected' | { hitTargetDescription: string } | 'done'> {
|
||||||
if (!options.force) {
|
if (!options.force) {
|
||||||
if ((options as any).__testHookBeforeHitTarget)
|
if ((options as any).__testHookBeforeHitTarget)
|
||||||
await (options as any).__testHookBeforeHitTarget();
|
await (options as any).__testHookBeforeHitTarget();
|
||||||
@ -451,7 +451,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||||||
return 'done';
|
return 'done';
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _finishPointerActionDetectLayoutShift(progress: Progress, actionName: string, point: types.Point, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions, action: (point: types.Point) => Promise<void>) {
|
private async _finishPointerActionDetectLayoutShift(progress: Progress, actionName: string, point: types.Point, options: types.PointerActionOptions & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions, action: (point: types.Point) => Promise<void>): Promise<'error:notconnected' | { hitTargetDescription: string } | 'done'> {
|
||||||
await progress.beforeInputAction(this);
|
await progress.beforeInputAction(this);
|
||||||
|
|
||||||
let hitTargetInterceptionHandle: js.JSHandle<HitTargetInterceptionResult> | undefined;
|
let hitTargetInterceptionHandle: js.JSHandle<HitTargetInterceptionResult> | undefined;
|
||||||
|
@ -728,6 +728,11 @@ export class InjectedScript {
|
|||||||
if (!event.isTrusted)
|
if (!event.isTrusted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Element was detached during the action, for example in some event handler.
|
||||||
|
// If events before that were correctly pointing to it, consider this a valid scenario.
|
||||||
|
if (!element.isConnected)
|
||||||
|
return;
|
||||||
|
|
||||||
// Determine the event point. Note that Firefox does not always have window.TouchEvent.
|
// Determine the event point. Note that Firefox does not always have window.TouchEvent.
|
||||||
const point = (!!window.TouchEvent && (event instanceof window.TouchEvent)) ? event.touches[0] : (event as MouseEvent | PointerEvent);
|
const point = (!!window.TouchEvent && (event instanceof window.TouchEvent)) ? event.touches[0] : (event as MouseEvent | PointerEvent);
|
||||||
if (!!point && (result === undefined || result === 'done')) {
|
if (!!point && (result === undefined || result === 'done')) {
|
||||||
@ -745,7 +750,11 @@ export class InjectedScript {
|
|||||||
const stop = () => {
|
const stop = () => {
|
||||||
if (this._hitTargetInterceptor === listener)
|
if (this._hitTargetInterceptor === listener)
|
||||||
this._hitTargetInterceptor = undefined;
|
this._hitTargetInterceptor = undefined;
|
||||||
return result!;
|
// If we did not get any events, consider things working. Possible causes:
|
||||||
|
// - JavaScript is disabled (webkit-only).
|
||||||
|
// - Some <iframe> overlays the element from another frame.
|
||||||
|
// - Hovering a disabled control prevents any events from firing.
|
||||||
|
return result || 'done';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: this removes previous listener, just in case there are two concurrent clicks
|
// Note: this removes previous listener, just in case there are two concurrent clicks
|
||||||
|
@ -68,6 +68,19 @@ it('should block click when mousedown succeeds but mouseup fails', async ({ page
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should click when element detaches in mousedown', async ({ page, server }) => {
|
||||||
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
|
await page.$eval('button', button => {
|
||||||
|
button.addEventListener('mousedown', () => {
|
||||||
|
(window as any).result = 'Mousedown';
|
||||||
|
button.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.click('button', { timeout: 1000 });
|
||||||
|
expect(await page.evaluate('result')).toBe('Mousedown');
|
||||||
|
});
|
||||||
|
|
||||||
it('should not block programmatic events', async ({ page, server }) => {
|
it('should not block programmatic events', async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
await page.$eval('button', button => {
|
await page.$eval('button', button => {
|
||||||
|
Loading…
Reference in New Issue
Block a user