fix(test runner): revert error location from top frame (#11250)

This does not play nicely with some internal Playwright errors, so
it needs more tweaks.
This commit is contained in:
Dmitry Gozman 2022-01-07 11:06:47 -08:00 committed by GitHub
parent 303d49e8de
commit 058f98d3dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 22 additions and 17 deletions

View File

@ -346,18 +346,17 @@ export function formatError(config: FullConfig, error: TestError, highlightCode:
const tokens = ['']; const tokens = [''];
let location: Location | undefined; let location: Location | undefined;
if (stack) { if (stack) {
const parsed = prepareErrorStack(stack); const parsed = prepareErrorStack(stack, file);
tokens.push(parsed.message); tokens.push(parsed.message);
location = parsed.location; location = parsed.location;
if (location) { if (location) {
try { try {
// Stack will have /private/var/folders instead of /var/folders on Mac. const source = fs.readFileSync(location.file, 'utf8');
const realFile = fs.realpathSync(location.file);
const source = fs.readFileSync(realFile, 'utf8');
const codeFrame = codeFrameColumns(source, { start: location }, { highlightCode }); const codeFrame = codeFrameColumns(source, { start: location }, { highlightCode });
if (!file || file !== realFile) { // Convert /var/folders to /private/var/folders on Mac.
if (!file || fs.realpathSync(file) !== location.file) {
tokens.push(''); tokens.push('');
tokens.push(colors.gray(` at `) + `${relativeFilePath(config, realFile)}:${location.line}`); tokens.push(colors.gray(` at `) + `${relativeFilePath(config, location.file)}:${location.line}`);
} }
tokens.push(''); tokens.push('');
tokens.push(codeFrame); tokens.push(codeFrame);
@ -388,11 +387,15 @@ function indent(lines: string, tab: string) {
return lines.replace(/^(?=.+$)/gm, tab); return lines.replace(/^(?=.+$)/gm, tab);
} }
export function prepareErrorStack(stack: string): { export function prepareErrorStack(stack: string, file?: string): {
message: string; message: string;
stackLines: string[]; stackLines: string[];
location?: Location; location?: Location;
} { } {
if (file) {
// Stack will have /private/var/folders instead of /var/folders on Mac.
file = fs.realpathSync(file);
}
const lines = stack.split('\n'); const lines = stack.split('\n');
let firstStackLine = lines.findIndex(line => line.startsWith(' at ')); let firstStackLine = lines.findIndex(line => line.startsWith(' at '));
if (firstStackLine === -1) if (firstStackLine === -1)
@ -402,8 +405,11 @@ export function prepareErrorStack(stack: string): {
let location: Location | undefined; let location: Location | undefined;
for (const line of stackLines) { for (const line of stackLines) {
const parsed = stackUtils.parseLine(line); const parsed = stackUtils.parseLine(line);
if (parsed && parsed.file) { if (!parsed || !parsed.file)
location = { file: path.join(process.cwd(), parsed.file), column: parsed.column || 0, line: parsed.line || 0 }; continue;
const resolvedFile = path.join(process.cwd(), parsed.file);
if (!file || resolvedFile === file) {
location = { file: resolvedFile, column: parsed.column || 0, line: parsed.line || 0 };
break; break;
} }
} }

View File

@ -229,12 +229,12 @@ class JSONReporter implements Reporter {
annotations: test.annotations, annotations: test.annotations,
expectedStatus: test.expectedStatus, expectedStatus: test.expectedStatus,
projectName: test.titlePath()[1], projectName: test.titlePath()[1],
results: test.results.map(r => this._serializeTestResult(r)), results: test.results.map(r => this._serializeTestResult(r, test)),
status: test.outcome(), status: test.outcome(),
}; };
} }
private _serializeTestResult(result: TestResult): JSONReportTestResult { private _serializeTestResult(result: TestResult, test: TestCase): JSONReportTestResult {
const steps = result.steps.filter(s => s.category === 'test.step'); const steps = result.steps.filter(s => s.category === 'test.step');
const jsonResult: JSONReportTestResult = { const jsonResult: JSONReportTestResult = {
workerIndex: result.workerIndex, workerIndex: result.workerIndex,
@ -253,7 +253,7 @@ class JSONReporter implements Reporter {
})), })),
}; };
if (result.error?.stack) if (result.error?.stack)
jsonResult.errorLocation = prepareErrorStack(result.error.stack).location; jsonResult.errorLocation = prepareErrorStack(result.error.stack, test.location.file).location;
return jsonResult; return jsonResult;
} }

View File

@ -86,7 +86,7 @@ test('should print an error in a codeframe', async ({ runInlineTest }) => {
expect(result.output).toContain(`> 7 | const error = new Error('my-message');`); expect(result.output).toContain(`> 7 | const error = new Error('my-message');`);
}); });
test('should print codeframe from a helper', async ({ runInlineTest }) => { test('should not print codeframe from a helper', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
'helper.ts': ` 'helper.ts': `
export function ohMy() { export function ohMy() {
@ -106,10 +106,9 @@ test('should print codeframe from a helper', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(1); expect(result.exitCode).toBe(1);
expect(result.failed).toBe(1); expect(result.failed).toBe(1);
expect(result.output).toContain('Error: oh my'); expect(result.output).toContain('Error: oh my');
expect(result.output).toContain(` at helper.ts:5`); expect(result.output).toContain(` 7 | test('foobar', async ({}) => {`);
expect(result.output).toContain(` 4 | export function ohMy() {`); expect(result.output).toContain(`> 8 | ohMy();`);
expect(result.output).toContain(`> 5 | throw new Error('oh my');`); expect(result.output).toContain(` | ^`);
expect(result.output).toContain(` | ^`);
}); });
test('should print slow tests', async ({ runInlineTest }) => { test('should print slow tests', async ({ runInlineTest }) => {