Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(@jest/reporters): improve annotation formatting of GitHubActionsReporter #12826

Merged
merged 22 commits into from May 9, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions e2e/__tests__/__snapshots__/annotationExample.test.js.snap
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`failing snapshot example 1`] = `"some thing"`;

exports[`passing snapshot example 1`] = `"some thing"`;
41 changes: 41 additions & 0 deletions e2e/__tests__/annotationExample.test.js
@@ -0,0 +1,41 @@
test.todo('work in progress example');

test('passing example', () => {
expect(10).toBe(10);
});

test('passing snapshot example', () => {
expect('some thing').toMatchSnapshot();
});

let i = 0;

jest.retryTimes(3, {logErrorsBeforeRetry: true});

test('retryTimes example', () => {
i++;
if (i === 3) {
expect(true).toBeTruthy();
} else {
expect(true).toBeFalsy();
}
});

test('failing snapshot example', () => {
expect('nothing').toMatchSnapshot();
});

test.skip('skipped example', () => {
expect(10).toBe(10);
});

test('failing example', () => {
expect(10).toBe(1);
});

describe('nested', () => {
test('failing example', () => {
// eslint-disable-next-line no-undef
expect(abc).toBe(1);
});
});
8 changes: 4 additions & 4 deletions packages/jest-message-util/src/index.ts
Expand Up @@ -234,11 +234,11 @@ const removeInternalStackEntries = (
});
};

const formatPaths = (
export const formatPath = (
line: string,
config: StackTraceConfig,
relativeTestPath: string | null,
line: string,
) => {
): string => {
// Extract the file path from the trace line.
const match = line.match(/(^\s*at .*?\(?)([^()]+)(:[0-9]+:[0-9]+\)?.*$)/);
if (!match) {
Expand Down Expand Up @@ -317,7 +317,7 @@ export const formatStackTrace = (
.filter(Boolean)
.map(
line =>
STACK_INDENT + formatPaths(config, relativeTestPath, trimPaths(line)),
STACK_INDENT + formatPath(trimPaths(line), config, relativeTestPath),
)
.join('\n');

Expand Down
2 changes: 2 additions & 0 deletions packages/jest-reporters/package.json
Expand Up @@ -12,6 +12,7 @@
"./package.json": "./package.json"
},
"dependencies": {
"@actions/core": "^1.8.0",
"@bcoe/v8-coverage": "^0.2.3",
"@jest/console": "^28.1.0",
"@jest/test-result": "^28.1.0",
Expand All @@ -29,6 +30,7 @@
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-source-maps": "^4.0.0",
"istanbul-reports": "^3.1.3",
"jest-message-util": "^28.1.0",
"jest-util": "^28.1.0",
"jest-worker": "^28.1.0",
"slash": "^3.0.0",
Expand Down
79 changes: 37 additions & 42 deletions packages/jest-reporters/src/GitHubActionsReporter.ts
Expand Up @@ -5,55 +5,50 @@
* LICENSE file in the root directory of this source tree.
*/

import {error as errorAnnotation} from '@actions/core';
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved
import stripAnsi = require('strip-ansi');
import type {
AggregatedResult,
TestContext,
TestResult,
} from '@jest/test-result';
import type {Test, TestCaseResult} from '@jest/test-result';
import {
// formatPath,
getStackTraceLines,
getTopFrame,
separateMessageFromStack,
} from 'jest-message-util';
import BaseReporter from './BaseReporter';

const lineAndColumnInStackTrace = /^.*?:([0-9]+):([0-9]+).*$/;

function replaceEntities(s: string): string {
// https://github.com/actions/toolkit/blob/b4639928698a6bfe1c4bdae4b2bfdad1cb75016d/packages/core/src/command.ts#L80-L85
const substitutions: Array<[RegExp, string]> = [
[/%/g, '%25'],
[/\r/g, '%0D'],
[/\n/g, '%0A'],
];
return substitutions.reduce((acc, sub) => acc.replace(...sub), s);
}
const errorTitleSeparator = ' \u203A ';

export default class GitHubActionsReporter extends BaseReporter {
static readonly filename = __filename;

override onRunComplete(
_testContexts?: Set<TestContext>,
aggregatedResults?: AggregatedResult,
override onTestCaseResult(
test: Test,
{failureMessages, ancestorTitles, title}: TestCaseResult,
): void {
const messages = getMessages(aggregatedResults?.testResults);

for (const message of messages) {
this.log(message);
}
failureMessages.forEach(failureMessage => {
const {message, stack} = separateMessageFromStack(failureMessage);

const stackLines = getStackTraceLines(stack);
// const formattedLines = stackLines.map(line =>
// formatPath(line, test.context.config, null),
// );
const topFrame = getTopFrame(stackLines);

const errorTitle = [...ancestorTitles, title].join(errorTitleSeparator);
let errorMessage = stripAnsi([message, ...stackLines].join('\n'));

const rootDir = test.context.config.rootDir;
const testPath = test.path;
const cwd = process.cwd();

errorMessage = `rootDir: ${rootDir} | testPath: ${testPath} | cwd: ${cwd}`;

errorAnnotation(errorMessage, {
file: test.path,
startColumn: topFrame?.column,
startLine: topFrame?.line,
title: errorTitle,
});
});
}
}

function getMessages(results: Array<TestResult> | undefined) {
if (!results) return [];

return results.flatMap(({testFilePath, testResults}) =>
testResults
.filter(r => r.status === 'failed')
.flatMap(r => r.failureMessages)
.map(m => stripAnsi(m))
.map(m => replaceEntities(m))
.map(m => lineAndColumnInStackTrace.exec(m))
.filter((m): m is RegExpExecArray => m !== null)
.map(
([message, line, col]) =>
`\n::error file=${testFilePath},line=${line},col=${col}::${message}`,
),
);
}
118 changes: 0 additions & 118 deletions packages/jest-reporters/src/__tests__/GitHubActionsReporter.test.js

This file was deleted.