From b339d457e3432d13d997a3787ae6c3e77bfaa0e5 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 17 Jan 2025 10:17:26 -0800 Subject: [PATCH 01/32] chore: dogfood jest-style aria snapshots (#34365) --- .../should-traverse-up-down-1.yml | 13 ++++++ .../should-traverse-up-down-2.yml | 13 ++++++ .../should-traverse-up-down-3.yml | 13 ++++++ .../stable-test-runner/package-lock.json | 46 +++++++++---------- .../stable-test-runner/package.json | 2 +- .../playwright-test/ui-mode-test-tree.spec.ts | 3 ++ 6 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-1.yml create mode 100644 tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-2.yml create mode 100644 tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-3.yml diff --git a/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-1.yml b/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-1.yml new file mode 100644 index 0000000000..2f556153ec --- /dev/null +++ b/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-1.yml @@ -0,0 +1,13 @@ +- tree: + - treeitem "[icon-circle-outline] a.test.ts" [expanded]: + - group: + - treeitem "[icon-circle-outline] passes" [selected]: + - button "Run" + - button "Show source" + - button "Watch" + - treeitem "[icon-circle-outline] fails" + - treeitem "[icon-circle-outline] suite" + - treeitem "[icon-circle-outline] b.test.ts" [expanded]: + - group: + - treeitem "[icon-circle-outline] passes" + - treeitem "[icon-circle-outline] fails" \ No newline at end of file diff --git a/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-2.yml b/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-2.yml new file mode 100644 index 0000000000..7a9622befd --- /dev/null +++ b/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-2.yml @@ -0,0 +1,13 @@ +- tree: + - treeitem "[icon-circle-outline] a.test.ts" [expanded]: + - group: + - treeitem "[icon-circle-outline] passes" + - treeitem "[icon-circle-outline] fails" [selected]: + - button "Run" + - button "Show source" + - button "Watch" + - treeitem "[icon-circle-outline] suite" + - treeitem "[icon-circle-outline] b.test.ts" [expanded]: + - group: + - treeitem "[icon-circle-outline] passes" + - treeitem "[icon-circle-outline] fails" \ No newline at end of file diff --git a/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-3.yml b/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-3.yml new file mode 100644 index 0000000000..2f556153ec --- /dev/null +++ b/tests/playwright-test/__screenshots__/ui-mode-test-tree.spec.ts/should-traverse-up-down-3.yml @@ -0,0 +1,13 @@ +- tree: + - treeitem "[icon-circle-outline] a.test.ts" [expanded]: + - group: + - treeitem "[icon-circle-outline] passes" [selected]: + - button "Run" + - button "Show source" + - button "Watch" + - treeitem "[icon-circle-outline] fails" + - treeitem "[icon-circle-outline] suite" + - treeitem "[icon-circle-outline] b.test.ts" [expanded]: + - group: + - treeitem "[icon-circle-outline] passes" + - treeitem "[icon-circle-outline] fails" \ No newline at end of file diff --git a/tests/playwright-test/stable-test-runner/package-lock.json b/tests/playwright-test/stable-test-runner/package-lock.json index 191deff43f..e3abdb32c5 100644 --- a/tests/playwright-test/stable-test-runner/package-lock.json +++ b/tests/playwright-test/stable-test-runner/package-lock.json @@ -5,16 +5,16 @@ "packages": { "": { "dependencies": { - "@playwright/test": "1.49.0-beta-1731772650000" + "@playwright/test": "1.50.0-alpha-2025-01-17" } }, "node_modules/@playwright/test": { - "version": "1.49.0-beta-1731772650000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-beta-1731772650000.tgz", - "integrity": "sha512-0d7DBoGZ23lv1/EkNoFXj5fQ9k3qlYHRE7la68zXihtjTH1DdwEtgdMgXR4UEScF2r/YNXaGRZ7sK/DVu9f6Aw==", + "version": "1.50.0-alpha-2025-01-17", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.0-alpha-2025-01-17.tgz", + "integrity": "sha512-fMUwMcP0YE2knged9GJXqv3fpT2xoywTtqYaSzpZmjnNESF+CUUAGY2hHm9/fz/v9ijcjyd62hYFbqS5KeKuHQ==", "license": "Apache-2.0", "dependencies": { - "playwright": "1.49.0-beta-1731772650000" + "playwright": "1.50.0-alpha-2025-01-17" }, "bin": { "playwright": "cli.js" @@ -38,12 +38,12 @@ } }, "node_modules/playwright": { - "version": "1.49.0-beta-1731772650000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-beta-1731772650000.tgz", - "integrity": "sha512-+LLjx+DMLjx1qiBtLuURTLV3LmFxvQOSaVp9EDMH/qYpclhsp/W41vNxxZEqf8CIsL0BKHIVQYU+6D3OLnJq8g==", + "version": "1.50.0-alpha-2025-01-17", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.0-alpha-2025-01-17.tgz", + "integrity": "sha512-LRavQ9Qu27nHvJ57f+7UDBTAEWhGKV+MS2qLAJpF8HXtfSMVlLK82W9Oba41lCNUzgLoAuFv0wCO/RcHqLz7yQ==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.49.0-beta-1731772650000" + "playwright-core": "1.50.0-alpha-2025-01-17" }, "bin": { "playwright": "cli.js" @@ -56,9 +56,9 @@ } }, "node_modules/playwright-core": { - "version": "1.49.0-beta-1731772650000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-beta-1731772650000.tgz", - "integrity": "sha512-W1HbioibWPPsazFzU/PL9QzGEGubxizQOyMON8/d7DjOpNBqfzuemNuAsNBXucUEVbUlOOzMuoAEX/iqXUOl6Q==", + "version": "1.50.0-alpha-2025-01-17", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.0-alpha-2025-01-17.tgz", + "integrity": "sha512-XkoLZ+7J5ybDq68xSlofPziH1Y8It9LpMisxtBfebjKWbVY8BzctlB1Da9udKDP0oWQPNq4tUnwW0hkeET3lUg==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -70,11 +70,11 @@ }, "dependencies": { "@playwright/test": { - "version": "1.49.0-beta-1731772650000", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0-beta-1731772650000.tgz", - "integrity": "sha512-0d7DBoGZ23lv1/EkNoFXj5fQ9k3qlYHRE7la68zXihtjTH1DdwEtgdMgXR4UEScF2r/YNXaGRZ7sK/DVu9f6Aw==", + "version": "1.50.0-alpha-2025-01-17", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.0-alpha-2025-01-17.tgz", + "integrity": "sha512-fMUwMcP0YE2knged9GJXqv3fpT2xoywTtqYaSzpZmjnNESF+CUUAGY2hHm9/fz/v9ijcjyd62hYFbqS5KeKuHQ==", "requires": { - "playwright": "1.49.0-beta-1731772650000" + "playwright": "1.50.0-alpha-2025-01-17" } }, "fsevents": { @@ -84,18 +84,18 @@ "optional": true }, "playwright": { - "version": "1.49.0-beta-1731772650000", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0-beta-1731772650000.tgz", - "integrity": "sha512-+LLjx+DMLjx1qiBtLuURTLV3LmFxvQOSaVp9EDMH/qYpclhsp/W41vNxxZEqf8CIsL0BKHIVQYU+6D3OLnJq8g==", + "version": "1.50.0-alpha-2025-01-17", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.0-alpha-2025-01-17.tgz", + "integrity": "sha512-LRavQ9Qu27nHvJ57f+7UDBTAEWhGKV+MS2qLAJpF8HXtfSMVlLK82W9Oba41lCNUzgLoAuFv0wCO/RcHqLz7yQ==", "requires": { "fsevents": "2.3.2", - "playwright-core": "1.49.0-beta-1731772650000" + "playwright-core": "1.50.0-alpha-2025-01-17" } }, "playwright-core": { - "version": "1.49.0-beta-1731772650000", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0-beta-1731772650000.tgz", - "integrity": "sha512-W1HbioibWPPsazFzU/PL9QzGEGubxizQOyMON8/d7DjOpNBqfzuemNuAsNBXucUEVbUlOOzMuoAEX/iqXUOl6Q==" + "version": "1.50.0-alpha-2025-01-17", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.0-alpha-2025-01-17.tgz", + "integrity": "sha512-XkoLZ+7J5ybDq68xSlofPziH1Y8It9LpMisxtBfebjKWbVY8BzctlB1Da9udKDP0oWQPNq4tUnwW0hkeET3lUg==" } } } diff --git a/tests/playwright-test/stable-test-runner/package.json b/tests/playwright-test/stable-test-runner/package.json index eb5df89830..559cfc10e1 100644 --- a/tests/playwright-test/stable-test-runner/package.json +++ b/tests/playwright-test/stable-test-runner/package.json @@ -1,6 +1,6 @@ { "private": true, "dependencies": { - "@playwright/test": "1.49.0-beta-1731772650000" + "@playwright/test": "1.50.0-alpha-2025-01-17" } } diff --git a/tests/playwright-test/ui-mode-test-tree.spec.ts b/tests/playwright-test/ui-mode-test-tree.spec.ts index 69abd60cfd..e663b986b6 100644 --- a/tests/playwright-test/ui-mode-test-tree.spec.ts +++ b/tests/playwright-test/ui-mode-test-tree.spec.ts @@ -164,6 +164,7 @@ test('should traverse up/down', async ({ runUITest }) => { - treeitem "[icon-circle-outline] fails" - treeitem "[icon-circle-outline] suite" [expanded=false] `); + await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(); await page.keyboard.press('ArrowDown'); await expect.poll(dumpTestTree(page)).toContain(` @@ -180,6 +181,7 @@ test('should traverse up/down', async ({ runUITest }) => { - treeitem "[icon-circle-outline] fails" [selected] - treeitem "[icon-circle-outline] suite" [expanded=false] `); + await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(); await page.keyboard.press('ArrowUp'); await expect.poll(dumpTestTree(page)).toContain(` @@ -196,6 +198,7 @@ test('should traverse up/down', async ({ runUITest }) => { - treeitem "[icon-circle-outline] fails" - treeitem "[icon-circle-outline] suite" [expanded=false] `); + await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(); }); test('should expand / collapse groups', async ({ runUITest }) => { From 1b21ec9cd84387abe1537d15862e3b9bf5f1f387 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 17 Jan 2025 10:17:49 -0800 Subject: [PATCH 02/32] chore: remove --save-trace codegen option (#34362) --- packages/playwright-core/src/cli/program.ts | 7 - .../playwright-core/src/server/recorder.ts | 3 +- .../src/server/recorder/recorderCollection.ts | 22 +-- .../src/server/recorder/recorderRunner.ts | 8 +- .../src/server/recorder/recorderUtils.ts | 26 +-- .../src/utils/isomorphic/recorderUtils.ts | 163 ------------------ tests/library/inspector/cli-codegen-2.spec.ts | 13 +- 7 files changed, 16 insertions(+), 226 deletions(-) delete mode 100644 packages/playwright-core/src/utils/isomorphic/recorderUtils.ts diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index a69414f2b2..ce568b8dd3 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -65,7 +65,6 @@ commandWithOpenOptions('codegen [url]', 'open page and generate code for user ac [ ['-o, --output ', 'saves the generated script to a file'], ['--target ', `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit`, codegenId()], - ['--save-trace ', 'record a trace for the session and save it to a file'], ['--test-id-attribute ', 'use the specified attribute to generate data test ID selectors'], ]).action(function(url, options) { codegen(options, url).catch(logErrorAndExit); @@ -353,7 +352,6 @@ type Options = { saveHar?: string; saveHarGlob?: string; saveStorage?: string; - saveTrace?: string; timeout: string; timezone?: string; viewportSize?: string; @@ -508,8 +506,6 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro if (closingBrowser) return; closingBrowser = true; - if (options.saveTrace) - await context.tracing.stop({ path: options.saveTrace }); if (options.saveStorage) await context.storageState({ path: options.saveStorage }).catch(e => null); if (options.saveHar) @@ -536,9 +532,6 @@ async function launchContext(options: Options, extraOptions: LaunchOptions): Pro context.setDefaultTimeout(timeout); context.setDefaultNavigationTimeout(timeout); - if (options.saveTrace) - await context.tracing.start({ screenshots: true, snapshots: true }); - // Omit options that we add automatically for presentation purpose. delete launchOptions.headless; delete launchOptions.executablePath; diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index f763d5491b..59705c2eb4 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -27,9 +27,8 @@ import { Debugger } from './debugger'; import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation'; import { ContextRecorder, generateFrameSelector } from './recorder/contextRecorder'; import type { IRecorderAppFactory, IRecorderApp, IRecorder } from './recorder/recorderFrontend'; -import { metadataToCallLog } from './recorder/recorderUtils'; +import { buildFullSelector, metadataToCallLog } from './recorder/recorderUtils'; import type * as actions from '@recorder/actions'; -import { buildFullSelector } from '../utils/isomorphic/recorderUtils'; import { stringifySelector } from '../utils/isomorphic/selectorParser'; import type { Frame } from './frames'; import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot'; diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts index 2643dece91..162d5d6813 100644 --- a/packages/playwright-core/src/server/recorder/recorderCollection.ts +++ b/packages/playwright-core/src/server/recorder/recorderCollection.ts @@ -20,10 +20,8 @@ import type { Page } from '../page'; import type { Signal } from '../../../../recorder/src/actions'; import type * as actions from '@recorder/actions'; import { monotonicTime } from '../../utils/time'; -import { callMetadataForAction, collapseActions } from './recorderUtils'; -import { serializeError } from '../errors'; +import { collapseActions } from './recorderUtils'; import { performAction } from './recorderRunner'; -import type { CallMetadata } from '@protocol/callMetadata'; import { isUnderTest } from '../../utils/debug'; export class RecorderCollection extends EventEmitter { @@ -46,8 +44,8 @@ export class RecorderCollection extends EventEmitter { } async performAction(actionInContext: actions.ActionInContext) { - await this._addAction(actionInContext, async callMetadata => { - await performAction(callMetadata, this._pageAliases, actionInContext); + await this._addAction(actionInContext, async () => { + await performAction(this._pageAliases, actionInContext); }); } @@ -60,7 +58,7 @@ export class RecorderCollection extends EventEmitter { this._addAction(actionInContext).catch(() => {}); } - private async _addAction(actionInContext: actions.ActionInContext, callback?: (callMetadata: CallMetadata) => Promise) { + private async _addAction(actionInContext: actions.ActionInContext, callback?: () => Promise) { if (!this._enabled) return; if (actionInContext.action.name === 'openPage' || actionInContext.action.name === 'closePage') { @@ -69,18 +67,10 @@ export class RecorderCollection extends EventEmitter { return; } - const { callMetadata, mainFrame } = callMetadataForAction(this._pageAliases, actionInContext); - await mainFrame.instrumentation.onBeforeCall(mainFrame, callMetadata); this._actions.push(actionInContext); this._fireChange(); - const error = await callback?.(callMetadata).catch((e: Error) => e); - callMetadata.endTime = monotonicTime(); - actionInContext.endTime = callMetadata.endTime; - callMetadata.error = error ? serializeError(error) : undefined; - // Do not wait for onAfterCall so that performAction returned immediately after the action. - mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata).then(() => { - this._fireChange(); - }).catch(() => {}); + await callback?.().catch(); + actionInContext.endTime = monotonicTime(); } signal(pageAlias: string, frame: Frame, signal: Signal) { diff --git a/packages/playwright-core/src/server/recorder/recorderRunner.ts b/packages/playwright-core/src/server/recorder/recorderRunner.ts index 1b33895f98..bb225ec5ab 100644 --- a/packages/playwright-core/src/server/recorder/recorderRunner.ts +++ b/packages/playwright-core/src/server/recorder/recorderRunner.ts @@ -16,14 +16,14 @@ import { serializeExpectedTextValues } from '../../utils'; import { toKeyboardModifiers } from '../codegen/language'; -import type { CallMetadata } from '../instrumentation'; +import { serverSideCallMetadata } from '../instrumentation'; import type { Page } from '../page'; import type * as actions from '@recorder/actions'; import type * as types from '../types'; -import { mainFrameForAction } from './recorderUtils'; -import { buildFullSelector } from '../../utils/isomorphic/recorderUtils'; +import { buildFullSelector, mainFrameForAction } from './recorderUtils'; -export async function performAction(callMetadata: CallMetadata, pageAliases: Map, actionInContext: actions.ActionInContext) { +export async function performAction(pageAliases: Map, actionInContext: actions.ActionInContext) { + const callMetadata = serverSideCallMetadata(); const mainFrame = mainFrameForAction(pageAliases, actionInContext); const { action } = actionInContext; diff --git a/packages/playwright-core/src/server/recorder/recorderUtils.ts b/packages/playwright-core/src/server/recorder/recorderUtils.ts index 990ba959d6..77ef329d27 100644 --- a/packages/playwright-core/src/server/recorder/recorderUtils.ts +++ b/packages/playwright-core/src/server/recorder/recorderUtils.ts @@ -19,8 +19,10 @@ import type { CallLog, CallLogStatus } from '@recorder/recorderTypes'; import type { Page } from '../page'; import type { Frame } from '../frames'; import type * as actions from '@recorder/actions'; -import { createGuid } from '../../utils'; -import { buildFullSelector, traceParamsForAction } from '../../utils/isomorphic/recorderUtils'; + +export function buildFullSelector(framePath: string[], selector: string) { + return [...framePath, selector].join(' >> internal:control=enter-frame >> '); +} export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog { let title = metadata.apiName || metadata.method; @@ -70,26 +72,6 @@ export async function frameForAction(pageAliases: Map, actionInCon return result.frame; } -export function callMetadataForAction(pageAliases: Map, actionInContext: actions.ActionInContext): { callMetadata: CallMetadata, mainFrame: Frame } { - const mainFrame = mainFrameForAction(pageAliases, actionInContext); - const { method, apiName, params } = traceParamsForAction(actionInContext); - - const callMetadata: CallMetadata = { - id: `call@${createGuid()}`, - apiName, - objectId: mainFrame.guid, - pageId: mainFrame._page.guid, - frameId: mainFrame.guid, - startTime: actionInContext.startTime, - endTime: 0, - type: 'Frame', - method, - params, - log: [], - }; - return { callMetadata, mainFrame }; -} - export function collapseActions(actions: actions.ActionInContext[]): actions.ActionInContext[] { const result: actions.ActionInContext[] = []; for (const action of actions) { diff --git a/packages/playwright-core/src/utils/isomorphic/recorderUtils.ts b/packages/playwright-core/src/utils/isomorphic/recorderUtils.ts deleted file mode 100644 index 7ef45e5ece..0000000000 --- a/packages/playwright-core/src/utils/isomorphic/recorderUtils.ts +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type * as recorderActions from '@recorder/actions'; -import type * as channels from '@protocol/channels'; -import type * as types from '../../server/types'; - -export function buildFullSelector(framePath: string[], selector: string) { - return [...framePath, selector].join(' >> internal:control=enter-frame >> '); -} - -const kDefaultTimeout = 5_000; - -export function traceParamsForAction(actionInContext: recorderActions.ActionInContext): { method: string, apiName: string, params: any } { - const { action } = actionInContext; - - switch (action.name) { - case 'navigate': { - const params: channels.FrameGotoParams = { - url: action.url, - }; - return { method: 'goto', apiName: 'page.goto', params }; - } - case 'openPage': - case 'closePage': - throw new Error('Not reached'); - } - - const selector = buildFullSelector(actionInContext.frame.framePath, action.selector); - switch (action.name) { - case 'click': { - const params: channels.FrameClickParams = { - selector, - strict: true, - modifiers: toKeyboardModifiers(action.modifiers), - button: action.button, - clickCount: action.clickCount, - position: action.position, - }; - return { method: 'click', apiName: 'locator.click', params }; - } - case 'press': { - const params: channels.FramePressParams = { - selector, - strict: true, - key: [...toKeyboardModifiers(action.modifiers), action.key].join('+'), - }; - return { method: 'press', apiName: 'locator.press', params }; - } - case 'fill': { - const params: channels.FrameFillParams = { - selector, - strict: true, - value: action.text, - }; - return { method: 'fill', apiName: 'locator.fill', params }; - } - case 'setInputFiles': { - const params: channels.FrameSetInputFilesParams = { - selector, - strict: true, - localPaths: action.files, - }; - return { method: 'setInputFiles', apiName: 'locator.setInputFiles', params }; - } - case 'check': { - const params: channels.FrameCheckParams = { - selector, - strict: true, - }; - return { method: 'check', apiName: 'locator.check', params }; - } - case 'uncheck': { - const params: channels.FrameUncheckParams = { - selector, - strict: true, - }; - return { method: 'uncheck', apiName: 'locator.uncheck', params }; - } - case 'select': { - const params: channels.FrameSelectOptionParams = { - selector, - strict: true, - options: action.options.map(option => ({ value: option })), - }; - return { method: 'selectOption', apiName: 'locator.selectOption', params }; - } - case 'assertChecked': { - const params: channels.FrameExpectParams = { - selector: action.selector, - expression: 'to.be.checked', - isNot: !action.checked, - timeout: kDefaultTimeout, - }; - return { method: 'expect', apiName: 'expect.toBeChecked', params }; - } - case 'assertText': { - const params: channels.FrameExpectParams = { - selector, - expression: 'to.have.text', - expectedText: [], - isNot: false, - timeout: kDefaultTimeout, - }; - return { method: 'expect', apiName: 'expect.toContainText', params }; - } - case 'assertValue': { - const params: channels.FrameExpectParams = { - selector, - expression: 'to.have.value', - expectedValue: undefined, - isNot: false, - timeout: kDefaultTimeout, - }; - return { method: 'expect', apiName: 'expect.toHaveValue', params }; - } - case 'assertVisible': { - const params: channels.FrameExpectParams = { - selector, - expression: 'to.be.visible', - isNot: false, - timeout: kDefaultTimeout, - }; - return { method: 'expect', apiName: 'expect.toBeVisible', params }; - } - case 'assertSnapshot': { - const params: channels.FrameExpectParams = { - selector, - expression: 'to.match.snapshot', - expectedText: [], - isNot: false, - timeout: kDefaultTimeout, - }; - return { method: 'expect', apiName: 'expect.toMatchAriaSnapshot', params }; - } - } -} - -export function toKeyboardModifiers(modifiers: number): types.SmartKeyboardModifier[] { - const result: types.SmartKeyboardModifier[] = []; - if (modifiers & 1) - result.push('Alt'); - if (modifiers & 2) - result.push('ControlOrMeta'); - if (modifiers & 4) - result.push('ControlOrMeta'); - if (modifiers & 8) - result.push('Shift'); - return result; -} diff --git a/tests/library/inspector/cli-codegen-2.spec.ts b/tests/library/inspector/cli-codegen-2.spec.ts index 920ecbdf76..205bbbae5e 100644 --- a/tests/library/inspector/cli-codegen-2.spec.ts +++ b/tests/library/inspector/cli-codegen-2.spec.ts @@ -451,27 +451,16 @@ await page1.GotoAsync("about:blank?foo");`); await recorder.waitForOutput('JavaScript', `await page.goto('${server.PREFIX}/page2.html');`); }); - test('should --save-trace', async ({ runCLI }, testInfo) => { - const traceFileName = testInfo.outputPath('trace.zip'); - const cli = runCLI([`--save-trace=${traceFileName}`], { - autoExitWhen: ' ', - }); - await cli.waitForCleanExit(); - expect(fs.existsSync(traceFileName)).toBeTruthy(); - }); - test('should save assets via SIGINT', async ({ runCLI, platform }, testInfo) => { test.skip(platform === 'win32', 'SIGINT not supported on Windows'); - const traceFileName = testInfo.outputPath('trace.zip'); const storageFileName = testInfo.outputPath('auth.json'); const harFileName = testInfo.outputPath('har.har'); - const cli = runCLI([`--save-trace=${traceFileName}`, `--save-storage=${storageFileName}`, `--save-har=${harFileName}`]); + 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); - expect(fs.existsSync(traceFileName)).toBeTruthy(); expect(fs.existsSync(storageFileName)).toBeTruthy(); expect(fs.existsSync(harFileName)).toBeTruthy(); }); From 3c160df06a31c01e20eeaf50af3abcd1d7f02a0b Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Fri, 17 Jan 2025 12:34:59 -0800 Subject: [PATCH 03/32] chore: mark v1.51.0-next (#34382) --- package-lock.json | 60 +++++++++---------- package.json | 2 +- .../playwright-browser-chromium/package.json | 4 +- .../playwright-browser-firefox/package.json | 4 +- .../playwright-browser-webkit/package.json | 4 +- packages/playwright-chromium/package.json | 4 +- packages/playwright-core/package.json | 2 +- packages/playwright-ct-core/package.json | 6 +- packages/playwright-ct-react/package.json | 4 +- packages/playwright-ct-react17/package.json | 4 +- packages/playwright-ct-svelte/package.json | 4 +- packages/playwright-ct-vue/package.json | 4 +- packages/playwright-firefox/package.json | 4 +- packages/playwright-test/package.json | 4 +- packages/playwright-webkit/package.json | 4 +- packages/playwright/package.json | 4 +- 16 files changed, 59 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index 411ef13b62..8c242d4bb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playwright-internal", - "version": "1.50.0-next", + "version": "1.51.0-next", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -7751,10 +7751,10 @@ "version": "0.0.0" }, "packages/playwright": { - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "bin": { "playwright": "cli.js" @@ -7768,11 +7768,11 @@ }, "packages/playwright-browser-chromium": { "name": "@playwright/browser-chromium", - "version": "1.50.0-next", + "version": "1.51.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "engines": { "node": ">=18" @@ -7780,11 +7780,11 @@ }, "packages/playwright-browser-firefox": { "name": "@playwright/browser-firefox", - "version": "1.50.0-next", + "version": "1.51.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "engines": { "node": ">=18" @@ -7792,22 +7792,22 @@ }, "packages/playwright-browser-webkit": { "name": "@playwright/browser-webkit", - "version": "1.50.0-next", + "version": "1.51.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "engines": { "node": ">=18" } }, "packages/playwright-chromium": { - "version": "1.50.0-next", + "version": "1.51.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "bin": { "playwright": "cli.js" @@ -7817,7 +7817,7 @@ } }, "packages/playwright-core": { - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -7828,11 +7828,11 @@ }, "packages/playwright-ct-core": { "name": "@playwright/experimental-ct-core", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "playwright": "1.50.0-next", - "playwright-core": "1.50.0-next", + "playwright": "1.51.0-next", + "playwright-core": "1.51.0-next", "vite": "^5.2.8" }, "engines": { @@ -7841,10 +7841,10 @@ }, "packages/playwright-ct-react": { "name": "@playwright/experimental-ct-react", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -7856,10 +7856,10 @@ }, "packages/playwright-ct-react17": { "name": "@playwright/experimental-ct-react17", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -7871,10 +7871,10 @@ }, "packages/playwright-ct-svelte": { "name": "@playwright/experimental-ct-svelte", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "bin": { @@ -7889,10 +7889,10 @@ }, "packages/playwright-ct-vue": { "name": "@playwright/experimental-ct-vue", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { @@ -7903,11 +7903,11 @@ } }, "packages/playwright-firefox": { - "version": "1.50.0-next", + "version": "1.51.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "bin": { "playwright": "cli.js" @@ -7918,10 +7918,10 @@ }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.50.0-next", + "version": "1.51.0-next", "license": "Apache-2.0", "dependencies": { - "playwright": "1.50.0-next" + "playwright": "1.51.0-next" }, "bin": { "playwright": "cli.js" @@ -7931,11 +7931,11 @@ } }, "packages/playwright-webkit": { - "version": "1.50.0-next", + "version": "1.51.0-next", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "bin": { "playwright": "cli.js" diff --git a/package.json b/package.json index 5eafd4d805..76a2b426f7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-browser-chromium/package.json b/packages/playwright-browser-chromium/package.json index 909d5d3261..6d3d2cd294 100644 --- a/packages/playwright-browser-chromium/package.json +++ b/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" } } diff --git a/packages/playwright-browser-firefox/package.json b/packages/playwright-browser-firefox/package.json index 635cfd6811..3eb00e556d 100644 --- a/packages/playwright-browser-firefox/package.json +++ b/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" } } diff --git a/packages/playwright-browser-webkit/package.json b/packages/playwright-browser-webkit/package.json index 61811e3684..1072b6b401 100644 --- a/packages/playwright-browser-webkit/package.json +++ b/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" } } diff --git a/packages/playwright-chromium/package.json b/packages/playwright-chromium/package.json index 22cabe176c..32b966d476 100644 --- a/packages/playwright-chromium/package.json +++ b/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" } } diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 9b484085dd..d99b9e2d6a 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index 60144fa7c3..e9376d1963 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-core", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright Component Testing Helpers", "repository": { "type": "git", @@ -26,8 +26,8 @@ } }, "dependencies": { - "playwright-core": "1.50.0-next", + "playwright-core": "1.51.0-next", "vite": "^5.2.8", - "playwright": "1.50.0-next" + "playwright": "1.51.0-next" } } diff --git a/packages/playwright-ct-react/package.json b/packages/playwright-ct-react/package.json index 39a8e98a15..dde3c05e54 100644 --- a/packages/playwright-ct-react/package.json +++ b/packages/playwright-ct-react/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-react17/package.json b/packages/playwright-ct-react17/package.json index 41665bfb9b..f8dce715b5 100644 --- a/packages/playwright-ct-react17/package.json +++ b/packages/playwright-ct-react17/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-react17", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright Component Testing for React", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@vitejs/plugin-react": "^4.2.1" }, "bin": { diff --git a/packages/playwright-ct-svelte/package.json b/packages/playwright-ct-svelte/package.json index 7afba03632..fa01d16d2e 100644 --- a/packages/playwright-ct-svelte/package.json +++ b/packages/playwright-ct-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-svelte", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright Component Testing for Svelte", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@sveltejs/vite-plugin-svelte": "^3.0.1" }, "devDependencies": { diff --git a/packages/playwright-ct-vue/package.json b/packages/playwright-ct-vue/package.json index fe48461394..fb35b79143 100644 --- a/packages/playwright-ct-vue/package.json +++ b/packages/playwright-ct-vue/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/experimental-ct-vue", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "Playwright Component Testing for Vue", "repository": { "type": "git", @@ -30,7 +30,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@playwright/experimental-ct-core": "1.50.0-next", + "@playwright/experimental-ct-core": "1.51.0-next", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { diff --git a/packages/playwright-firefox/package.json b/packages/playwright-firefox/package.json index 44832c5532..2d34bcf3ee 100644 --- a/packages/playwright-firefox/package.json +++ b/packages/playwright-firefox/package.json @@ -1,6 +1,6 @@ { "name": "playwright-firefox", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate Firefox", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" } } diff --git a/packages/playwright-test/package.json b/packages/playwright-test/package.json index dcf74e1111..82a134b3d4 100644 --- a/packages/playwright-test/package.json +++ b/packages/playwright-test/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/test", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,6 @@ }, "scripts": {}, "dependencies": { - "playwright": "1.50.0-next" + "playwright": "1.51.0-next" } } diff --git a/packages/playwright-webkit/package.json b/packages/playwright-webkit/package.json index 9fd72b1c84..c07d8d00ab 100644 --- a/packages/playwright-webkit/package.json +++ b/packages/playwright-webkit/package.json @@ -1,6 +1,6 @@ { "name": "playwright-webkit", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate WebKit", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" } } diff --git a/packages/playwright/package.json b/packages/playwright/package.json index a525d5deee..90a21cb807 100644 --- a/packages/playwright/package.json +++ b/packages/playwright/package.json @@ -1,6 +1,6 @@ { "name": "playwright", - "version": "1.50.0-next", + "version": "1.51.0-next", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -56,7 +56,7 @@ }, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0-next" + "playwright-core": "1.51.0-next" }, "optionalDependencies": { "fsevents": "2.3.2" From d082805ea97c502a67ecb92a39b434508e99a48f Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Fri, 17 Jan 2025 23:29:26 +0100 Subject: [PATCH 04/32] chore(bidi): disable thottling of background tabs in Firefox (#34381) --- .../src/server/bidi/third_party/firefoxPrefs.ts | 9 +++++++++ tests/bidi/expectations/bidi-firefox-nightly-library.txt | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) 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 7c08bebe6d..d59ac0bc13 100644 --- a/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts +++ b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts @@ -133,6 +133,12 @@ function defaultProfilePreferences( 'dom.max_chrome_script_run_time': 0, 'dom.max_script_run_time': 0, + // Disable background timer throttling to allow tests to run in parallel + // without a decrease in performance. + 'dom.min_background_timeout_value': 0, + 'dom.min_background_timeout_value_without_budget_throttling': 0, + 'dom.timeout.enable_budget_timer_throttling': false, + // Only load extensions from the application and user profile // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION 'extensions.autoDisableScopes': 0, @@ -175,6 +181,9 @@ function defaultProfilePreferences( // Show chrome errors and warnings in the error console 'javascript.options.showInConsole': true, + // Do not throttle rendering (requestAnimationFrame) in background tabs + 'layout.testing.top-level-always-active': true, + // Disable download and usage of OpenH264: and Widevine plugins 'media.gmp-manager.updateEnabled': false, diff --git a/tests/bidi/expectations/bidi-firefox-nightly-library.txt b/tests/bidi/expectations/bidi-firefox-nightly-library.txt index 70ff33fb60..f527d5fb61 100644 --- a/tests/bidi/expectations/bidi-firefox-nightly-library.txt +++ b/tests/bidi/expectations/bidi-firefox-nightly-library.txt @@ -101,7 +101,6 @@ library/inspector/cli-codegen-3.spec.ts › cli codegen › should generate fram library/page-clock.spec.ts › popup › should run time before popup [timeout] library/page-clock.spec.ts › popup › should tick after popup [timeout] library/page-clock.spec.ts › popup › should tick before popup [timeout] -library/popup.spec.ts › should not throttle rAF in the opener page [timeout] library/popup.spec.ts › should not throw when click closes popup [timeout] library/popup.spec.ts › should use viewport size from window features [timeout] library/trace-viewer.spec.ts › should serve css without content-type [timeout] From 9970446f517e0562b543a032c62ce5fe8ae78cbb Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 17 Jan 2025 14:35:15 -0800 Subject: [PATCH 05/32] chore: remove toMatchAriaSnapshot.path (#34379) --- docs/src/api/class-locatorassertions.md | 9 ++------- .../src/matchers/toMatchAriaSnapshot.ts | 4 +--- packages/playwright/types/test.d.ts | 9 ++------- .../playwright-test/aria-snapshot-file.spec.ts | 18 ------------------ 4 files changed, 5 insertions(+), 35 deletions(-) diff --git a/docs/src/api/class-locatorassertions.md b/docs/src/api/class-locatorassertions.md index 63b487208f..35f64ea711 100644 --- a/docs/src/api/class-locatorassertions.md +++ b/docs/src/api/class-locatorassertions.md @@ -2274,13 +2274,8 @@ assertThat(page.locator("body")).matchesAriaSnapshot(new LocatorAssertions.Match * langs: js - `name` <[string]> -Name of the snapshot to store in the snapshot folder corresponding to this test. Generates ordinal name if not specified. - -### option: LocatorAssertions.toMatchAriaSnapshot#2.path -* since: v1.50 -- `path` <[string]> - -Path to the YAML snapshot file. +Name of the snapshot to store in the snapshot (screenshot) folder corresponding to this test. +Generates sequential names if not specified. ### option: LocatorAssertions.toMatchAriaSnapshot#2.timeout = %%-js-assertions-timeout-%% * since: v1.50 diff --git a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts index 152ceb6ba9..c291770fd5 100644 --- a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts @@ -59,9 +59,7 @@ export async function toMatchAriaSnapshot( if (isString(expectedParam)) { expected = expectedParam; } else { - if (expectedParam?.path) { - expectedPath = expectedParam.path; - } else if (expectedParam?.name) { + if (expectedParam?.name) { expectedPath = testInfo.snapshotPath(sanitizeFilePathBeforeExtension(expectedParam.name)); } else { let snapshotNames = (testInfo as any)[snapshotNamesSymbol] as SnapshotNames; diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index e01271ceec..b67377eb6d 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -8697,16 +8697,11 @@ interface LocatorAssertions { */ toMatchAriaSnapshot(options?: { /** - * Name of the snapshot to store in the snapshot folder corresponding to this test. Generates ordinal name if not - * specified. + * Name of the snapshot to store in the snapshot (screenshot) folder corresponding to this test. Generates sequential + * names if not specified. */ name?: string; - /** - * Path to the YAML snapshot file. - */ - path?: string; - /** * Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`. */ diff --git a/tests/playwright-test/aria-snapshot-file.spec.ts b/tests/playwright-test/aria-snapshot-file.spec.ts index c05ae3897d..18ebc072cc 100644 --- a/tests/playwright-test/aria-snapshot-file.spec.ts +++ b/tests/playwright-test/aria-snapshot-file.spec.ts @@ -42,24 +42,6 @@ test('should match snapshot with name', async ({ runInlineTest }, testInfo) => { expect(result.exitCode).toBe(0); }); -test('should match snapshot with path', async ({ runInlineTest }, testInfo) => { - const result = await runInlineTest({ - '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({ path: path.resolve(__dirname, 'test.yml') }); - }); - ` - }); - - expect(result.exitCode).toBe(0); -}); - test('should generate multiple missing', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ 'playwright.config.ts': ` From 372d4196d7b92868d855d3737642f008a28df808 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 17 Jan 2025 21:15:47 -0800 Subject: [PATCH 06/32] chore: step timeout improvements (#34386) --- docs/src/test-api/class-test.md | 2 +- packages/playwright/src/common/testType.ts | 15 ++++++++++++-- tests/playwright-test/test-step.spec.ts | 23 +++++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index 00c7a5f0ee..77c0c5028c 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1822,7 +1822,7 @@ Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout * since: v1.50 - `timeout` <[float]> -Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout). +The maximum time, in milliseconds, allowed for the step to complete. If the step does not complete within the specified timeout, the [`method: Test.step`] method will throw a [TimeoutError]. Defaults to `0` (no timeout). ## method: Test.use * since: v1.10 diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index 4c9e6d8f90..58d813a99e 100644 --- a/packages/playwright/src/common/testType.ts +++ b/packages/playwright/src/common/testType.ts @@ -270,9 +270,20 @@ export class TestTypeImpl { const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box }); return await zones.run('stepZone', step, async () => { try { - const result = await raceAgainstDeadline(async () => body(), options.timeout ? monotonicTime() + options.timeout : 0); + let result: Awaited>> | undefined = undefined; + result = await raceAgainstDeadline(async () => { + try { + return await body(); + } catch (e) { + // If the step timed out, the test fixtures will tear down, which in turn + // will abort unfinished actions in the step body. Record such errors here. + if (result?.timedOut) + testInfo._failWithError(e); + throw e; + } + }, options.timeout ? monotonicTime() + options.timeout : 0); if (result.timedOut) - throw new errors.TimeoutError(`Step timeout ${options.timeout}ms exceeded.`); + throw new errors.TimeoutError(`Step timeout of ${options.timeout}ms exceeded.`); step.complete({}); return result.result; } catch (error) { diff --git a/tests/playwright-test/test-step.spec.ts b/tests/playwright-test/test-step.spec.ts index 648cbeb52e..7d509fda25 100644 --- a/tests/playwright-test/test-step.spec.ts +++ b/tests/playwright-test/test-step.spec.ts @@ -399,7 +399,7 @@ test('step timeout option', async ({ runInlineTest }) => { }, { reporter: '', workers: 1 }); expect(result.exitCode).toBe(1); expect(result.failed).toBe(1); - expect(result.output).toContain('Error: Step timeout 100ms exceeded.'); + expect(result.output).toContain('Error: Step timeout of 100ms exceeded.'); }); test('step timeout longer than test timeout', async ({ runInlineTest }) => { @@ -422,6 +422,27 @@ test('step timeout longer than test timeout', async ({ runInlineTest }) => { expect(result.output).toContain('Test timeout of 900ms exceeded.'); }); +test('step timeout includes interrupted action errors', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('step with timeout', async ({ page }) => { + await test.step('my step', async () => { + await page.waitForTimeout(100_000); + }, { timeout: 1000 }); + }); + ` + }, { reporter: '', workers: 1 }); + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + // Should include 2 errors, one for the step timeout and one for the aborted action. + expect.soft(result.output).toContain('TimeoutError: Step timeout of 1000ms exceeded.'); + expect.soft(result.output).toContain(`> 4 | await test.step('my step', async () => {`); + expect.soft(result.output).toContain('Error: page.waitForTimeout: Test ended.'); + expect.soft(result.output.split('Error: page.waitForTimeout: Test ended.').length).toBe(2); + expect.soft(result.output).toContain('> 5 | await page.waitForTimeout(100_000);'); +}); + test('step timeout is errors.TimeoutError', async ({ runInlineTest }) => { const result = await runInlineTest({ 'a.test.ts': ` From 86768b9ebcba37c19fda8e4bb5c6ac4c4d4bb582 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Sat, 18 Jan 2025 19:04:56 +0300 Subject: [PATCH 07/32] test: add test for consistent hyphen rendering in headless/headed (#34159) --- tests/library/headful.spec.ts | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/library/headful.spec.ts b/tests/library/headful.spec.ts index b74563850a..13445b9ce4 100644 --- a/tests/library/headful.spec.ts +++ b/tests/library/headful.spec.ts @@ -313,3 +313,46 @@ it('headless and headful should use same default fonts', async ({ page, browserN } await headlessBrowser.close(); }); + +it('should have the same hyphen rendering on headless and headed', { + annotation: { + type: 'issue', + description: 'https://github.com/microsoft/playwright/issues/33590' + } +}, async ({ browserType, page, headless, server }) => { + const content = ` + + + + + + +
+ supercalifragilisticexpialidocious +
+ + + `; + server.setRoute('/hyphenated.html', (req, res) => { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(content); + }); + const oppositeBrowser = await browserType.launch({ headless: !headless }); + const oppositePage = await oppositeBrowser.newPage(); + await oppositePage.goto(server.PREFIX + '/hyphenated.html'); + await page.goto(server.PREFIX + '/hyphenated.html'); + + const [divHeight1, divHeight2] = await Promise.all([ + page.evaluate(() => document.querySelector('.hyphenated').getBoundingClientRect().height), + oppositePage.evaluate(() => document.querySelector('.hyphenated').getBoundingClientRect().height), + ]); + expect(divHeight1).toBe(divHeight2); + await oppositeBrowser.close(); +}); From 99fb188cb4c05ec12f9671e185ede5c520683641 Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Mon, 20 Jan 2025 09:06:01 +0100 Subject: [PATCH 08/32] chore: move attachment link back to tree item, make it flash yellow (#34353) --- packages/html-reporter/src/links.css | 10 ------- packages/html-reporter/src/links.tsx | 11 +++---- packages/html-reporter/src/testResultView.tsx | 17 ++--------- packages/html-reporter/src/treeItem.css | 13 ++++---- packages/html-reporter/src/treeItem.tsx | 8 ++--- .../trace-viewer/src/ui/attachmentsTab.css | 8 +++++ .../trace-viewer/src/ui/attachmentsTab.tsx | 30 +++++++++---------- packages/trace-viewer/src/ui/workbench.tsx | 11 +++++-- packages/web/src/uiUtils.ts | 24 +++++++++++++++ tests/playwright-test/reporter-html.spec.ts | 10 +++---- 10 files changed, 79 insertions(+), 63 deletions(-) diff --git a/packages/html-reporter/src/links.css b/packages/html-reporter/src/links.css index eb9390844b..4abe8a6caa 100644 --- a/packages/html-reporter/src/links.css +++ b/packages/html-reporter/src/links.css @@ -60,11 +60,6 @@ color: var(--color-scale-orange-6); border: 1px solid var(--color-scale-orange-4); } - .label-color-gray { - background-color: var(--color-scale-gray-0); - color: var(--color-scale-gray-6); - border: 1px solid var(--color-scale-gray-4); - } } @media(prefers-color-scheme: dark) { @@ -98,11 +93,6 @@ color: var(--color-scale-orange-2); border: 1px solid var(--color-scale-orange-4); } - .label-color-gray { - background-color: var(--color-scale-gray-9); - color: var(--color-scale-gray-2); - border: 1px solid var(--color-scale-gray-4); - } } .attachment-body { diff --git a/packages/html-reporter/src/links.tsx b/packages/html-reporter/src/links.tsx index 5f199568b5..5b79102ffe 100644 --- a/packages/html-reporter/src/links.tsx +++ b/packages/html-reporter/src/links.tsx @@ -21,7 +21,7 @@ import { TreeItem } from './treeItem'; import { CopyToClipboard } from './copyToClipboard'; import './links.css'; import { linkifyText } from '@web/renderUtils'; -import { clsx } from '@web/uiUtils'; +import { clsx, useFlash } from '@web/uiUtils'; export function navigate(href: string | URL) { window.history.pushState({}, '', href); @@ -73,7 +73,8 @@ export const AttachmentLink: React.FunctionComponent<{ linkName?: string, openInNewTab?: boolean, }> = ({ attachment, result, href, linkName, openInNewTab }) => { - const isAnchored = useIsAnchored('attachment-' + result.attachments.indexOf(attachment)); + const [flash, triggerFlash] = useFlash(); + useAnchor('attachment-' + result.attachments.indexOf(attachment), triggerFlash); return {attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()} {attachment.path && {linkName || attachment.name}} @@ -84,7 +85,7 @@ export const AttachmentLink: React.FunctionComponent<{ )} } loadChildren={attachment.body ? () => { return [
{linkifyText(attachment.body!)}
]; - } : undefined} depth={0} style={{ lineHeight: '32px' }} selected={isAnchored}>
; + } : undefined} depth={0} style={{ lineHeight: '32px' }} flash={flash}>; }; export const SearchParamsContext = React.createContext(new URLSearchParams(window.location.hash.slice(1))); @@ -118,12 +119,12 @@ const kMissingContentType = 'x-playwright/missing'; export type AnchorID = string | string[] | ((id: string) => boolean) | undefined; -export function useAnchor(id: AnchorID, onReveal: () => void) { +export function useAnchor(id: AnchorID, onReveal: React.EffectCallback) { const searchParams = React.useContext(SearchParamsContext); const isAnchored = useIsAnchored(id); React.useEffect(() => { if (isAnchored) - onReveal(); + return onReveal(); }, [isAnchored, onReveal, searchParams]); } diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index 64497b3bcd..681f4b507a 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -176,6 +176,7 @@ const StepTreeItem: React.FC<{ }> = ({ test, step, result, depth }) => { return {msToString(step.duration)} + {step.attachments.length > 0 && { evt.stopPropagation(); }}>{icons.attachment()}} {statusIcon(step.error || step.duration === -1 ? 'failed' : (step.skipped ? 'skipped' : 'passed'))} {step.title} {step.count > 1 && <> ✕ {step.count}} @@ -183,20 +184,6 @@ const StepTreeItem: React.FC<{ } loadChildren={step.steps.length || step.snippet ? () => { const snippet = step.snippet ? [] : []; const steps = step.steps.map((s, i) => ); - const attachments = step.attachments.map(attachmentIndex => ( - - - {icons.attachment()}{result.attachments[attachmentIndex].name} - - - )); - return snippet.concat(steps, attachments); + return snippet.concat(steps); } : undefined} depth={depth}/>; }; diff --git a/packages/html-reporter/src/treeItem.css b/packages/html-reporter/src/treeItem.css index f37a759c2d..b957d1ec5c 100644 --- a/packages/html-reporter/src/treeItem.css +++ b/packages/html-reporter/src/treeItem.css @@ -25,11 +25,14 @@ cursor: pointer; } -.tree-item-title.selected { - text-decoration: underline var(--color-underlinenav-icon); - text-decoration-thickness: 1.5px; -} - .tree-item-body { min-height: 18px; } + +.yellow-flash { + animation: yellowflash-bg 2s; +} +@keyframes yellowflash-bg { + from { background: var(--color-attention-subtle); } + to { background: transparent; } +} diff --git a/packages/html-reporter/src/treeItem.tsx b/packages/html-reporter/src/treeItem.tsx index 926a398a05..7ae9b840f8 100644 --- a/packages/html-reporter/src/treeItem.tsx +++ b/packages/html-reporter/src/treeItem.tsx @@ -25,12 +25,12 @@ export const TreeItem: React.FunctionComponent<{ onClick?: () => void, expandByDefault?: boolean, depth: number, - selected?: boolean, style?: React.CSSProperties, -}> = ({ title, loadChildren, onClick, expandByDefault, depth, selected, style }) => { + flash?: boolean +}> = ({ title, loadChildren, onClick, expandByDefault, depth, style, flash }) => { const [expanded, setExpanded] = React.useState(expandByDefault || false); - return
- { onClick?.(); setExpanded(!expanded); }} > + return
+ { onClick?.(); setExpanded(!expanded); }} > {loadChildren && !!expanded && icons.downArrow()} {loadChildren && !expanded && icons.rightArrow()} {!loadChildren && {icons.rightArrow()}} diff --git a/packages/trace-viewer/src/ui/attachmentsTab.css b/packages/trace-viewer/src/ui/attachmentsTab.css index c2455fc3c5..7d487bb3f2 100644 --- a/packages/trace-viewer/src/ui/attachmentsTab.css +++ b/packages/trace-viewer/src/ui/attachmentsTab.css @@ -55,3 +55,11 @@ a.codicon-cloud-download:hover{ background-color: var(--vscode-list-inactiveSelectionBackground) } + +.yellow-flash { + animation: yellowflash-bg 2s; +} +@keyframes yellowflash-bg { + from { background: var(--vscode-peekViewEditor-matchHighlightBackground); } + to { background: transparent; } +} diff --git a/packages/trace-viewer/src/ui/attachmentsTab.tsx b/packages/trace-viewer/src/ui/attachmentsTab.tsx index cf9ed2e681..7a636a83b0 100644 --- a/packages/trace-viewer/src/ui/attachmentsTab.tsx +++ b/packages/trace-viewer/src/ui/attachmentsTab.tsx @@ -17,36 +17,38 @@ import * as React from 'react'; import './attachmentsTab.css'; import { ImageDiffView } from '@web/shared/imageDiffView'; -import type { ActionTraceEventInContext, MultiTraceModel } from './modelUtil'; +import type { MultiTraceModel } from './modelUtil'; import { PlaceholderPanel } from './placeholderPanel'; import type { AfterActionTraceEventAttachment } from '@trace/trace'; import { CodeMirrorWrapper, lineHeight } from '@web/components/codeMirrorWrapper'; import { isTextualMimeType } from '@isomorphic/mimeType'; import { Expandable } from '@web/components/expandable'; import { linkifyText } from '@web/renderUtils'; -import { clsx } from '@web/uiUtils'; +import { clsx, useFlash } from '@web/uiUtils'; type Attachment = AfterActionTraceEventAttachment & { traceUrl: string }; type ExpandableAttachmentProps = { attachment: Attachment; - reveal: boolean; - highlight: boolean; + reveal?: any; }; -const ExpandableAttachment: React.FunctionComponent = ({ attachment, reveal, highlight }) => { +const ExpandableAttachment: React.FunctionComponent = ({ attachment, reveal }) => { const [expanded, setExpanded] = React.useState(false); const [attachmentText, setAttachmentText] = React.useState(null); const [placeholder, setPlaceholder] = React.useState(null); + const [flash, triggerFlash] = useFlash(); const ref = React.useRef(null); const isTextAttachment = isTextualMimeType(attachment.contentType); const hasContent = !!attachment.sha1 || !!attachment.path; React.useEffect(() => { - if (reveal) + if (reveal) { ref.current?.scrollIntoView({ behavior: 'smooth' }); - }, [reveal]); + return triggerFlash(); + } + }, [reveal, triggerFlash]); React.useEffect(() => { if (expanded && attachmentText === null && placeholder === null) { @@ -66,14 +68,14 @@ const ExpandableAttachment: React.FunctionComponent = }, [attachmentText]); const title = - {linkifyText(attachment.name)} + {linkifyText(attachment.name)} {hasContent && download} ; if (!isTextAttachment || !hasContent) return
{title}
; - return <> + return
{placeholder && {placeholder}} @@ -87,14 +89,13 @@ const ExpandableAttachment: React.FunctionComponent = wrapLines={false}>
} - ; +
; }; export const AttachmentsTab: React.FunctionComponent<{ model: MultiTraceModel | undefined, - selectedAction: ActionTraceEventInContext | undefined, - revealedAttachment?: AfterActionTraceEventAttachment, -}> = ({ model, selectedAction, revealedAttachment }) => { + revealedAttachment?: [AfterActionTraceEventAttachment, number], +}> = ({ model, revealedAttachment }) => { const { diffMap, screenshots, attachments } = React.useMemo(() => { const attachments = new Set(); const screenshots = new Set(); @@ -153,8 +154,7 @@ export const AttachmentsTab: React.FunctionComponent<{ return
isEqualAttachment(a, selected)) ?? false} - reveal={!!revealedAttachment && isEqualAttachment(a, revealedAttachment)} + reveal={(!!revealedAttachment && isEqualAttachment(a, revealedAttachment[0])) ? revealedAttachment : undefined} />
; })} diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index c6fcd7e54c..de59892772 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -59,7 +59,7 @@ export const Workbench: React.FunctionComponent<{ }> = ({ model, showSourcesFirst, rootDir, fallbackLocation, isLive, hideTimeline, status, annotations, inert, onOpenExternally, revealSource }) => { const [selectedCallId, setSelectedCallId] = React.useState(undefined); const [revealedError, setRevealedError] = React.useState(undefined); - const [revealedAttachment, setRevealedAttachment] = React.useState(undefined); + const [revealedAttachment, setRevealedAttachment] = React.useState<[attachment: AfterActionTraceEventAttachment, renderCounter: number] | undefined>(undefined); const [highlightedCallId, setHighlightedCallId] = React.useState(); const [highlightedEntry, setHighlightedEntry] = React.useState(); const [highlightedConsoleMessage, setHighlightedConsoleMessage] = React.useState(); @@ -148,7 +148,12 @@ export const Workbench: React.FunctionComponent<{ const revealAttachment = React.useCallback((attachment: AfterActionTraceEventAttachment) => { selectPropertiesTab('attachments'); - setRevealedAttachment(attachment); + setRevealedAttachment(currentValue => { + if (!currentValue) + return [attachment, 0]; + const revealCounter = currentValue[1]; + return [attachment, revealCounter + 1]; + }); }, [selectPropertiesTab]); React.useEffect(() => { @@ -238,7 +243,7 @@ export const Workbench: React.FunctionComponent<{ id: 'attachments', title: 'Attachments', count: attachments.length, - render: () => + render: () => }; const tabs: TabbedPaneTabModel[] = [ diff --git a/packages/web/src/uiUtils.ts b/packages/web/src/uiUtils.ts index 3544ec4bdc..a0b7a59c36 100644 --- a/packages/web/src/uiUtils.ts +++ b/packages/web/src/uiUtils.ts @@ -14,6 +14,7 @@ limitations under the License. */ +import type { EffectCallback } from 'react'; import React from 'react'; // Recalculates the value when dependencies change. @@ -224,3 +225,26 @@ export function scrollIntoViewIfNeeded(element: Element | undefined) { const kControlCodesRe = '\\u0000-\\u0020\\u007f-\\u009f'; export const kWebLinkRe = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|www\\.)[^\\s' + kControlCodesRe + '"]{2,}[^\\s' + kControlCodesRe + '"\')}\\],:;.!?]', 'ug'); + +/** + * Manages flash animation state. + * Calling `trigger` will turn `flash` to true for a second, and then back to false. + * If `trigger` is called while a flash is ongoing, the ongoing flash will be cancelled and after 50ms a new flash is started. + * @returns [flash, trigger] + */ +export function useFlash(): [boolean, EffectCallback] { + const [flash, setFlash] = React.useState(false); + const trigger = React.useCallback(() => { + const timeouts: any[] = []; + setFlash(currentlyFlashing => { + timeouts.push(setTimeout(() => setFlash(false), 1000)); + if (!currentlyFlashing) + return true; + + timeouts.push(setTimeout(() => setFlash(true), 50)); + return false; + }); + return () => timeouts.forEach(clearTimeout); + }, [setFlash]); + return [flash, trigger]; +} diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 2640cb61c9..57ef76a7ca 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -959,10 +959,9 @@ for (const useIntermediateMergeReport of [true, false] as const) { await showReport(); await page.getByRole('link', { name: 'passing' }).click(); - const attachment = page.getByTestId('attachments').getByText('foo-2', { exact: true }); + const attachment = page.getByText('foo-2', { exact: true }); await expect(attachment).not.toBeInViewport(); - await page.getByLabel('attach "foo-2"').click(); - await page.getByTitle('see "foo-2"').click(); + await page.getByLabel(`attach "foo-2"`).getByTitle('reveal attachment').click(); await expect(attachment).toBeInViewport(); await page.reload(); @@ -989,10 +988,9 @@ for (const useIntermediateMergeReport of [true, false] as const) { await showReport(); await page.getByRole('link', { name: 'passing' }).click(); - const attachment = page.getByTestId('attachments').getByText('attachment', { exact: true }); + const attachment = page.getByText('attachment', { exact: true }); await expect(attachment).not.toBeInViewport(); - await page.getByLabel('step').click(); - await page.getByTitle('see "attachment"').click(); + await page.getByLabel('step').getByTitle('reveal attachment').click(); await expect(attachment).toBeInViewport(); }); From e20b6d1617354191c635ece258b346290a25ce03 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 20 Jan 2025 16:28:41 +0000 Subject: [PATCH 09/32] feat(chromium-tip-of-tree): roll to r1295 (#34372) --- packages/playwright-core/browsers.json | 4 ++-- .../playwright-core/src/server/chromium/chromiumSwitches.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 0895f005b1..54d42fe041 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -9,9 +9,9 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1293", + "revision": "1295", "installByDefault": false, - "browserVersion": "133.0.6943.0" + "browserVersion": "134.0.6960.0" }, { "name": "firefox", diff --git a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts index 4774c13ed7..9d64d58f1c 100644 --- a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts +++ b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts @@ -38,7 +38,10 @@ export const chromiumSwitches = [ // ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230 // LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds. // PlzDedicatedWorker - https://github.com/microsoft/playwright/issues/31747 - '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker', + // DeferRendererTasksAfterInput - this makes Page.frameScheduledNavigation arrive much later after a click, + // making our navigation auto-wait after click not working. Can be removed once we deperecate noWaitAfter. + // See https://github.com/microsoft/playwright/pull/34372. + '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker,DeferRendererTasksAfterInput', '--allow-pre-commit-input', '--disable-hang-monitor', '--disable-ipc-flooding-protection', From 333e994e7dccd079f4a01fbb3c189a05a04ae182 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 21 Jan 2025 18:28:41 +0000 Subject: [PATCH 10/32] fix(step.skip): show a skipped indicator in UI mode (#34407) --- packages/playwright/src/worker/testInfo.ts | 4 +-- packages/playwright/src/worker/testTracing.ts | 4 +-- packages/trace-viewer/src/ui/actionList.css | 4 +++ packages/trace-viewer/src/ui/actionList.tsx | 9 +++-- tests/playwright-test/ui-mode-trace.spec.ts | 33 +++++++++++++++++-- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index 3eba797853..6e6ab4660c 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -322,7 +322,7 @@ export class TestInfoImpl implements TestInfo { location: data.location, }; this._onStepBegin(payload); - this._tracing.appendBeforeActionForStep(stepId, parentStep?.stepId, data.apiName || data.title, data.params, data.location ? [data.location] : []); + this._tracing.appendBeforeActionForStep(stepId, parentStep?.stepId, data.category, data.apiName || data.title, data.params, data.location ? [data.location] : []); return step; } @@ -421,7 +421,7 @@ export class TestInfoImpl implements TestInfo { } else { // trace viewer has no means of representing attachments outside of a step, so we create an artificial action const callId = `attach@${++this._lastStepId}`; - this._tracing.appendBeforeActionForStep(callId, this._findLastStageStep(this._steps)?.stepId, `attach "${attachment.name}"`, undefined, []); + this._tracing.appendBeforeActionForStep(callId, this._findLastStageStep(this._steps)?.stepId, 'attach', `attach "${attachment.name}"`, undefined, []); this._tracing.appendAfterActionForStep(callId, undefined, [attachment]); } diff --git a/packages/playwright/src/worker/testTracing.ts b/packages/playwright/src/worker/testTracing.ts index eb0ce9d807..fde5ea5f3f 100644 --- a/packages/playwright/src/worker/testTracing.ts +++ b/packages/playwright/src/worker/testTracing.ts @@ -245,14 +245,14 @@ export class TestTracing { }); } - appendBeforeActionForStep(callId: string, parentId: string | undefined, apiName: string, params: Record | undefined, stack: StackFrame[]) { + appendBeforeActionForStep(callId: string, parentId: string | undefined, category: string, apiName: string, params: Record | undefined, stack: StackFrame[]) { this._appendTraceEvent({ type: 'before', callId, parentId, startTime: monotonicTime(), class: 'Test', - method: 'step', + method: category, apiName, params: Object.fromEntries(Object.entries(params || {}).map(([name, value]) => [name, generatePreview(value)])), stack, diff --git a/packages/trace-viewer/src/ui/actionList.css b/packages/trace-viewer/src/ui/actionList.css index 0cb3cb5f54..f167352593 100644 --- a/packages/trace-viewer/src/ui/actionList.css +++ b/packages/trace-viewer/src/ui/actionList.css @@ -41,6 +41,10 @@ color: var(--vscode-editorCodeLens-foreground); } +.action-skipped { + margin-right: 4px; +} + .action-icon { flex: none; display: flex; diff --git a/packages/trace-viewer/src/ui/actionList.tsx b/packages/trace-viewer/src/ui/actionList.tsx index 87e78416b0..2c6932bd45 100644 --- a/packages/trace-viewer/src/ui/actionList.tsx +++ b/packages/trace-viewer/src/ui/actionList.tsx @@ -15,7 +15,7 @@ */ import type { ActionTraceEvent, AfterActionTraceEventAttachment } from '@trace/trace'; -import { msToString } from '@web/uiUtils'; +import { clsx, msToString } from '@web/uiUtils'; import * as React from 'react'; import './actionList.css'; import * as modelUtil from './modelUtil'; @@ -25,6 +25,7 @@ import { TreeView } from '@web/components/treeView'; import type { ActionTraceEventInContext, ActionTreeItem } from './modelUtil'; import type { Boundaries } from './geometry'; import { ToolbarButton } from '@web/components/toolbarButton'; +import { testStatusIcon } from './testUtils'; export interface ActionListProps { actions: ActionTraceEventInContext[], @@ -119,6 +120,7 @@ export const renderAction = ( const parameterString = actionParameterDisplayString(action, sdkLanguage || 'javascript'); + const isSkipped = action.class === 'Test' && action.method === 'test.step.skip'; let time: string = ''; if (action.endTime) time = msToString(action.endTime - action.startTime); @@ -149,9 +151,10 @@ export const renderAction = ( {action.method === 'goto' && action.params.url &&
{action.params.url}
} {action.class === 'APIRequestContext' && action.params.url &&
{excludeOrigin(action.params.url)}
}
- {(showDuration || showBadges || showAttachments) &&
} + {(showDuration || showBadges || showAttachments || isSkipped) &&
} {showAttachments && revealAttachment(action.attachments![0])} />} - {showDuration &&
{time || }
} + {showDuration && !isSkipped &&
{time || }
} + {isSkipped && } {showBadges &&
revealConsole?.()}> {!!errors &&
{errors}
} {!!warnings &&
{warnings}
} diff --git a/tests/playwright-test/ui-mode-trace.spec.ts b/tests/playwright-test/ui-mode-trace.spec.ts index 23a321338b..8001623721 100644 --- a/tests/playwright-test/ui-mode-trace.spec.ts +++ b/tests/playwright-test/ui-mode-trace.spec.ts @@ -398,7 +398,7 @@ test('should show custom fixture titles in actions tree', async ({ runUITest }) const { page } = await runUITest({ 'a.test.ts': ` import { test as base, expect } from '@playwright/test'; - + const test = base.extend({ fixture1: [async ({}, use) => { await use(); @@ -457,7 +457,7 @@ test('attachments tab shows all but top-level .push attachments', async ({ runUI - tree: - treeitem /step/: - group: - - treeitem /attach \\"foo-attach\\"/ + - treeitem /attach \\"foo-attach\\"/ - treeitem /attach \\"bar-push\\"/ - treeitem /attach \\"bar-attach\\"/ `); @@ -470,3 +470,32 @@ test('attachments tab shows all but top-level .push attachments', async ({ runUI - button /bar-attach/ `); }); + +test('skipped steps should have an indicator', async ({ runUITest }) => { + const { page } = await runUITest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('test with steps', async ({}) => { + await test.step('outer', async () => { + await test.step.skip('skipped1', () => {}); + }); + await test.step.skip('skipped2', () => {}); + }); + `, + }); + + await page.getByRole('treeitem', { name: 'test with steps' }).dblclick(); + const actionsTree = page.getByTestId('actions-tree'); + await actionsTree.getByRole('treeitem', { name: 'outer' }).click(); + await page.keyboard.press('ArrowRight'); + await expect(actionsTree).toMatchAriaSnapshot(` + - tree: + - treeitem /outer/ [expanded]: + - group: + - treeitem /skipped1/ + - treeitem /skipped2/ + `); + const skippedMarker = actionsTree.getByRole('treeitem', { name: 'skipped1' }).locator('.action-skipped'); + await expect(skippedMarker).toBeVisible(); + await expect(skippedMarker).toHaveAccessibleName('skipped'); +}); From 888feb06bea8116e0eb2364d49901f75ea118f3c Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 21 Jan 2025 18:30:09 +0000 Subject: [PATCH 11/32] fix(list reporter): do not break after output without trailing eol (#34410) --- packages/playwright/src/reporters/list.ts | 3 ++ packages/playwright/src/worker/workerMain.ts | 8 +++- .../playwright-test-fixtures.ts | 1 + tests/playwright-test/reporter-list.spec.ts | 45 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/packages/playwright/src/reporters/list.ts b/packages/playwright/src/reporters/list.ts index 4f885946e5..aef9fcbe2f 100644 --- a/packages/playwright/src/reporters/list.ts +++ b/packages/playwright/src/reporters/list.ts @@ -129,6 +129,8 @@ class ListReporter extends TerminalReporter { if (this._needNewLine) { this._needNewLine = false; process.stdout.write('\n'); + ++this._lastRow; + this._lastColumn = 0; } } @@ -210,6 +212,7 @@ class ListReporter extends TerminalReporter { process.stdout.write('\n'); } ++this._lastRow; + this._lastColumn = 0; } private _updateLine(row: number, text: string, prefix: string) { diff --git a/packages/playwright/src/worker/workerMain.ts b/packages/playwright/src/worker/workerMain.ts index ed323a701b..b4878ff659 100644 --- a/packages/playwright/src/worker/workerMain.ts +++ b/packages/playwright/src/worker/workerMain.ts @@ -75,16 +75,20 @@ export class WorkerMain extends ProcessRunner { process.on('unhandledRejection', reason => this.unhandledError(reason)); process.on('uncaughtException', error => this.unhandledError(error)); - process.stdout.write = (chunk: string | Buffer) => { + process.stdout.write = (chunk: string | Buffer, cb?: any) => { this.dispatchEvent('stdOut', stdioChunkToParams(chunk)); this._currentTest?._tracing.appendStdioToTrace('stdout', chunk); + if (typeof cb === 'function') + process.nextTick(cb); return true; }; if (!process.env.PW_RUNNER_DEBUG) { - process.stderr.write = (chunk: string | Buffer) => { + process.stderr.write = (chunk: string | Buffer, cb?: any) => { this.dispatchEvent('stdErr', stdioChunkToParams(chunk)); this._currentTest?._tracing.appendStdioToTrace('stderr', chunk); + if (typeof cb === 'function') + process.nextTick(cb); return true; }; } diff --git a/tests/playwright-test/playwright-test-fixtures.ts b/tests/playwright-test/playwright-test-fixtures.ts index 23c26a3e3c..5d6aab5b8e 100644 --- a/tests/playwright-test/playwright-test-fixtures.ts +++ b/tests/playwright-test/playwright-test-fixtures.ts @@ -229,6 +229,7 @@ export function cleanEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { // END: Reserved CI PW_TEST_HTML_REPORT_OPEN: undefined, PLAYWRIGHT_HTML_OPEN: undefined, + PW_TEST_DEBUG_REPORTERS: undefined, PW_TEST_REPORTER: undefined, PW_TEST_REPORTER_WS_ENDPOINT: undefined, PW_TEST_SOURCE_TRANSFORM: undefined, diff --git a/tests/playwright-test/reporter-list.spec.ts b/tests/playwright-test/reporter-list.spec.ts index 1d488cc253..8e6128065a 100644 --- a/tests/playwright-test/reporter-list.spec.ts +++ b/tests/playwright-test/reporter-list.spec.ts @@ -258,6 +258,51 @@ for (const useIntermediateMergeReport of [false, true] as const) { expect(text).toContain('1) a.test.ts:3:15 › passes › outer 1.0 › inner 1.1 ──'); expect(result.exitCode).toBe(1); }); + + test('print stdio', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', async ({}) => { + await new Promise(resolve => process.stdout.write('line1', () => resolve())); + await new Promise(resolve => process.stdout.write('line2\\n', () => resolve())); + await new Promise(resolve => process.stderr.write(Buffer.from(''), () => resolve())); + }); + + test('passes 2', async ({}) => { + await new Promise(resolve => process.stdout.write('partial', () => resolve())); + }); + + test('passes 3', async ({}) => { + await new Promise(resolve => process.stdout.write('full\\n', () => resolve())); + }); + + test('passes 4', async ({}) => { + }); + `, + }, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(4); + const expected = [ + '#0 : 1 a.test.ts:3:15 › passes', + 'line1line2', + `#0 : ${POSITIVE_STATUS_MARK} 1 a.test.ts:3:15 › passes`, + '', + '#3 : 2 a.test.ts:9:15 › passes 2', + `partial#3 : ${POSITIVE_STATUS_MARK} 2 a.test.ts:9:15 › passes 2`, + '', + '#5 : 3 a.test.ts:13:15 › passes 3', + 'full', + `#5 : ${POSITIVE_STATUS_MARK} 3 a.test.ts:13:15 › passes 3`, + '#7 : 4 a.test.ts:17:15 › passes 4', + `#7 : ${POSITIVE_STATUS_MARK} 4 a.test.ts:17:15 › passes 4`, + ]; + const lines = result.output.split('\n'); + const firstIndex = lines.indexOf(expected[0]); + expect(firstIndex, 'first line should be there').not.toBe(-1); + for (let i = 0; i < expected.length; ++i) + expect(lines[firstIndex + i]).toContain(expected[i]); + }); }); } From 6234c3b15e1147a87f29555596a58881b4e3ed9b Mon Sep 17 00:00:00 2001 From: Tasawar Hussain <31658686+tasawar-hussain@users.noreply.github.com> Date: Wed, 22 Jan 2025 02:50:33 +0500 Subject: [PATCH 12/32] docs: update test-fixtures-js.md (#34399) --- docs/src/test-fixtures-js.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/test-fixtures-js.md b/docs/src/test-fixtures-js.md index 9bdd4391ad..1f806d06ab 100644 --- a/docs/src/test-fixtures-js.md +++ b/docs/src/test-fixtures-js.md @@ -407,7 +407,7 @@ Automatic fixtures are set up for each test/worker, even when the test does not Here is an example fixture that automatically attaches debug logs when the test fails, so we can later review the logs in the reporter. Note how it uses [TestInfo] object that is available in each test/fixture to retrieve metadata about the test being run. ```js title="my-test.ts" -import * as debug from 'debug'; +import debug from 'debug'; import * as fs from 'fs'; import { test as base } from '@playwright/test'; From cf3bcd7d4a32c81e7e96ccfd948877bfe927573a Mon Sep 17 00:00:00 2001 From: Playwright Service <89237858+playwrightmachine@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:21:47 -0800 Subject: [PATCH 13/32] feat(chromium-tip-of-tree): roll to r1296 (#34414) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- packages/playwright-core/browsers.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 54d42fe041..384b2f3ea6 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -9,9 +9,9 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1295", + "revision": "1296", "installByDefault": false, - "browserVersion": "134.0.6960.0" + "browserVersion": "134.0.6970.0" }, { "name": "firefox", From cf90c0f122578141e8c7a961133c2c652a0285db Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Wed, 22 Jan 2025 07:53:53 +0000 Subject: [PATCH 14/32] fix(aria snapshot): make rebase work when options are specified (#34409) --- .../src/matchers/toMatchAriaSnapshot.ts | 2 +- packages/playwright/src/runner/rebase.ts | 17 ++++--- .../update-aria-snapshot.spec.ts | 44 +++++++++++++++++++ 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts index c291770fd5..d379d8610c 100644 --- a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts @@ -134,7 +134,7 @@ export async function toMatchAriaSnapshot( } return { pass: true, message: () => '', name: 'toMatchAriaSnapshot' }; } else { - const suggestedRebaseline = `toMatchAriaSnapshot(\`\n${escapeTemplateString(indent(typedReceived.regex, '{indent} '))}\n{indent}\`)`; + const suggestedRebaseline = `\`\n${escapeTemplateString(indent(typedReceived.regex, '{indent} '))}\n{indent}\``; return { pass: false, message: () => '', name: 'toMatchAriaSnapshot', suggestedRebaseline }; } } diff --git a/packages/playwright/src/runner/rebase.ts b/packages/playwright/src/runner/rebase.ts index de18df465b..196944fd88 100644 --- a/packages/playwright/src/runner/rebase.ts +++ b/packages/playwright/src/runner/rebase.ts @@ -68,24 +68,27 @@ export async function applySuggestedRebaselines(config: FullConfigInternal, repo traverse(fileNode, { CallExpression: path => { const node = path.node; - if (node.arguments.length !== 1) + if (node.arguments.length < 1) return; if (!t.isMemberExpression(node.callee)) return; const argument = node.arguments[0]; if (!t.isStringLiteral(argument) && !t.isTemplateLiteral(argument)) return; - - const matcher = node.callee.property; + const prop = node.callee.property; + if (!prop.loc || !argument.start || !argument.end) + return; + // Replacements are anchored by the location of the call expression. + // However, replacement text is meant to only replace the first argument. for (const replacement of replacements) { // In Babel, rows are 1-based, columns are 0-based. - if (matcher.loc!.start.line !== replacement.location.line) + if (prop.loc.start.line !== replacement.location.line) continue; - if (matcher.loc!.start.column + 1 !== replacement.location.column) + if (prop.loc.start.column + 1 !== replacement.location.column) continue; - const indent = lines[matcher.loc!.start.line - 1].match(/^\s*/)![0]; + const indent = lines[prop.loc.start.line - 1].match(/^\s*/)![0]; const newText = replacement.code.replace(/\{indent\}/g, indent); - ranges.push({ start: matcher.start!, end: node.end!, oldText: source.substring(matcher.start!, node.end!), newText }); + ranges.push({ start: argument.start, end: argument.end, oldText: source.substring(argument.start, argument.end), newText }); // We can have multiple, hopefully equal, replacements for the same location, // for example when a single test runs multiple times because of projects or retries. // Do not apply multiple replacements for the same assertion. diff --git a/tests/playwright-test/update-aria-snapshot.spec.ts b/tests/playwright-test/update-aria-snapshot.spec.ts index 15afff31e2..0052d3d3d9 100644 --- a/tests/playwright-test/update-aria-snapshot.spec.ts +++ b/tests/playwright-test/update-aria-snapshot.spec.ts @@ -454,6 +454,50 @@ test('should generate baseline for input values', async ({ runInlineTest }, test expect(result2.exitCode).toBe(0); }); +test('should update when options are specified', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + '.git/marker': '', + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('test', async ({ page }) => { + await page.setContent(\`\`); + await expect(page.locator('body')).toMatchAriaSnapshot(\`\`, { timeout: 2500 }); + await expect(page.locator('body')).toMatchAriaSnapshot('', + { + timeout: 2500 + }); + }); + ` + }); + + expect(result.exitCode).toBe(0); + const patchPath = testInfo.outputPath('test-results/rebaselines.patch'); + const data = fs.readFileSync(patchPath, 'utf-8'); + expect(trimPatch(data)).toBe(`diff --git a/a.spec.ts b/a.spec.ts +--- a/a.spec.ts ++++ b/a.spec.ts +@@ -2,8 +2,12 @@ + import { test, expect } from '@playwright/test'; + test('test', async ({ page }) => { + await page.setContent(\`\`); +- await expect(page.locator('body')).toMatchAriaSnapshot(\`\`, { timeout: 2500 }); +- await expect(page.locator('body')).toMatchAriaSnapshot('', ++ await expect(page.locator('body')).toMatchAriaSnapshot(\` ++ - textbox: hello world ++ \`, { timeout: 2500 }); ++ await expect(page.locator('body')).toMatchAriaSnapshot(\` ++ - textbox: hello world ++ \`, + { + timeout: 2500 + }); +`); + + execSync(`patch -p1 < ${patchPath}`, { cwd: testInfo.outputPath() }); + const result2 = await runInlineTest({}); + expect(result2.exitCode).toBe(0); +}); + test('should not update snapshots when locator did not match', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ '.git/marker': '', From 214b103b462c6777c29155e07cd0b2778c0746b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:52:00 +0300 Subject: [PATCH 15/32] chore(deps-dev): bump undici from 5.28.4 to 5.28.5 (#34419) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c242d4bb9..67b1fe085d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6931,10 +6931,11 @@ } }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz", + "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==", "dev": true, + "license": "MIT", "dependencies": { "@fastify/busboy": "^2.0.0" }, From a689e534ac402c5efb53c38fd659da2abb64ddd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:23:05 +0100 Subject: [PATCH 16/32] chore(deps): bump vite from 5.4.6 to 5.4.14 (#34420) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 11 ++++++----- package.json | 2 +- packages/playwright-ct-core/package.json | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67b1fe085d..d7eae56cba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,7 @@ "react-dom": "^18.1.0", "ssim.js": "^3.5.0", "typescript": "^5.7.2", - "vite": "^5.4.6", + "vite": "^5.4.14", "ws": "^8.17.1", "xml2js": "^0.5.0", "yaml": "^2.6.0" @@ -7028,9 +7028,10 @@ } }, "node_modules/vite": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", - "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -7834,7 +7835,7 @@ "dependencies": { "playwright": "1.51.0-next", "playwright-core": "1.51.0-next", - "vite": "^5.2.8" + "vite": "^5.4.14" }, "engines": { "node": ">=18" diff --git a/package.json b/package.json index 76a2b426f7..0bde8c158c 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "react-dom": "^18.1.0", "ssim.js": "^3.5.0", "typescript": "^5.7.2", - "vite": "^5.4.6", + "vite": "^5.4.14", "ws": "^8.17.1", "xml2js": "^0.5.0", "yaml": "^2.6.0" diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index e9376d1963..d97a0f56f2 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "playwright-core": "1.51.0-next", - "vite": "^5.2.8", + "vite": "^5.4.14", "playwright": "1.51.0-next" } } From 49b3bbb9209de304082f67f56e2b1a950fbcbde0 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Wed, 22 Jan 2025 06:17:54 -0800 Subject: [PATCH 17/32] docs: release notes for v1.50 js (#34380) --- docs/src/release-notes-js.md | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index 9d26d41551..d3760a7112 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -6,6 +6,74 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.50 + +### Test runner + +* New option [`option: Test.step.timeout`] allows specifying a maximum run time for an individual test step. A timed-out step will fail the execution of the test. + + ```js + test('some test', async ({ page }) => { + await test.step('a step', async () => { + // This step can time out separately from the test + }, { timeout: 1000 }); + }); + ``` + +* New method [`method: Test.step.skip`] to disable execution of a test step. + + ```js + test('some test', async ({ page }) => { + await test.step('before running step', async () => { + // Normal step + }); + + await test.step.skip('not yet ready', async () => { + // This step is skipped + }); + + await test.step('after running step', async () => { + // This step still runs even though the previous one was skipped + }); + }); + ``` + +* Expanded [`method: LocatorAssertions.toMatchAriaSnapshot#2`] to allow storing of aria snapshots in separate YAML files. +* Added method [`method: LocatorAssertions.toHaveAccessibleErrorMessage`] to assert the Locator points to an element with a given [aria errormessage](https://w3c.github.io/aria/#aria-errormessage). +* Option [`property: TestConfig.updateSnapshots`] added the configuration enum `changed`. `changed` updates only the snapshots that have changed, whereas `all` now updates all snapshots, regardless of whether there are any differences. +* New option [`property: TestConfig.updateSourceMethod`] defines the way source code is updated when [`property: TestConfig.updateSnapshots`] is configured. Added `overwrite` and `3-way` modes that write the changes into source code, on top of existing `patch` mode that creates a patch file. + + ```bash + npx playwright test --update-snapshots=changed --update-source-method=3way + ``` + +* Option [`property: TestConfig.webServer`] added a `gracefulShutdown` field for specifying a process kill signal other than the default `SIGKILL`. +* Exposed [`property: TestStep.attachments`] from the reporter API to allow retrieval of all attachments created by that step. + +### UI updates + +* Updated default HTML reporter to improve display of attachments. +* New button for picking elements to produce aria snapshots. +* Additional details (such as keys pressed) are now displayed alongside action API calls in traces. +* Display of `canvas` content in traces is error-prone. Display is now disabled by default, and can be enabled via the `Display canvas content` UI setting. +* `Call` and `Network` panels now display additional time information. + +### Breaking + +* [`method: LocatorAssertions.toBeEditable`] and [`method: Locator.isEditable`] now throw if the target element is not ``, ``); + await page.check('input'); + expect(await page.evaluate(() => window['checkbox'].checked)).toBe(true); }); it('should not check the checked box', async ({ page }) => {