From b0f15b320fddec3e62ec1254111c6875ec484114 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Tue, 17 Sep 2024 08:37:49 +0200 Subject: [PATCH] fix(recorder): reattach toolbar if it was unmounted by framework hydration (#32637) Closes https://github.com/microsoft/playwright/issues/32632. A side effect of Remix's hydration implementation is that it throws away the entire DOM. This is broadly discussed in https://github.com/remix-run/remix/issues/4822 - there might be a fix in coming React versions, but who knows. Besides breaking browser extensions, this also deletes our toolbar! This PR fixes it by periodically checking in on `x-pw-glass`, and remounting it if it was unmounted. Hacky but effective! --- .../src/server/injected/highlight.ts | 3 ++- .../src/server/injected/recorder/recorder.ts | 7 +++++++ tests/library/inspector/cli-codegen-3.spec.ts | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/playwright-core/src/server/injected/highlight.ts b/packages/playwright-core/src/server/injected/highlight.ts index c85216e106..c89df54a6f 100644 --- a/packages/playwright-core/src/server/injected/highlight.ts +++ b/packages/playwright-core/src/server/injected/highlight.ts @@ -90,7 +90,8 @@ export class Highlight { } install() { - this._injectedScript.document.documentElement.appendChild(this._glassPaneElement); + if (!this._injectedScript.document.documentElement.contains(this._glassPaneElement)) + this._injectedScript.document.documentElement.appendChild(this._glassPaneElement); } setLanguage(language: Language) { diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index 3ad40d53e2..dfbd4608f0 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -1036,7 +1036,14 @@ export class Recorder { addEventListener(this.document, 'focus', event => this._onFocus(event), true), addEventListener(this.document, 'scroll', event => this._onScroll(event), true), ]; + this.highlight.install(); + // some frameworks erase the DOM on hydration, this ensures it's reattached + const recreationInterval = setInterval(() => { + this.highlight.install(); + }, 500); + this._listeners.push(() => clearInterval(recreationInterval)); + this.overlay?.install(); this.document.adoptedStyleSheets.push(this._stylesheet); } diff --git a/tests/library/inspector/cli-codegen-3.spec.ts b/tests/library/inspector/cli-codegen-3.spec.ts index 6828576456..b2f0c39988 100644 --- a/tests/library/inspector/cli-codegen-3.spec.ts +++ b/tests/library/inspector/cli-codegen-3.spec.ts @@ -739,4 +739,21 @@ await page.GetByLabel("Coun\\"try").ClickAsync();`); expect.soft(sources1.get('Java')!.text).toContain(`assertThat(page.getByRole(AriaRole.TEXTBOX)).isVisible()`); expect.soft(sources1.get('C#')!.text).toContain(`await Expect(page.GetByRole(AriaRole.Textbox)).ToBeVisibleAsync()`); }); + + test('should keep toolbar visible even if webpage erases content in hydration', async ({ openRecorder }) => { + const recorder = await openRecorder(); + + const hydrate = () => { + setTimeout(() => { + document.documentElement.innerHTML = '

Post-Hydration Content

'; + }, 500); + }; + await recorder.setContentAndWait(` +

Pre-Hydration Content

+ + `); + + await expect(recorder.page.getByText('Post-Hydration Content')).toBeVisible(); + await expect(recorder.page.locator('x-pw-glass')).toBeVisible(); + }); });