diff --git a/lib/index.mts b/lib/index.mts index 3329daa..e2bffb2 100644 --- a/lib/index.mts +++ b/lib/index.mts @@ -1,6 +1,6 @@ import type { TestEvent } from 'node:test/reporters'; import { issueCommand } from './command.mjs'; -import { transformFilename } from './utils.mjs'; +import { isSubtestsFailedError, transformFilename } from './utils.mjs'; interface FailedTestInfo { name: string; @@ -20,6 +20,11 @@ export default async function* ghaReporter( // eslint-disable-next-line sonarjs/no-collapsible-if if (isGHA) { if (event.type === 'test:fail') { + const { error } = event.data.details; + if (isSubtestsFailedError(error)) { + continue; + } + failedTests.push({ name: event.data.name, file: transformFilename(event.data.file), diff --git a/lib/utils.mts b/lib/utils.mts index 639099b..be04465 100644 --- a/lib/utils.mts +++ b/lib/utils.mts @@ -13,3 +13,9 @@ export const escapeProperty = (s: unknown): string => String(s).replace(/[%\r\n: export const transformFilename = (s: string | undefined): string | undefined => s?.startsWith('file://') ? new URL(s).pathname : s; + +export const isSubtestsFailedError = (error: Error): boolean => + 'code' in error && + 'failureType' in error && + error.code === 'ERR_TEST_FAILURE' && + error.failureType === 'subtestsFailed'; diff --git a/test/integration/index.spec.mts b/test/integration/index.spec.mts index 3491636..c9d34cc 100644 --- a/test/integration/index.spec.mts +++ b/test/integration/index.spec.mts @@ -33,13 +33,12 @@ void describe('GitHub Actions Reporter', () => { const thisDir = dirname(fileURLToPath(import.meta.url)); const result = await runner([`${thisDir}/test${extname(fileURLToPath(import.meta.url))}`]); const lines = result.trim().split('\n'); - equal(lines.length, 4); + equal(lines.length, 3); equal(lines[0], '::group::Test Failures'); match( lines[1]!, /^::error title=will generate a report entry on failure,file=[^,]+,line=\d+,col=\d+::Expected 2 to equal 1$/u, ); - match(lines[2]!, /^::error title=Sample test suite,file=[^,]+,line=\d+,col=\d+::1 subtest failed$/u); - equal(lines[3], '::endgroup::'); + equal(lines[2], '::endgroup::'); }); }); diff --git a/test/unit/utils.spec.mts b/test/unit/utils.spec.mts index 521fa98..f451615 100644 --- a/test/unit/utils.spec.mts +++ b/test/unit/utils.spec.mts @@ -1,6 +1,24 @@ import { equal } from 'node:assert/strict'; import { describe, test } from 'node:test'; -import { escapeData, escapeProperty, transformFilename } from '../../lib/utils.mjs'; +import { escapeData, escapeProperty, isSubtestsFailedError, transformFilename } from '../../lib/utils.mjs'; + +interface TestError extends Error { + code?: string; + failureType?: string; +} + +const createTestError = (message: string, code: string | undefined, failureType: string | undefined): TestError => { + const result = new Error(message) as TestError; + if (code !== undefined) { + result.code = code; + } + + if (failureType !== undefined) { + result.failureType = failureType; + } + + return result; +}; await describe('utils', async () => { await test('escapeData', () => { @@ -29,4 +47,25 @@ await describe('utils', async () => { equal(transformFilename(input), input); }); }); + + await describe('isSubtestsFailedError', async () => { + const nonTestErrors: Record = { + 'Plain Error': createTestError('Plain Error', undefined, undefined), + 'No failureType': createTestError('No failureType', 'ERR_TEST_FAILURE', undefined), + 'No code': createTestError('No code', undefined, 'subtestsFailed'), + testCodeFailure: createTestError('non-subtestsFailed', 'ERR_TEST_FAILURE', 'testCodeFailure'), + }; + + for (const [name, error] of Object.entries(nonTestErrors)) { + // eslint-disable-next-line no-await-in-loop + await test(`should return false for ${name}`, () => { + equal(isSubtestsFailedError(error), false); + }); + } + + await test('should return true for subtestsFailed', () => { + const error = createTestError('subtestsFailed', 'ERR_TEST_FAILURE', 'subtestsFailed'); + equal(isSubtestsFailedError(error), true); + }); + }); });