Skip to content

Early Adopter Offer:Get 40% off Core & Scale for your first year with code EARLYQFView pricing

Detox test reporting

@qualflare/detox reports your React Native suite from inside the run — statuses, per-attempt retry history, steps and your own metadata — and resolves whether the run targeted iOS or Android without you configuring it twice. Because Detox has no test runner of its own and drives Jest, it builds on Qualflare's Jest reporter rather than reimplementing one. What a reporter cannot see is Detox’s own artifacts directory, and qf collect --artifacts-dir attaches those screenshots, videos and device logs to the tests they came from. This guide covers the setup, why the artifacts are matched after the run rather than during it, and the hosted, historical analysis — AI failure clustering, flaky-test scoring and per-launch risk — that sits on top.

What a Detox run actually produces

Two separate things, which is the whole reason Detox reporting is usually half-done.

The first is the test results, and they are Jest’s. detox test is a wrapper that drives Jest with a Detox test environment, so everything Jest knows — which tests ran, how long each took, which attempt of a retry passed, the failure message and stack — is available to any Jest reporter listed in jest.config, exactly as it would be in a unit-test run.

The second is the artifacts, and they are Detox’s alone. Its artifact plugins write screenshots, screen recordings, device logs, instruments recordings and UI hierarchy snapshots to disk, one directory per test:

# Detox drives Jest, and writes its artifacts to ./artifacts by default
detox test --configuration ios.sim.debug \
  --take-screenshots failing \
  --record-videos failing \
  --record-logs failing
artifacts/
└── ios.sim.debug.2026-09-27 13-30-00Z/
    ├── ✓ Login should sign in/
    │   ├── test.png
    │   └── device.log
    └── ✗ Login should handle a_b paths/
        ├── test.png
        ├── test.mp4
        └── device.log

Note what is and is not in those directory names. The glyph records the result, the name is the test’s full name with anything filesystem-hostile replaced — the slash in a/b has become an underscore — and a retried test gets (2), (3) on its later invocations. There is no test number and no timestamp per test. Nothing inside the directory says which test it belongs to either: the directory name is the only link between an artifact and a result, and on its own it is just a folder on a CI machine that gets discarded with the workspace.

Why the artifacts are matched after the run

The obvious place to pick up those directories is the Jest reporter, in onRunComplete. That cannot work, and the reason is structural rather than a timing quirk worth retrying around:

  • Detox finalises video, and flushes device logs, in its artifacts plugin’s onBeforeCleanup.
  • That runs inside detox.cleanup().
  • Detox’s Jest integration calls detox.cleanup() from Jest’s globalTeardown.
  • And Jest runs globalTeardown after every reporter’s onRunComplete.

A reporter-side scan therefore runs before Detox has finished writing, and would systematically miss the videos — the single largest and most useful artifact on a failing flow. A delay or a retry inside the reporter would not fix the ordering; it would make the miss intermittent, which is harder to diagnose than missing it every time. So the scan belongs in qf collect, which runs as a separate command after the Detox process has exited. There the race does not need managing, because it does not exist.

Send Detox results to Qualflare

Install the Jest reporter — it is the reporter a Detox run needs:

npm install --save-dev @qualflare/detox

Add it to the Jest config that detox test runs, keeping Detox’s own reporter alongside it. Declaring framework: 'detox' is what makes the launch read as a Detox suite rather than a generic Jest one:

// e2e/jest.config.js — the config `detox test` runs
module.exports = {
  rootDir: '..',
  testMatch: ['<rootDir>/e2e/**/*.test.js'],
  testTimeout: 120000,
  // Keep Detox's own reporter: it is what prints progress.
  reporters: [
    'detox/runners/jest/reporter',
    '@qualflare/detox/reporter',
  ],
  globalSetup: 'detox/runners/jest/globalSetup',
  globalTeardown: 'detox/runners/jest/globalTeardown',
  testEnvironment: 'detox/runners/jest/testEnvironment',
};

Then run the suite and upload both halves in one command:

# The Jest-side report, plus Detox's artifacts directory
qf myapp collect ./qualflare-results --artifacts-dir ./artifacts

Nothing is scanned unless --artifacts-dir names a directory. ./artifacts is Detox’s default and would be an easy guess, but attaching files from a directory you did not name is how a previous run’s video ends up on today’s launch — and that mistake is invisible from the dashboard, which is exactly the kind of wrong answer worth refusing to give. Point it at the artifacts root and the newest <configuration>.<timestamp> run inside it is used; point it at one specific run directory and that one is used as given.

Video and logs are opt-in

# Screenshots upload by default. Video and device logs are opt-in,
# because either can be the largest thing in a report by an order of magnitude.
qf myapp collect ./qualflare-results \
  --artifacts-dir ./artifacts \
  --upload-artifacts video,trace

Annotate tests from inside the suite

The reporter exposes a small runtime API, so a Detox test can label itself, group its actions into steps, and attach data the screenshots do not show:

import { qualflare } from '@qualflare/detox';

it('signs in', async () => {
  qualflare.label('team', 'identity');
  qualflare.tag('smoke');

  await qualflare.step('enter credentials', async () => {
    await element(by.id('email')).typeText('[email protected]');
    await element(by.id('password')).typeText('correct horse');
  });

  await element(by.id('login')).tap();
  await expect(element(by.text('Welcome'))).toBeVisible();
});

CI: upload even when the suite fails

