diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index 4cfa512a4f..116fca63bc 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -259,6 +259,28 @@ class RecordActionTool implements RecorderTool { }); } + onContextMenu(event: MouseEvent) { + // the 'contextmenu' event is triggered by a right-click or equivalent action, + // and it prevents the click event from firing for that action, so we always + // convert 'contextmenu' into a right-click. + if (this._shouldIgnoreMouseEvent(event)) + return; + if (this._actionInProgress(event)) + return; + if (this._consumedDueToNoModel(event, this._hoveredModel)) + return; + + this._performAction({ + name: 'click', + selector: this._hoveredModel!.selector, + position: positionForEvent(event), + signals: [], + button: 'right', + modifiers: 0, + clickCount: 0 + }); + } + onPointerDown(event: PointerEvent) { if (this._shouldIgnoreMouseEvent(event)) return; diff --git a/tests/library/inspector/cli-codegen-3.spec.ts b/tests/library/inspector/cli-codegen-3.spec.ts index 2c0fff974a..8ad14c87b9 100644 --- a/tests/library/inspector/cli-codegen-3.spec.ts +++ b/tests/library/inspector/cli-codegen-3.spec.ts @@ -559,6 +559,60 @@ await page.GetByLabel("Coun\\"try").ClickAsync();`); ]); }); + test('should consume contextmenu events, despite a custom context menu', async ({ page, openRecorder }) => { + const recorder = await openRecorder(); + + await recorder.setContentAndWait(` + +
+ + `); + + await recorder.hoverOverElement('button'); + expect(await page.evaluate('log')).toEqual(['button: pointermove', 'button: mousemove']); + + const [message] = await Promise.all([ + page.waitForEvent('console', msg => msg.type() !== 'error'), + recorder.waitForOutput('JavaScript', `button: 'right'`), + recorder.trustedClick({ button: 'right' }), + ]); + expect(message.text()).toBe('right-clicked'); + expect(await page.evaluate('log')).toEqual([ + // hover + 'button: pointermove', + 'button: mousemove', + // trusted right click + 'button: pointerup', + 'button: pointermove', + 'button: mousemove', + 'button: pointerdown', + 'button: mousedown', + 'button: contextmenu', + 'menu: pointerup', + 'menu: mouseup' + ]); + }); + test('should assert value', async ({ openRecorder }) => { const recorder = await openRecorder(); diff --git a/tests/library/inspector/inspectorTest.ts b/tests/library/inspector/inspectorTest.ts index 1d5e30825a..6ebbc1fdd1 100644 --- a/tests/library/inspector/inspectorTest.ts +++ b/tests/library/inspector/inspectorTest.ts @@ -186,9 +186,9 @@ class Recorder { await this.page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); } - async trustedClick() { - await this.page.mouse.down(); - await this.page.mouse.up(); + async trustedClick(options?: { button?: 'left' | 'right' | 'middle' }) { + await this.page.mouse.down(options); + await this.page.mouse.up(options); } async focusElement(selector: string): Promise