From 1bdd5cfd1cc895cbdf8a3c7583c7c97e904c4ac2 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 26 Apr 2024 13:52:36 -0700 Subject: [PATCH] feat(ctrl_or_meta): add a universal ctrl-meta modifier --- docs/src/api/params.md | 2 +- docs/src/input.md | 16 ++++++++ .../playwright-core/src/protocol/validator.ts | 16 ++++---- packages/playwright-core/src/server/dom.ts | 4 +- packages/playwright-core/src/server/input.ts | 20 +++++++--- packages/playwright-core/src/server/page.ts | 2 +- .../src/server/recorder/utils.ts | 9 +++-- packages/playwright-core/src/server/types.ts | 3 +- packages/playwright-core/types/types.d.ts | 32 +++++++-------- packages/protocol/src/channels.ts | 32 +++++++-------- packages/protocol/src/protocol.yml | 8 ++++ .../library/browsercontext-page-event.spec.ts | 8 ++-- tests/library/inspector/cli-codegen-2.spec.ts | 6 +-- tests/page/page-keyboard.spec.ts | 39 ++++++++----------- 14 files changed, 114 insertions(+), 83 deletions(-) diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 2d704888677d2b..b40f8d4c7d55fa 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -97,7 +97,7 @@ A point to use relative to the top-left corner of element padding box. If not sp element. ## input-modifiers -- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"Meta"|"Shift">>> +- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">>> Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current modifiers back. If not specified, currently pressed modifiers are used. diff --git a/docs/src/input.md b/docs/src/input.md index f340e03f4845c6..7f14d27ec8a757 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -217,6 +217,10 @@ await page.getByText('Item').click({ button: 'right' }); // Shift + click await page.getByText('Item').click({ modifiers: ['Shift'] }); +// Ctrl + click or Windows and Linux +// Meta + click on macOS +await page.getByText('Item').click({ modifiers: ['ControlOrMeta'] }); + // Hover over element await page.getByText('Item').hover(); @@ -237,6 +241,10 @@ page.getByText("Item").click(new Locator.ClickOptions().setButton(MouseButton.RI // Shift + click page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.SHIFT))); +// Ctrl + click or Windows and Linux +// Meta + click on macOS +page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.CONTROL_OR_META))); + // Hover over element page.getByText("Item").hover(); @@ -257,6 +265,10 @@ await page.get_by_text("Item").click(button="right") # Shift + click await page.get_by_text("Item").click(modifiers=["Shift"]) +# Ctrl + click or Windows and Linux +# Meta + click on macOS +await page.get_by_text("Item").click(modifiers=["ControlOrMeta"]) + # Hover over element await page.get_by_text("Item").hover() @@ -297,6 +309,10 @@ await page.GetByText("Item").ClickAsync(new() { Button = MouseButton.Right }); // Shift + click await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.Shift } }); +// Ctrl + click or Windows and Linux +// Meta + click on macOS +await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.ControlOrMeta } }); + // Hover over element await page.GetByText("Item").HoverAsync(); diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 20f56e8d8951b6..f29428e2a8674d 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1342,7 +1342,7 @@ scheme.FrameClickParams = tObject({ strict: tOptional(tBoolean), force: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), delay: tOptional(tNumber), button: tOptional(tEnum(['left', 'right', 'middle'])), @@ -1372,7 +1372,7 @@ scheme.FrameDblclickParams = tObject({ strict: tOptional(tBoolean), force: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), delay: tOptional(tNumber), button: tOptional(tEnum(['left', 'right', 'middle'])), @@ -1450,7 +1450,7 @@ scheme.FrameHoverParams = tObject({ selector: tString, strict: tOptional(tBoolean), force: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), trial: tOptional(tBoolean), @@ -1597,7 +1597,7 @@ scheme.FrameTapParams = tObject({ strict: tOptional(tBoolean), force: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), trial: tOptional(tBoolean), @@ -1792,7 +1792,7 @@ scheme.ElementHandleCheckResult = tOptional(tObject({})); scheme.ElementHandleClickParams = tObject({ force: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), delay: tOptional(tNumber), button: tOptional(tEnum(['left', 'right', 'middle'])), @@ -1808,7 +1808,7 @@ scheme.ElementHandleContentFrameResult = tObject({ scheme.ElementHandleDblclickParams = tObject({ force: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), delay: tOptional(tNumber), button: tOptional(tEnum(['left', 'right', 'middle'])), @@ -1838,7 +1838,7 @@ scheme.ElementHandleGetAttributeResult = tObject({ }); scheme.ElementHandleHoverParams = tObject({ force: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), trial: tOptional(tBoolean), @@ -1962,7 +1962,7 @@ scheme.ElementHandleSetInputFilesResult = tOptional(tObject({})); scheme.ElementHandleTapParams = tObject({ force: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean), - modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))), + modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))), position: tOptional(tType('Point')), timeout: tOptional(tNumber), trial: tOptional(tBoolean), diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index b413018b2cfef9..eb2f2068f1e98e 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -449,11 +449,11 @@ export class ElementHandle extends js.JSHandle { progress.throwIfAborted(); // Avoid action that has side-effects. let restoreModifiers: types.KeyboardModifier[] | undefined; if (options && options.modifiers) - restoreModifiers = await this._page.keyboard._ensureModifiers(options.modifiers); + restoreModifiers = await this._page.keyboard.ensureModifiers(options.modifiers); progress.log(` performing ${actionName} action`); await action(point); if (restoreModifiers) - await this._page.keyboard._ensureModifiers(restoreModifiers); + await this._page.keyboard.ensureModifiers(restoreModifiers); if (hitTargetInterceptionHandle) { const stopHitTargetInterception = hitTargetInterceptionHandle.evaluate(h => h.stop()).catch(e => 'done' as const).finally(() => { hitTargetInterceptionHandle?.dispose(); diff --git a/packages/playwright-core/src/server/input.ts b/packages/playwright-core/src/server/input.ts index d6cc5bbd551402..a70ee114a4f593 100644 --- a/packages/playwright-core/src/server/input.ts +++ b/packages/playwright-core/src/server/input.ts @@ -44,11 +44,9 @@ export class Keyboard { private _pressedModifiers = new Set(); private _pressedKeys = new Set(); private _raw: RawKeyboard; - private _page: Page; - constructor(raw: RawKeyboard, page: Page) { + constructor(raw: RawKeyboard) { this._raw = raw; - this._page = page; } async down(key: string) { @@ -61,7 +59,8 @@ export class Keyboard { await this._raw.keydown(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location, autoRepeat, text); } - private _keyDescriptionForString(keyString: string): KeyDescription { + private _keyDescriptionForString(str: string): KeyDescription { + const keyString = resolveSmartModifierString(str); let description = usKeyboardLayout.get(keyString); assert(description, `Unknown key: "${keyString}"`); const shift = this._pressedModifiers.has('Shift'); @@ -126,7 +125,8 @@ export class Keyboard { await this.up(tokens[i]); } - async _ensureModifiers(modifiers: types.KeyboardModifier[]): Promise { + async ensureModifiers(mm: types.SmartKeyboardModifier[]): Promise { + const modifiers = mm.map(resolveSmartModifier); for (const modifier of modifiers) { if (!kModifiers.includes(modifier)) throw new Error('Unknown modifier ' + modifier); @@ -148,6 +148,16 @@ export class Keyboard { } } +export function resolveSmartModifierString(key: string): string { + if (key === 'ControlOrMeta') + return process.platform === 'darwin' ? 'Meta' : 'Control'; + return key; +} + +export function resolveSmartModifier(m: types.SmartKeyboardModifier): types.KeyboardModifier { + return resolveSmartModifierString(m) as types.KeyboardModifier; +} + export interface RawMouse { move(x: number, y: number, button: types.MouseButton | 'none', buttons: Set, modifiers: Set, forClick: boolean): Promise; down(x: number, y: number, button: types.MouseButton, buttons: Set, modifiers: Set, clickCount: number): Promise; diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 07a3df8fc5a1c7..28ff203a3c1cea 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -183,7 +183,7 @@ export class Page extends SdkObject { this._delegate = delegate; this._browserContext = browserContext; this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate)); - this.keyboard = new input.Keyboard(delegate.rawKeyboard, this); + this.keyboard = new input.Keyboard(delegate.rawKeyboard); this.mouse = new input.Mouse(delegate.rawMouse, this); this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this); this._timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings); diff --git a/packages/playwright-core/src/server/recorder/utils.ts b/packages/playwright-core/src/server/recorder/utils.ts index d4c09b5a51772a..883a8ab129aefe 100644 --- a/packages/playwright-core/src/server/recorder/utils.ts +++ b/packages/playwright-core/src/server/recorder/utils.ts @@ -15,6 +15,7 @@ */ import type { Frame } from '../frames'; +import type { SmartKeyboardModifier } from '../types'; import type * as actions from './recorderActions'; export type MouseClickOptions = Parameters[2]; @@ -36,14 +37,14 @@ export function toClickOptions(action: actions.ClickAction): { method: 'click' | return { method, options }; } -export function toModifiers(modifiers: number): ('Alt' | 'Control' | 'Meta' | 'Shift')[] { - const result: ('Alt' | 'Control' | 'Meta' | 'Shift')[] = []; +export function toModifiers(modifiers: number): SmartKeyboardModifier[] { + const result: SmartKeyboardModifier[] = []; if (modifiers & 1) result.push('Alt'); if (modifiers & 2) - result.push('Control'); + result.push('ControlOrMeta'); if (modifiers & 4) - result.push('Meta'); + result.push('ControlOrMeta'); if (modifiers & 8) result.push('Shift'); return result; diff --git a/packages/playwright-core/src/server/types.ts b/packages/playwright-core/src/server/types.ts index e2ba06d50bec5d..bcdc20751e56f3 100644 --- a/packages/playwright-core/src/server/types.ts +++ b/packages/playwright-core/src/server/types.ts @@ -105,10 +105,11 @@ export type ProxySettings = { }; export type KeyboardModifier = 'Alt' | 'Control' | 'Meta' | 'Shift'; +export type SmartKeyboardModifier = KeyboardModifier | 'ControlOrMeta'; export type MouseButton = 'left' | 'right' | 'middle'; export type PointerActionOptions = { - modifiers?: KeyboardModifier[]; + modifiers?: SmartKeyboardModifier[]; position?: Point; }; diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 19cf7cebeb0a7b..dfbfa1c65524a1 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -2066,7 +2066,7 @@ export interface Page { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -2181,7 +2181,7 @@ export interface Page { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -3071,7 +3071,7 @@ export interface Page { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -4185,7 +4185,7 @@ export interface Page { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -5761,7 +5761,7 @@ export interface Frame { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -5848,7 +5848,7 @@ export interface Frame { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -6541,7 +6541,7 @@ export interface Frame { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -7231,7 +7231,7 @@ export interface Frame { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -9840,7 +9840,7 @@ export interface ElementHandle extends JSHandle { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -9919,7 +9919,7 @@ export interface ElementHandle extends JSHandle { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -10080,7 +10080,7 @@ export interface ElementHandle extends JSHandle { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -10637,7 +10637,7 @@ export interface ElementHandle extends JSHandle { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -11285,7 +11285,7 @@ export interface Locator { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -11397,7 +11397,7 @@ export interface Locator { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -12070,7 +12070,7 @@ export interface Locator { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You @@ -12866,7 +12866,7 @@ export interface Locator { * Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * current modifiers back. If not specified, currently pressed modifiers are used. */ - modifiers?: Array<"Alt"|"Control"|"Meta"|"Shift">; + modifiers?: Array<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">; /** * Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index d9c1ec6ad978a5..edc8e2cb7eb35f 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -2458,7 +2458,7 @@ export type FrameClickParams = { strict?: boolean, force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -2470,7 +2470,7 @@ export type FrameClickOptions = { strict?: boolean, force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -2510,7 +2510,7 @@ export type FrameDblclickParams = { strict?: boolean, force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -2521,7 +2521,7 @@ export type FrameDblclickOptions = { strict?: boolean, force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -2633,7 +2633,7 @@ export type FrameHoverParams = { selector: string, strict?: boolean, force?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -2642,7 +2642,7 @@ export type FrameHoverParams = { export type FrameHoverOptions = { strict?: boolean, force?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -2867,7 +2867,7 @@ export type FrameTapParams = { strict?: boolean, force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -2876,7 +2876,7 @@ export type FrameTapOptions = { strict?: boolean, force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -3210,7 +3210,7 @@ export type ElementHandleCheckResult = void; export type ElementHandleClickParams = { force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -3221,7 +3221,7 @@ export type ElementHandleClickParams = { export type ElementHandleClickOptions = { force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -3238,7 +3238,7 @@ export type ElementHandleContentFrameResult = { export type ElementHandleDblclickParams = { force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -3248,7 +3248,7 @@ export type ElementHandleDblclickParams = { export type ElementHandleDblclickOptions = { force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, delay?: number, button?: 'left' | 'right' | 'middle', @@ -3290,7 +3290,7 @@ export type ElementHandleGetAttributeResult = { }; export type ElementHandleHoverParams = { force?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -3298,7 +3298,7 @@ export type ElementHandleHoverParams = { }; export type ElementHandleHoverOptions = { force?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -3488,7 +3488,7 @@ export type ElementHandleSetInputFilesResult = void; export type ElementHandleTapParams = { force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, @@ -3496,7 +3496,7 @@ export type ElementHandleTapParams = { export type ElementHandleTapOptions = { force?: boolean, noWaitAfter?: boolean, - modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], + modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[], position?: Point, timeout?: number, trial?: boolean, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index b2442f90979f54..3f1d75f0d04fa3 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1791,6 +1791,7 @@ Frame: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -1842,6 +1843,7 @@ Frame: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -1956,6 +1958,7 @@ Frame: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -2164,6 +2167,7 @@ Frame: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -2442,6 +2446,7 @@ ElementHandle: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -2475,6 +2480,7 @@ ElementHandle: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -2532,6 +2538,7 @@ ElementHandle: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? @@ -2718,6 +2725,7 @@ ElementHandle: literals: - Alt - Control + - ControlOrMeta - Meta - Shift position: Point? diff --git a/tests/library/browsercontext-page-event.spec.ts b/tests/library/browsercontext-page-event.spec.ts index ed1beac27aaca5..b851dd0ce9b1e0 100644 --- a/tests/library/browsercontext-page-event.spec.ts +++ b/tests/library/browsercontext-page-event.spec.ts @@ -170,7 +170,7 @@ it('should work with Shift-clicking', async ({ browser, server, browserName }) = await context.close(); }); -it('should work with Ctrl-clicking', async ({ browser, server, isMac, browserName }) => { +it('should work with Ctrl-clicking', async ({ browser, server, browserName }) => { it.fixme(browserName === 'firefox', 'Reports an opener in this case.'); const context = await browser.newContext(); @@ -179,13 +179,13 @@ it('should work with Ctrl-clicking', async ({ browser, server, isMac, browserNam await page.setContent('yo'); const [popup] = await Promise.all([ context.waitForEvent('page'), - page.click('a', { modifiers: [isMac ? 'Meta' : 'Control'] }), + page.click('a', { modifiers: ['ControlOrMeta'] }), ]); expect(await popup.opener()).toBe(null); await context.close(); }); -it('should not hang on ctrl-click during provisional load', async ({ context, page, server, isMac, isWindows, browserName, isLinux }) => { +it('should not hang on ctrl-click during provisional load', async ({ context, page, server, isWindows, browserName, isLinux }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11595' }); it.skip(browserName === 'chromium', 'Chromium does not dispatch renderer messages while navigation is provisional.'); it.fixme(browserName === 'webkit' && isWindows, 'Timesout while trying to click'); @@ -195,7 +195,7 @@ it('should not hang on ctrl-click during provisional load', async ({ context, pa server.setRoute('/slow.html', () => {}); const [popup] = await Promise.all([ context.waitForEvent('page'), - server.waitForRequest('/slow.html').then(() => page.click('a', { modifiers: [isMac ? 'Meta' : 'Control'] })), + server.waitForRequest('/slow.html').then(() => page.click('a', { modifiers: ['ControlOrMeta'] })), page.evaluate(url => setTimeout(() => location.href = url, 0), server.CROSS_PROCESS_PREFIX + '/slow.html'), ]); expect(popup).toBeTruthy(); diff --git a/tests/library/inspector/cli-codegen-2.spec.ts b/tests/library/inspector/cli-codegen-2.spec.ts index 246a9876d76320..277d5ebd824fb7 100644 --- a/tests/library/inspector/cli-codegen-2.spec.ts +++ b/tests/library/inspector/cli-codegen-2.spec.ts @@ -337,14 +337,14 @@ await page.GetByRole(AriaRole.Button, new() { Name = "click me" }).ClickAsync(); } }); - test('should record open in a new tab with url', async ({ page, openRecorder, browserName, platform }) => { + test('should record open in a new tab with url', async ({ page, openRecorder, browserName }) => { const recorder = await openRecorder(); await recorder.setContentAndWait(`link`); const locator = await recorder.hoverOverElement('a'); expect(locator).toBe(`getByRole('link', { name: 'link' })`); - await page.click('a', { modifiers: [platform === 'darwin' ? 'Meta' : 'Control'] }); + await page.click('a', { modifiers: ['ControlOrMeta'] }); const sources = await recorder.waitForOutput('JavaScript', 'page1'); if (browserName !== 'firefox') { @@ -361,7 +361,7 @@ await page1.GotoAsync("about:blank?foo");`); expect(sources.get('JavaScript')!.text).toContain(` const page1Promise = page.waitForEvent('popup'); await page.getByRole('link', { name: 'link' }).click({ - modifiers: ['${platform === 'darwin' ? 'Meta' : 'Control'}'] + modifiers: ['ControlOrMeta'] }); const page1 = await page1Promise;`); } diff --git a/tests/page/page-keyboard.spec.ts b/tests/page/page-keyboard.spec.ts index 94a7c23c90b4d5..027648f48e7a3a 100644 --- a/tests/page/page-keyboard.spec.ts +++ b/tests/page/page-keyboard.spec.ts @@ -318,10 +318,9 @@ it('should handle selectAll', async ({ page, server, isMac }) => { await page.goto(server.PREFIX + '/input/textarea.html'); const textarea = await page.$('textarea'); await textarea.type('some text'); - const modifier = isMac ? 'Meta' : 'Control'; - await page.keyboard.down(modifier); + await page.keyboard.down('ControlOrMeta'); await page.keyboard.press('a'); - await page.keyboard.up(modifier); + await page.keyboard.up('ControlOrMeta'); await page.keyboard.press('Backspace'); expect(await page.$eval('textarea', textarea => textarea.value)).toBe(''); }); @@ -346,10 +345,9 @@ it('should be able to prevent selectAll', async ({ page, server, isMac }) => { event.preventDefault(); }, false); }); - const modifier = isMac ? 'Meta' : 'Control'; - await page.keyboard.down(modifier); + await page.keyboard.down('ControlOrMeta'); await page.keyboard.press('a'); - await page.keyboard.up(modifier); + await page.keyboard.up('ControlOrMeta'); await page.keyboard.press('Backspace'); expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex'); }); @@ -469,39 +467,36 @@ it('should dispatch a click event on a button when Enter gets pressed', async ({ expect((await actual.jsonValue()).clicked).toBe(true); }); -it('should support simple copy-pasting', async ({ page, isMac, browserName }) => { - const modifier = isMac ? 'Meta' : 'Control'; +it('should support simple copy-pasting', async ({ page }) => { await page.setContent(`
123
`); await page.focus('div'); - await page.keyboard.press(`${modifier}+KeyA`); - await page.keyboard.press(`${modifier}+KeyC`); - await page.keyboard.press(`${modifier}+KeyV`); - await page.keyboard.press(`${modifier}+KeyV`); + await page.keyboard.press(`ControlOrMeta+KeyA`); + await page.keyboard.press(`ControlOrMeta+KeyC`); + await page.keyboard.press(`ControlOrMeta+KeyV`); + await page.keyboard.press(`ControlOrMeta+KeyV`); expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123'); }); -it('should support simple cut-pasting', async ({ page, isMac }) => { - const modifier = isMac ? 'Meta' : 'Control'; +it('should support simple cut-pasting', async ({ page }) => { await page.setContent(`
123
`); await page.focus('div'); - await page.keyboard.press(`${modifier}+KeyA`); - await page.keyboard.press(`${modifier}+KeyX`); - await page.keyboard.press(`${modifier}+KeyV`); - await page.keyboard.press(`${modifier}+KeyV`); + await page.keyboard.press(`ControlOrMeta+KeyA`); + await page.keyboard.press(`ControlOrMeta+KeyX`); + await page.keyboard.press(`ControlOrMeta+KeyV`); + await page.keyboard.press(`ControlOrMeta+KeyV`); expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123'); }); -it('should support undo-redo', async ({ page, isMac, browserName, isLinux }) => { +it('should support undo-redo', async ({ page, browserName, isLinux }) => { it.fixme(browserName === 'webkit' && isLinux, 'https://github.com/microsoft/playwright/issues/12000'); - const modifier = isMac ? 'Meta' : 'Control'; await page.setContent(`
`); const div = page.locator('div'); await expect(div).toHaveText(''); await div.type('123'); await expect(div).toHaveText('123'); - await page.keyboard.press(`${modifier}+KeyZ`); + await page.keyboard.press(`ControlOrMeta+KeyZ`); await expect(div).toHaveText(''); - await page.keyboard.press(`Shift+${modifier}+KeyZ`); + await page.keyboard.press(`Shift+ControlOrMeta+KeyZ`); await expect(div).toHaveText('123'); });