The single most common way to lose Detox artifacts is to let a failing detox test end the job before anything is uploaded. A red run is precisely the one whose screenshots and video you want, so the upload step has to run regardless:

# .github/workflows/e2e.yml
- name: Detox test
  run: |
    detox test --configuration ios.sim.debug \
      --take-screenshots failing --record-videos failing
  continue-on-error: true

# Runs even when the suite failed — a red run is the one whose
# screenshots and video you actually want.
- name: Upload results
  if: always()
  run: |
    qf login "$QF_PROJECT" "$QF_TOKEN" --force
    qf "$QF_PROJECT" collect ./qualflare-results \
      --artifacts-dir ./artifacts \
      --upload-artifacts video,trace

Common Detox reporting problems

“No artifacts appeared on any test”

Check stderr from the upload. A scan that found directories but matched no tests says so explicitly, rather than passing silently — that warning exists because a matching problem and a run with genuinely no artifacts look identical once the data is in a dashboard. The usual cause is pointing --artifacts-dir at a different run than the report came from.

“Screenshots arrived but the video did not”

Two independent switches, and both are needed: Detox has to record the video (--record-videos failing) and the upload has to include it (--upload-artifacts video). Screenshots need only the first, because images upload by default.

“A test’s artifacts landed on the wrong test”

Two tests whose full names differ only in a character Detox has to replace — a slash versus an underscore — produce the same directory name, and Detox itself cannot tell them apart either. Renaming one test fixes it permanently; there is no way to recover the distinction from disk.

“Old artifacts keep showing up”

Detox appends a new <configuration>.<timestamp> directory per run rather than clearing the root, so a long-lived CI workspace accumulates them. Pointing --artifacts-dir at the root picks the newest, but clearing the directory between runs is the more predictable habit.

Detox is one of twelve frameworks with a native Qualflare reporter behind it — in its case Jest’s, because that is genuinely what runs the tests. Weighing tools? See how Qualflare compares to other test management platforms, or browse all framework reporting guides.

Frequently asked questions

Is there a dedicated Qualflare reporter for Detox?

Yes — @qualflare/detox, which is the package to install. What is worth knowing is how it is built, because it explains why setup is so small: Detox does not implement a test runner of its own. `detox test` drives Jest, so a reporter listed in jest.config runs normally inside a Detox run, and @qualflare/detox uses Qualflare’s Jest reporter rather than reimplementing one. It adds the Detox-specific configuration — labelling the launch as Detox, and resolving iOS or Android from the run itself — and you never install or name the Jest package. The one thing no reporter can see is Detox’s artifacts directory, which the CLI attaches with --artifacts-dir.

Why are Detox artifacts attached by the CLI instead of by the reporter?

Because of an ordering in Jest that no reporter can work around. Detox finalises video (and flushes device logs) in its artifact plugin’s onBeforeCleanup, which runs inside detox.cleanup(), which Detox’s Jest integration calls from Jest’s globalTeardown. Jest runs globalTeardown after every reporter’s onRunComplete — measured directly, not assumed. A reporter-side scan would therefore run before Detox had finished writing and would systematically miss videos, the largest and most useful artifact. Adding a delay or a retry inside the reporter would only make the miss intermittent, which is worse than missing it consistently. `qf collect` runs as a separate command after the Detox process has exited, so the race does not exist there.

How does Qualflare know which artifact belongs to which test?

Detox names each per-test artifact directory after the test’s full name, prefixed with ✓ or ✗ and suffixed " (2)", " (3)" for later invocations of a retried test. The CLI computes that name forward from each test in the report and looks it up, rather than parsing a directory name back into a test name. That direction matters because the transformation is lossy: a slash in a test name becomes an underscore, so "a/b" and "a_b" produce the same directory and inverting it would be guesswork. Computing it forward is exact. Directories that match no test are reported on stderr with a count and the first few names, and a scan that matched nothing at all while finding directories is treated as a warning in its own right — because that is far more likely to be a matching bug than a run with no artifacts, and the two look identical from a dashboard.

Does Qualflare run my Detox tests or provision simulators?

No. Qualflare is a results-management and observability layer, not a device-execution cloud. It never boots a simulator or emulator, never builds your app, and does not schedule your run. It reads the report your run already wrote and the artifacts Detox already saved, so it works the same whether the run happened on your laptop, a CI macOS runner, a self-hosted Android emulator, or a device cloud.

Do retried Detox tests show every attempt?

Yes. Jest reports each attempt of a retried test, so the reporter records the whole history — a test that failed twice and then passed arrives marked flaky with the failed attempts attached rather than collapsing into a single green result. Detox writes one artifact directory per invocation, suffixed " (2)" and so on, and all of them attach to the same test with the attempt named in the attachment, so a screenshot of the failure that eventually passed is not lost.

Will uploading Detox videos make my reports enormous?

Only if you ask for them. Screenshots upload by default; video, device logs, instruments recordings and UI hierarchy snapshots are opt-in through --upload-artifacts, because a screen recording of a mobile flow is routinely the largest thing in a report by an order of magnitude and should be a deliberate choice rather than a surprise. Pairing --record-videos failing with --upload-artifacts video is the usual middle ground: a recording exists only for the tests that failed, and only those are uploaded.

Setup reflects the Qualflare CLI (docs.qualflare.com) and Detox 20 as of September 2026; the artifact naming and CLI flags were read from Detox’s own source. Published 27 September 2026. Written by İbrahim Süren, founder of Qualflare.