fix(reporters): error context exception with page.evaluate (#35670)

This commit is contained in:
Max Schmitt 2025-04-23 08:55:38 +02:00 committed by GitHub
parent 5030e97303
commit c454681d05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 28 deletions

View File

@ -24,6 +24,7 @@ import { codeFrameColumns } from './transform/babelBundle';
import type { MetadataWithCommitInfo } from './isomorphic/types';
import type { TestInfoImpl } from './worker/testInfo';
import type { Location } from '../types/test';
export async function attachErrorContext(testInfo: TestInfoImpl, format: 'markdown' | 'json', sourceCache: Map<string, string>, ariaSnapshot: string | undefined) {
if (format === 'json') {
@ -91,31 +92,32 @@ export async function attachErrorContext(testInfo: TestInfoImpl, format: 'markdo
const parsedError = error.stack ? parseErrorStack(error.stack, path.sep) : undefined;
const inlineMessage = stripAnsiEscapes(parsedError?.message || error.message || '').split('\n')[0];
const location = parsedError?.location || { file: testInfo.file, line: testInfo.line, column: testInfo.column };
const source = await loadSource(location.file, sourceCache);
const codeFrame = codeFrameColumns(
source,
{
start: {
line: location.line,
column: location.column
const loadedSource = await loadSource(parsedError?.location, testInfo, sourceCache);
if (loadedSource) {
const codeFrame = codeFrameColumns(
loadedSource.source,
{
start: {
line: loadedSource.location.line,
column: loadedSource.location.column
},
},
},
{
highlightCode: false,
linesAbove: 100,
linesBelow: 100,
message: inlineMessage || undefined,
}
);
lines.push(
'',
'# Test source',
'',
'```ts',
codeFrame,
'```',
);
{
highlightCode: false,
linesAbove: 100,
linesBelow: 100,
message: inlineMessage || undefined,
}
);
lines.push(
'',
'# Test source',
'',
'```ts',
codeFrame,
'```',
);
}
if (metadata.gitDiff) {
lines.push(
@ -139,12 +141,33 @@ export async function attachErrorContext(testInfo: TestInfoImpl, format: 'markdo
}
}
async function loadSource(file: string, sourceCache: Map<string, string>) {
async function loadSource(
errorLocation: Location | undefined,
testLocation: Location,
sourceCache: Map<string, string>
): Promise<{ location: Location, source: string } | undefined> {
if (errorLocation) {
const source = await loadSourceCached(errorLocation.file, sourceCache);
if (source)
return { location: errorLocation, source };
}
// If the error location is not available on the disk (e.g. fake page.evaluate in-browser error), then fallback to the test file.
const source = await loadSourceCached(testLocation.file, sourceCache);
if (source)
return { location: testLocation, source };
return undefined;
}
async function loadSourceCached(file: string, sourceCache: Map<string, string>): Promise<string | undefined> {
let source = sourceCache.get(file);
if (!source) {
// A mild race is Ok here.
source = await fs.readFile(file, 'utf8');
sourceCache.set(file, source);
try {
// A mild race is Ok here.
source = await fs.readFile(file, 'utf8');
sourceCache.set(file, source);
} catch (e) {
// Ignore errors.
}
}
return source;
}

View File

@ -15,6 +15,7 @@
*/
import path from 'path';
import fs from 'fs';
import { test, expect } from './playwright-test-fixtures';
for (const useIntermediateMergeReport of [false, true] as const) {
@ -206,5 +207,26 @@ for (const useIntermediateMergeReport of [false, true] as const) {
expect(text).toContain(`Error Context: ${path.join('test-results', 'a-one', 'error-context.md')}`);
expect(result.exitCode).toBe(1);
});
test('should show error context if exception contains non-existent file', async ({ runInlineTest, useIntermediateMergeReport }) => {
const result = await runInlineTest({
'a.test.js': `
const { test, expect } = require('@playwright/test');
test('one', async ({ page }) => {
await page.evaluate(() => {
throw new Error('error');
});
});
`,
}, { reporter: 'line' });
if (useIntermediateMergeReport)
expect(result.output).toContain(`Error Context: ${path.join('blob-report', 'resources')}`);
else
expect(result.output).toContain(`Error Context: ${path.join('test-results', 'a-one', 'error-context.md')}`);
const file = /Error Context: (.*)/.exec(result.output)?.[1];
const content = await fs.promises.readFile(path.join(result.report.config.rootDir, file), 'utf8');
expect(content).toContain('^ Error: page.evaluate: Error: error');
expect(result.exitCode).toBe(1);
});
});
}