From 96f3d19819221f794c4bb720d6d3d0c738f0e48f Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 29 Apr 2024 08:15:12 -0700 Subject: [PATCH] feat(ctrl_or_meta): add a universal ctrl-meta modifier (#30572) Fixes https://github.com/microsoft/playwright/issues/12168 --- docs/src/api/class-elementhandle.md | 2 +- docs/src/api/class-frame.md | 3 +- docs/src/api/class-keyboard.md | 6 +- docs/src/api/class-locator.md | 3 +- docs/src/api/class-page.md | 3 +- docs/src/api/params.md | 5 +- 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 | 98 ++++++++++++------- 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 ++++---- 19 files changed, 171 insertions(+), 112 deletions(-) diff --git a/docs/src/api/class-elementhandle.md b/docs/src/api/class-elementhandle.md index 3f27509a0d580..df3bd3a164fd0 100644 --- a/docs/src/api/class-elementhandle.md +++ b/docs/src/api/class-elementhandle.md @@ -692,7 +692,7 @@ generate the text for. A superset of the [`param: key`] values can be found `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. -Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. +Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case. diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md index d9d2b92d07424..50e157b15cdd7 100644 --- a/docs/src/api/class-frame.md +++ b/docs/src/api/class-frame.md @@ -1394,7 +1394,8 @@ generate the text for. A superset of the [`param: key`] values can be found `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. -Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. +Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`. +`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case. diff --git a/docs/src/api/class-keyboard.md b/docs/src/api/class-keyboard.md index a37869df75aad..64a539cfbe9f9 100644 --- a/docs/src/api/class-keyboard.md +++ b/docs/src/api/class-keyboard.md @@ -151,7 +151,8 @@ generate the text for. A superset of the [`param: key`] values can be found `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. -Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. +Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`. +`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case. @@ -227,7 +228,8 @@ generate the text for. A superset of the [`param: key`] values can be found `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. -Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. +Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`. +`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case. diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index d12f368807f6c..3213a9eb06346 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -1761,7 +1761,8 @@ generate the text for. A superset of the [`param: key`] values can be found `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. -Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. +Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`. +`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case. diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index d4183b4c62993..3bab7170f89f1 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -3013,7 +3013,8 @@ generate the text for. A superset of the [`param: key`] values can be found `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`, `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. -Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. +Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`. +`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case. diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 2d704888677d2..4434bd2db9aa2 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -97,10 +97,11 @@ 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. +modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to "Control" on Windows +and Linux and to "Meta" on macOS. ## input-button - `button` <[MouseButton]<"left"|"right"|"middle">> diff --git a/docs/src/input.md b/docs/src/input.md index f340e03f4845c..7f14d27ec8a75 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 20f56e8d8951b..f29428e2a8674 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 b413018b2cfef..eb2f2068f1e98 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 d6cc5bbd55140..a70ee114a4f59 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 07a3df8fc5a1c..28ff203a3c1ce 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 d4c09b5a51772..883a8ab129aef 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 e2ba06d50bec5..bcdc20751e56f 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 19cf7cebeb0a7..9a09ede72e710 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -2064,9 +2064,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -2179,9 +2180,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -3069,9 +3071,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -3591,7 +3594,8 @@ export interface Page { * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * etc. * - * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. * @@ -4183,9 +4187,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -5759,9 +5764,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -5846,9 +5852,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -6539,9 +6546,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -6897,7 +6905,8 @@ export interface Frame { * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * etc. * - * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. * @@ -7229,9 +7238,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -9838,9 +9848,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -9917,9 +9928,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -10078,9 +10090,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -10224,7 +10237,8 @@ export interface ElementHandle extends JSHandle { * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * etc. * - * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + * `ControlOrMeta`. * * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. * @@ -10635,9 +10649,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -11283,9 +11298,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -11395,9 +11411,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -12068,9 +12085,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -12437,7 +12455,8 @@ export interface Locator { * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * etc. * - * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. * @@ -12864,9 +12883,10 @@ 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. + * current modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to + * "Control" on Windows and Linux and to "Meta" on macOS. */ - 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 @@ -18316,7 +18336,8 @@ export interface Keyboard { * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * etc. * - * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. * @@ -18365,7 +18386,8 @@ export interface Keyboard { * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * etc. * - * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`. + * Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, + * `ControlOrMeta`. `ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS. * * Holding down `Shift` will type the text that corresponds to the `key` in the upper case. * diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index d9c1ec6ad978a..edc8e2cb7eb35 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 b2442f90979f5..3f1d75f0d04fa 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 ed1beac27aaca..b851dd0ce9b1e 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 246a9876d7632..277d5ebd824fb 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 94a7c23c90b4d..027648f48e7a3 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'); });