The Harness Jest runner currently reports progress at test-file granularity. That works for normal Jest unit tests, but Harness test files are much more expensive to run because they involve the React Native runtime, bridge communication, Metro/app readiness, and device or simulator execution. When a file contains multiple Harness test cases, the Jest CLI can only show that the whole file is running, not which individual test case is currently active or completed.
Refactoring the runner to use Jest's event-emitting runner API would let the Jest CLI display per-test-case progress while preserving the existing file-level result behavior.
Observed Findings
packages/jest/src/index.ts implements the current runner as a callback-style Jest runner with runTests(tests, watcher, onStart, onResult, onFailure, options).
packages/jest/src/execute-run.ts calls onStart, onResult, and onFailure at test-file granularity only.
packages/jest/src/run.ts already converts completed Harness results into Jest TestResult objects, including per-test assertion results with titles, statuses, ancestor titles, durations, and errors.
packages/jest/src/harness-session.ts already listens to raw bridge events, but those events are currently only forwarded into Harness plugin hooks and are not exposed to the Jest runner as Jest TestEvents.
packages/bridge/src/shared/test-runner.ts already defines runtime runner events such as test-started and test-finished, but those events currently include only flattened fields like name, suite, file, duration, status, and error.
packages/runtime/src/runner/runSuite.ts emits test-started before each test runs and test-finished for passed, failed, skipped, todo, and dynamically skipped tests.
packages/runtime/src/runner/runSuite.ts has access to the current TestSuite and its parent chain, so it can derive ancestorTitles and fullName at runtime.
packages/runtime/src/collector/functions.ts knows declaration-time metadata such as test.only, test.skip, and test.todo, but the exported TestCase model currently collapses this to status: 'active' | 'skipped' | 'todo'.
- Runtime
context.skip() is an execution outcome, not a declaration mode. It should result in a final skipped status, but should not be reported as declared mode: 'skip'.
Suggested Behavior
- Refactor
packages/jest to implement Jest's event-emitting runner interface while preserving the existing behavior for file-level starts, successes, failures, watch mode, Harness session reuse, and final Jest TestResult generation.
- Emit Jest file-level events equivalent to the current callbacks:
test-file-start
test-file-success
test-file-failure
- Translate Harness runtime test events into Jest test-case events:
test-started -> test-case-start
test-finished -> test-case-result
- Enrich Harness runtime test events with enough metadata for direct Jest mapping:
ancestorTitles: string[]
fullName: string
startedAt: number
- declaration metadata such as
declarationMode?: 'only' | 'skip' | 'todo'
- Keep declaration mode separate from execution status:
- declared
test.skip maps to Jest mode: 'skip'
- declared
test.todo maps to Jest mode: 'todo'
- declared
test.only maps to Jest mode: 'only'
- normal tests have no Jest mode
- dynamic
context.skip() keeps normal declaration mode but finishes with skipped status
- Avoid deriving Jest
mode from status, because status: 'skipped' can also come from focus filtering, test name filtering, skipped suites, or runtime context.skip().
- Decide and document how skipped suites should behave for per-test events. If
describe.skip(...) should show individual skipped test cases in Jest output, runSuite must not return before emitting per-test skipped events for descendants.
Resolution Summary
The Jest runner should continue producing the same final file-level results it produces today, but it should also emit Jest-compatible per-test-case progress events. Harness runtime events should carry explicit declaration metadata and hierarchy information so the Jest adapter can report each test case accurately without guessing from final status alone.
The Harness Jest runner currently reports progress at test-file granularity. That works for normal Jest unit tests, but Harness test files are much more expensive to run because they involve the React Native runtime, bridge communication, Metro/app readiness, and device or simulator execution. When a file contains multiple Harness test cases, the Jest CLI can only show that the whole file is running, not which individual test case is currently active or completed.
Refactoring the runner to use Jest's event-emitting runner API would let the Jest CLI display per-test-case progress while preserving the existing file-level result behavior.
Observed Findings
packages/jest/src/index.tsimplements the current runner as a callback-style Jest runner withrunTests(tests, watcher, onStart, onResult, onFailure, options).packages/jest/src/execute-run.tscallsonStart,onResult, andonFailureat test-file granularity only.packages/jest/src/run.tsalready converts completed Harness results into JestTestResultobjects, including per-test assertion results with titles, statuses, ancestor titles, durations, and errors.packages/jest/src/harness-session.tsalready listens to raw bridge events, but those events are currently only forwarded into Harness plugin hooks and are not exposed to the Jest runner as JestTestEvents.packages/bridge/src/shared/test-runner.tsalready defines runtime runner events such astest-startedandtest-finished, but those events currently include only flattened fields likename,suite,file,duration,status, anderror.packages/runtime/src/runner/runSuite.tsemitstest-startedbefore each test runs andtest-finishedfor passed, failed, skipped, todo, and dynamically skipped tests.packages/runtime/src/runner/runSuite.tshas access to the currentTestSuiteand itsparentchain, so it can deriveancestorTitlesandfullNameat runtime.packages/runtime/src/collector/functions.tsknows declaration-time metadata such astest.only,test.skip, andtest.todo, but the exportedTestCasemodel currently collapses this tostatus: 'active' | 'skipped' | 'todo'.context.skip()is an execution outcome, not a declaration mode. It should result in a final skipped status, but should not be reported as declaredmode: 'skip'.Suggested Behavior
packages/jestto implement Jest's event-emitting runner interface while preserving the existing behavior for file-level starts, successes, failures, watch mode, Harness session reuse, and final JestTestResultgeneration.test-file-starttest-file-successtest-file-failuretest-started->test-case-starttest-finished->test-case-resultancestorTitles: string[]fullName: stringstartedAt: numberdeclarationMode?: 'only' | 'skip' | 'todo'test.skipmaps to Jestmode: 'skip'test.todomaps to Jestmode: 'todo'test.onlymaps to Jestmode: 'only'context.skip()keeps normal declaration mode but finishes with skipped statusmodefromstatus, becausestatus: 'skipped'can also come from focus filtering, test name filtering, skipped suites, or runtimecontext.skip().describe.skip(...)should show individual skipped test cases in Jest output,runSuitemust not return before emitting per-test skipped events for descendants.Resolution Summary
The Jest runner should continue producing the same final file-level results it produces today, but it should also emit Jest-compatible per-test-case progress events. Harness runtime events should carry explicit declaration metadata and hierarchy information so the Jest adapter can report each test case accurately without guessing from final status alone.