chore: show steps for fixtures (#22860)

Fixes https://github.com/microsoft/playwright/issues/22565
This commit is contained in:
Pavel Feldman 2023-05-06 10:25:32 -07:00 committed by GitHub
parent 21ffa0b6ad
commit 9771b1ee74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 257 additions and 37 deletions

View file

@ -16,7 +16,7 @@
import { formatLocation, debugTest } from '../util'; import { formatLocation, debugTest } from '../util';
import { ManualPromise, zones } from 'playwright-core/lib/utils'; import { ManualPromise, zones } from 'playwright-core/lib/utils';
import type { TestInfoImpl } from './testInfo'; import type { TestInfoImpl, TestStepInternal } from './testInfo';
import type { FixtureDescription, TimeoutManager } from './timeoutManager'; import type { FixtureDescription, TimeoutManager } from './timeoutManager';
import { fixtureParameterNames, type FixturePool, type FixtureRegistration, type FixtureScope } from '../common/fixtures'; import { fixtureParameterNames, type FixturePool, type FixtureRegistration, type FixtureScope } from '../common/fixtures';
import type { WorkerInfo } from '../../types/test'; import type { WorkerInfo } from '../../types/test';
@ -74,6 +74,12 @@ class Fixture {
} }
} }
// Break the regustration function into before/after steps. Create these before/after stacks
// w/o scopes, and create single mutable step that will be converted into the after step.
const shouldGenerateStep = !this.registration.name.startsWith('_') && !this.registration.option && this.registration.scope === 'test';
let mutableStepOnStack: TestStepInternal | undefined;
let afterStep: TestStepInternal | undefined;
let called = false; let called = false;
const useFuncStarted = new ManualPromise<void>(); const useFuncStarted = new ManualPromise<void>();
debugTest(`setup ${this.registration.name}`); debugTest(`setup ${this.registration.name}`);
@ -85,7 +91,17 @@ class Fixture {
this._useFuncFinished = new ManualPromise<void>(); this._useFuncFinished = new ManualPromise<void>();
useFuncStarted.resolve(); useFuncStarted.resolve();
await this._useFuncFinished; await this._useFuncFinished;
if (shouldGenerateStep) {
afterStep = testInfo._addStep({
wallTime: Date.now(),
title: `fixture: ${this.registration.name}`,
category: 'fixture',
}, testInfo._afterHooksStep);
mutableStepOnStack!.stepId = afterStep.stepId;
}
}; };
const workerInfo: WorkerInfo = { config: testInfo.config, parallelIndex: testInfo.parallelIndex, workerIndex: testInfo.workerIndex, project: testInfo.project }; const workerInfo: WorkerInfo = { config: testInfo.config, parallelIndex: testInfo.parallelIndex, workerIndex: testInfo.workerIndex, project: testInfo.project };
const info = this.registration.scope === 'worker' ? workerInfo : testInfo; const info = this.registration.scope === 'worker' ? workerInfo : testInfo;
testInfo._timeoutManager.setCurrentFixture(this._runnableDescription); testInfo._timeoutManager.setCurrentFixture(this._runnableDescription);
@ -99,7 +115,16 @@ class Fixture {
}; };
try { try {
const result = zones.preserve(async () => { const result = zones.preserve(async () => {
return await this.registration.fn(params, useFunc, info); if (!shouldGenerateStep)
return await this.registration.fn(params, useFunc, info);
await testInfo._runAsStep({
title: `fixture: ${this.registration.name}`,
category: 'fixture',
}, async step => {
mutableStepOnStack = step;
return await this.registration.fn(params, useFunc, info);
});
}); });
if (result instanceof Promise) if (result instanceof Promise)
@ -110,6 +135,12 @@ class Fixture {
handleError(e); handleError(e);
} }
await useFuncStarted; await useFuncStarted;
if (shouldGenerateStep) {
mutableStepOnStack?.complete({});
this._selfTeardownComplete?.finally(() => {
afterStep?.complete({});
});
}
testInfo._timeoutManager.setCurrentFixture(undefined); testInfo._timeoutManager.setCurrentFixture(undefined);
} }

