diff --git a/.github/workflows/tests_bidi.yml b/.github/workflows/tests_bidi.yml index 99b756c2b3..f2e38765f7 100644 --- a/.github/workflows/tests_bidi.yml +++ b/.github/workflows/tests_bidi.yml @@ -7,7 +7,8 @@ on: - main paths: - .github/workflows/tests_bidi.yml - - packages/playwright-core/src/server/bidi/* + - packages/playwright-core/src/server/bidi/** + - tests/bidi/** schedule: # Run every day at midnight - cron: '0 0 * * *' @@ -18,7 +19,6 @@ env: jobs: test_bidi: name: BiDi - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ubuntu-24.04 permissions: diff --git a/.github/workflows/tests_components.yml b/.github/workflows/tests_components.yml index 224d275d7a..093d5de0c4 100644 --- a/.github/workflows/tests_components.yml +++ b/.github/workflows/tests_components.yml @@ -20,7 +20,6 @@ env: jobs: test_components: name: ${{ matrix.os }} - Node.js ${{ matrix.node-version }} - if: github.repository == 'microsoft/playwright' strategy: fail-fast: false matrix: diff --git a/.github/workflows/tests_others.yml b/.github/workflows/tests_others.yml index 9a3ee5bd0e..ed18e1541c 100644 --- a/.github/workflows/tests_others.yml +++ b/.github/workflows/tests_others.yml @@ -21,7 +21,6 @@ env: jobs: test_stress: name: Stress - ${{ matrix.os }} - if: github.repository == 'microsoft/playwright' strategy: fail-fast: false matrix: @@ -58,7 +57,6 @@ jobs: test_webview2: name: WebView2 - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: windows-2022 permissions: @@ -89,7 +87,6 @@ jobs: test_clock_frozen_time_linux: name: time library - ${{ matrix.clock }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} permissions: id-token: write # This is required for OIDC login (azure/login) to succeed @@ -115,7 +112,6 @@ jobs: test_clock_frozen_time_test_runner: name: time test runner - ${{ matrix.clock }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ubuntu-22.04 permissions: @@ -140,7 +136,6 @@ jobs: test_electron: name: Electron - ${{ matrix.os }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false diff --git a/.github/workflows/tests_primary.yml b/.github/workflows/tests_primary.yml index 40588286ed..b8ec84ee56 100644 --- a/.github/workflows/tests_primary.yml +++ b/.github/workflows/tests_primary.yml @@ -27,7 +27,6 @@ env: jobs: test_linux: name: ${{ matrix.os }} (${{ matrix.browser }} - Node.js ${{ matrix.node-version }}) - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -60,7 +59,6 @@ jobs: test_linux_chromium_tot: name: ${{ matrix.os }} (chromium tip-of-tree) - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -85,7 +83,6 @@ jobs: test_test_runner: name: Test Runner - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -130,7 +127,6 @@ jobs: test_web_components: name: Web Components - if: github.repository == 'microsoft/playwright' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -166,7 +162,6 @@ jobs: test_vscode_extension: name: VSCode Extension - if: github.repository == 'microsoft/playwright' runs-on: ubuntu-latest env: PWTEST_BOT_NAME: "vscode-extension" @@ -203,7 +198,6 @@ jobs: test_package_installations: name: "Installation Test ${{ matrix.os }}" - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index 887ce68b15..5bf017453d 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -26,7 +26,6 @@ permissions: jobs: test_linux: name: ${{ matrix.os }} (${{ matrix.browser }}) - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -47,7 +46,6 @@ jobs: test_mac: name: ${{ matrix.os }} (${{ matrix.browser }}) - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -75,7 +73,6 @@ jobs: test_win: name: "Windows" - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -95,7 +92,6 @@ jobs: test-package-installations-other-node-versions: name: "Installation Test ${{ matrix.os }} (${{ matrix.node_version }})" - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.os }} strategy: @@ -129,7 +125,6 @@ jobs: headed_tests: name: "headed ${{ matrix.browser }} (${{ matrix.os }})" - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -156,7 +151,6 @@ jobs: transport_linux: name: "Transport" - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -178,7 +172,6 @@ jobs: tracing_linux: name: Tracing ${{ matrix.browser }} ${{ matrix.channel }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false @@ -206,7 +199,6 @@ jobs: test_chromium_channels: name: Test ${{ matrix.channel }} on ${{ matrix.runs-on }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.runs-on }} strategy: @@ -229,7 +221,6 @@ jobs: chromium_tot: name: Chromium tip-of-tree ${{ matrix.os }}${{ matrix.headed }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.os }} strategy: @@ -252,7 +243,6 @@ jobs: chromium_tot_headless_shell: name: Chromium tip-of-tree headless-shell-${{ matrix.os }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.os }} strategy: @@ -274,7 +264,6 @@ jobs: firefox_beta: name: Firefox Beta ${{ matrix.os }} - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} runs-on: ${{ matrix.os }} strategy: @@ -296,7 +285,6 @@ jobs: build-playwright-driver: name: "build-playwright-driver" - if: github.repository == 'microsoft/playwright' runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -310,7 +298,6 @@ jobs: test_channel_chromium: name: Test channel=chromium - if: github.repository == 'microsoft/playwright' environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} strategy: fail-fast: false diff --git a/.github/workflows/tests_service.yml b/.github/workflows/tests_service.yml index ebd78ff37c..2d68740006 100644 --- a/.github/workflows/tests_service.yml +++ b/.github/workflows/tests_service.yml @@ -10,7 +10,6 @@ env: jobs: test: name: "Service" - if: github.repository == 'microsoft/playwright' strategy: fail-fast: false matrix: diff --git a/.github/workflows/tests_video.yml b/.github/workflows/tests_video.yml index 82a03e0907..0733b082a0 100644 --- a/.github/workflows/tests_video.yml +++ b/.github/workflows/tests_video.yml @@ -14,7 +14,6 @@ env: jobs: video_linux: name: "Video Linux" - if: github.repository == 'microsoft/playwright' environment: allow-uploading-flakiness-results strategy: fail-fast: false diff --git a/.github/workflows/trigger_tests.yml b/.github/workflows/trigger_tests.yml index 5626b170e9..1ea2ec424d 100644 --- a/.github/workflows/trigger_tests.yml +++ b/.github/workflows/trigger_tests.yml @@ -9,7 +9,6 @@ on: jobs: trigger: name: "trigger" - if: github.repository == 'microsoft/playwright' runs-on: ubuntu-24.04 steps: - run: | diff --git a/docs/src/api/class-locatorassertions.md b/docs/src/api/class-locatorassertions.md index 35f64ea711..8f8233fae4 100644 --- a/docs/src/api/class-locatorassertions.md +++ b/docs/src/api/class-locatorassertions.md @@ -2250,7 +2250,6 @@ Asserts that the target element matches the given [accessibility snapshot](../ar ```js await expect(page.locator('body')).toMatchAriaSnapshot(); await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'snapshot' }); -await expect(page.locator('body')).toMatchAriaSnapshot({ path: '/path/to/snapshot.yml' }); ``` ```python async diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index e20c621556..1826120327 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -15,13 +15,13 @@ }, { "name": "firefox", - "revision": "1471", + "revision": "1472", "installByDefault": true, "browserVersion": "134.0" }, { "name": "firefox-beta", - "revision": "1467", + "revision": "1468", "installByDefault": false, "browserVersion": "133.0b9" }, diff --git a/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts index d59ac0bc13..bf082793b6 100644 --- a/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts +++ b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts @@ -139,6 +139,9 @@ function defaultProfilePreferences( 'dom.min_background_timeout_value_without_budget_throttling': 0, 'dom.timeout.enable_budget_timer_throttling': false, + // Disable HTTPS-First upgrades + 'dom.security.https_first': false, + // Only load extensions from the application and user profile // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION 'extensions.autoDisableScopes': 0, diff --git a/packages/playwright-core/src/server/firefox/protocol.d.ts b/packages/playwright-core/src/server/firefox/protocol.d.ts index 3b661cb6cb..ccf81fd171 100644 --- a/packages/playwright-core/src/server/firefox/protocol.d.ts +++ b/packages/playwright-core/src/server/firefox/protocol.d.ts @@ -301,6 +301,11 @@ export module Protocol { forcedColors: ("active"|"none")|null; }; export type setForcedColorsReturnValue = void; + export type setContrastParameters = { + browserContextId?: string; + contrast: ("less"|"more"|"custom"|"no-preference")|null; + }; + export type setContrastReturnValue = void; export type setVideoRecordingOptionsParameters = { browserContextId?: string; options?: { @@ -530,6 +535,7 @@ export module Protocol { colorScheme?: ("dark"|"light"|"no-preference"); reducedMotion?: ("reduce"|"no-preference"); forcedColors?: ("active"|"none"); + contrast?: ("less"|"more"|"custom"|"no-preference"); }; export type setEmulatedMediaReturnValue = void; export type setCacheDisabledParameters = { @@ -1131,6 +1137,7 @@ export module Protocol { "Browser.setColorScheme": Browser.setColorSchemeParameters; "Browser.setReducedMotion": Browser.setReducedMotionParameters; "Browser.setForcedColors": Browser.setForcedColorsParameters; + "Browser.setContrast": Browser.setContrastParameters; "Browser.setVideoRecordingOptions": Browser.setVideoRecordingOptionsParameters; "Browser.cancelDownload": Browser.cancelDownloadParameters; "Heap.collectGarbage": Heap.collectGarbageParameters; @@ -1213,6 +1220,7 @@ export module Protocol { "Browser.setColorScheme": Browser.setColorSchemeReturnValue; "Browser.setReducedMotion": Browser.setReducedMotionReturnValue; "Browser.setForcedColors": Browser.setForcedColorsReturnValue; + "Browser.setContrast": Browser.setContrastReturnValue; "Browser.setVideoRecordingOptions": Browser.setVideoRecordingOptionsReturnValue; "Browser.cancelDownload": Browser.cancelDownloadReturnValue; "Heap.collectGarbage": Heap.collectGarbageReturnValue; diff --git a/packages/playwright-core/src/utils/httpServer.ts b/packages/playwright-core/src/utils/httpServer.ts index 8da2a0e0d0..1d78df4659 100644 --- a/packages/playwright-core/src/utils/httpServer.ts +++ b/packages/playwright-core/src/utils/httpServer.ts @@ -214,12 +214,6 @@ export class HttpServer { } private _onRequest(request: http.IncomingMessage, response: http.ServerResponse) { - response.setHeader('Access-Control-Allow-Origin', '*'); - response.setHeader('Access-Control-Request-Method', '*'); - response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET'); - if (request.headers.origin) - response.setHeader('Access-Control-Allow-Headers', request.headers.origin); - if (request.method === 'OPTIONS') { response.writeHead(200); response.end(); diff --git a/packages/playwright/src/common/configLoader.ts b/packages/playwright/src/common/configLoader.ts index 13d7eac187..b9f24b929a 100644 --- a/packages/playwright/src/common/configLoader.ts +++ b/packages/playwright/src/common/configLoader.ts @@ -240,8 +240,8 @@ function validateConfig(file: string, config: Config) { } if ('updateSnapshots' in config && config.updateSnapshots !== undefined) { - if (typeof config.updateSnapshots !== 'string' || !['all', 'none', 'missing'].includes(config.updateSnapshots)) - throw errorWithFile(file, `config.updateSnapshots must be one of "all", "none" or "missing"`); + if (typeof config.updateSnapshots !== 'string' || !['all', 'changed', 'missing', 'none'].includes(config.updateSnapshots)) + throw errorWithFile(file, `config.updateSnapshots must be one of "all", "changed", "missing" or "none"`); } if ('workers' in config && config.workers !== undefined) { diff --git a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts index d379d8610c..e4944fbfc1 100644 --- a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts @@ -30,12 +30,13 @@ import path from 'path'; type ToMatchAriaSnapshotExpected = { name?: string; path?: string; + timeout?: number; } | string; export async function toMatchAriaSnapshot( this: ExpectMatcherState, receiver: LocatorEx, - expectedParam: ToMatchAriaSnapshotExpected, + expectedParam?: ToMatchAriaSnapshotExpected, options: { timeout?: number } = {}, ): Promise> { const matcherName = 'toMatchAriaSnapshot'; @@ -55,9 +56,11 @@ export async function toMatchAriaSnapshot( }; let expected: string; + let timeout: number; let expectedPath: string | undefined; if (isString(expectedParam)) { expected = expectedParam; + timeout = options.timeout ?? this.timeout; } else { if (expectedParam?.name) { expectedPath = testInfo.snapshotPath(sanitizeFilePathBeforeExtension(expectedParam.name)); @@ -71,6 +74,7 @@ export async function toMatchAriaSnapshot( expectedPath = testInfo.snapshotPath(sanitizeForFilePath(trimLongString(fullTitleWithoutSpec)) + '.yml'); } expected = await fs.promises.readFile(expectedPath, 'utf8').catch(() => ''); + timeout = expectedParam?.timeout ?? this.timeout; } const generateMissingBaseline = updateSnapshots === 'missing' && !expected; @@ -84,7 +88,6 @@ export async function toMatchAriaSnapshot( } } - const timeout = options.timeout ?? this.timeout; expected = unshift(expected); const { matches: pass, received, log, timedOut } = await receiver._expect('to.match.aria', { expectedValue: expected, isNot: this.isNot, timeout }); const typedReceived = received as MatcherReceived | typeof kNoElementsFoundError; diff --git a/packages/playwright/src/program.ts b/packages/playwright/src/program.ts index 93969662bb..ebedca536b 100644 --- a/packages/playwright/src/program.ts +++ b/packages/playwright/src/program.ts @@ -286,7 +286,7 @@ function overridesFromOptions(options: { [key: string]: any }): ConfigCLIOverrid if (['all', 'changed', 'missing', 'none'].includes(options.updateSnapshots)) updateSnapshots = options.updateSnapshots; else - updateSnapshots = 'updateSnapshots' in options ? 'changed' : 'missing'; + updateSnapshots = 'updateSnapshots' in options ? 'changed' : undefined; const overrides: ConfigCLIOverrides = { forbidOnly: options.forbidOnly ? true : undefined, diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 1ac35f6201..fd42612df8 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -8690,7 +8690,6 @@ interface LocatorAssertions { * ```js * await expect(page.locator('body')).toMatchAriaSnapshot(); * await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'snapshot' }); - * await expect(page.locator('body')).toMatchAriaSnapshot({ path: '/path/to/snapshot.yml' }); * ``` * * @param options diff --git a/tests/assets/input/keyboard.html b/tests/assets/input/keyboard.html index fd962c7518..049ed439be 100644 --- a/tests/assets/input/keyboard.html +++ b/tests/assets/input/keyboard.html @@ -10,13 +10,13 @@ let textarea = document.querySelector('textarea'); textarea.focus(); textarea.addEventListener('keydown', event => { - log('Keydown:', event.key, event.code, event.which, modifiers(event)); + log('Keydown:', event.key, event.code, getLocation(event), modifiers(event)); }); textarea.addEventListener('keypress', event => { - log('Keypress:', event.key, event.code, event.which, event.charCode, modifiers(event)); + log('Keypress:', event.key, event.code, getLocation(event), event.charCode, modifiers(event)); }); textarea.addEventListener('keyup', event => { - log('Keyup:', event.key, event.code, event.which, modifiers(event)); + log('Keyup:', event.key, event.code, getLocation(event), modifiers(event)); }); function modifiers(event) { let m = []; @@ -28,6 +28,15 @@ m.push('Shift') return '[' + m.join(' ') + ']'; } + function getLocation(event) { + switch (event.location) { + case KeyboardEvent.DOM_KEY_LOCATION_STANDARD: return 'STANDARD'; + case KeyboardEvent.DOM_KEY_LOCATION_LEFT: return 'LEFT'; + case KeyboardEvent.DOM_KEY_LOCATION_RIGHT: return 'RIGHT'; + case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD: return 'NUMPAD'; + default: return 'Unknown: ' + event.location; + }; + } function log(...args) { console.log.apply(console, args); result += args.join(' ') + '\n'; diff --git a/tests/bidi/csvReporter.ts b/tests/bidi/csvReporter.ts index 821f6e6f48..0c5b94330b 100644 --- a/tests/bidi/csvReporter.ts +++ b/tests/bidi/csvReporter.ts @@ -50,7 +50,8 @@ class CsvReporter implements Reporter { if (test.ok() && !fixme) continue; const row = []; - row.push(csvEscape(`${file.title} :: ${test.title}`)); + const [, , , ...titles] = test.titlePath(); + row.push(csvEscape(`${file.title} :: ${titles.join(' › ')}`)); row.push(test.expectedStatus); row.push(test.outcome()); if (fixme) { @@ -67,7 +68,7 @@ class CsvReporter implements Reporter { const csv = rows.map(r => r.join(',')).join('\n'); const reportFile = path.resolve(this._options.configDir, this._options.outputFile || 'test-results.csv'); this._pendingWrite = (async () => { - await fs.mkdirSync(path.dirname(reportFile), { recursive: true }); + await fs.promises.mkdir(path.dirname(reportFile), { recursive: true }); await fs.promises.writeFile(reportFile, csv); })(); } diff --git a/tests/library/inspector/cli-codegen-2.spec.ts b/tests/library/inspector/cli-codegen-2.spec.ts index 205bbbae5e..6419a6999c 100644 --- a/tests/library/inspector/cli-codegen-2.spec.ts +++ b/tests/library/inspector/cli-codegen-2.spec.ts @@ -459,8 +459,13 @@ await page1.GotoAsync("about:blank?foo");`); const cli = runCLI([`--save-storage=${storageFileName}`, `--save-har=${harFileName}`]); await cli.waitFor(`import { test, expect } from '@playwright/test'`); await cli.process.kill('SIGINT'); - const { exitCode } = await cli.process.exited; - expect(exitCode).toBe(130); + const { exitCode, signal } = await cli.process.exited; + if (exitCode !== null) { + expect(exitCode).toBe(130); + } else { + // If the runner is slow enough, the process will be forcibly terminated by the signal + expect(signal).toBe('SIGINT'); + } expect(fs.existsSync(storageFileName)).toBeTruthy(); expect(fs.existsSync(harFileName)).toBeTruthy(); }); diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-bounding-box-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-bounding-box-bidi-firefox-nightly.png new file mode 100644 index 0000000000..c2c3ddca29 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-bounding-box-bidi-firefox-nightly.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-bidi-firefox-nightly.png new file mode 100644 index 0000000000..35c53377f9 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-bidi-firefox-nightly.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-offset-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-offset-bidi-firefox-nightly.png new file mode 100644 index 0000000000..f554b1d62c Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-offset-bidi-firefox-nightly.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-larger-than-viewport-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-larger-than-viewport-bidi-firefox-nightly.png new file mode 100644 index 0000000000..6d28cddcea Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-larger-than-viewport-bidi-firefox-nightly.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-padding-border-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-padding-border-bidi-firefox-nightly.png new file mode 100644 index 0000000000..fc34319896 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-padding-border-bidi-firefox-nightly.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-rotate-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-rotate-bidi-firefox-nightly.png new file mode 100644 index 0000000000..5aab14c4b9 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-rotate-bidi-firefox-nightly.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-scrolled-into-view-bidi-firefox-nightly.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-scrolled-into-view-bidi-firefox-nightly.png new file mode 100644 index 0000000000..fc34319896 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-scrolled-into-view-bidi-firefox-nightly.png differ diff --git a/tests/page/expect-matcher-result.spec.ts-snapshots/screenshot-sanity-bidi-firefox-nightly.png b/tests/page/expect-matcher-result.spec.ts-snapshots/screenshot-sanity-bidi-firefox-nightly.png new file mode 100644 index 0000000000..122a4f0ae0 Binary files /dev/null and b/tests/page/expect-matcher-result.spec.ts-snapshots/screenshot-sanity-bidi-firefox-nightly.png differ diff --git a/tests/page/locator-misc-2.spec.ts-snapshots/screenshot-element-bounding-box-bidi-firefox-nightly.png b/tests/page/locator-misc-2.spec.ts-snapshots/screenshot-element-bounding-box-bidi-firefox-nightly.png new file mode 100644 index 0000000000..9e208f86d8 Binary files /dev/null and b/tests/page/locator-misc-2.spec.ts-snapshots/screenshot-element-bounding-box-bidi-firefox-nightly.png differ diff --git a/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-bidi-firefox-nightly.png b/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-bidi-firefox-nightly.png new file mode 100644 index 0000000000..7b16493355 Binary files /dev/null and b/tests/page/page-add-locator-handler.spec.ts-snapshots/screenshot-grid-bidi-firefox-nightly.png differ diff --git a/tests/page/page-event-request.spec.ts b/tests/page/page-event-request.spec.ts index e1fc29eb59..b2e13a4c2f 100644 --- a/tests/page/page-event-request.spec.ts +++ b/tests/page/page-event-request.spec.ts @@ -41,6 +41,20 @@ it('should fire for fetches', async ({ page, server }) => { expect(requests.length).toBe(2); }); +it('should fire for fetches with keepalive: true', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/34497' + } +}, async ({ page, server, browserName }) => { + it.fixme(browserName === 'firefox'); + const requests = []; + page.on('request', request => requests.push(request)); + await page.goto(server.EMPTY_PAGE); + await page.evaluate(() => fetch('/empty.html', { keepalive: true })); + expect(requests.length).toBe(2); +}); + it('should report requests and responses handled by service worker', async ({ page, server, isAndroid, isElectron }) => { it.fixme(isAndroid); it.fixme(isElectron); diff --git a/tests/page/page-keyboard.spec.ts b/tests/page/page-keyboard.spec.ts index 0d922584f4..86ff5508b6 100644 --- a/tests/page/page-keyboard.spec.ts +++ b/tests/page/page-keyboard.spec.ts @@ -93,18 +93,18 @@ it('should report shiftKey', async ({ page, server, browserName, platform }) => const codeForKey = { 'Shift': 16, 'Alt': 18, 'Control': 17 }; for (const modifierKey in codeForKey) { await keyboard.down(modifierKey); - expect(await page.evaluate('getResult()')).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']'); + expect(await page.evaluate('getResult()')).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left LEFT [' + modifierKey + ']'); await keyboard.down('!'); // Shift+! will generate a keypress if (modifierKey === 'Shift') - expect(await page.evaluate('getResult()')).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']'); + expect(await page.evaluate('getResult()')).toBe('Keydown: ! Digit1 STANDARD [' + modifierKey + ']\nKeypress: ! Digit1 STANDARD 33 [' + modifierKey + ']'); else - expect(await page.evaluate('getResult()')).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']'); + expect(await page.evaluate('getResult()')).toBe('Keydown: ! Digit1 STANDARD [' + modifierKey + ']'); await keyboard.up('!'); - expect(await page.evaluate('getResult()')).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']'); + expect(await page.evaluate('getResult()')).toBe('Keyup: ! Digit1 STANDARD [' + modifierKey + ']'); await keyboard.up(modifierKey); - expect(await page.evaluate('getResult()')).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []'); + expect(await page.evaluate('getResult()')).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left LEFT []'); } }); @@ -112,31 +112,31 @@ it('should report multiple modifiers', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); const keyboard = page.keyboard; await keyboard.down('Control'); - expect(await page.evaluate('getResult()')).toBe('Keydown: Control ControlLeft 17 [Control]'); + expect(await page.evaluate('getResult()')).toBe('Keydown: Control ControlLeft LEFT [Control]'); await keyboard.down('Alt'); - expect(await page.evaluate('getResult()')).toBe('Keydown: Alt AltLeft 18 [Alt Control]'); + expect(await page.evaluate('getResult()')).toBe('Keydown: Alt AltLeft LEFT [Alt Control]'); await keyboard.down(';'); - expect(await page.evaluate('getResult()')).toBe('Keydown: ; Semicolon 186 [Alt Control]'); + expect(await page.evaluate('getResult()')).toBe('Keydown: ; Semicolon STANDARD [Alt Control]'); await keyboard.up(';'); - expect(await page.evaluate('getResult()')).toBe('Keyup: ; Semicolon 186 [Alt Control]'); + expect(await page.evaluate('getResult()')).toBe('Keyup: ; Semicolon STANDARD [Alt Control]'); await keyboard.up('Control'); - expect(await page.evaluate('getResult()')).toBe('Keyup: Control ControlLeft 17 [Alt]'); + expect(await page.evaluate('getResult()')).toBe('Keyup: Control ControlLeft LEFT [Alt]'); await keyboard.up('Alt'); - expect(await page.evaluate('getResult()')).toBe('Keyup: Alt AltLeft 18 []'); + expect(await page.evaluate('getResult()')).toBe('Keyup: Alt AltLeft LEFT []'); }); it('should send proper codes while typing', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.type('!'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: ! Digit1 49 []', - 'Keypress: ! Digit1 33 33 []', - 'Keyup: ! Digit1 49 []'].join('\n')); + ['Keydown: ! Digit1 STANDARD []', + 'Keypress: ! Digit1 STANDARD 33 []', + 'Keyup: ! Digit1 STANDARD []'].join('\n')); await page.keyboard.type('^'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: ^ Digit6 54 []', - 'Keypress: ^ Digit6 94 94 []', - 'Keyup: ^ Digit6 54 []'].join('\n')); + ['Keydown: ^ Digit6 STANDARD []', + 'Keypress: ^ Digit6 STANDARD 94 []', + 'Keyup: ^ Digit6 STANDARD []'].join('\n')); }); it('should send proper codes while typing with shift', async ({ page, server }) => { @@ -145,10 +145,10 @@ it('should send proper codes while typing with shift', async ({ page, server }) await keyboard.down('Shift'); await page.keyboard.type('~'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: Shift ShiftLeft 16 [Shift]', - 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode - 'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode - 'Keyup: ~ Backquote 192 [Shift]'].join('\n')); + ['Keydown: Shift ShiftLeft LEFT [Shift]', + 'Keydown: ~ Backquote STANDARD [Shift]', + 'Keypress: ~ Backquote STANDARD 126 [Shift]', + 'Keyup: ~ Backquote STANDARD [Shift]'].join('\n')); await keyboard.up('Shift'); }); @@ -173,54 +173,54 @@ it('should press plus', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.press('+'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: + Equal 187 []', // 192 is ` keyCode - 'Keypress: + Equal 43 43 []', // 126 is ~ charCode - 'Keyup: + Equal 187 []'].join('\n')); + ['Keydown: + Equal STANDARD []', + 'Keypress: + Equal STANDARD 43 []', + 'Keyup: + Equal STANDARD []'].join('\n')); }); it('should press shift plus', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.press('Shift++'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: Shift ShiftLeft 16 [Shift]', - 'Keydown: + Equal 187 [Shift]', // 192 is ` keyCode - 'Keypress: + Equal 43 43 [Shift]', // 126 is ~ charCode - 'Keyup: + Equal 187 [Shift]', - 'Keyup: Shift ShiftLeft 16 []'].join('\n')); + ['Keydown: Shift ShiftLeft LEFT [Shift]', + 'Keydown: + Equal STANDARD [Shift]', + 'Keypress: + Equal STANDARD 43 [Shift]', + 'Keyup: + Equal STANDARD [Shift]', + 'Keyup: Shift ShiftLeft LEFT []'].join('\n')); }); it('should support plus-separated modifiers', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.press('Shift+~'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: Shift ShiftLeft 16 [Shift]', - 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode - 'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode - 'Keyup: ~ Backquote 192 [Shift]', - 'Keyup: Shift ShiftLeft 16 []'].join('\n')); + ['Keydown: Shift ShiftLeft LEFT [Shift]', + 'Keydown: ~ Backquote STANDARD [Shift]', + 'Keypress: ~ Backquote STANDARD 126 [Shift]', + 'Keyup: ~ Backquote STANDARD [Shift]', + 'Keyup: Shift ShiftLeft LEFT []'].join('\n')); }); it('should support multiple plus-separated modifiers', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.press('Control+Shift+~'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: Control ControlLeft 17 [Control]', - 'Keydown: Shift ShiftLeft 16 [Control Shift]', - 'Keydown: ~ Backquote 192 [Control Shift]', // 192 is ` keyCode - 'Keyup: ~ Backquote 192 [Control Shift]', - 'Keyup: Shift ShiftLeft 16 [Control]', - 'Keyup: Control ControlLeft 17 []'].join('\n')); + ['Keydown: Control ControlLeft LEFT [Control]', + 'Keydown: Shift ShiftLeft LEFT [Control Shift]', + 'Keydown: ~ Backquote STANDARD [Control Shift]', + 'Keyup: ~ Backquote STANDARD [Control Shift]', + 'Keyup: Shift ShiftLeft LEFT [Control]', + 'Keyup: Control ControlLeft LEFT []'].join('\n')); }); it('should shift raw codes', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.press('Shift+Digit3'); expect(await page.evaluate('getResult()')).toBe( - ['Keydown: Shift ShiftLeft 16 [Shift]', - 'Keydown: # Digit3 51 [Shift]', // 51 is # keyCode - 'Keypress: # Digit3 35 35 [Shift]', // 35 is # charCode - 'Keyup: # Digit3 51 [Shift]', - 'Keyup: Shift ShiftLeft 16 []'].join('\n')); + ['Keydown: Shift ShiftLeft LEFT [Shift]', + 'Keydown: # Digit3 STANDARD [Shift]', + 'Keypress: # Digit3 STANDARD 35 [Shift]', + 'Keyup: # Digit3 STANDARD [Shift]', + 'Keyup: Shift ShiftLeft LEFT []'].join('\n')); }); it('should specify repeat property', async ({ page, server }) => { @@ -710,7 +710,7 @@ it('should have correct Keydown/Keyup order when pressing Escape key', async ({ await page.goto(server.PREFIX + '/input/keyboard.html'); await page.keyboard.press('Escape'); expect(await page.evaluate('getResult()')).toBe(` -Keydown: Escape Escape 27 [] -Keyup: Escape Escape 27 [] +Keydown: Escape Escape STANDARD [] +Keyup: Escape Escape STANDARD [] `.trim()); }); diff --git a/tests/playwright-test/aria-snapshot-file.spec.ts b/tests/playwright-test/aria-snapshot-file.spec.ts index 18ebc072cc..f9c2563f37 100644 --- a/tests/playwright-test/aria-snapshot-file.spec.ts +++ b/tests/playwright-test/aria-snapshot-file.spec.ts @@ -152,3 +152,60 @@ test('should generate snapshot name', async ({ runInlineTest }, testInfo) => { const snapshot2 = await fs.promises.readFile(testInfo.outputPath('__snapshots__/a.spec.ts/test-name-2.yml'), 'utf8'); expect(snapshot2).toBe('- heading "hello world 2" [level=1]'); }); + +for (const updateSnapshots of ['all', 'changed', 'missing', 'none']) { + test(`should update snapshot with the update-snapshots=${updateSnapshots} (config)`, async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + export default { + snapshotPathTemplate: '__snapshots__/{testFilePath}/{arg}{ext}', + updateSnapshots: '${updateSnapshots}', + }; + `, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('test', async ({ page }) => { + await page.setContent(\`

New content

\`); + await expect(page.locator('body')).toMatchAriaSnapshot({ timeout: 1 }); + }); + `, + '__snapshots__/a.spec.ts/test-1.yml': '- heading "Old content" [level=1]', + }); + + const rebase = updateSnapshots === 'all' || updateSnapshots === 'changed'; + expect(result.exitCode).toBe(rebase ? 0 : 1); + if (rebase) { + const snapshotOutputPath = testInfo.outputPath('__snapshots__/a.spec.ts/test-1.yml'); + expect(result.output).toContain(`A snapshot is generated at`); + const data = fs.readFileSync(snapshotOutputPath); + expect(data.toString()).toBe('- heading "New content" [level=1]'); + } else { + expect(result.output).toContain(`expect.toMatchAriaSnapshot`); + } + }); +} + +test('should respect timeout', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + export default { + snapshotPathTemplate: '__snapshots__/{testFilePath}/{arg}{ext}', + }; + `, + 'test.yml': ` + - heading "hello world" + `, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + import path from 'path'; + test('test', async ({ page }) => { + await page.setContent(\`

hello world

\`); + await expect(page.locator('body')).toMatchAriaSnapshot({ timeout: 1 }); + }); + `, + '__snapshots__/a.spec.ts/test-1.yml': '- heading "new world" [level=1]', + }); + + expect(result.exitCode).toBe(1); + expect(result.output).toContain(`Timed out 1ms waiting for`); +}); diff --git a/tests/playwright-test/golden.spec.ts b/tests/playwright-test/golden.spec.ts index bf3dbb8880..83ae3442c7 100644 --- a/tests/playwright-test/golden.spec.ts +++ b/tests/playwright-test/golden.spec.ts @@ -341,6 +341,36 @@ test('should update snapshot with the update-snapshots flag', async ({ runInline expect(data.toString()).toBe(ACTUAL_SNAPSHOT); }); +for (const updateSnapshots of ['all', 'changed', 'missing', 'none']) { + test(`should update snapshot with the update-snapshots=${updateSnapshots} (config)`, async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': `export default { updateSnapshots: '${updateSnapshots}' };`, + ...files, + 'a.spec.js-snapshots/snapshot.txt': 'Hello world', + 'a.spec.js': ` + const { test, expect } = require('./helper'); + test('is a test', ({}) => { + expect('Hello world updated').toMatchSnapshot('snapshot.txt'); + }); + ` + }); + + const rebase = updateSnapshots === 'all' || updateSnapshots === 'changed'; + expect(result.exitCode).toBe(rebase ? 0 : 1); + if (rebase) { + const snapshotOutputPath = testInfo.outputPath('a.spec.js-snapshots/snapshot.txt'); + if (updateSnapshots === 'all') + expect(result.output).toContain(`${snapshotOutputPath} is not the same, writing actual.`); + if (updateSnapshots === 'changed') + expect(result.output).toContain(`${snapshotOutputPath} does not match, writing actual.`); + const data = fs.readFileSync(snapshotOutputPath); + expect(data.toString()).toBe('Hello world updated'); + } else { + expect(result.output).toContain(`toMatchSnapshot`); + } + }); +} + test('should ignore text snapshot with the ignore-snapshots flag', async ({ runInlineTest }, testInfo) => { const EXPECTED_SNAPSHOT = 'Hello world'; const ACTUAL_SNAPSHOT = 'Hello world updated'; @@ -1140,3 +1170,25 @@ test('should throw if a Promise was passed to toMatchSnapshot', async ({ runInli expect(result.exitCode).toBe(0); expect(result.passed).toBe(1); }); + + +test('should respect update snapshot option from config', async ({ runInlineTest }, testInfo) => { + const EXPECTED_SNAPSHOT = 'Hello world'; + const ACTUAL_SNAPSHOT = 'Hello world updated'; + const result = await runInlineTest({ + ...files, + 'a.spec.js-snapshots/snapshot.txt': EXPECTED_SNAPSHOT, + 'a.spec.js': ` + const { test, expect } = require('./helper'); + test('is a test', ({}) => { + expect('${ACTUAL_SNAPSHOT}').toMatchSnapshot('snapshot.txt'); + }); + ` + }, { 'update-snapshots': true }); + + expect(result.exitCode).toBe(0); + const snapshotOutputPath = testInfo.outputPath('a.spec.js-snapshots/snapshot.txt'); + expect(result.output).toContain(`${snapshotOutputPath} does not match, writing actual.`); + const data = fs.readFileSync(snapshotOutputPath); + expect(data.toString()).toBe(ACTUAL_SNAPSHOT); +});