From 2265d61324d701df2bf6deffb388ec88c542a54c Mon Sep 17 00:00:00 2001 From: James Kip Date: Fri, 1 May 2026 10:14:51 -0500 Subject: [PATCH] feat(recorder): allow Shift+click to interact with page during Pick Locator mode When the Pick Locator tool is active, holding Shift now lets mouse events pass through to the page instead of being consumed for locator selection. This allows users to interact with the page (e.g., open dropdowns, click buttons) without leaving Pick Locator mode, then release Shift to resume picking. Fixes https://github.com/microsoft/playwright/issues/33280 Co-Authored-By: Claude Opus 4.6 --- packages/injected/src/recorder/recorder.ts | 21 +++++++++++++ .../cli-codegen-pick-locator.spec.ts | 30 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/packages/injected/src/recorder/recorder.ts b/packages/injected/src/recorder/recorder.ts index 7b4c98f59ba99..ced81c7bc4b7e 100644 --- a/packages/injected/src/recorder/recorder.ts +++ b/packages/injected/src/recorder/recorder.ts @@ -89,6 +89,9 @@ class InspectTool implements RecorderTool { } onClick(event: MouseEvent) { + // Shift+click bypasses locator picking and interacts with the page. + if (event.shiftKey) + return; consumeEvent(event); if (event.button !== 0) return; @@ -97,22 +100,34 @@ class InspectTool implements RecorderTool { } onPointerDown(event: PointerEvent) { + if (event.shiftKey) + return; consumeEvent(event); } onPointerUp(event: PointerEvent) { + if (event.shiftKey) + return; consumeEvent(event); } onMouseDown(event: MouseEvent) { + if (event.shiftKey) + return; consumeEvent(event); } onMouseUp(event: MouseEvent) { + if (event.shiftKey) + return; consumeEvent(event); } onMouseMove(event: MouseEvent) { + if (event.shiftKey) { + this._reset(false); + return; + } consumeEvent(event); let target: HTMLElement | null = this._recorder.deepEventTarget(event); if (!target.isConnected) @@ -139,6 +154,8 @@ class InspectTool implements RecorderTool { } onMouseEnter(event: MouseEvent) { + if (event.shiftKey) + return; consumeEvent(event); } @@ -152,6 +169,10 @@ class InspectTool implements RecorderTool { onKeyDown(event: KeyboardEvent) { consumeEvent(event); + if (event.key === 'Shift') { + this._reset(false); + return; + } if (event.key === 'Escape') { if (this._assertVisibility) this._recorder.setMode('recording'); diff --git a/tests/library/inspector/cli-codegen-pick-locator.spec.ts b/tests/library/inspector/cli-codegen-pick-locator.spec.ts index 154d0290f11a8..c090c43f6eb68 100644 --- a/tests/library/inspector/cli-codegen-pick-locator.spec.ts +++ b/tests/library/inspector/cli-codegen-pick-locator.spec.ts @@ -30,6 +30,36 @@ test.describe(() => { `); }); + test('should shift-click to interact with page while picking locator', async ({ openRecorder }) => { + const { recorder } = await openRecorder(); + await recorder.setContentAndWait(` + +
initial
+ `); + + // Enter pick locator mode. + await recorder.page.click('x-pw-tool-item.pick-locator'); + + // Shift+click the button - should interact with the page, not pick a locator. + const button = recorder.page.getByRole('button', { name: 'Click me' }); + await recorder.trustedMove(button); + await recorder.page.keyboard.down('Shift'); + await recorder.trustedClick(); + await recorder.page.keyboard.up('Shift'); + + // Verify the page interaction happened. + await expect(recorder.page.locator('#target')).toHaveText('clicked'); + + // Now click without Shift - should pick the locator. + const target = recorder.page.locator('#target'); + await recorder.trustedMove(target); + await recorder.trustedClick(); + await recorder.recorderPage.getByRole('tab', { name: 'Locator' }).click(); + await expect(recorder.recorderPage.locator('.tab-locator .CodeMirror')).toMatchAriaSnapshot(` + - text: "getByText('clicked')" + `); + }); + test('should update locator highlight', async ({ openRecorder }) => { const { recorder } = await openRecorder(); await recorder.setContentAndWait(`