From af2340c35e361619b3546692581492a51dcbf897 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 16 Apr 2020 15:38:41 -0700 Subject: [PATCH] fix(click): explicitly fail when element detached during click (#1835) We used to timeout instead. --- src/injected/injected.ts | 8 ++++++++ test/click.spec.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/injected/injected.ts b/src/injected/injected.ts index 7f781b6405..ff7a443793 100644 --- a/src/injected/injected.ts +++ b/src/injected/injected.ts @@ -286,12 +286,16 @@ class Injected { // and only force layout during actual rafs as a small optimisation. if (++counter === 1) return false; + if (!node.isConnected) + return 'Element is not attached to the DOM'; const clientRect = element.getBoundingClientRect(); const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height }; const isDisplayedAndStable = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height && rect.width > 0 && rect.height > 0; lastRect = rect; return isDisplayedAndStable; }); + if (typeof result === 'string') + throw new Error(result); if (!result) throw new Error(`waiting for element to be displayed and not moving failed: timeout exceeded`); } @@ -303,11 +307,15 @@ class Injected { if (!element) throw new Error('Element is not attached to the DOM'); const result = await this.poll('raf', timeout, () => { + if (!element!.isConnected) + return 'Element is not attached to the DOM'; let hitElement = this._deepElementFromPoint(document, point.x, point.y); while (hitElement && hitElement !== element) hitElement = this._parentElementOrShadowHost(hitElement); return hitElement === element; }); + if (typeof result === 'string') + throw new Error(result); if (!result) throw new Error(`waiting for element to receive mouse events failed: timeout exceeded`); } diff --git a/test/click.spec.js b/test/click.spec.js index f749004611..70ce13adac 100644 --- a/test/click.spec.js +++ b/test/click.spec.js @@ -554,6 +554,25 @@ describe('Page.click', function() { if (error2) expect(error2.message).toContain('timeout exceeded'); }); + it('should fail when element detaches after animation', async({page, server}) => { + await page.setContent(``); + await page.$eval('button', button => { + button.style.transition = 'margin-left 100000ms linear'; + }); + await page.$eval('button', button => { + button.style.marginLeft = '100000px'; + }); + const handle = await page.$('button'); + const promise = handle.click().catch(e => e); + await page.$eval('button', button => { + button.style.marginLeft = button.getBoundingClientRect().left + 'px'; + button.style.transition = ''; + button.remove(); + }); + const error = await promise; + expect(await page.evaluate(() => window.clicked)).toBe(undefined); + expect(error.message).toContain('Element is not attached to the DOM'); + }); }); describe('Page.check', function() {