Jest test reporting
Keep Jest’s terminal summary and local reports for debugging alongside CI. When you split a suite across jobs, gather every shard’s results and upload once to Qualflare for hosted reporting and history across local runs, CI runs and monorepo packages. A native Jest reporter captures results as the run happens, so steps, attachments and metadata arrive with them rather than being reconstructed from a results file.
Keep local reports; add shared history
Jest’s reporting is intentionally local: rich terminal output, a machine-readable
--json file, and — if you add a package like
jest-html-reporters — a local HTML report. All of it is
useful for inspecting an individual run, whether you run locally or save the report as a CI artifact.
Shared reporting adds a place to gather sharded results and compare pass/fail history over time.
Keep using your local reports while uploading the local or CI runs you want to track.
Send Jest results to Qualflare
First create a project and access token, install the CLI, and save a local login:
npm install -g @qualflare/cli
qf login my-project # paste your project access token when prompted my-project is a local alias; the token selects the project.
In the config below, replace staging with your
environment’s Identifier, not its display name.
Install the native reporter as a dev dependency:
npm install --save-dev @qualflare/jest
Add it to reporters alongside Jest's own, so you keep the terminal output:
// jest.config.cjs (or use export default in an ESM config)
module.exports = {
reporters: ['default', ['@qualflare/jest/reporter', { environment: 'staging' }]],
};
Then run Jest as usual. The reporter writes a directory and makes no network calls.
Your testEnvironment is untouched:
npx jest # writes ./qualflare-results, including when tests fail Upload the results in a separate command, including after a failed test run:
qf my-project collect ./qualflare-results Requires Jest 29 or newer and Node 18+. Use qf 0.1.24+ for image attachments. See the Jest reporter setup for steps, attachments and configuration options. For multiple CI jobs, use the gathering workflow below.
Without the reporter: the JSON results file
If you would rather not add a dependency, Jest's own --json output still
works for results and durations, without native steps, attachments or test metadata:
# Jest writes a JSON results file natively — no extra reporter needed
npx jest --json --outputFile=jest-results.json
Upload it with the Jest-aware parser; the CLI attaches your Git branch and commit automatically.
JSON does not inherit the native reporter’s environment setting, so replace
staging in every JSON collect command below with
your environment Identifier:
# Upload to Qualflare with the Jest-aware parser
qf my-project collect jest-results.json --format jest --environment staging
For a single CI job, this GitHub Actions example uses the JSON route. Store your project token in the
QF_TOKEN secret.
qf login ci --force reads that secret and saves the
ci alias in this job; collect uses the saved login.
See CLI authentication.
# Steps after checkout, Node and npm ci.
# JSON route: the run and upload use the same file.
- name: Run Jest
run: npx jest --json --outputFile=jest-results.json
- name: Upload results to Qualflare
if: ${{ !cancelled() }} # also runs after test failures
env:
QF_TOKEN: ${{ secrets.QF_TOKEN }}
QF_BRANCH: ${{ github.head_ref || github.ref_name }}
QF_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
npm install -g @qualflare/cli
qf login ci --force
qf ci collect jest-results.json --format jest --environment staging Jest sharding: split, gather, then upload once
Jest 28+ supports --shard=i/N.
It splits test files across invocations:
1/3, 2/3
and 3/3 together cover a three-way run.
A custom test sequencer must implement sharding. Splitting tests does not gather their output;
each CI job still needs to save its results for a final reporting job.
Native reporter: gather reports and attachments
With the Jest 29+ reporter configured above, each process writes uniquely named files. This workflow gives every shard the same run ID, saves each directory as a separate artifact, then downloads all three into one directory before collecting. Keep attachments beside their reports.
# .github/workflows/jest-shards.yml
# Commit the native reporter config and dependency shown above first.
name: Jest shards
on: [push, pull_request]
permissions:
contents: read
env:
QUALFLARE_RUN_ID: ${{ github.run_id }}-${{ github.run_attempt }}
QF_BRANCH: ${{ github.head_ref || github.ref_name }}
QF_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx jest --shard=${{ matrix.shard }}/3
- name: Save this shard's results and attachments
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: jest-native-${{ github.run_attempt }}-${{ matrix.shard }}
path: qualflare-results/
if-no-files-found: error
report:
needs: test
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: 22
# Named downloads share one destination, without nested folders.
# A missing artifact fails the job before the upload step.
- uses: actions/download-artifact@v4
with:
name: jest-native-${{ github.run_attempt }}-1
path: qualflare-results/
- uses: actions/download-artifact@v4
with:
name: jest-native-${{ github.run_attempt }}-2
path: qualflare-results/
- uses: actions/download-artifact@v4
with:
name: jest-native-${{ github.run_attempt }}-3
path: qualflare-results/
- name: Upload one launch
env:
QF_TOKEN: ${{ secrets.QF_TOKEN }}
run: |
npm install -g @qualflare/cli
qf login ci --force
qf ci collect ./qualflare-results fail-fast: false lets the other shards finish after a failure.
The upload conditions preserve failed-test results without turning the failing test job green.
Each named download is required: if a shard produces no artifact, the final upload does not run.
Artifact names include the run attempt, so rerun all shards together
rather than combining a retry with old artifacts.
The CLI reads JSON files directly inside the directory, not nested shard folders.
Named downloads above extract to the same path; if you use a pattern-based download instead, set
merge-multiple: true and check that every expected shard arrived.
GitHub does not expose repository secrets to fork pull requests; those runs can retain artifacts,
but need an authorized upload step to send them to Qualflare.
Keep the shared QUALFLARE_RUN_ID unique to this run.
Without a shared ID, separate local processes can produce different run IDs, and directory collection
selects the newest run by default. See run selection and sharding
before intentionally combining mixed runs. Native reports already contain shard indexes; they do not need the CLI’s
--shard flag.
Built-in JSON: one file per shard
Without the native reporter, give each shard a different output filename. After gathering them, pass all files in shard order to one collect command:
# Run these in separate jobs; keep a distinct file per shard.
npx jest --shard=1/3 --json --outputFile=jest-results-1.json
npx jest --shard=2/3 --json --outputFile=jest-results-2.json
npx jest --shard=3/3 --json --outputFile=jest-results-3.json
# After gathering all three files and logging in, upload once.
# Explicit order maps Jest shards 1, 2, 3 to CLI indexes 0, 1, 2.
qf ci collect jest-results-1.json jest-results-2.json jest-results-3.json --format jest --shard --environment staging To adapt the workflow above, replace the test command and artifact upload with:
# In the test job, replace the Jest command and artifact settings:
- run: npx jest --shard=${{ matrix.shard }}/3 --json --outputFile=jest-results-${{ matrix.shard }}.json
- name: Save this shard's JSON
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: jest-json-${{ github.run_attempt }}-${{ matrix.shard }}
path: jest-results-${{ matrix.shard }}.json
if-no-files-found: error
In all three download steps, change the artifact name prefix from
jest-native- to jest-json-
and the destination path to ..
Keep the login step, then replace directory collection with the explicit three-file command above.
Jest numbers shards from 1; the CLI’s --shard
assigns indexes from 0 in input-file order. Use explicit filenames rather than relying on glob sorting.
Monorepos and Jest projects
Jest’s projects option runs multiple package suites
in one invocation. Add it to your existing config; keep the reporter configuration too:
// jest.config.cjs — add projects to your existing reporter config
module.exports = {
reporters: ['default', ['@qualflare/jest/reporter', { environment: 'staging' }]],
projects: ['<rootDir>/packages/*'],
}; Separate package jobs still need the same gather-then-collect step. Use a shared native run ID only for results that belong in the same launch.
What you get on top of Jest
- AI failure clustering. Group related failures to investigate common causes rather than inspect every stack trace separately. Analysis is subject to your plan’s AI allowance.
- Flaky detection and retries. Compare pass/fail history across runs. The native reporter also records retry attempts; enable
jest.retryTimes(n, { logErrorsBeforeRetry: true })for failed-attempt error messages. - Monorepo aggregation. Gather every package’s results and collect once for a single launch across the repo.
- Launch analysis. Review failing areas, risk assessments and suggested next steps when AI analysis is available.
- History, trends & defects. Pass rate, slowest tests, and flakiness over time across branches — plus a defect you can open straight from a failing run.
Raw Jest output vs Qualflare
| --json / jest-html-reporters | Qualflare | |
|---|---|---|
| History across CI runs | — | Yes |
| Aggregates monorepo packages | — | Yes |
| AI failure clustering (root cause) | — | Yes |
| Flaky scoring over time | — | Yes |
| Local, zero-setup, offline | Yes | — |
Complementary: keep --json / jest-html-reporters for debugging
alongside CI, and upload local or CI runs to Qualflare for shared history.
Bring your Jest results into one launch
Start with a local run or your existing CI pipeline. Follow the reporter setup, gather your results, and upload once. Uploads and AI analyses have separate plan allowances.
Open Jest setup guideQualflare works the same with Playwright, pytest, Cypress, JUnit and 20+ more frameworks. Testing a mobile app too? See our Android (Espresso), iOS (XCTest), and Maestro guides. Weighing tools? See how it compares to other test management platforms, or browse all framework reporting guides. Hitting non-deterministic specs? See our guide to fixing flaky Jest tests in CI.
Frequently asked questions
How do I send Jest results to Qualflare?
Install @qualflare/jest and add @qualflare/jest/reporter alongside your existing reporters, using your Qualflare environment Identifier. Install @qualflare/cli and authenticate with qf login my-project. Run npx jest, then upload the generated directory with qf my-project collect ./qualflare-results, even if tests failed. Alternatively, run npx jest --json --outputFile=jest-results.json and upload with qf my-project collect jest-results.json --format jest --environment staging, replacing staging with your environment Identifier. Both local and CI runs can become tracked launches; AI analysis has a separate plan allowance.
Do I need the reporter, or is the JSON file enough?
Both work. The native @qualflare/jest reporter can record retries, steps, attachments, labels, links, tags and priority. It writes uniquely named report files, which you gather before uploading. Jest’s built-in JSON is enough for results and durations, but does not provide the native reporter’s enrichment. The reporter requires Jest 29+, Node 18+, and @qualflare/cli v0.1.24 or newer for image attachments.
Does Qualflare detect flaky Jest tests?
Qualflare uses pass/fail history across runs to surface intermittent failures. The native Jest reporter also records retry attempts when you use jest.retryTimes(). Enable jest.retryTimes(n, { logErrorsBeforeRetry: true }) to include the error messages from failed attempts; without that option, attempt statuses are still captured.
Can I use the jest-junit reporter instead of JSON?
Yes. Upload the XML file with qf my-project collect results.xml --format junit --environment staging after logging in, replacing staging with your environment Identifier. The imported detail depends on what your JUnit reporter writes. Use the native @qualflare/jest reporter when you need per-attempt retries, steps and attachments.
Does it work with a monorepo or Jest “projects”?
Yes. Jest’s projects configuration runs multiple package suites in one invocation. Upload its result file, or gather the files from separate package jobs and collect them once. Native reports intended for one launch need a shared run ID; keep unrelated runs separate.
How do I merge Jest shards into one report?
Run every Jest shard, save each job’s results as a distinct artifact, and gather all artifacts in a final job. For the native reporter, share a QUALFLARE_RUN_ID across the shards and collect the gathered qualflare-results directory once. For built-in JSON, use a unique filename per shard and pass all files in shard order to qf ci collect with --format jest --shard and --environment set to your environment Identifier. Jest’s --shard indexes start at 1; the CLI assigns file indexes starting at 0.
Does it work in GitHub Actions and other CI for Node?
Yes. In each uploading job, install the CLI, expose your project token as QF_TOKEN, and run qf login ci --force before qf ci collect. Use a condition that still uploads after failed tests and set QF_BRANCH and QF_COMMIT to the source branch and commit. The test job should remain failed when tests fail. The same run, gather and upload pattern works in other CI systems.
Recent updates
Setup reflects the Qualflare CLI (docs.qualflare.com) and Jest’s built-in JSON output. Updated November 5, 2026. Written by İbrahim Süren, Qualflare.