diff --git a/docs/src/test-api/class-testinfo.md b/docs/src/test-api/class-testinfo.md index aceac10f3c..475e531be0 100644 --- a/docs/src/test-api/class-testinfo.md +++ b/docs/src/test-api/class-testinfo.md @@ -248,6 +248,12 @@ Optional description that will be reflected in a test report. Test function as passed to `test(title, testFunction)`. +## property: TestInfo.testId +* since: v1.32 +- type: <[string]> + +Test id matching the test case id in the reporter API. + ## property: TestInfo.line * since: v1.10 - type: <[int]> diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index f4281378c7..a1c9f27afb 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -123,14 +123,14 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps if (this._state) { const o = this._state.options; - if (o.name !== options.name || !o.screenshots !== !options.screenshots || !o.snapshots !== !options.snapshots) + if (!o.screenshots !== !options.screenshots || !o.snapshots !== !options.snapshots) throw new Error('Tracing has been already started with different options'); return; } // TODO: passing the same name for two contexts makes them write into a single file // and conflict. const traceName = options.name || createGuid(); - // Init the state synchrounously. + // Init the state synchronously. this._state = { options, traceName, traceFile: '', networkFile: '', tracesDir: '', resourcesDir: '', filesCount: 0, traceSha1s: new Set(), networkSha1s: new Set(), recording: false }; const state = this._state; diff --git a/packages/trace/src/traceUtils.ts b/packages/playwright-core/src/utils/isomorphic/traceUtils.ts similarity index 100% rename from packages/trace/src/traceUtils.ts rename to packages/playwright-core/src/utils/isomorphic/traceUtils.ts diff --git a/packages/playwright-core/src/utils/traceUtils.ts b/packages/playwright-core/src/utils/traceUtils.ts index 43fcbe6bb8..2f3d3a2f21 100644 --- a/packages/playwright-core/src/utils/traceUtils.ts +++ b/packages/playwright-core/src/utils/traceUtils.ts @@ -17,7 +17,7 @@ import fs from 'fs'; import type EventEmitter from 'events'; import type { ClientSideCallMetadata, StackFrame } from '@protocol/channels'; -import type { SerializedClientSideCallMetadata, SerializedStack, SerializedStackFrame } from '@trace/traceUtils'; +import type { SerializedClientSideCallMetadata, SerializedStack, SerializedStackFrame } from './isomorphic/traceUtils'; import { yazl, yauzl } from '../zipBundle'; import { ManualPromise } from './manualPromise'; import type { ActionTraceEvent } from '@trace/trace'; diff --git a/packages/playwright-test/src/DEPS.list b/packages/playwright-test/src/DEPS.list index 08d491f52e..863c838067 100644 --- a/packages/playwright-test/src/DEPS.list +++ b/packages/playwright-test/src/DEPS.list @@ -5,3 +5,6 @@ common/ [cli.ts] ** + +[index.ts] +@testIsomorphic/** diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index e29931346a..2a51bdecfc 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -24,6 +24,7 @@ import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWor import type { TestInfoImpl } from './worker/testInfo'; import { rootTestType } from './common/testType'; import { type ContextReuseMode } from './common/types'; +import { artifactsFolderName } from './isomorphic/folders'; export { expect } from './matchers/expect'; export { store } from './store'; export const _baseTest: TestType<{}, {}> = rootTestType.test; @@ -79,7 +80,7 @@ const playwrightFixtures: Fixtures = ({ let dir: string | undefined; await use(() => { if (!dir) { - dir = path.join(workerInfo.project.outputDir, '.playwright-artifacts-' + workerInfo.workerIndex); + dir = path.join(workerInfo.project.outputDir, artifactsFolderName(workerInfo.workerIndex)); fs.mkdirSync(dir, { recursive: true }); } return dir; @@ -88,7 +89,7 @@ const playwrightFixtures: Fixtures = ({ await removeFolders([dir]); }, { scope: 'worker', _title: 'playwright configuration' } as any], - _browserOptions: [async ({ playwright, headless, channel, launchOptions, connectOptions }, use) => { + _browserOptions: [async ({ playwright, headless, channel, launchOptions, connectOptions, _artifactsDir }, use) => { const options: LaunchOptions = { handleSIGINT: false, timeout: 0, @@ -98,6 +99,7 @@ const playwrightFixtures: Fixtures = ({ options.headless = headless; if (channel !== undefined) options.channel = channel; + options.tracesDir = path.join(_artifactsDir(), 'traces'); for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) { (browserType as BrowserTypeImpl)._defaultLaunchOptions = options; @@ -255,6 +257,7 @@ const playwrightFixtures: Fixtures = ({ const temporaryScreenshots: string[] = []; const testInfoImpl = testInfo as TestInfoImpl; const reusedContexts = new Set(); + let traceOrdinal = 0; const createInstrumentationListener = (context?: BrowserContext) => { return { @@ -287,7 +290,11 @@ const playwrightFixtures: Fixtures = ({ if (captureTrace) { const title = [path.relative(testInfo.project.testDir, testInfo.file) + ':' + testInfo.line, ...testInfo.titlePath.slice(1)].join(' › '); if (!(tracing as any)[kTracingStarted]) { - await tracing.start({ ...traceOptions, title }); + const ordinalSuffix = traceOrdinal ? `-${traceOrdinal}` : ''; + ++traceOrdinal; + const retrySuffix = testInfo.retry ? `-${testInfo.retry}` : ''; + const name = `${testInfo.testId}${retrySuffix}${ordinalSuffix}`; + await tracing.start({ ...traceOptions, title, name }); (tracing as any)[kTracingStarted] = true; } else { await tracing.startChunk({ title }); diff --git a/packages/playwright-test/src/isomorphic/DEPS.list b/packages/playwright-test/src/isomorphic/DEPS.list new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/playwright-test/src/isomorphic/folders.ts b/packages/playwright-test/src/isomorphic/folders.ts new file mode 100644 index 0000000000..c50dea1be2 --- /dev/null +++ b/packages/playwright-test/src/isomorphic/folders.ts @@ -0,0 +1,19 @@ +/** + * 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. + */ + +export function artifactsFolderName(workerIndex: number) { + return `.playwright-artifacts-${workerIndex}`; +} diff --git a/packages/playwright-test/src/worker/testInfo.ts b/packages/playwright-test/src/worker/testInfo.ts index 78ee51193f..21821cd90b 100644 --- a/packages/playwright-test/src/worker/testInfo.ts +++ b/packages/playwright-test/src/worker/testInfo.ts @@ -56,6 +56,7 @@ export class TestInfoImpl implements TestInfo { _lastStepId = 0; // ------------ TestInfo fields ------------ + readonly testId: string; readonly repeatEachIndex: number; readonly retry: number; readonly workerIndex: number; @@ -109,6 +110,7 @@ export class TestInfoImpl implements TestInfo { onStepEnd: (payload: StepEndPayload) => void, ) { this._test = test; + this.testId = test.id; this._onStepBegin = onStepBegin; this._onStepEnd = onStepEnd; this._startTime = monotonicTime(); diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index 93cb1ab7a9..70517b64a7 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -2244,6 +2244,11 @@ export interface TestInfo { */ stdout: Array; + /** + * Test id matching the test case id in the reporter API. + */ + testId: string; + /** * Timeout in milliseconds for the currently running test. Zero means no timeout. Learn more about * [various timeouts](https://playwright.dev/docs/test-timeouts). diff --git a/packages/trace-viewer/src/DEPS.list b/packages/trace-viewer/src/DEPS.list index f69b330116..f2614cff83 100644 --- a/packages/trace-viewer/src/DEPS.list +++ b/packages/trace-viewer/src/DEPS.list @@ -1,4 +1,5 @@ [*] +@isomorphic/** @trace/** @web/** ui/ diff --git a/packages/trace-viewer/src/traceModel.ts b/packages/trace-viewer/src/traceModel.ts index edf5f0877f..536e470df1 100644 --- a/packages/trace-viewer/src/traceModel.ts +++ b/packages/trace-viewer/src/traceModel.ts @@ -16,7 +16,7 @@ import type * as trace from '@trace/trace'; import type * as traceV3 from './versions/traceV3'; -import { parseClientSideCallMetadata } from '@trace/traceUtils'; +import { parseClientSideCallMetadata } from '@isomorphic/traceUtils'; import type zip from '@zip.js/zip.js'; // @ts-ignore import zipImport from '@zip.js/zip.js/dist/zip-no-worker-inflate.min.js'; diff --git a/packages/trace-viewer/src/ui/watchMode.tsx b/packages/trace-viewer/src/ui/watchMode.tsx index 124317275a..108178df7d 100644 --- a/packages/trace-viewer/src/ui/watchMode.tsx +++ b/packages/trace-viewer/src/ui/watchMode.tsx @@ -20,8 +20,8 @@ import '@web/common.css'; import React from 'react'; import { TreeView } from '@web/components/treeView'; import type { TreeState } from '@web/components/treeView'; -import { TeleReporterReceiver, TeleSuite } from '../../../playwright-test/src/isomorphic/teleReceiver'; -import type { TeleTestCase } from '../../../playwright-test/src/isomorphic/teleReceiver'; +import { TeleReporterReceiver, TeleSuite } from '@testIsomorphic/teleReceiver'; +import type { TeleTestCase } from '@testIsomorphic/teleReceiver'; import type { FullConfig, Suite, TestCase, TestResult, TestStep, Location } from '../../../playwright-test/types/testReporter'; import { SplitView } from '@web/components/splitView'; import { MultiTraceModel } from './modelUtil'; diff --git a/packages/trace-viewer/tsconfig.json b/packages/trace-viewer/tsconfig.json index 17689aaa4d..350af62edd 100644 --- a/packages/trace-viewer/tsconfig.json +++ b/packages/trace-viewer/tsconfig.json @@ -20,6 +20,7 @@ "@isomorphic/*": ["../playwright-core/src/utils/isomorphic/*"], "@protocol/*": ["../protocol/src/*"], "@recorder/*": ["../recorder/src/*"], + "@testIsomorphic/*": ["../playwright-test/src/isomorphic/*"], "@trace/*": ["../trace/src/*"], "@web/*": ["../web/src/*"], // Resolving type dependencies will start processing types in @playwright/test diff --git a/packages/trace-viewer/vite.config.ts b/packages/trace-viewer/vite.config.ts index efc0ba148e..d0a6847956 100644 --- a/packages/trace-viewer/vite.config.ts +++ b/packages/trace-viewer/vite.config.ts @@ -32,6 +32,7 @@ export default defineConfig({ '@injected': path.resolve(__dirname, '../playwright-core/src/server/injected'), '@isomorphic': path.resolve(__dirname, '../playwright-core/src/utils/isomorphic'), '@protocol': path.resolve(__dirname, '../protocol/src'), + '@testIsomorphic': path.resolve(__dirname, '../playwright-test/src/isomorphic'), '@trace': path.resolve(__dirname, '../trace/src'), '@web': path.resolve(__dirname, '../web/src'), }, diff --git a/packages/trace-viewer/vite.sw.config.ts b/packages/trace-viewer/vite.sw.config.ts index 3fe9c9a168..8b2359aa4c 100644 --- a/packages/trace-viewer/vite.sw.config.ts +++ b/packages/trace-viewer/vite.sw.config.ts @@ -31,6 +31,7 @@ export default defineConfig({ alias: { '@isomorphic': path.resolve(__dirname, '../playwright-core/src/utils/isomorphic'), '@protocol': path.resolve(__dirname, '../protocol/src'), + '@testIsomorphic': path.resolve(__dirname, '../playwright-core/src/utils/testIsomorphic'), '@trace': path.resolve(__dirname, '../trace/src'), '@web': path.resolve(__dirname, '../web/src'), }, diff --git a/tests/config/utils.ts b/tests/config/utils.ts index 7a713382ef..9f829506de 100644 --- a/tests/config/utils.ts +++ b/tests/config/utils.ts @@ -17,7 +17,7 @@ import type { Frame, Page } from 'playwright-core'; import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile'; import type { StackFrame } from '../../packages/protocol/src/channels'; -import { parseClientSideCallMetadata } from '../../packages/trace/src/traceUtils'; +import { parseClientSideCallMetadata } from '../../packages/playwright-core/lib/utils/isomorphic/traceUtils'; import type { ActionTraceEvent } from '../../packages/trace/src/trace'; export async function attachFrame(page: Page, frameId: string, url: string): Promise { diff --git a/tsconfig.json b/tsconfig.json index ce56169d58..40783b818f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,7 @@ "@isomorphic/*": ["./packages/playwright-core/src/utils/isomorphic/*"], "@protocol/*": ["./packages/protocol/src/*"], "@recorder/*": ["./packages/recorder/src/*"], + "@testIsomorphic/*": ["./packages/playwright-test/src/isomorphic/*"], "@trace/*": ["./packages/trace/src/*"], "@web/*": ["./packages/web/src/*"], "playwright-core/lib/*": ["./packages/playwright-core/src/*"], diff --git a/utils/check_deps.js b/utils/check_deps.js index 385f805dbb..89a18eb7e5 100644 --- a/utils/check_deps.js +++ b/utils/check_deps.js @@ -29,6 +29,7 @@ for (const package of fs.readdirSync(packagesDir)) packages.set(package, packagesDir + '/' + package + '/src/'); packages.set('injected', packagesDir + '/playwright-core/src/server/injected/'); packages.set('isomorphic', packagesDir + '/playwright-core/src/utils/isomorphic/'); +packages.set('testIsomorphic', packagesDir + '/playwright-test/src/isomorphic/'); const peerDependencies = ['electron', 'react', 'react-dom', '@zip.js/zip.js'];