feat(ctrl_or_meta): add a universal ctrl-meta modifier (#30572)

Fixes https://github.com/microsoft/playwright/issues/12168
This commit is contained in:
Pavel Feldman 2024-04-29 08:15:12 -07:00 committed by GitHub
parent ebafb95054
commit 96f3d19819
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 171 additions and 112 deletions

View file

@ -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`, `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. `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. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -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`, `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. `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. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -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`, `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. `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. 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`, `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. `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. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -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`, `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. `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. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -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`, `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc. `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. Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

View file

@ -97,10 +97,11 @@ A point to use relative to the top-left corner of element padding box. If not sp
element. element.
## input-modifiers ## 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 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 ## input-button
- `button` <[MouseButton]<"left"|"right"|"middle">> - `button` <[MouseButton]<"left"|"right"|"middle">>

View file

@ -217,6 +217,10 @@ await page.getByText('Item').click({ button: 'right' });
// Shift + click // Shift + click
await page.getByText('Item').click({ modifiers: ['Shift'] }); 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 // Hover over element
await page.getByText('Item').hover(); await page.getByText('Item').hover();
@ -237,6 +241,10 @@ page.getByText("Item").click(new Locator.ClickOptions().setButton(MouseButton.RI
// Shift + click // Shift + click
page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.SHIFT))); 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 // Hover over element
page.getByText("Item").hover(); page.getByText("Item").hover();
@ -257,6 +265,10 @@ await page.get_by_text("Item").click(button="right")
# Shift + click # Shift + click
await page.get_by_text("Item").click(modifiers=["Shift"]) 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 # Hover over element
await page.get_by_text("Item").hover() await page.get_by_text("Item").hover()
@ -297,6 +309,10 @@ await page.GetByText("Item").ClickAsync(new() { Button = MouseButton.Right });
// Shift + click // Shift + click
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.Shift } }); 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 // Hover over element
await page.GetByText("Item").HoverAsync(); await page.GetByText("Item").HoverAsync();

View file

