feat(test-runner): allow specifying fine-grained trace options (#10147)
This commit is contained in:
parent
8582a19e6b
commit
7b64161a37
|
|
@ -166,13 +166,17 @@ Learn more about [automatic screenshots](./test-configuration.md#automatic-scree
|
||||||
## property: TestOptions.timezoneId = %%-context-option-timezoneid-%%
|
## property: TestOptions.timezoneId = %%-context-option-timezoneid-%%
|
||||||
|
|
||||||
## property: TestOptions.trace
|
## property: TestOptions.trace
|
||||||
- type: <[Screenshot]<"off"|"on"|"retain-on-failure"|"on-first-retry">>
|
- type: <[Object]|[TraceMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">>
|
||||||
|
- `mode` <[TraceMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">> Trace recording mode.
|
||||||
|
- `screenshots` <[boolean]> Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview. Defaults to true. Optional.
|
||||||
|
- `snapshots` <[boolean]> Whether to capture DOM snapshot on every action. Defaults to true. Optional.
|
||||||
|
- `sources` <[boolean]> Whether to include source files for trace actions. Defaults to true. Optional.
|
||||||
|
|
||||||
Whether to record a trace for each test. Defaults to `'off'`.
|
Whether to record trace for each test. Defaults to `'off'`.
|
||||||
* `'off'`: Do not record a trace.
|
* `'off'`: Do not record trace.
|
||||||
* `'on'`: Record a trace for each test.
|
* `'on'`: Record trace for each test.
|
||||||
* `'retain-on-failure'`: Record a trace for each test, but remove it from successful test runs.
|
* `'retain-on-failure'`: Record trace for each test, but remove all traces from successful test runs.
|
||||||
* `'on-first-retry'`: Record a trace only when retrying a test for the first time.
|
* `'on-first-retry'`: Record trace only when retrying a test for the first time.
|
||||||
|
|
||||||
Learn more about [recording trace](./test-configuration.md#record-test-trace).
|
Learn more about [recording trace](./test-configuration.md#record-test-trace).
|
||||||
|
|
||||||
|
|
@ -181,7 +185,7 @@ Learn more about [recording trace](./test-configuration.md#record-test-trace).
|
||||||
## property: TestOptions.video
|
## property: TestOptions.video
|
||||||
- type: <[Object]|[VideoMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">>
|
- type: <[Object]|[VideoMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">>
|
||||||
- `mode` <[VideoMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">> Video recording mode.
|
- `mode` <[VideoMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">> Video recording mode.
|
||||||
- `size` <[Object]> Size of the recorded video.
|
- `size` <[Object]> Size of the recorded video. Optional.
|
||||||
- `width` <[int]>
|
- `width` <[int]>
|
||||||
- `height` <[int]>
|
- `height` <[int]>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,9 +176,13 @@ export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({
|
||||||
if (process.env.PWDEBUG)
|
if (process.env.PWDEBUG)
|
||||||
testInfo.setTimeout(0);
|
testInfo.setTimeout(0);
|
||||||
|
|
||||||
if (trace === 'retry-with-trace')
|
let traceMode = typeof trace === 'string' ? trace : trace.mode;
|
||||||
trace = 'on-first-retry';
|
if (traceMode as any === 'retry-with-trace')
|
||||||
const captureTrace = (trace === 'on' || trace === 'retain-on-failure' || (trace === 'on-first-retry' && testInfo.retry === 1));
|
traceMode = 'on-first-retry';
|
||||||
|
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true };
|
||||||
|
const traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined };
|
||||||
|
|
||||||
|
const captureTrace = (traceMode === 'on' || traceMode === 'retain-on-failure' || (traceMode === 'on-first-retry' && testInfo.retry === 1));
|
||||||
const temporaryTraceFiles: string[] = [];
|
const temporaryTraceFiles: string[] = [];
|
||||||
const temporaryScreenshots: string[] = [];
|
const temporaryScreenshots: string[] = [];
|
||||||
|
|
||||||
|
|
@ -188,7 +192,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({
|
||||||
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 (!(context.tracing as any)[kTracingStarted]) {
|
if (!(context.tracing as any)[kTracingStarted]) {
|
||||||
await context.tracing.start({ screenshots: true, snapshots: true, sources: true, title });
|
await context.tracing.start({ ...traceOptions, title });
|
||||||
(context.tracing as any)[kTracingStarted] = true;
|
(context.tracing as any)[kTracingStarted] = true;
|
||||||
} else {
|
} else {
|
||||||
await context.tracing.startChunk({ title });
|
await context.tracing.startChunk({ title });
|
||||||
|
|
@ -253,7 +257,7 @@ export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({
|
||||||
// 3. Determine whether we need the artifacts.
|
// 3. Determine whether we need the artifacts.
|
||||||
const testFailed = testInfo.status !== testInfo.expectedStatus;
|
const testFailed = testInfo.status !== testInfo.expectedStatus;
|
||||||
const isHook = !!hookType(testInfo);
|
const isHook = !!hookType(testInfo);
|
||||||
const preserveTrace = captureTrace && !isHook && (trace === 'on' || (testFailed && trace === 'retain-on-failure') || (trace === 'on-first-retry' && testInfo.retry === 1));
|
const preserveTrace = captureTrace && !isHook && (traceMode === 'on' || (testFailed && traceMode === 'retain-on-failure') || (traceMode === 'on-first-retry' && testInfo.retry === 1));
|
||||||
const captureScreenshots = !isHook && (screenshot === 'on' || (screenshot === 'only-on-failure' && testFailed));
|
const captureScreenshots = !isHook && (screenshot === 'on' || (screenshot === 'only-on-failure' && testFailed));
|
||||||
|
|
||||||
const traceAttachments: string[] = [];
|
const traceAttachments: string[] = [];
|
||||||
|
|
|
||||||
17
packages/playwright-test/types/test.d.ts
vendored
17
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -2686,15 +2686,15 @@ export interface PlaywrightWorkerOptions {
|
||||||
*/
|
*/
|
||||||
screenshot: 'off' | 'on' | 'only-on-failure';
|
screenshot: 'off' | 'on' | 'only-on-failure';
|
||||||
/**
|
/**
|
||||||
* Whether to record a trace for each test. Defaults to `'off'`.
|
* Whether to record trace for each test. Defaults to `'off'`.
|
||||||
* - `'off'`: Do not record a trace.
|
* - `'off'`: Do not record trace.
|
||||||
* - `'on'`: Record a trace for each test.
|
* - `'on'`: Record trace for each test.
|
||||||
* - `'retain-on-failure'`: Record a trace for each test, but remove it from successful test runs.
|
* - `'retain-on-failure'`: Record trace for each test, but remove all traces from successful test runs.
|
||||||
* - `'on-first-retry'`: Record a trace only when retrying a test for the first time.
|
* - `'on-first-retry'`: Record trace only when retrying a test for the first time.
|
||||||
*
|
*
|
||||||
* Learn more about [recording trace](https://playwright.dev/docs/test-configuration#record-test-trace).
|
* Learn more about [recording trace](https://playwright.dev/docs/test-configuration#record-test-trace).
|
||||||
*/
|
*/
|
||||||
trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace';
|
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean };
|
||||||
/**
|
/**
|
||||||
* Whether to record video for each test. Defaults to `'off'`.
|
* Whether to record video for each test. Defaults to `'off'`.
|
||||||
* - `'off'`: Do not record video.
|
* - `'off'`: Do not record video.
|
||||||
|
|
@ -2704,10 +2704,11 @@ export interface PlaywrightWorkerOptions {
|
||||||
*
|
*
|
||||||
* Learn more about [recording video](https://playwright.dev/docs/test-configuration#record-video).
|
* Learn more about [recording video](https://playwright.dev/docs/test-configuration#record-video).
|
||||||
*/
|
*/
|
||||||
video: VideoMode | { mode: VideoMode, size: ViewportSize };
|
video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-video';
|
export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
|
||||||
|
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Playwright Test provides many options to configure test environment, [Browser], [BrowserContext] and more.
|
* Playwright Test provides many options to configure test environment, [Browser], [BrowserContext] and more.
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||||
|
import { ZipFileSystem } from '../../packages/playwright-core/lib/utils/vfs';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
test('should stop tracing with trace: on-first-retry, when not retrying', async ({ runInlineTest }, testInfo) => {
|
test('should stop tracing with trace: on-first-retry, when not retrying', async ({ runInlineTest }, testInfo) => {
|
||||||
|
|
@ -92,7 +93,7 @@ test('should not throw with trace: on-first-retry and two retries in the same wo
|
||||||
test('should not throw with trace and timeouts', async ({ runInlineTest }, testInfo) => {
|
test('should not throw with trace and timeouts', async ({ runInlineTest }, testInfo) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
module.exports = { timeout: 2000, repeatEach: 20, use: { trace: 'on' } };
|
module.exports = { timeout: 2000, repeatEach: 10, use: { trace: 'on' } };
|
||||||
`,
|
`,
|
||||||
'a.spec.ts': `
|
'a.spec.ts': `
|
||||||
const { test } = pwt;
|
const { test } = pwt;
|
||||||
|
|
@ -110,3 +111,57 @@ test('should not throw with trace and timeouts', async ({ runInlineTest }, testI
|
||||||
expect(stripAscii(result.output)).not.toContain('tracing.stopChunk:');
|
expect(stripAscii(result.output)).not.toContain('tracing.stopChunk:');
|
||||||
expect(stripAscii(result.output)).not.toContain('tracing.stop:');
|
expect(stripAscii(result.output)).not.toContain('tracing.stop:');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should save sources when requested', async ({ runInlineTest }, testInfo) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
module.exports = {
|
||||||
|
use: {
|
||||||
|
trace: 'on',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'a.spec.ts': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('pass', async ({ page }) => {
|
||||||
|
await page.evaluate(2 + 2);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
}, { workers: 1 });
|
||||||
|
expect(result.exitCode).toEqual(0);
|
||||||
|
const resources = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
|
||||||
|
expect([...resources.keys()].filter(f => f.includes('src@'))).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not save sources when not requested', async ({ runInlineTest }, testInfo) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'playwright.config.ts': `
|
||||||
|
module.exports = {
|
||||||
|
use: {
|
||||||
|
trace: {
|
||||||
|
mode: 'on',
|
||||||
|
sources: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'a.spec.ts': `
|
||||||
|
const { test } = pwt;
|
||||||
|
test('pass', async ({ page }) => {
|
||||||
|
await page.evaluate(2 + 2);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
}, { workers: 1 });
|
||||||
|
expect(result.exitCode).toEqual(0);
|
||||||
|
const resources = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
|
||||||
|
expect([...resources.keys()].filter(f => f.includes('src@'))).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function parseTrace(file: string): Promise<Map<string, Buffer>> {
|
||||||
|
const zipFS = new ZipFileSystem(file);
|
||||||
|
const resources = new Map<string, Buffer>();
|
||||||
|
for (const entry of await zipFS.entries())
|
||||||
|
resources.set(entry, await zipFS.read(entry));
|
||||||
|
zipFS.close();
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
|
||||||
7
utils/generate_types/overrides-test.d.ts
vendored
7
utils/generate_types/overrides-test.d.ts
vendored
|
|
@ -300,11 +300,12 @@ export interface PlaywrightWorkerOptions {
|
||||||
channel: BrowserChannel | undefined;
|
channel: BrowserChannel | undefined;
|
||||||
launchOptions: LaunchOptions;
|
launchOptions: LaunchOptions;
|
||||||
screenshot: 'off' | 'on' | 'only-on-failure';
|
screenshot: 'off' | 'on' | 'only-on-failure';
|
||||||
trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace';
|
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean };
|
||||||
video: VideoMode | { mode: VideoMode, size: ViewportSize };
|
video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-video';
|
export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
|
||||||
|
export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';
|
||||||
|
|
||||||
export interface PlaywrightTestOptions {
|
export interface PlaywrightTestOptions {
|
||||||
acceptDownloads: boolean | undefined;
|
acceptDownloads: boolean | undefined;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue