chore: nest api steps by time (#22801)

Fixes https://github.com/microsoft/playwright/issues/22786
This commit is contained in:
Pavel Feldman 2023-05-03 16:04:20 -07:00 committed by GitHub
parent 7fce2df391
commit 9a61a55ea1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 13 deletions

View file

@ -278,6 +278,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
category: 'pw:api',
title: apiCall,
wallTime,
laxParent: true,
});
userData.userObject = step;
},

View file

@ -34,6 +34,8 @@ interface TestStepInternal {
wallTime: number;
location?: Location;
steps: TestStepInternal[];
laxParent?: boolean;
endWallTime?: number;
error?: TestInfoError;
}
@ -210,16 +212,31 @@ export class TestInfoImpl implements TestInfo {
_addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps'>): TestStepInternal {
const stepId = `${data.category}@${data.title}@${++this._lastStepId}`;
const parentStep = zones.zoneData<TestStepInternal>('stepZone', captureRawStack());
let callbackHandled = false;
let parentStep = zones.zoneData<TestStepInternal>('stepZone', captureRawStack());
// For out-of-stack calls, locate the enclosing step.
let isLaxParent = false;
if (!parentStep && data.laxParent) {
const visit = (step: TestStepInternal) => {
// Never nest into under another lax element, it could be a series
// of no-reply actions, ala page.continue().
if (!step.endWallTime && step.category === data.category && !step.laxParent)
parentStep = step;
step.steps.forEach(visit);
};
this._steps.forEach(visit);
isLaxParent = !!parentStep;
}
const step: TestStepInternal = {
stepId,
...data,
laxParent: isLaxParent,
steps: [],
complete: result => {
if (callbackHandled)
if (step.endWallTime)
return;
callbackHandled = true;
step.endWallTime = Date.now();
let error: TestInfoError | undefined;
if (result.error instanceof Error) {
// Step function threw an error.
@ -236,7 +253,7 @@ export class TestInfoImpl implements TestInfo {
const payload: StepEndPayload = {
testId: this._test.id,
stepId,
wallTime: Date.now(),
wallTime: step.endWallTime,
error,
};
this._onStepEnd(payload);

View file

@ -348,7 +348,7 @@ test('should report api steps', async ({ runInlineTest }) => {
`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\"}]}`,
`begin {\"title\":\"page.waitForNavigation\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"page.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}`,
`end {\"title\":\"page.waitForNavigation\",\"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.goto(data:text/html,<button></button>)\",\"category\":\"pw:api\"}`,
`begin {\"title\":\"page.click(button)\",\"category\":\"pw:api\"}`,
`end {\"title\":\"page.click(button)\",\"category\":\"pw:api\"}`,

View file

@ -89,7 +89,7 @@ test('should report api step hierarchy', async ({ runInlineTest }) => {
}, { reporter: '', workers: 1 });
expect(result.exitCode).toBe(0);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
category: 'hook',
@ -199,7 +199,7 @@ test('should report before hooks step error', async ({ runInlineTest }) => {
}, { reporter: '', workers: 1 });
expect(result.exitCode).toBe(1);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
category: 'hook',
@ -244,7 +244,7 @@ test('should not report nested after hooks', async ({ runInlineTest }) => {
}, { reporter: '', workers: 1, timeout: 2000 });
expect(result.exitCode).toBe(1);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
category: 'hook',
@ -363,7 +363,7 @@ test('should report expect step locations', async ({ runInlineTest }) => {
}, { reporter: '', workers: 1 });
expect(result.exitCode).toBe(0);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
category: 'hook',
@ -441,7 +441,7 @@ test('should report custom expect steps', async ({ runInlineTest }) => {
}, { reporter: '', workers: 1 });
expect(result.exitCode).toBe(0);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
category: 'hook',
@ -509,7 +509,7 @@ test('should mark step as failed when soft expect fails', async ({ runInlineTest
}, { reporter: '', workers: 1 });
expect(result.exitCode).toBe(1);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{ title: 'Before Hooks', category: 'hook' },
{
@ -750,7 +750,7 @@ test('should not mark page.close as failed when page.click fails', async ({ runI
}, { reporter: '' });
expect(result.exitCode).toBe(1);
const objects = result.output.split('\n').filter(line => line.startsWith('%% ')).map(line => line.substring(3).trim()).filter(Boolean).map(line => JSON.parse(line));
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
category: 'hook',
@ -834,3 +834,55 @@ test('should not mark page.close as failed when page.click fails', async ({ runI
},
]);
});
test('should nest page.continue insize page.goto steps', async ({ runInlineTest }) => {
const result = await runInlineTest({
'reporter.ts': stepHierarchyReporter,
'playwright.config.ts': `module.exports = { reporter: './reporter', };`,
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('pass', async ({ page }) => {
await page.route('**/*', route => route.fulfill('<html></html>'));
await page.goto('http://localhost:1234');
});
`
}, { reporter: '' });
expect(result.exitCode).toBe(0);
const objects = result.outputLines.map(line => JSON.parse(line));
expect(objects).toEqual([
{
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: 'page.route',
category: 'pw:api',
location: { file: 'a.test.ts', line: 'number', column: 'number' },
},
{
title: 'page.goto(http://localhost:1234)',
category: 'pw:api',
location: { file: 'a.test.ts', line: 'number', column: 'number' },
steps: [
{
title: 'route.fulfill',
category: 'pw:api',
location: { file: 'a.test.ts', line: 'number', column: 'number' },
},
]
},
{
title: 'After Hooks',
category: 'hook',
steps: [
{ title: 'browserContext.close', category: 'pw:api' },
],
},
]);
});