chore: provide error context to test server (#35339)
This commit is contained in:
parent
3d2aa1a5e4
commit
87bc0f48ef
|
@ -22,7 +22,7 @@ import { setBoxedStackPrefixes, asLocator, createGuid, currentZone, debugMode, i
|
|||
|
||||
import { currentTestInfo } from './common/globals';
|
||||
import { rootTestType } from './common/testType';
|
||||
import { attachErrorPrompts } from './prompt';
|
||||
import { attachErrorContext, attachErrorPrompts } from './prompt';
|
||||
|
||||
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, VideoMode } from '../types/test';
|
||||
import type { ContextReuseMode } from './common/config';
|
||||
|
@ -61,6 +61,7 @@ type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
|
|||
_optionContextReuseMode: ContextReuseMode,
|
||||
_optionConnectOptions: PlaywrightWorkerOptions['connectOptions'],
|
||||
_reuseContext: boolean,
|
||||
_optionAttachErrorContext: boolean,
|
||||
};
|
||||
|
||||
const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||
|
@ -245,13 +246,13 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
playwright._defaultContextNavigationTimeout = undefined;
|
||||
}, { auto: 'all-hooks-included', title: 'context configuration', box: true } as any],
|
||||
|
||||
_setupArtifacts: [async ({ playwright, screenshot }, use, testInfo) => {
|
||||
_setupArtifacts: [async ({ playwright, screenshot, _optionAttachErrorContext }, use, testInfo) => {
|
||||
// This fixture has a separate zero-timeout slot to ensure that artifact collection
|
||||
// happens even after some fixtures or hooks time out.
|
||||
// Now that default test timeout is known, we can replace zero with an actual value.
|
||||
testInfo.setTimeout(testInfo.project.timeout);
|
||||
|
||||
const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot);
|
||||
const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot, _optionAttachErrorContext);
|
||||
await artifactsRecorder.willStartTest(testInfo as TestInfoImpl);
|
||||
|
||||
const tracingGroupSteps: TestStepInternal[] = [];
|
||||
|
@ -393,6 +394,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
|||
|
||||
_optionContextReuseMode: ['none', { scope: 'worker', option: true }],
|
||||
_optionConnectOptions: [undefined, { scope: 'worker', option: true }],
|
||||
_optionAttachErrorContext: [false, { scope: 'worker', option: true }],
|
||||
|
||||
_reuseContext: [async ({ video, _optionContextReuseMode }, use) => {
|
||||
let mode = _optionContextReuseMode;
|
||||
|
@ -621,10 +623,12 @@ class ArtifactsRecorder {
|
|||
private _screenshotRecorder: SnapshotRecorder;
|
||||
private _pageSnapshot: string | undefined;
|
||||
private _sourceCache: Map<string, string> = new Map();
|
||||
private _attachErrorContext: boolean;
|
||||
|
||||
constructor(playwright: PlaywrightImpl, artifactsDir: string, screenshot: ScreenshotOption) {
|
||||
constructor(playwright: PlaywrightImpl, artifactsDir: string, screenshot: ScreenshotOption, attachErrorContext: boolean) {
|
||||
this._playwright = playwright;
|
||||
this._artifactsDir = artifactsDir;
|
||||
this._attachErrorContext = attachErrorContext;
|
||||
const screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
|
||||
this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts');
|
||||
|
||||
|
@ -703,7 +707,10 @@ class ArtifactsRecorder {
|
|||
})));
|
||||
|
||||
await this._screenshotRecorder.persistTemporary();
|
||||
await attachErrorPrompts(this._testInfo, this._sourceCache, this._pageSnapshot);
|
||||
if (this._attachErrorContext)
|
||||
await attachErrorContext(this._testInfo, this._pageSnapshot);
|
||||
else
|
||||
await attachErrorPrompts(this._testInfo, this._sourceCache, this._pageSnapshot);
|
||||
}
|
||||
|
||||
private async _startTraceChunkOnContextCreation(tracing: Tracing) {
|
||||
|
|
|
@ -102,6 +102,7 @@ export interface TestServerInterface {
|
|||
projects?: string[];
|
||||
reuseContext?: boolean;
|
||||
connectWsEndpoint?: string;
|
||||
attachErrorContext?: boolean;
|
||||
}): Promise<{
|
||||
status: reporterTypes.FullResult['status'];
|
||||
}>;
|
||||
|
|
|
@ -127,6 +127,19 @@ export async function attachErrorPrompts(testInfo: TestInfo, sourceCache: Map<st
|
|||
}
|
||||
}
|
||||
|
||||
export async function attachErrorContext(testInfo: TestInfo, ariaSnapshot: string | undefined) {
|
||||
if (!ariaSnapshot)
|
||||
return;
|
||||
|
||||
(testInfo as TestInfoImpl)._attach({
|
||||
name: `_error-context`,
|
||||
contentType: 'application/json',
|
||||
body: Buffer.from(JSON.stringify({
|
||||
pageSnapshot: ariaSnapshot,
|
||||
})),
|
||||
}, undefined);
|
||||
}
|
||||
|
||||
async function loadSource(file: string, sourceCache: Map<string, string>) {
|
||||
let source = sourceCache.get(file);
|
||||
if (!source) {
|
||||
|
|
|
@ -314,6 +314,7 @@ export class TestServerDispatcher implements TestServerInterface {
|
|||
...(params.headed !== undefined ? { headless: !params.headed } : {}),
|
||||
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
||||
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined,
|
||||
_optionAttachErrorContext: params.attachErrorContext,
|
||||
},
|
||||
...(params.updateSnapshots ? { updateSnapshots: params.updateSnapshots } : {}),
|
||||
...(params.updateSourceMethod ? { updateSourceMethod: params.updateSourceMethod } : {}),
|
||||
|
|
|
@ -355,3 +355,26 @@ test('should report parallelIndex', async ({ runInlineTest }, testInfo) => {
|
|||
expect(result.report.suites[0].specs[1].tests[0].results[0].parallelIndex).toBe(1);
|
||||
expect(result.report.suites[0].specs[2].tests[0].results[0].parallelIndex).toBe(1);
|
||||
});
|
||||
|
||||
test('attaches error context', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
export default { use: { _optionAttachErrorContext: true } };
|
||||
`,
|
||||
'a.test.js': `
|
||||
const { test, expect } = require('@playwright/test');
|
||||
test('one', async ({ page }, testInfo) => {
|
||||
await page.setContent('<button>Click me</button>');
|
||||
throw new Error('kaboom');
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'json' });
|
||||
|
||||
const errorContext = result.report.suites[0].specs[0].tests[0].results[0].attachments.find(a => a.name === '_error-context');
|
||||
expect(errorContext).toBeDefined();
|
||||
expect(errorContext!.contentType).toBe('application/json');
|
||||
const json = JSON.parse(Buffer.from(errorContext!.body, 'base64').toString('utf-8'));
|
||||
expect(json).toEqual({
|
||||
pageSnapshot: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue