-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
feat: Add GitHub Actions Reporter #11320
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
Changes from 12 commits
4b441a1
6b299e2
b6cb64b
48e2778
195062d
808bd5c
5c4884f
97a7b1c
fc6dd30
6289d6a
5ab275e
ec6eb0d
a35a0d9
e72e447
20a6562
d8c0454
c7c4ac0
bb4bbe6
e4d5c01
1ae84fb
8b5414f
c963d7a
0461d49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
|
|
||
| import {GITHUB_ACTIONS} from 'ci-info'; | ||
| import type {AggregatedResult, TestResult} from '@jest/test-result'; | ||
| import BaseReporter from './BaseReporter'; | ||
| import type {Context} from './types'; | ||
|
|
||
| const lineAndColumnInStackTrace = /^.*?:([0-9]+):([0-9]+).*$/; | ||
|
|
||
| function replaceEntities(s: string): string { | ||
| const substitutions: Array<[RegExp, string]> = [ | ||
| [/%/, '%25'], | ||
| [/\r/g, '%0D'], | ||
| [/\n/g, '%0A'], | ||
| ]; | ||
| return substitutions.reduce((acc, sub) => acc.replace(...sub), s); | ||
| } | ||
|
|
||
| export default class GithubActionsReporter extends BaseReporter { | ||
| onRunComplete( | ||
| _contexts?: Set<Context>, | ||
| aggregatedResults?: AggregatedResult, | ||
| ): void { | ||
| if (!GITHUB_ACTIONS) { | ||
|
||
| return; | ||
| } | ||
|
|
||
| const messages = getMessages(aggregatedResults?.testResults); | ||
|
|
||
| for (const message of messages) { | ||
| this.log(message); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function getMessages(results: Array<TestResult> | undefined) { | ||
| if (!results) return []; | ||
|
|
||
| return results.reduce( | ||
| flatMap(({testFilePath, testResults}) => | ||
| testResults | ||
| .filter(r => r.status === 'failed') | ||
| .reduce( | ||
| flatMap(r => r.failureMessages), | ||
| [], | ||
| ) | ||
| .map(m => replaceEntities(m)) | ||
| .map(m => lineAndColumnInStackTrace.exec(m)) | ||
| .filter((m): m is RegExpExecArray => m !== null) | ||
| .map( | ||
| ([message, line, col]) => | ||
| `::error file=${testFilePath},line=${line},col=${col}::${message}`, | ||
| ), | ||
| ), | ||
| [], | ||
| ); | ||
| } | ||
|
|
||
| function flatMap<In, Out>(map: (x: In) => Array<Out>) { | ||
ockham marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return (out: Array<Out>, entry: In) => out.concat(...map(entry)); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| 'use strict'; | ||
|
|
||
| let GithubActionsReporter; | ||
|
|
||
| const write = process.stderr.write; | ||
| const globalConfig = { | ||
| rootDir: 'root', | ||
| watch: false, | ||
| }; | ||
|
|
||
| let results = []; | ||
|
|
||
| function requireReporter() { | ||
| jest.isolateModules(() => { | ||
| GithubActionsReporter = require('../GithubActionsReporter').default; | ||
| }); | ||
| } | ||
|
|
||
| beforeEach(() => { | ||
| process.stderr.write = result => results.push(result); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| results = []; | ||
| process.stderr.write = write; | ||
| }); | ||
|
|
||
| const aggregatedResults = { | ||
| numFailedTestSuites: 1, | ||
| numFailedTests: 1, | ||
| numPassedTestSuites: 0, | ||
| numTotalTestSuites: 1, | ||
| numTotalTests: 1, | ||
| snapshot: { | ||
| added: 0, | ||
| didUpdate: false, | ||
| failure: false, | ||
| filesAdded: 0, | ||
| filesRemoved: 0, | ||
| filesRemovedList: [], | ||
| filesUnmatched: 0, | ||
| filesUpdated: 0, | ||
| matched: 0, | ||
| total: 0, | ||
| unchecked: 0, | ||
| uncheckedKeysByFile: [], | ||
| unmatched: 0, | ||
| updated: 0, | ||
| }, | ||
| startTime: 0, | ||
| success: false, | ||
| testResults: [ | ||
| { | ||
| numFailingTests: 1, | ||
| numPassingTests: 0, | ||
| numPendingTests: 0, | ||
| numTodoTests: 0, | ||
| openHandles: [], | ||
| perfStats: { | ||
| end: 1234, | ||
| runtime: 1234, | ||
| slow: false, | ||
| start: 0, | ||
| }, | ||
| skipped: false, | ||
| snapshot: { | ||
| added: 0, | ||
| fileDeleted: false, | ||
| matched: 0, | ||
| unchecked: 0, | ||
| uncheckedKeys: [], | ||
| unmatched: 0, | ||
| updated: 0, | ||
| }, | ||
| testFilePath: '/home/runner/work/jest/jest/some.test.js', | ||
| testResults: [ | ||
| { | ||
| ancestorTitles: [Array], | ||
| duration: 7, | ||
| failureDetails: [Array], | ||
| failureMessages: [ | ||
| ` | ||
| Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n | ||
| \n | ||
| Expected: \u001b[32m\"b\"\u001b[39m\n | ||
| Received: \u001b[31m\"a\"\u001b[39m\n | ||
| at Object.<anonymous> (/home/runner/work/jest/jest/some.test.js:4:17)\n | ||
| at Object.asyncJestTest (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)\n | ||
| at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:45:12\n | ||
| at new Promise (<anonymous>)\n | ||
| at mapper (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:28:19)\n | ||
| at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:75:41\n | ||
| at processTicksAndRejections (internal/process/task_queues.js:93:5) | ||
| `, | ||
| ], | ||
| fullName: 'asserts that a === b', | ||
| location: null, | ||
| numPassingAsserts: 0, | ||
| status: 'failed', | ||
| title: 'asserts that a === b', | ||
| }, | ||
| ], | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| test("reporter returns empty string if GITHUB_ACTIONS isn't set", () => { | ||
| requireReporter(); | ||
| const testReporter = new GithubActionsReporter(globalConfig); | ||
| testReporter.onRunComplete(new Set(), aggregatedResults); | ||
| expect(results.join('').replace(/\\/g, '/')).toMatchSnapshot(); | ||
ockham marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }); | ||
|
|
||
| test('reporter extracts the correct filename, line, and column', () => { | ||
| jest.doMock('ci-info', () => ({GITHUB_ACTIONS: true})); | ||
|
|
||
| requireReporter(); | ||
| const testReporter = new GithubActionsReporter(globalConfig); | ||
| testReporter.onRunComplete(new Set(), aggregatedResults); | ||
| expect(results.join('').replace(/\\/g, '/')).toMatchSnapshot(); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
|
||
| exports[`reporter extracts the correct filename, line, and column 1`] = ` | ||
| "::error file=/home/runner/work/jest/jest/some.test.js,line=4,col=17::%0A Error: <dim>expect(</><red>received</><dim>).</>toBe<dim>(</><green>expected</><dim>) // Object.is equality</>%0A%0A %0A%0A Expected: <green>"b"</>%0A%0A Received: <red>"a"</>%0A%0A at Object.<anonymous> (/home/runner/work/jest/jest/some.test.js:4:17)%0A%0A at Object.asyncJestTest (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)%0A%0A at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:45:12%0A%0A at new Promise (<anonymous>)%0A%0A at mapper (/home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:28:19)%0A%0A at /home/runner/work/jest/jest/node_modules/jest-jasmine2/build/queueRunner.js:75:41%0A%0A at processTicksAndRejections (internal/process/task_queues.js:93:5)%0A | ||
ockham marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| " | ||
| `; | ||
|
|
||
| exports[`reporter returns empty string if GITHUB_ACTIONS isn't set 1`] = `""`; | ||
Uh oh!
There was an error while loading. Please reload this page.