@ -1342,7 +1342,7 @@ scheme.FrameClickParams = tObject({
strict: tOptional(tBoolean), strict: tOptional(tBoolean),
force: tOptional(tBoolean), force: tOptional(tBoolean),
noWaitAfter: 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')), position: tOptional(tType('Point')),
delay: tOptional(tNumber), delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])), button: tOptional(tEnum(['left', 'right', 'middle'])),
@ -1372,7 +1372,7 @@ scheme.FrameDblclickParams = tObject({
strict: tOptional(tBoolean), strict: tOptional(tBoolean),
force: tOptional(tBoolean), force: tOptional(tBoolean),
noWaitAfter: 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')), position: tOptional(tType('Point')),
delay: tOptional(tNumber), delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])), button: tOptional(tEnum(['left', 'right', 'middle'])),
@ -1450,7 +1450,7 @@ scheme.FrameHoverParams = tObject({
selector: tString, selector: tString,
strict: tOptional(tBoolean), strict: tOptional(tBoolean),
force: 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')), position: tOptional(tType('Point')),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
trial: tOptional(tBoolean), trial: tOptional(tBoolean),
@ -1597,7 +1597,7 @@ scheme.FrameTapParams = tObject({
strict: tOptional(tBoolean), strict: tOptional(tBoolean),
force: tOptional(tBoolean), force: tOptional(tBoolean),
noWaitAfter: 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')), position: tOptional(tType('Point')),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
trial: tOptional(tBoolean), trial: tOptional(tBoolean),
@ -1792,7 +1792,7 @@ scheme.ElementHandleCheckResult = tOptional(tObject({}));
scheme.ElementHandleClickParams = tObject({ scheme.ElementHandleClickParams = tObject({
force: tOptional(tBoolean), force: tOptional(tBoolean),
noWaitAfter: 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')), position: tOptional(tType('Point')),
delay: tOptional(tNumber), delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])), button: tOptional(tEnum(['left', 'right', 'middle'])),
@ -1808,7 +1808,7 @@ scheme.ElementHandleContentFrameResult = tObject({
scheme.ElementHandleDblclickParams = tObject({ scheme.ElementHandleDblclickParams = tObject({
force: tOptional(tBoolean), force: tOptional(tBoolean),
noWaitAfter: 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')), position: tOptional(tType('Point')),
delay: tOptional(tNumber), delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])), button: tOptional(tEnum(['left', 'right', 'middle'])),
@ -1838,7 +1838,7 @@ scheme.ElementHandleGetAttributeResult = tObject({
}); });
scheme.ElementHandleHoverParams = tObject({ scheme.ElementHandleHoverParams = tObject({
force: 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')), position: tOptional(tType('Point')),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
trial: tOptional(tBoolean), trial: tOptional(tBoolean),
@ -1962,7 +1962,7 @@ scheme.ElementHandleSetInputFilesResult = tOptional(tObject({}));
scheme.ElementHandleTapParams = tObject({ scheme.ElementHandleTapParams = tObject({
force: tOptional(tBoolean), force: tOptional(tBoolean),
noWaitAfter: 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')), position: tOptional(tType('Point')),
timeout: tOptional(tNumber), timeout: tOptional(tNumber),
trial: tOptional(tBoolean), trial: tOptional(tBoolean),

View file

@ -449,11 +449,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
progress.throwIfAborted(); // Avoid action that has side-effects. progress.throwIfAborted(); // Avoid action that has side-effects.
let restoreModifiers: types.KeyboardModifier[] | undefined; let restoreModifiers: types.KeyboardModifier[] | undefined;
if (options && options.modifiers) 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`); progress.log(` performing ${actionName} action`);
await action(point); await action(point);
if (restoreModifiers) if (restoreModifiers)
await this._page.keyboard._ensureModifiers(restoreModifiers); await this._page.keyboard.ensureModifiers(restoreModifiers);
if (hitTargetInterceptionHandle) { if (hitTargetInterceptionHandle) {
const stopHitTargetInterception = hitTargetInterceptionHandle.evaluate(h => h.stop()).catch(e => 'done' as const).finally(() => { const stopHitTargetInterception = hitTargetInterceptionHandle.evaluate(h => h.stop()).catch(e => 'done' as const).finally(() => {
hitTargetInterceptionHandle?.dispose(); hitTargetInterceptionHandle?.dispose();

View file

@ -44,11 +44,9 @@ export class Keyboard {
private _pressedModifiers = new Set<types.KeyboardModifier>(); private _pressedModifiers = new Set<types.KeyboardModifier>();
private _pressedKeys = new Set<string>(); private _pressedKeys = new Set<string>();
private _raw: RawKeyboard; private _raw: RawKeyboard;
private _page: Page;
constructor(raw: RawKeyboard, page: Page) { constructor(raw: RawKeyboard) {
this._raw = raw; this._raw = raw;
this._page = page;
} }
async down(key: string) { 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); 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); let description = usKeyboardLayout.get(keyString);
assert(description, `Unknown key: "${keyString}"`); assert(description, `Unknown key: "${keyString}"`);
const shift = this._pressedModifiers.has('Shift'); const shift = this._pressedModifiers.has('Shift');
@ -126,7 +125,8 @@ export class Keyboard {
await this.up(tokens[i]); await this.up(tokens[i]);
} }
async _ensureModifiers(modifiers: types.KeyboardModifier[]): Promise<types.KeyboardModifier[]> { async ensureModifiers(mm: types.SmartKeyboardModifier[]): Promise<types.KeyboardModifier[]> {
const modifiers = mm.map(resolveSmartModifier);
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (!kModifiers.includes(modifier)) if (!kModifiers.includes(modifier))
throw new Error('Unknown modifier ' + 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 { export interface RawMouse {
move(x: number, y: number, button: types.MouseButton | 'none', buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, forClick: boolean): Promise<void>; move(x: number, y: number, button: types.MouseButton | 'none', buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, forClick: boolean): Promise<void>;
down(x: number, y: number, button: types.MouseButton, buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, clickCount: number): Promise<void>; down(x: number, y: number, button: types.MouseButton, buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, clickCount: number): Promise<void>;

View file

@ -183,7 +183,7 @@ export class Page extends SdkObject {
this._delegate = delegate; this._delegate = delegate;
this._browserContext = browserContext; this._browserContext = browserContext;
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate)); 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.mouse = new input.Mouse(delegate.rawMouse, this);
this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this); this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this);
this._timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings); this._timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings);

View file

@ -15,6 +15,7 @@
*/ */
import type { Frame } from '../frames'; import type { Frame } from '../frames';
import type { SmartKeyboardModifier } from '../types';
import type * as actions from './recorderActions'; import type * as actions from './recorderActions';
export type MouseClickOptions = Parameters<Frame['click']>[2]; export type MouseClickOptions = Parameters<Frame['click']>[2];
@ -36,14 +37,14 @@ export function toClickOptions(action: actions.ClickAction): { method: 'click' |
return { method, options }; return { method, options };
} }
export function toModifiers(modifiers: number): ('Alt' | 'Control' | 'Meta' | 'Shift')[] { export function toModifiers(modifiers: number): SmartKeyboardModifier[] {
const result: ('Alt' | 'Control' | 'Meta' | 'Shift')[] = []; const result: SmartKeyboardModifier[] = [];
if (modifiers & 1) if (modifiers & 1)
result.push('Alt'); result.push('Alt');
if (modifiers & 2) if (modifiers & 2)
result.push('Control'); result.push('ControlOrMeta');
if (modifiers & 4) if (modifiers & 4)
result.push('Meta'); result.push('ControlOrMeta');
if (modifiers & 8) if (modifiers & 8)
result.push('Shift'); result.push('Shift');
return result; return result;

View file

@ -105,10 +105,11 @@ export type ProxySettings = {
}; };
export type KeyboardModifier = 'Alt' | 'Control' | 'Meta' | 'Shift'; export type KeyboardModifier = 'Alt' | 'Control' | 'Meta' | 'Shift';
export type SmartKeyboardModifier = KeyboardModifier | 'ControlOrMeta';
export type MouseButton = 'left' | 'right' | 'middle'; export type MouseButton = 'left' | 'right' | 'middle';
export type PointerActionOptions = { export type PointerActionOptions = {
modifiers?: KeyboardModifier[]; modifiers?: SmartKeyboardModifier[];
position?: Point; position?: Point;
}; };

View file

@ -2064,9 +2064,10 @@ export interface Page {
/** /**
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * 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 * 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 * 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 * 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 * 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 * 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`, * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,
* etc. * 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. * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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`, * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,
* etc. * 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. * 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 * 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 * 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<T=Node> extends JSHandle<T> {
/** /**
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * 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 * 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<T=Node> extends JSHandle<T> {
/** /**
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * 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 * 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<T=Node> extends JSHandle<T> {
/** /**
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * 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 * 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<T=Node> extends JSHandle<T> {
* `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,
* etc. * 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. * Holding down `Shift` will type the text that corresponds to the `key` in the upper case.
* *
@ -10635,9 +10649,10 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
/** /**
* Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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`, * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,
* etc. * 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. * 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 * 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 * 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`, * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,
* etc. * 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. * 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`, * `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`,
* etc. * 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. * Holding down `Shift` will type the text that corresponds to the `key` in the upper case.
* *

View file

@ -2458,7 +2458,7 @@ export type FrameClickParams = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -2470,7 +2470,7 @@ export type FrameClickOptions = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -2510,7 +2510,7 @@ export type FrameDblclickParams = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -2521,7 +2521,7 @@ export type FrameDblclickOptions = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -2633,7 +2633,7 @@ export type FrameHoverParams = {
selector: string, selector: string,
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -2642,7 +2642,7 @@ export type FrameHoverParams = {
export type FrameHoverOptions = { export type FrameHoverOptions = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -2867,7 +2867,7 @@ export type FrameTapParams = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -2876,7 +2876,7 @@ export type FrameTapOptions = {
strict?: boolean, strict?: boolean,
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -3210,7 +3210,7 @@ export type ElementHandleCheckResult = void;
export type ElementHandleClickParams = { export type ElementHandleClickParams = {
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -3221,7 +3221,7 @@ export type ElementHandleClickParams = {
export type ElementHandleClickOptions = { export type ElementHandleClickOptions = {
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -3238,7 +3238,7 @@ export type ElementHandleContentFrameResult = {
export type ElementHandleDblclickParams = { export type ElementHandleDblclickParams = {
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -3248,7 +3248,7 @@ export type ElementHandleDblclickParams = {
export type ElementHandleDblclickOptions = { export type ElementHandleDblclickOptions = {
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
delay?: number, delay?: number,
button?: 'left' | 'right' | 'middle', button?: 'left' | 'right' | 'middle',
@ -3290,7 +3290,7 @@ export type ElementHandleGetAttributeResult = {
}; };
export type ElementHandleHoverParams = { export type ElementHandleHoverParams = {
force?: boolean, force?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -3298,7 +3298,7 @@ export type ElementHandleHoverParams = {
}; };
export type ElementHandleHoverOptions = { export type ElementHandleHoverOptions = {
force?: boolean, force?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -3488,7 +3488,7 @@ export type ElementHandleSetInputFilesResult = void;
export type ElementHandleTapParams = { export type ElementHandleTapParams = {
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,
@ -3496,7 +3496,7 @@ export type ElementHandleTapParams = {
export type ElementHandleTapOptions = { export type ElementHandleTapOptions = {
force?: boolean, force?: boolean,
noWaitAfter?: boolean, noWaitAfter?: boolean,
modifiers?: ('Alt' | 'Control' | 'Meta' | 'Shift')[], modifiers?: ('Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift')[],
position?: Point, position?: Point,
timeout?: number, timeout?: number,
trial?: boolean, trial?: boolean,

View file

@ -1791,6 +1791,7 @@ Frame:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -1842,6 +1843,7 @@ Frame:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -1956,6 +1958,7 @@ Frame:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -2164,6 +2167,7 @@ Frame:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -2442,6 +2446,7 @@ ElementHandle:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -2475,6 +2480,7 @@ ElementHandle:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -2532,6 +2538,7 @@ ElementHandle:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?
@ -2718,6 +2725,7 @@ ElementHandle:
literals: literals:
- Alt - Alt
- Control - Control
- ControlOrMeta
- Meta - Meta
- Shift - Shift
position: Point? position: Point?

View file

@ -170,7 +170,7 @@ it('should work with Shift-clicking', async ({ browser, server, browserName }) =
await context.close(); 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.'); it.fixme(browserName === 'firefox', 'Reports an opener in this case.');
const context = await browser.newContext(); const context = await browser.newContext();
@ -179,13 +179,13 @@ it('should work with Ctrl-clicking', async ({ browser, server, isMac, browserNam
await page.setContent('<a href="/one-style.html">yo</a>'); await page.setContent('<a href="/one-style.html">yo</a>');
const [popup] = await Promise.all([ const [popup] = await Promise.all([
context.waitForEvent('page'), context.waitForEvent('page'),
page.click('a', { modifiers: [isMac ? 'Meta' : 'Control'] }), page.click('a', { modifiers: ['ControlOrMeta'] }),
]); ]);
expect(await popup.opener()).toBe(null); expect(await popup.opener()).toBe(null);
await context.close(); 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.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.skip(browserName === 'chromium', 'Chromium does not dispatch renderer messages while navigation is provisional.');
it.fixme(browserName === 'webkit' && isWindows, 'Timesout while trying to click'); 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', () => {}); server.setRoute('/slow.html', () => {});
const [popup] = await Promise.all([ const [popup] = await Promise.all([
context.waitForEvent('page'), 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'), page.evaluate(url => setTimeout(() => location.href = url, 0), server.CROSS_PROCESS_PREFIX + '/slow.html'),
]); ]);
expect(popup).toBeTruthy(); expect(popup).toBeTruthy();

View file

@ -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(); const recorder = await openRecorder();
await recorder.setContentAndWait(`<a href="about:blank?foo">link</a>`); await recorder.setContentAndWait(`<a href="about:blank?foo">link</a>`);
const locator = await recorder.hoverOverElement('a'); const locator = await recorder.hoverOverElement('a');
expect(locator).toBe(`getByRole('link', { name: 'link' })`); 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'); const sources = await recorder.waitForOutput('JavaScript', 'page1');
if (browserName !== 'firefox') { if (browserName !== 'firefox') {
@ -361,7 +361,7 @@ await page1.GotoAsync("about:blank?foo");`);
expect(sources.get('JavaScript')!.text).toContain(` expect(sources.get('JavaScript')!.text).toContain(`
const page1Promise = page.waitForEvent('popup'); const page1Promise = page.waitForEvent('popup');
await page.getByRole('link', { name: 'link' }).click({ await page.getByRole('link', { name: 'link' }).click({
modifiers: ['${platform === 'darwin' ? 'Meta' : 'Control'}'] modifiers: ['ControlOrMeta']
}); });
const page1 = await page1Promise;`); const page1 = await page1Promise;`);
} }

View file

@ -318,10 +318,9 @@ it('should handle selectAll', async ({ page, server, isMac }) => {
await page.goto(server.PREFIX + '/input/textarea.html'); await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea'); const textarea = await page.$('textarea');
await textarea.type('some text'); await textarea.type('some text');
const modifier = isMac ? 'Meta' : 'Control'; await page.keyboard.down('ControlOrMeta');
await page.keyboard.down(modifier);
await page.keyboard.press('a'); await page.keyboard.press('a');
await page.keyboard.up(modifier); await page.keyboard.up('ControlOrMeta');
await page.keyboard.press('Backspace'); await page.keyboard.press('Backspace');
expect(await page.$eval('textarea', textarea => textarea.value)).toBe(''); 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(); event.preventDefault();
}, false); }, false);
}); });
const modifier = isMac ? 'Meta' : 'Control'; await page.keyboard.down('ControlOrMeta');
await page.keyboard.down(modifier);
await page.keyboard.press('a'); await page.keyboard.press('a');
await page.keyboard.up(modifier); await page.keyboard.up('ControlOrMeta');
await page.keyboard.press('Backspace'); await page.keyboard.press('Backspace');
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex'); 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); expect((await actual.jsonValue()).clicked).toBe(true);
}); });
it('should support simple copy-pasting', async ({ page, isMac, browserName }) => { it('should support simple copy-pasting', async ({ page }) => {
const modifier = isMac ? 'Meta' : 'Control';
await page.setContent(`<div contenteditable>123</div>`); await page.setContent(`<div contenteditable>123</div>`);
await page.focus('div'); await page.focus('div');
await page.keyboard.press(`${modifier}+KeyA`); await page.keyboard.press(`ControlOrMeta+KeyA`);
await page.keyboard.press(`${modifier}+KeyC`); await page.keyboard.press(`ControlOrMeta+KeyC`);
await page.keyboard.press(`${modifier}+KeyV`); await page.keyboard.press(`ControlOrMeta+KeyV`);
await page.keyboard.press(`${modifier}+KeyV`); await page.keyboard.press(`ControlOrMeta+KeyV`);
expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123'); expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123');
}); });
it('should support simple cut-pasting', async ({ page, isMac }) => { it('should support simple cut-pasting', async ({ page }) => {
const modifier = isMac ? 'Meta' : 'Control';
await page.setContent(`<div contenteditable>123</div>`); await page.setContent(`<div contenteditable>123</div>`);
await page.focus('div'); await page.focus('div');
await page.keyboard.press(`${modifier}+KeyA`); await page.keyboard.press(`ControlOrMeta+KeyA`);
await page.keyboard.press(`${modifier}+KeyX`); await page.keyboard.press(`ControlOrMeta+KeyX`);
await page.keyboard.press(`${modifier}+KeyV`); await page.keyboard.press(`ControlOrMeta+KeyV`);
await page.keyboard.press(`${modifier}+KeyV`); await page.keyboard.press(`ControlOrMeta+KeyV`);
expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123'); 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'); it.fixme(browserName === 'webkit' && isLinux, 'https://github.com/microsoft/playwright/issues/12000');
const modifier = isMac ? 'Meta' : 'Control';
await page.setContent(`<div contenteditable></div>`); await page.setContent(`<div contenteditable></div>`);
const div = page.locator('div'); const div = page.locator('div');
await expect(div).toHaveText(''); await expect(div).toHaveText('');
await div.type('123'); await div.type('123');
await expect(div).toHaveText('123'); await expect(div).toHaveText('123');
await page.keyboard.press(`${modifier}+KeyZ`); await page.keyboard.press(`ControlOrMeta+KeyZ`);
await expect(div).toHaveText(''); await expect(div).toHaveText('');
await page.keyboard.press(`Shift+${modifier}+KeyZ`); await page.keyboard.press(`Shift+ControlOrMeta+KeyZ`);
await expect(div).toHaveText('123'); await expect(div).toHaveText('123');
}); });