From f8ae4deee68dd1974e29d2b1bc34968efc91fb09 Mon Sep 17 00:00:00 2001 From: Jozef Izso Date: Sun, 15 Mar 2026 13:07:22 +0100 Subject: [PATCH] Report jest-junit testsuite errors as failures Test errors are different (represented differently in the JUnit XML output from jest-junit) to test _failures_. Failures are tests which ran and failed, errors are for tests/test suites which did not even run because the test code itself didn't build or didn't execute correctly. jest-junit has an option to enable reporting of test suite errors, but test-reporter then interprets these as successful tests. --- .../__outputs__/jest-test-errors-results.md | 22 +++++++ .../__snapshots__/jest-junit.test.ts.snap | 63 +++++++++++++++++++ __tests__/fixtures/test-errors/jest/files.txt | 3 + .../test-errors/jest/jest-test-results.xml | 25 ++++++++ __tests__/jest-junit.test.ts | 23 +++++++ src/parsers/jest-junit/jest-junit-parser.ts | 7 ++- src/parsers/jest-junit/jest-junit-types.ts | 1 + 7 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 __tests__/__outputs__/jest-test-errors-results.md create mode 100644 __tests__/fixtures/test-errors/jest/files.txt create mode 100644 __tests__/fixtures/test-errors/jest/jest-test-results.xml diff --git a/__tests__/__outputs__/jest-test-errors-results.md b/__tests__/__outputs__/jest-test-errors-results.md new file mode 100644 index 0000000..e7b98ff --- /dev/null +++ b/__tests__/__outputs__/jest-test-errors-results.md @@ -0,0 +1,22 @@ +![Tests failed](https://img.shields.io/badge/tests-2%20failed-critical) +|Report|Passed|Failed|Skipped|Time| +|:---|---:|---:|---:|---:| +|[fixtures/test-errors/jest/jest-test-results.xml](#user-content-r0)||2 ❌||646ms| +## ❌ fixtures/test-errors/jest/jest-test-results.xml +**2** tests were completed in **646ms** with **0** passed, **2** failed and **0** skipped. +|Test suite|Passed|Failed|Skipped|Time| +|:---|---:|---:|---:|---:| +|[libs/bar.spec.ts](#user-content-r0s0)||1 ❌||0ms| +|[libs/foo.spec.ts](#user-content-r0s1)||1 ❌||0ms| +### ❌ libs/bar.spec.ts +``` +Test suite failed to run + ❌ libs/bar.spec.ts + ● Test suite failed to run +``` +### ❌ libs/foo.spec.ts +``` +Test suite failed to run + ❌ libs/foo.spec.ts + ● Test suite failed to run +``` \ No newline at end of file diff --git a/__tests__/__snapshots__/jest-junit.test.ts.snap b/__tests__/__snapshots__/jest-junit.test.ts.snap index eca0092..dc71713 100644 --- a/__tests__/__snapshots__/jest-junit.test.ts.snap +++ b/__tests__/__snapshots__/jest-junit.test.ts.snap @@ -1,5 +1,68 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +exports[`jest-junit tests jest testsuite errors example test results matches snapshot 1`] = ` +TestRunResult { + "path": "fixtures/test-errors/jest/jest-test-results.xml", + "suites": [ + TestSuiteResult { + "groups": [ + TestGroupResult { + "name": "Test suite failed to run", + "tests": [ + TestCaseResult { + "error": { + "details": " ● Test suite failed to run + + tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file + + 13 "typeRoots": ["./src/lib/types", "./node_modules/@types"], + ~~~~~~~~~~~ +", + "line": undefined, + "path": undefined, + }, + "name": "libs/foo.spec.ts", + "result": "failed", + "time": 0, + }, + ], + }, + ], + "name": "libs/foo.spec.ts", + "totalTime": 0, + }, + TestSuiteResult { + "groups": [ + TestGroupResult { + "name": "Test suite failed to run", + "tests": [ + TestCaseResult { + "error": { + "details": " ● Test suite failed to run + + tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file + + 13 "typeRoots": ["./src/lib/types", "./node_modules/@types"], + ~~~~~~~~~~~ +", + "line": undefined, + "path": undefined, + }, + "name": "libs/bar.spec.ts", + "result": "failed", + "time": 0, + }, + ], + }, + ], + "name": "libs/bar.spec.ts", + "totalTime": 0, + }, + ], + "totalTime": 646, +} +`; + exports[`jest-junit tests parsing ESLint report without timing information works - PR #134 1`] = ` TestRunResult { "path": "fixtures/jest-junit-eslint.xml", diff --git a/__tests__/fixtures/test-errors/jest/files.txt b/__tests__/fixtures/test-errors/jest/files.txt new file mode 100644 index 0000000..486856e --- /dev/null +++ b/__tests__/fixtures/test-errors/jest/files.txt @@ -0,0 +1,3 @@ +libs/bar.spec.ts +libs/foo.spec.ts +tsconfig.json diff --git a/__tests__/fixtures/test-errors/jest/jest-test-results.xml b/__tests__/fixtures/test-errors/jest/jest-test-results.xml new file mode 100644 index 0000000..bc65075 --- /dev/null +++ b/__tests__/fixtures/test-errors/jest/jest-test-results.xml @@ -0,0 +1,25 @@ + + + + + ● Test suite failed to run + + tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file + + 13 "typeRoots": ["./src/lib/types", "./node_modules/@types"], + ~~~~~~~~~~~ + + + + + + ● Test suite failed to run + + tsconfig.json:13:3 - error TS6258: 'typeRoots' should be set inside the 'compilerOptions' object of the config json file + + 13 "typeRoots": ["./src/lib/types", "./node_modules/@types"], + ~~~~~~~~~~~ + + + + diff --git a/__tests__/jest-junit.test.ts b/__tests__/jest-junit.test.ts index 50f225d..86fd8a8 100644 --- a/__tests__/jest-junit.test.ts +++ b/__tests__/jest-junit.test.ts @@ -351,4 +351,27 @@ describe('jest-junit tests', () => { // Report should have the title as the first line expect(report).toMatch(/^# My Custom Title\n## 1 passed, 4 failed and 1 skipped\n/) }) + + it('jest testsuite errors example test results matches snapshot', async () => { + const fixturePath = path.join(__dirname, 'fixtures', 'test-errors', 'jest', 'jest-test-results.xml') + const trackedFilesPath = path.join(__dirname, 'fixtures', 'test-errors', 'jest', 'files.txt') + const outputPath = path.join(__dirname, '__outputs__', 'jest-test-errors-results.md') + const filePath = normalizeFilePath(path.relative(__dirname, fixturePath)) + const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'}) + + const trackedFiles = fs.readFileSync(trackedFilesPath, {encoding: 'utf8'}).split(/\n\r?/g) + const opts: ParseOptions = { + parseErrors: true, + trackedFiles + //workDir: '/home/dorny/dorny/jest/' + } + + const parser = new JestJunitParser(opts) + const result = await parser.parse(filePath, fileContent) + expect(result).toMatchSnapshot() + + const report = getReport([result]) + fs.mkdirSync(path.dirname(outputPath), {recursive: true}) + fs.writeFileSync(outputPath, report) + }) }) diff --git a/src/parsers/jest-junit/jest-junit-parser.ts b/src/parsers/jest-junit/jest-junit-parser.ts index 764fa3a..c4b7f3d 100644 --- a/src/parsers/jest-junit/jest-junit-parser.ts +++ b/src/parsers/jest-junit/jest-junit-parser.ts @@ -75,17 +75,18 @@ export class JestJunitParser implements TestParser { } private getTestCaseResult(test: TestCase): TestExecutionResult { - if (test.failure) return 'failed' + if (test.failure || test.error) return 'failed' if (test.skipped) return 'skipped' return 'success' } private getTestCaseError(tc: TestCase): TestCaseError | undefined { - if (!this.options.parseErrors || !tc.failure) { + if (!this.options.parseErrors || !(tc.failure || tc.error)) { return undefined } - const details = typeof tc.failure[0] === 'string' ? tc.failure[0] : tc.failure[0]['_'] + const message = tc.failure ? tc.failure[0] : tc.error ? tc.error[0] : 'unknown failure' + const details = typeof message === 'string' ? message : message['_'] let path let line diff --git a/src/parsers/jest-junit/jest-junit-types.ts b/src/parsers/jest-junit/jest-junit-types.ts index 3ca03b9..2e0d700 100644 --- a/src/parsers/jest-junit/jest-junit-types.ts +++ b/src/parsers/jest-junit/jest-junit-types.ts @@ -31,4 +31,5 @@ export interface TestCase { } failure?: string[] skipped?: string[] + error?: string[] }