diff --git a/.nx/version-plans/version-plan-1785943295154.md b/.nx/version-plans/version-plan-1785943295154.md new file mode 100644 index 00000000..0099f1e5 --- /dev/null +++ b/.nx/version-plans/version-plan-1785943295154.md @@ -0,0 +1,5 @@ +--- +__default__: patch +--- + +Skipped tests now appear as skipped in Jest output and compatible result consumers. diff --git a/packages/jest/src/__tests__/execute-run.test.ts b/packages/jest/src/__tests__/execute-run.test.ts index 6d0e0918..12966f90 100644 --- a/packages/jest/src/__tests__/execute-run.test.ts +++ b/packages/jest/src/__tests__/execute-run.test.ts @@ -418,6 +418,61 @@ describe('executeRun', () => { ]); }); + it.each([ + ['it.skip', 'skip', 0], + ['context.skip', undefined, 5], + ] as const)('reports %s to Jest as pending', async (_label, declarationMode, duration) => { + let testRunnerListener: + | ((event: TestRunnerTestStartedEvent | TestRunnerTestFinishedEvent) => void) + | undefined; + const { emitEvent, calls: emittedEvents } = makeEmitEvent(); + const session = makeSession({ + onTestRunnerEvent: vi.fn((listener) => { + testRunnerListener = listener as typeof testRunnerListener; + return () => undefined; + }), + }); + + mockRunHarnessTestFile.mockImplementation(async () => { + testRunnerListener?.({ + type: 'test-started', + file: 'example.ts', + suite: 'suite', + name: 'does not run', + ancestorTitles: ['suite'], + fullName: 'suite does not run', + startedAt: 10, + declarationMode, + }); + testRunnerListener?.({ + type: 'test-finished', + file: 'example.ts', + suite: 'suite', + name: 'does not run', + ancestorTitles: ['suite'], + fullName: 'suite does not run', + startedAt: 10, + declarationMode, + duration, + status: 'skipped', + }); + + return makeFileRunResult(); + }); + + await executeRun(session, [makeTest()], makeWatcher(), emitEvent, makeGlobalConfig()); + + expect(emittedEvents).toContainEqual([ + 'test-case-result', + 'example.ts', + expect.objectContaining({ + fullName: 'suite does not run', + numPassingAsserts: 0, + status: 'pending', + }), + ]); + }); + it('includes pending promise diagnostics in live test-case failures', async () => { let testRunnerListener: | ((event: TestRunnerTestStartedEvent | TestRunnerTestFinishedEvent) => void) diff --git a/packages/jest/src/__tests__/run.test.ts b/packages/jest/src/__tests__/run.test.ts index 9ee9df81..034d8fb9 100644 --- a/packages/jest/src/__tests__/run.test.ts +++ b/packages/jest/src/__tests__/run.test.ts @@ -71,4 +71,43 @@ describe('runHarnessTestFile', () => { expect.objectContaining({ testTimeout: 15000 }), ); }); + + it('reports declaration-time and runtime skips to Jest as pending', async () => { + const session = createSession(15000); + vi.mocked(session.runTestFile).mockResolvedValue({ + ...createHarnessResult(), + tests: [ + { + name: 'passes', + status: 'passed', + duration: 1, + }, + { + name: 'declaration skip', + status: 'skipped', + duration: 0, + declarationMode: 'skip', + }, + { + name: 'runtime skip', + status: 'skipped', + duration: 1, + }, + ], + }); + + const { jestResult } = await runHarnessTestFile({ + testPath: '/project/example.harness.ts', + session, + globalConfig: createGlobalConfig(), + projectConfig: createProjectConfig(), + }); + + expect(jestResult.numPendingTests).toBe(2); + expect(jestResult.testResults).toEqual([ + expect.objectContaining({ title: 'passes', status: 'passed' }), + expect.objectContaining({ title: 'declaration skip', status: 'pending' }), + expect.objectContaining({ title: 'runtime skip', status: 'pending' }), + ]); + }); }); diff --git a/packages/jest/src/__tests__/test-file-platform-filter.test.ts b/packages/jest/src/__tests__/test-file-platform-filter.test.ts index ef2c492f..a854c8ea 100644 --- a/packages/jest/src/__tests__/test-file-platform-filter.test.ts +++ b/packages/jest/src/__tests__/test-file-platform-filter.test.ts @@ -73,7 +73,7 @@ describe('createPlatformSkippedTestResult', () => { expect(result.testResults).toHaveLength(1); expect(result.testResults[0]).toEqual( expect.objectContaining({ - status: 'skipped', + status: 'pending', title: 'swift.ios.harness.ts', fullName: 'swift.ios.harness.ts', }), diff --git a/packages/jest/src/execute-run.ts b/packages/jest/src/execute-run.ts index 7488d0e7..04a14e8f 100644 --- a/packages/jest/src/execute-run.ts +++ b/packages/jest/src/execute-run.ts @@ -32,6 +32,7 @@ import { import { formatHarnessErrorMessage } from './format-harness-error.js'; import { logger } from '@react-native-harness/tools'; import { printSummary, writeTraceFile } from './diagnostics/index.js'; +import { toJestStatus } from './toJestStatus.js'; const diagnosticsLogger = logger.child('diagnostics'); @@ -109,7 +110,7 @@ const emitHarnessTestFinished = async ( location, numPassingAsserts: event.status === 'passed' ? 1 : 0, startedAt: event.startedAt, - status: event.status, + status: toJestStatus(event.status), title: event.name, }; diff --git a/packages/jest/src/toJestStatus.ts b/packages/jest/src/toJestStatus.ts new file mode 100644 index 00000000..5dc385f7 --- /dev/null +++ b/packages/jest/src/toJestStatus.ts @@ -0,0 +1,6 @@ +import type { Status } from '@jest/test-result'; +import type { TestResultStatus } from '@react-native-harness/bridge'; + +// Jest's own runners expose skipped assertions as `pending` to reporters. +export const toJestStatus = (status: TestResultStatus): Status => + status === 'skipped' ? 'pending' : status; diff --git a/packages/jest/src/toTestResult.ts b/packages/jest/src/toTestResult.ts index e4159493..fb02273c 100644 --- a/packages/jest/src/toTestResult.ts +++ b/packages/jest/src/toTestResult.ts @@ -1,4 +1,6 @@ -import type { Status, TestResult } from '@jest/test-result'; +import type { TestResult } from '@jest/test-result'; +import type { TestResultStatus } from '@react-native-harness/bridge'; +import { toJestStatus } from './toJestStatus.js'; export type Options = { stats: { @@ -17,7 +19,7 @@ export type Options = { testPath?: string; title?: string; fullName?: string; - status: Status; + status: TestResultStatus; location?: { column: number; line: number; @@ -75,7 +77,7 @@ const getTestResults = ({ failureMessages: actualErrorMessage ? [actualErrorMessage] : [], fullName: test.fullName || test.testPath || jestTestPath || '', numPassingAsserts: test.status === 'passed' ? 1 : 0, - status: test.status, + status: toJestStatus(test.status), title: test.title || '', location: test.location, };