View file

@ -26,7 +26,7 @@ import type { Location } from '../../types/testReporter';
import { getContainedPath, normalizeAndSaveAttachment, sanitizeForFilePath, serializeError, trimLongString } from '../util'; import { getContainedPath, normalizeAndSaveAttachment, sanitizeForFilePath, serializeError, trimLongString } from '../util';
import type * as trace from '@trace/trace'; import type * as trace from '@trace/trace';
interface TestStepInternal { export interface TestStepInternal {
complete(result: { error?: Error | TestInfoError }): void; complete(result: { error?: Error | TestInfoError }): void;
stepId: string; stepId: string;
title: string; title: string;
@ -56,6 +56,8 @@ export class TestInfoImpl implements TestInfo {
readonly _projectInternal: FullProjectInternal; readonly _projectInternal: FullProjectInternal;
readonly _configInternal: FullConfigInternal; readonly _configInternal: FullConfigInternal;
readonly _steps: TestStepInternal[] = []; readonly _steps: TestStepInternal[] = [];
_beforeHooksStep: TestStepInternal | undefined;
_afterHooksStep: TestStepInternal | undefined;
// ------------ TestInfo fields ------------ // ------------ TestInfo fields ------------
readonly testId: string; readonly testId: string;
@ -212,9 +214,10 @@ export class TestInfoImpl implements TestInfo {
} }
} }
_addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps'>): TestStepInternal { _addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps'>, parentStep?: TestStepInternal): TestStepInternal {
const stepId = `${data.category}@${data.title}@${++this._lastStepId}`; const stepId = `${data.category}@${data.title}@${++this._lastStepId}`;
let parentStep = zones.zoneData<TestStepInternal>('stepZone', captureRawStack()); if (!parentStep)
parentStep = zones.zoneData<TestStepInternal>('stepZone', captureRawStack()) || undefined;
// For out-of-stack calls, locate the enclosing step. // For out-of-stack calls, locate the enclosing step.
let isLaxParent = false; let isLaxParent = false;

View file

@ -322,6 +322,7 @@ export class WorkerMain extends ProcessRunner {
let testFunctionParams: object | null = null; let testFunctionParams: object | null = null;
await testInfo._runAsStep({ category: 'hook', title: 'Before Hooks' }, async step => { await testInfo._runAsStep({ category: 'hook', title: 'Before Hooks' }, async step => {
testInfo._beforeHooksStep = step;
// Note: wrap all preparation steps together, because failure/skip in any of them // Note: wrap all preparation steps together, because failure/skip in any of them
// prevents further setup and/or test from running. // prevents further setup and/or test from running.
const beforeHooksError = await testInfo._runAndFailOnError(async () => { const beforeHooksError = await testInfo._runAndFailOnError(async () => {
@ -392,6 +393,7 @@ export class WorkerMain extends ProcessRunner {
testInfo._timeoutManager.setCurrentRunnable({ type: 'afterEach', slot: afterHooksSlot }); testInfo._timeoutManager.setCurrentRunnable({ type: 'afterEach', slot: afterHooksSlot });
} }
await testInfo._runAsStep({ category: 'hook', title: 'After Hooks' }, async step => { await testInfo._runAsStep({ category: 'hook', title: 'After Hooks' }, async step => {
testInfo._afterHooksStep = step;
let firstAfterHooksError: TestInfoError | undefined; let firstAfterHooksError: TestInfoError | undefined;
await testInfo._runWithTimeout(async () => { await testInfo._runWithTimeout(async () => {
// Note: do not wrap all teardown steps together, because failure in any of them // Note: do not wrap all teardown steps together, because failure in any of them

View file

@ -84,7 +84,7 @@ function indexModel(context: ContextEntry) {
} }
function mergeActions(contexts: ContextEntry[]) { function mergeActions(contexts: ContextEntry[]) {
const map = new Map<number, ActionTraceEvent>(); const map = new Map<string, ActionTraceEvent>();
// Protocol call aka isPrimary contexts have startTime/endTime as server-side times. // Protocol call aka isPrimary contexts have startTime/endTime as server-side times.
// Step aka non-isPrimary contexts have startTime/endTime are client-side times. // Step aka non-isPrimary contexts have startTime/endTime are client-side times.
@ -95,7 +95,7 @@ function mergeActions(contexts: ContextEntry[]) {
for (const context of primaryContexts) { for (const context of primaryContexts) {
for (const action of context.actions) for (const action of context.actions)
map.set(action.wallTime, action); map.set(`${action.apiName}@${action.wallTime}`, action);
if (!offset && context.actions.length) if (!offset && context.actions.length)
offset = context.actions[0].startTime - context.actions[0].wallTime; offset = context.actions[0].startTime - context.actions[0].wallTime;
} }
@ -110,7 +110,8 @@ function mergeActions(contexts: ContextEntry[]) {
action.endTime = action.startTime + duration; action.endTime = action.startTime + duration;
} }
const existing = map.get(action.wallTime); const key = `${action.apiName}@${action.wallTime}`;
const existing = map.get(key);
if (existing && existing.apiName === action.apiName) { if (existing && existing.apiName === action.apiName) {
if (action.error) if (action.error)
existing.error = action.error; existing.error = action.error;
@ -118,7 +119,7 @@ function mergeActions(contexts: ContextEntry[]) {
existing.attachments = action.attachments; existing.attachments = action.attachments;
continue; continue;
} }
map.set(action.wallTime, action); map.set(key, action);
} }
} }

View file

@ -147,10 +147,14 @@ test('should reuse context with trace if mode=when-possible', async ({ runInline
expect(trace1.apiNames).toEqual([ expect(trace1.apiNames).toEqual([
'Before Hooks', 'Before Hooks',
'browserType.launch', 'browserType.launch',
'fixture: context',
'fixture: page',
'browserContext.newPage', 'browserContext.newPage',
'page.setContent', 'page.setContent',
'page.click', 'page.click',
'After Hooks', 'After Hooks',
'fixture: page',
'fixture: context',
'tracing.stopChunk', 'tracing.stopChunk',
]); ]);
expect(trace1.traceModel.storage().snapshotsForTest().length).toBeGreaterThan(0); expect(trace1.traceModel.storage().snapshotsForTest().length).toBeGreaterThan(0);
@ -159,11 +163,15 @@ test('should reuse context with trace if mode=when-possible', async ({ runInline
const trace2 = await parseTrace(testInfo.outputPath('test-results', 'reuse-two', 'trace.zip')); const trace2 = await parseTrace(testInfo.outputPath('test-results', 'reuse-two', 'trace.zip'));
expect(trace2.apiNames).toEqual([ expect(trace2.apiNames).toEqual([
'Before Hooks', 'Before Hooks',
'fixture: context',
'fixture: page',
'expect.toBe', 'expect.toBe',
'page.setContent', 'page.setContent',
'page.fill', 'page.fill',
'locator.click', 'locator.click',
'After Hooks', 'After Hooks',
'fixture: page',
'fixture: context',
'tracing.stopChunk', 'tracing.stopChunk',
]); ]);
expect(trace2.traceModel.storage().snapshotsForTest().length).toBeGreaterThan(0); expect(trace2.traceModel.storage().snapshotsForTest().length).toBeGreaterThan(0);

View file

@ -89,16 +89,22 @@ test('should record api trace', async ({ runInlineTest, server }, testInfo) => {
const trace1 = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip')); const trace1 = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
expect(trace1.apiNames).toEqual([ expect(trace1.apiNames).toEqual([
'Before Hooks', 'Before Hooks',
'fixture: request',
'apiRequest.newContext', 'apiRequest.newContext',
'tracing.start', 'tracing.start',
'browserType.launch', 'browserType.launch',
'fixture: context',
'browser.newContext', 'browser.newContext',
'tracing.start', 'tracing.start',
'fixture: page',
'browserContext.newPage', 'browserContext.newPage',
'page.goto', 'page.goto',
'apiRequestContext.get', 'apiRequestContext.get',
'After Hooks', 'After Hooks',
'fixture: page',
'fixture: context',
'browserContext.close', 'browserContext.close',
'fixture: request',
'tracing.stopChunk', 'tracing.stopChunk',
'apiRequestContext.dispose', 'apiRequestContext.dispose',
]); ]);
@ -115,16 +121,22 @@ test('should record api trace', async ({ runInlineTest, server }, testInfo) => {
expect(trace3.apiNames).toEqual([ expect(trace3.apiNames).toEqual([
'Before Hooks', 'Before Hooks',
'tracing.startChunk', 'tracing.startChunk',
'fixture: request',
'apiRequest.newContext', 'apiRequest.newContext',
'tracing.start', 'tracing.start',
'fixture: context',
'browser.newContext', 'browser.newContext',
'tracing.start', 'tracing.start',
'fixture: page',
'browserContext.newPage', 'browserContext.newPage',
'page.goto', 'page.goto',
'apiRequestContext.get', 'apiRequestContext.get',
'expect.toBe', 'expect.toBe',
'After Hooks', 'After Hooks',
'fixture: page',
'fixture: context',
'browserContext.close', 'browserContext.close',
'fixture: request',
'tracing.stopChunk', 'tracing.stopChunk',
'apiRequestContext.dispose', 'apiRequestContext.dispose',
'browser.close', 'browser.close',
@ -317,16 +329,22 @@ test('should not override trace file in afterAll', async ({ runInlineTest, serve
expect(trace1.apiNames).toEqual([ expect(trace1.apiNames).toEqual([
'Before Hooks', 'Before Hooks',
'browserType.launch', 'browserType.launch',
'fixture: context',
'browser.newContext', 'browser.newContext',
'tracing.start', 'tracing.start',
'fixture: page',
'browserContext.newPage', 'browserContext.newPage',
'page.goto', 'page.goto',
'After Hooks', 'After Hooks',
'fixture: page',
'fixture: context',
'browserContext.close', 'browserContext.close',
'afterAll hook', 'afterAll hook',
'fixture: request',
'apiRequest.newContext', 'apiRequest.newContext',
'tracing.start', 'tracing.start',
'apiRequestContext.get', 'apiRequestContext.get',
'fixture: request',
'tracing.stopChunk', 'tracing.stopChunk',
'apiRequestContext.dispose', 'apiRequestContext.dispose',
'browser.close', 'browser.close',

View file

@ -280,17 +280,25 @@ test('should report expect steps', async ({ runInlineTest }) => {
`begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"browser.newContext\",\"category\":\"pw:api\"},{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]},{\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}]}`,
`begin {\"title\":\"expect.not.toHaveTitle\",\"category\":\"expect\"}`, `begin {\"title\":\"expect.not.toHaveTitle\",\"category\":\"expect\"}`,
`end {\"title\":\"expect.not.toHaveTitle\",\"category\":\"expect\"}`, `end {\"title\":\"expect.not.toHaveTitle\",\"category\":\"expect\"}`,
`begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserContext.close\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"fixture: page\",\"category\":\"fixture\"},{\"title\":\"fixture: context\",\"category\":\"fixture\"},{\"title\":\"browserContext.close\",\"category\":\"pw:api\"}]}`,
]); ]);
}); });
@ -341,13 +349,19 @@ test('should report api steps', async ({ runInlineTest }) => {
`begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`end {\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"fixture: request\",\"category\":\"fixture\"}`,
`begin {\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}`, `begin {\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}`, `end {\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"browser.newContext\",\"category\":\"pw:api\"},{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"},{\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"fixture: request\",\"category\":\"fixture\",\"steps\":[{\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}]}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]},{\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]},{\"title\":\"fixture: request\",\"category\":\"fixture\",\"steps\":[{\"title\":\"apiRequest.newContext\",\"category\":\"pw:api\"}]}]}`,
`begin {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\"}`, `begin {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}`, `begin {\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}`,
`end {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\",\"steps\":[{\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\",\"steps\":[{\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}]}`,
@ -361,11 +375,17 @@ test('should report api steps', async ({ runInlineTest }) => {
`begin {"title":"apiRequestContext.get(http://localhost2)","category":"pw:api"}`, `begin {"title":"apiRequestContext.get(http://localhost2)","category":"pw:api"}`,
`end {"title":"apiRequestContext.get(http://localhost2)","category":"pw:api","error":{"message":"<message>","stack":"<stack>","location":"<location>","snippet":"<snippet>"}}`, `end {"title":"apiRequestContext.get(http://localhost2)","category":"pw:api","error":{"message":"<message>","stack":"<stack>","location":"<location>","snippet":"<snippet>"}}`,
`begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"fixture: request\",\"category\":\"fixture\"}`,
`begin {\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"}`, `begin {\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"}`,
`end {\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"}`, `end {\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"}`,
`end {\"title\":\"fixture: request\",\"category\":\"fixture\",\"steps\":[{\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"},{\"title\":\"browserContext.close\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"fixture: request\",\"category\":\"fixture\",\"steps\":[{\"title\":\"apiRequestContext.dispose\",\"category\":\"pw:api\"}]},{\"title\":\"fixture: page\",\"category\":\"fixture\"},{\"title\":\"fixture: context\",\"category\":\"fixture\"},{\"title\":\"browserContext.close\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"beforeAll hook\",\"category\":\"hook\"}`, `begin {\"title\":\"beforeAll hook\",\"category\":\"hook\"}`,
`begin {\"title\":\"browser.newPage\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browser.newPage\",\"category\":\"pw:api\"}`,
@ -414,21 +434,29 @@ test('should report api step failure', async ({ runInlineTest }) => {
`begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"Before Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"browser.newContext\",\"category\":\"pw:api\"},{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]},{\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}]}`,
`begin {\"title\":\"page.setContent\",\"category\":\"pw:api\"}`, `begin {\"title\":\"page.setContent\",\"category\":\"pw:api\"}`,
`end {\"title\":\"page.setContent\",\"category\":\"pw:api\"}`, `end {\"title\":\"page.setContent\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"page.click(input)\",\"category\":\"pw:api\"}`, `begin {\"title\":\"page.click(input)\",\"category\":\"pw:api\"}`,
`end {\"title\":\"page.click(input)\",\"category\":\"pw:api\",\"error\":{\"message\":\"page.click: Timeout 1ms exceeded.\\n=========================== logs ===========================\\nwaiting for locator('input')\\n============================================================\",\"stack\":\"<stack>\",\"location\":\"<location>\",\"snippet\":\"<snippet>\"}}`, `end {\"title\":\"page.click(input)\",\"category\":\"pw:api\",\"error\":{\"message\":\"page.click: Timeout 1ms exceeded.\\n=========================== logs ===========================\\nwaiting for locator('input')\\n============================================================\",\"stack\":\"<stack>\",\"location\":\"<location>\",\"snippet\":\"<snippet>\"}}`,
`begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`, `begin {\"title\":\"After Hooks\",\"category\":\"hook\"}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserContext.close\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"browser.close\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browser.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browser.close\",\"category\":\"pw:api\"}`, `end {\"title\":\"browser.close\",\"category\":\"pw:api\"}`,
`end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserContext.close\",\"category\":\"pw:api\"},{\"title\":\"browser.close\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"fixture: page\",\"category\":\"fixture\"},{\"title\":\"fixture: context\",\"category\":\"fixture\"},{\"title\":\"browserContext.close\",\"category\":\"pw:api\"},{\"title\":\"browser.close\",\"category\":\"pw:api\"}]}`,
]); ]);
}); });
@ -480,19 +508,27 @@ test('should show nice stacks for locators', async ({ runInlineTest }) => {
`begin {"title":"Before Hooks","category":"hook"}`, `begin {"title":"Before Hooks","category":"hook"}`,
`begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`, `end {\"title\":\"browserType.launch\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `begin {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`, `end {\"title\":\"browser.newContext\",\"category\":\"pw:api\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {"title":"browserContext.newPage","category":"pw:api"}`, `begin {"title":"browserContext.newPage","category":"pw:api"}`,
`end {"title":"browserContext.newPage","category":"pw:api"}`, `end {"title":"browserContext.newPage","category":"pw:api"}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"browser.newContext\",\"category\":\"pw:api\"},{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`, `end {\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}`,
`end {\"title\":\"Before Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"browserType.launch\",\"category\":\"pw:api\"},{\"title\":\"fixture: context\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browser.newContext\",\"category\":\"pw:api\"}]},{\"title\":\"fixture: page\",\"category\":\"fixture\",\"steps\":[{\"title\":\"browserContext.newPage\",\"category\":\"pw:api\"}]}]}`,
`begin {"title":"page.setContent","category":"pw:api"}`, `begin {"title":"page.setContent","category":"pw:api"}`,
`end {"title":"page.setContent","category":"pw:api"}`, `end {"title":"page.setContent","category":"pw:api"}`,
`begin {"title":"locator.evaluate(button)","category":"pw:api"}`, `begin {"title":"locator.evaluate(button)","category":"pw:api"}`,
`end {"title":"locator.evaluate(button)","category":"pw:api"}`, `end {"title":"locator.evaluate(button)","category":"pw:api"}`,
`begin {"title":"After Hooks","category":"hook"}`, `begin {"title":"After Hooks","category":"hook"}`,
`begin {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: page\",\"category\":\"fixture\"}`,
`begin {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`end {\"title\":\"fixture: context\",\"category\":\"fixture\"}`,
`begin {"title":"browserContext.close","category":"pw:api"}`, `begin {"title":"browserContext.close","category":"pw:api"}`,
`end {"title":"browserContext.close","category":"pw:api"}`, `end {"title":"browserContext.close","category":"pw:api"}`,
`end {"title":"After Hooks","category":"hook","steps":[{"title":"browserContext.close","category":"pw:api"}]}`, `end {\"title\":\"After Hooks\",\"category\":\"hook\",\"steps\":[{\"title\":\"fixture: page\",\"category\":\"fixture\"},{\"title\":\"fixture: context\",\"category\":\"fixture\"},{\"title\":\"browserContext.close\",\"category\":\"pw:api\"}]}`,
]); ]);
}); });

View file

@ -100,12 +100,24 @@ test('should report api step hierarchy', async ({ runInlineTest }) => {
title: 'browserType.launch', title: 'browserType.launch',
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browser.newContext', title: 'fixture: context',
steps: [
{
category: 'pw:api',
title: 'browser.newContext',
},
]
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browserContext.newPage', title: 'fixture: page',
steps: [
{
category: 'pw:api',
title: 'browserContext.newPage',
},
]
}, },
], ],
}, },
@ -171,6 +183,14 @@ test('should report api step hierarchy', async ({ runInlineTest }) => {
category: 'hook', category: 'hook',
title: 'After Hooks', title: 'After Hooks',
steps: [ steps: [
{
category: 'fixture',
title: 'fixture: page',
},
{
category: 'fixture',
title: 'fixture: context',
},
{ {
category: 'pw:api', category: 'pw:api',
title: 'browserContext.close', title: 'browserContext.close',
@ -255,12 +275,24 @@ test('should not report nested after hooks', async ({ runInlineTest }) => {
title: 'browserType.launch', title: 'browserType.launch',
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browser.newContext', title: 'fixture: context',
steps: [
{
category: 'pw:api',
title: 'browser.newContext',
},
]
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browserContext.newPage', title: 'fixture: page',
steps: [
{
category: 'pw:api',
title: 'browserContext.newPage',
},
]
}, },
], ],
}, },
@ -277,6 +309,14 @@ test('should not report nested after hooks', async ({ runInlineTest }) => {
category: 'hook', category: 'hook',
title: 'After Hooks', title: 'After Hooks',
steps: [ steps: [
{
category: 'fixture',
title: 'fixture: page',
},
{
category: 'fixture',
title: 'fixture: context',
},
{ {
category: 'pw:api', category: 'pw:api',
title: 'browserContext.close', title: 'browserContext.close',
@ -332,16 +372,20 @@ test('should report test.step from fixtures', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
expect(result.outputLines).toEqual([ expect(result.outputLines).toEqual([
`begin Before Hooks`, `begin Before Hooks`,
`begin fixture: foo`,
`begin setup foo`, `begin setup foo`,
`end setup foo`, `end setup foo`,
`end fixture: foo`,
`end Before Hooks`, `end Before Hooks`,
`begin test step`, `begin test step`,
`begin inside foo`, `begin inside foo`,
`end inside foo`, `end inside foo`,
`end test step`, `end test step`,
`begin After Hooks`, `begin After Hooks`,
`begin fixture: foo`,
`begin teardown foo`, `begin teardown foo`,
`end teardown foo`, `end teardown foo`,
`end fixture: foo`,
`end After Hooks`, `end After Hooks`,
]); ]);
}); });
@ -374,12 +418,24 @@ test('should report expect step locations', async ({ runInlineTest }) => {
title: 'browserType.launch', title: 'browserType.launch',
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browser.newContext', title: 'fixture: context',
steps: [
{
category: 'pw:api',
title: 'browser.newContext',
},
]
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browserContext.newPage', title: 'fixture: page',
steps: [
{
category: 'pw:api',
title: 'browserContext.newPage',
},
]
}, },
], ],
}, },
@ -396,6 +452,14 @@ test('should report expect step locations', async ({ runInlineTest }) => {
category: 'hook', category: 'hook',
title: 'After Hooks', title: 'After Hooks',
steps: [ steps: [
{
category: 'fixture',
title: 'fixture: page',
},
{
category: 'fixture',
title: 'fixture: context',
},
{ {
category: 'pw:api', category: 'pw:api',
title: 'browserContext.close', title: 'browserContext.close',
@ -622,13 +686,25 @@ test('should nest steps based on zones', async ({ runInlineTest }) => {
category: 'pw:api' category: 'pw:api'
}, },
{ {
category: 'pw:api', category: 'fixture',
title: 'browser.newContext', title: 'fixture: context',
steps: [
{
category: 'pw:api',
title: 'browser.newContext',
},
]
}, },
{ {
title: 'browserContext.newPage', category: 'fixture',
category: 'pw:api' title: 'fixture: page',
} steps: [
{
category: 'pw:api',
title: 'browserContext.newPage',
},
]
},
] ]
}, },
{ {
@ -700,6 +776,14 @@ test('should nest steps based on zones', async ({ runInlineTest }) => {
], ],
location: { file: 'a.test.ts', line: 'number', column: 'number' } location: { file: 'a.test.ts', line: 'number', column: 'number' }
}, },
{
category: 'fixture',
title: 'fixture: page',
},
{
category: 'fixture',
title: 'fixture: context',
},
{ {
title: 'browserContext.close', title: 'browserContext.close',
category: 'pw:api' category: 'pw:api'
@ -856,8 +940,26 @@ test('should nest page.continue insize page.goto steps', async ({ runInlineTest
category: 'hook', category: 'hook',
steps: [ steps: [
{ title: 'browserType.launch', category: 'pw:api' }, { title: 'browserType.launch', category: 'pw:api' },
{ title: 'browser.newContext', category: 'pw:api' }, {
{ title: 'browserContext.newPage', category: 'pw:api' }, category: 'fixture',
title: 'fixture: context',
steps: [
{
category: 'pw:api',
title: 'browser.newContext',
},
]
},
{
category: 'fixture',
title: 'fixture: page',
steps: [
{
category: 'pw:api',
title: 'browserContext.newPage',
},
]
},
], ],
}, },
{ {
@ -881,6 +983,14 @@ test('should nest page.continue insize page.goto steps', async ({ runInlineTest
title: 'After Hooks', title: 'After Hooks',
category: 'hook', category: 'hook',
steps: [ steps: [
{
category: 'fixture',
title: 'fixture: page',
},
{
category: 'fixture',
title: 'fixture: context',
},
{ title: 'browserContext.close', category: 'pw:api' }, { title: 'browserContext.close', category: 'pw:api' },
], ],
}, },

View file

@ -208,10 +208,14 @@ test('should report toHaveScreenshot step with expectation name in title', async
expect(result.outputLines).toEqual([ expect(result.outputLines).toEqual([
`end browserType.launch`, `end browserType.launch`,
`end browser.newContext`, `end browser.newContext`,
`end fixture: context`,
`end browserContext.newPage`, `end browserContext.newPage`,
`end fixture: page`,
`end Before Hooks`, `end Before Hooks`,
`end expect.toHaveScreenshot(foo.png)`, `end expect.toHaveScreenshot(foo.png)`,
`end expect.toHaveScreenshot(is-a-test-1.png)`, `end expect.toHaveScreenshot(is-a-test-1.png)`,
`end fixture: page`,
`end fixture: context`,
`end browserContext.close`, `end browserContext.close`,
`end After Hooks`, `end After Hooks`,
]); ]);

View file

@ -108,6 +108,8 @@ test('should update trace live', async ({ runUITest, server }) => {
/page.gotohttp:\/\/localhost:\d+\/one.html/, /page.gotohttp:\/\/localhost:\d+\/one.html/,
/page.gotohttp:\/\/localhost:\d+\/two.html/, /page.gotohttp:\/\/localhost:\d+\/two.html/,
/After Hooks[\d.]+m?s/, /After Hooks[\d.]+m?s/,
/fixture: page[\d.]+m?s/,
/fixture: context[\d.]+m?s/,
/browserContext.close[\d.]+m?s/, /browserContext.close[\d.]+m?s/,
]); ]);
}); });

View file

@ -45,8 +45,9 @@ test('should merge trace events', async ({ runUITest, server }) => {
/locator.clickgetByRole\('button'\)[\d.]+m?s/, /locator.clickgetByRole\('button'\)[\d.]+m?s/,
/expect.toBe[\d.]+m?s/, /expect.toBe[\d.]+m?s/,
/After Hooks[\d.]+m?s/, /After Hooks[\d.]+m?s/,
/fixture: page[\d.]+m?s/,
/fixture: context[\d.]+m?s/,
/browserContext.close[\d.]+m?s/, /browserContext.close[\d.]+m?s/,
]); ]);
}); });
@ -73,6 +74,8 @@ test('should merge web assertion events', async ({ runUITest }, testInfo) => {
/page.setContent[\d.]+m?s/, /page.setContent[\d.]+m?s/,
/expect.toBeVisiblelocator\('button'\)[\d.]+m?s/, /expect.toBeVisiblelocator\('button'\)[\d.]+m?s/,
/After Hooks[\d.]+m?s/, /After Hooks[\d.]+m?s/,
/fixture: page[\d.]+m?s/,
/fixture: context[\d.]+m?s/,
/browserContext.close[\d.]+m?s/, /browserContext.close[\d.]+m?s/,
]); ]);
}); });
@ -148,6 +151,8 @@ test('should show snapshots for sync assertions', async ({ runUITest, server })
/locator\.clickgetByRole\('button'\)[\d.]+m?s/, /locator\.clickgetByRole\('button'\)[\d.]+m?s/,
/expect\.toBe[\d.]+m?s/, /expect\.toBe[\d.]+m?s/,
/After Hooks[\d.]+m?s/, /After Hooks[\d.]+m?s/,
/fixture: page[\d.]+m?s/,
/fixture: context[\d.]+m?s/,
/browserContext.close[\d.]+m?s/, /browserContext.close[\d.]+m?s/,
]); ]);