chore: introduce testInfo.testId (#21670)

This commit is contained in:
Pavel Feldman 2023-03-14 15:58:55 -07:00 committed by GitHub
parent 7666894d77
commit 27048adebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 58 additions and 10 deletions

View file

@ -248,6 +248,12 @@ Optional description that will be reflected in a test report.
Test function as passed to `test(title, testFunction)`. 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 ## property: TestInfo.line
* since: v1.10 * since: v1.10
- type: <[int]> - type: <[int]>

View file

@ -123,14 +123,14 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
if (this._state) { if (this._state) {
const o = this._state.options; 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'); throw new Error('Tracing has been already started with different options');
return; return;
} }
// TODO: passing the same name for two contexts makes them write into a single file // TODO: passing the same name for two contexts makes them write into a single file
// and conflict. // and conflict.
const traceName = options.name || createGuid(); 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 }; this._state = { options, traceName, traceFile: '', networkFile: '', tracesDir: '', resourcesDir: '', filesCount: 0, traceSha1s: new Set(), networkSha1s: new Set(), recording: false };
const state = this._state; const state = this._state;

View file

@ -17,7 +17,7 @@
import fs from 'fs'; import fs from 'fs';
import type EventEmitter from 'events'; import type EventEmitter from 'events';
import type { ClientSideCallMetadata, StackFrame } from '@protocol/channels'; 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 { yazl, yauzl } from '../zipBundle';
import { ManualPromise } from './manualPromise'; import { ManualPromise } from './manualPromise';
import type { ActionTraceEvent } from '@trace/trace'; import type { ActionTraceEvent } from '@trace/trace';

View file

@ -5,3 +5,6 @@ common/
[cli.ts] [cli.ts]
** **
[index.ts]
@testIsomorphic/**

View file

@ -24,6 +24,7 @@ import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWor
import type { TestInfoImpl } from './worker/testInfo'; import type { TestInfoImpl } from './worker/testInfo';
import { rootTestType } from './common/testType'; import { rootTestType } from './common/testType';
import { type ContextReuseMode } from './common/types'; import { type ContextReuseMode } from './common/types';
import { artifactsFolderName } from './isomorphic/folders';
export { expect } from './matchers/expect'; export { expect } from './matchers/expect';
export { store } from './store'; export { store } from './store';
export const _baseTest: TestType<{}, {}> = rootTestType.test; export const _baseTest: TestType<{}, {}> = rootTestType.test;
@ -79,7 +80,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
let dir: string | undefined; let dir: string | undefined;
await use(() => { await use(() => {
if (!dir) { 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 }); fs.mkdirSync(dir, { recursive: true });
} }
return dir; return dir;
@ -88,7 +89,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
await removeFolders([dir]); await removeFolders([dir]);
}, { scope: 'worker', _title: 'playwright configuration' } as any], }, { 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 = { const options: LaunchOptions = {
handleSIGINT: false, handleSIGINT: false,
timeout: 0, timeout: 0,
@ -98,6 +99,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
options.headless = headless; options.headless = headless;
if (channel !== undefined) if (channel !== undefined)
options.channel = channel; options.channel = channel;
options.tracesDir = path.join(_artifactsDir(), 'traces');
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) { for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
(browserType as BrowserTypeImpl)._defaultLaunchOptions = options; (browserType as BrowserTypeImpl)._defaultLaunchOptions = options;
@ -255,6 +257,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
const temporaryScreenshots: string[] = []; const temporaryScreenshots: string[] = [];
const testInfoImpl = testInfo as TestInfoImpl; const testInfoImpl = testInfo as TestInfoImpl;
const reusedContexts = new Set<BrowserContext>(); const reusedContexts = new Set<BrowserContext>();
let traceOrdinal = 0;
const createInstrumentationListener = (context?: BrowserContext) => { const createInstrumentationListener = (context?: BrowserContext) => {
return { return {
@ -287,7 +290,11 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
if (captureTrace) { if (captureTrace) {
const title = [path.relative(testInfo.project.testDir, testInfo.file) + ':' + testInfo.line, ...testInfo.titlePath.slice(1)].join(' '); const title = [path.relative(testInfo.project.testDir, testInfo.file) + ':' + testInfo.line, ...testInfo.titlePath.slice(1)].join(' ');
if (!(tracing as any)[kTracingStarted]) { 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; (tracing as any)[kTracingStarted] = true;
} else { } else {
await tracing.startChunk({ title }); await tracing.startChunk({ title });

View file

@ -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}`;
}

View file

@ -56,6 +56,7 @@ export class TestInfoImpl implements TestInfo {
_lastStepId = 0; _lastStepId = 0;
// ------------ TestInfo fields ------------ // ------------ TestInfo fields ------------
readonly testId: string;
readonly repeatEachIndex: number; readonly repeatEachIndex: number;
readonly retry: number; readonly retry: number;
readonly workerIndex: number; readonly workerIndex: number;
@ -109,6 +110,7 @@ export class TestInfoImpl implements TestInfo {
onStepEnd: (payload: StepEndPayload) => void, onStepEnd: (payload: StepEndPayload) => void,
) { ) {
this._test = test; this._test = test;
this.testId = test.id;
this._onStepBegin = onStepBegin; this._onStepBegin = onStepBegin;
this._onStepEnd = onStepEnd; this._onStepEnd = onStepEnd;
this._startTime = monotonicTime(); this._startTime = monotonicTime();

View file

@ -2244,6 +2244,11 @@ export interface TestInfo {
*/ */
stdout: Array<string|Buffer>; stdout: Array<string|Buffer>;
/**
* 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 * Timeout in milliseconds for the currently running test. Zero means no timeout. Learn more about
* [various timeouts](https://playwright.dev/docs/test-timeouts). * [various timeouts](https://playwright.dev/docs/test-timeouts).

View file

@ -1,4 +1,5 @@
[*] [*]
@isomorphic/**
@trace/** @trace/**
@web/** @web/**
ui/ ui/

View file

@ -16,7 +16,7 @@
import type * as trace from '@trace/trace'; import type * as trace from '@trace/trace';
import type * as traceV3 from './versions/traceV3'; 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'; import type zip from '@zip.js/zip.js';
// @ts-ignore // @ts-ignore
import zipImport from '@zip.js/zip.js/dist/zip-no-worker-inflate.min.js'; import zipImport from '@zip.js/zip.js/dist/zip-no-worker-inflate.min.js';

View file

@ -20,8 +20,8 @@ import '@web/common.css';
import React from 'react'; import React from 'react';
import { TreeView } from '@web/components/treeView'; import { TreeView } from '@web/components/treeView';
import type { TreeState } from '@web/components/treeView'; import type { TreeState } from '@web/components/treeView';
import { TeleReporterReceiver, TeleSuite } from '../../../playwright-test/src/isomorphic/teleReceiver'; import { TeleReporterReceiver, TeleSuite } from '@testIsomorphic/teleReceiver';
import type { TeleTestCase } from '../../../playwright-test/src/isomorphic/teleReceiver'; import type { TeleTestCase } from '@testIsomorphic/teleReceiver';
import type { FullConfig, Suite, TestCase, TestResult, TestStep, Location } from '../../../playwright-test/types/testReporter'; import type { FullConfig, Suite, TestCase, TestResult, TestStep, Location } from '../../../playwright-test/types/testReporter';
import { SplitView } from '@web/components/splitView'; import { SplitView } from '@web/components/splitView';
import { MultiTraceModel } from './modelUtil'; import { MultiTraceModel } from './modelUtil';

View file

@ -20,6 +20,7 @@
"@isomorphic/*": ["../playwright-core/src/utils/isomorphic/*"], "@isomorphic/*": ["../playwright-core/src/utils/isomorphic/*"],
"@protocol/*": ["../protocol/src/*"], "@protocol/*": ["../protocol/src/*"],
"@recorder/*": ["../recorder/src/*"], "@recorder/*": ["../recorder/src/*"],
"@testIsomorphic/*": ["../playwright-test/src/isomorphic/*"],
"@trace/*": ["../trace/src/*"], "@trace/*": ["../trace/src/*"],
"@web/*": ["../web/src/*"], "@web/*": ["../web/src/*"],
// Resolving type dependencies will start processing types in @playwright/test // Resolving type dependencies will start processing types in @playwright/test

View file

@ -32,6 +32,7 @@ export default defineConfig({
'@injected': path.resolve(__dirname, '../playwright-core/src/server/injected'), '@injected': path.resolve(__dirname, '../playwright-core/src/server/injected'),
'@isomorphic': path.resolve(__dirname, '../playwright-core/src/utils/isomorphic'), '@isomorphic': path.resolve(__dirname, '../playwright-core/src/utils/isomorphic'),
'@protocol': path.resolve(__dirname, '../protocol/src'), '@protocol': path.resolve(__dirname, '../protocol/src'),
'@testIsomorphic': path.resolve(__dirname, '../playwright-test/src/isomorphic'),
'@trace': path.resolve(__dirname, '../trace/src'), '@trace': path.resolve(__dirname, '../trace/src'),
'@web': path.resolve(__dirname, '../web/src'), '@web': path.resolve(__dirname, '../web/src'),
}, },

View file

@ -31,6 +31,7 @@ export default defineConfig({
alias: { alias: {
'@isomorphic': path.resolve(__dirname, '../playwright-core/src/utils/isomorphic'), '@isomorphic': path.resolve(__dirname, '../playwright-core/src/utils/isomorphic'),
'@protocol': path.resolve(__dirname, '../protocol/src'), '@protocol': path.resolve(__dirname, '../protocol/src'),
'@testIsomorphic': path.resolve(__dirname, '../playwright-core/src/utils/testIsomorphic'),
'@trace': path.resolve(__dirname, '../trace/src'), '@trace': path.resolve(__dirname, '../trace/src'),
'@web': path.resolve(__dirname, '../web/src'), '@web': path.resolve(__dirname, '../web/src'),
}, },

View file

@ -17,7 +17,7 @@
import type { Frame, Page } from 'playwright-core'; import type { Frame, Page } from 'playwright-core';
import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile'; import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile';
import type { StackFrame } from '../../packages/protocol/src/channels'; 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'; import type { ActionTraceEvent } from '../../packages/trace/src/trace';
export async function attachFrame(page: Page, frameId: string, url: string): Promise<Frame> { export async function attachFrame(page: Page, frameId: string, url: string): Promise<Frame> {

View file

@ -15,6 +15,7 @@
"@isomorphic/*": ["./packages/playwright-core/src/utils/isomorphic/*"], "@isomorphic/*": ["./packages/playwright-core/src/utils/isomorphic/*"],
"@protocol/*": ["./packages/protocol/src/*"], "@protocol/*": ["./packages/protocol/src/*"],
"@recorder/*": ["./packages/recorder/src/*"], "@recorder/*": ["./packages/recorder/src/*"],
"@testIsomorphic/*": ["./packages/playwright-test/src/isomorphic/*"],
"@trace/*": ["./packages/trace/src/*"], "@trace/*": ["./packages/trace/src/*"],
"@web/*": ["./packages/web/src/*"], "@web/*": ["./packages/web/src/*"],
"playwright-core/lib/*": ["./packages/playwright-core/src/*"], "playwright-core/lib/*": ["./packages/playwright-core/src/*"],

View file

@ -29,6 +29,7 @@ for (const package of fs.readdirSync(packagesDir))
packages.set(package, packagesDir + '/' + package + '/src/'); packages.set(package, packagesDir + '/' + package + '/src/');
packages.set('injected', packagesDir + '/playwright-core/src/server/injected/'); packages.set('injected', packagesDir + '/playwright-core/src/server/injected/');
packages.set('isomorphic', packagesDir + '/playwright-core/src/utils/isomorphic/'); 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']; const peerDependencies = ['electron', 'react', 'react-dom', '@zip.js/zip.js'];