diff --git a/packages/playwright-core/bin/reinstall_msedge_beta_linux.sh b/packages/playwright-core/bin/reinstall_msedge_beta_linux.sh index ececd05ace..a1531a95d0 100755 --- a/packages/playwright-core/bin/reinstall_msedge_beta_linux.sh +++ b/packages/playwright-core/bin/reinstall_msedge_beta_linux.sh @@ -32,6 +32,12 @@ if ! command -v curl >/dev/null; then apt-get install -y curl fi +# GnuPG is not preinstalled in slim images +if ! command -v gpg >/dev/null; then + apt-get update + apt-get install -y gpg +fi + # 3. Add the GPG key, the apt repo, update the apt cache, and install the package curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ diff --git a/packages/playwright-core/bin/reinstall_msedge_dev_linux.sh b/packages/playwright-core/bin/reinstall_msedge_dev_linux.sh index 6ab84c3100..7fde34e5b8 100755 --- a/packages/playwright-core/bin/reinstall_msedge_dev_linux.sh +++ b/packages/playwright-core/bin/reinstall_msedge_dev_linux.sh @@ -32,6 +32,12 @@ if ! command -v curl >/dev/null; then apt-get install -y curl fi +# GnuPG is not preinstalled in slim images +if ! command -v gpg >/dev/null; then + apt-get update + apt-get install -y gpg +fi + # 3. Add the GPG key, the apt repo, update the apt cache, and install the package curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ diff --git a/packages/playwright-core/bin/reinstall_msedge_stable_linux.sh b/packages/playwright-core/bin/reinstall_msedge_stable_linux.sh index e66f85bbba..4acb1dbf1b 100755 --- a/packages/playwright-core/bin/reinstall_msedge_stable_linux.sh +++ b/packages/playwright-core/bin/reinstall_msedge_stable_linux.sh @@ -32,6 +32,12 @@ if ! command -v curl >/dev/null; then apt-get install -y curl fi +# GnuPG is not preinstalled in slim images +if ! command -v gpg >/dev/null; then + apt-get update + apt-get install -y gpg +fi + # 3. Add the GPG key, the apt repo, update the apt cache, and install the package curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg install -o root -g root -m 644 /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ diff --git a/packages/playwright-core/src/server/bidi/bidiInput.ts b/packages/playwright-core/src/server/bidi/bidiInput.ts index e149bb4a3f..cf6770a479 100644 --- a/packages/playwright-core/src/server/bidi/bidiInput.ts +++ b/packages/playwright-core/src/server/bidi/bidiInput.ts @@ -19,6 +19,7 @@ import type * as types from '../types'; import type { BidiSession } from './bidiConnection'; import * as bidi from './third_party/bidiProtocol'; import { getBidiKeyValue } from './third_party/bidiKeyboard'; +import { resolveSmartModifierString } from '../input'; export class RawKeyboardImpl implements input.RawKeyboard { private _session: BidiSession; @@ -32,12 +33,14 @@ export class RawKeyboardImpl implements input.RawKeyboard { } async keydown(modifiers: Set, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise { + keyName = resolveSmartModifierString(keyName); const actions: bidi.Input.KeySourceAction[] = []; actions.push({ type: 'keyDown', value: getBidiKeyValue(keyName) }); await this._performActions(actions); } async keyup(modifiers: Set, keyName: string, description: input.KeyDescription): Promise { + keyName = resolveSmartModifierString(keyName); const actions: bidi.Input.KeySourceAction[] = []; actions.push({ type: 'keyUp', value: getBidiKeyValue(keyName) }); await this._performActions(actions); diff --git a/packages/playwright-core/src/utils/isomorphic/cssParser.ts b/packages/playwright-core/src/utils/isomorphic/cssParser.ts index 58d4df243b..9a791a1da4 100644 --- a/packages/playwright-core/src/utils/isomorphic/cssParser.ts +++ b/packages/playwright-core/src/utils/isomorphic/cssParser.ts @@ -43,7 +43,7 @@ export function parseCSS(selector: string, customNames: Set): { selector if (!(tokens[tokens.length - 1] instanceof css.EOFToken)) tokens.push(new css.EOFToken()); } catch (e) { - const newMessage = e.message + ` while parsing selector "${selector}"`; + const newMessage = e.message + ` while parsing css selector "${selector}". Did you mean to CSS.escape it?`; const index = (e.stack || '').indexOf(e.message); if (index !== -1) e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length); @@ -68,13 +68,13 @@ export function parseCSS(selector: string, customNames: Set): { selector (token instanceof css.PercentageToken); }); if (unsupportedToken) - throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing selector "${selector}"`); + throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`); let pos = 0; const names = new Set(); function unexpected() { - return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing selector "${selector}"`); + return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`); } function skipWhitespace() { @@ -246,7 +246,7 @@ export function parseCSS(selector: string, customNames: Set): { selector if (!isEOF()) throw unexpected(); if (result.some(arg => typeof arg !== 'object' || !('simples' in arg))) - throw new InvalidSelectorError(`Error while parsing selector "${selector}"`); + throw new InvalidSelectorError(`Error while parsing css selector "${selector}". Did you mean to CSS.escape it?`); return { selector: result as CSSComplexSelector[], names: Array.from(names) }; } diff --git a/tests/library/css-parser.spec.ts b/tests/library/css-parser.spec.ts index 3bf8a742e6..e6ff046579 100644 --- a/tests/library/css-parser.spec.ts +++ b/tests/library/css-parser.spec.ts @@ -77,7 +77,8 @@ it('should throw on malformed css', async () => { } catch (e) { error = e; } - expect(error.message).toContain(`while parsing selector "${selector}"`); + expect(error.message).toContain(`while parsing css selector "${selector}"`); + expect(error.message).toContain(`Did you mean to CSS.escape it?`); } expectError(''); diff --git a/tests/page/expect-boolean.spec.ts b/tests/page/expect-boolean.spec.ts index 49b06b7747..d5b4c4d3b5 100644 --- a/tests/page/expect-boolean.spec.ts +++ b/tests/page/expect-boolean.spec.ts @@ -479,7 +479,7 @@ test('should print unknown engine error', async ({ page }) => { test('should print selector syntax error', async ({ page }) => { const error = await expect(page.locator('row]')).toBeVisible().catch(e => e); - expect(error.message).toContain(`Unexpected token "]" while parsing selector "row]"`); + expect(error.message).toContain(`Unexpected token "]" while parsing css selector "row]"`); }); test.describe(() => { diff --git a/tests/page/selectors-misc.spec.ts b/tests/page/selectors-misc.spec.ts index 605ff6c5a0..f656e9d512 100644 --- a/tests/page/selectors-misc.spec.ts +++ b/tests/page/selectors-misc.spec.ts @@ -415,7 +415,7 @@ it('should work with internal:has=', async ({ page, server }) => { const error3 = await page.$(`div >> internal:has=33`).catch(e => e); expect(error3.message).toContain('Malformed selector: internal:has=33'); const error4 = await page.$(`div >> internal:has="span!"`).catch(e => e); - expect(error4.message).toContain('Unexpected token "!" while parsing selector "span!"'); + expect(error4.message).toContain('Unexpected token "!" while parsing css selector "span!"'); }); it('should work with internal:has-not=', async ({ page }) => {