Skip to content

Report exports: PDF, JUnit XML and JSON

AutoTestX results can leave the app as files people and other tools read:

  • PDF of a run, a suite run or a release report, to read, sign off or attach to a ticket.
  • JUnit XML, the test-result format of GitHub, GitLab, Azure DevOps and Jenkins, so a pipeline shows AutoTestX tests in its own test tab.
  • JSON in a versioned envelope, for integrations and archiving.

An export reads the same records as the report on screen, so the two always agree. Whoever can open a run or a suite run can export it, Viewers included. A private test’s runs export only for the test’s owner, the same as its report.

From How
A pipeline atx run --suite Smoke --junit results.xml (CLI)
A local run atx run test.json --junit results.xml (CLI)
The API The routes below, with a session or an API token
Method Path Returns
GET /api/executions/:id/report.junit.xml A suite run as JUnit XML
GET /api/runs/:id/report.junit.xml One run as JUnit XML
GET /api/executions/:id/export.json A suite run and every run it started, as autotestx.execution/1
GET /api/runs/:id/export.json One run, as autotestx.run/1
GET /api/runs/:id/report.pdf One run’s report as PDF
GET /api/executions/:id/report.pdf A suite run’s report as PDF
GET /api/releases/:release/report.pdf The release evidence report as PDF. ?environment= as on /report.
GET /api/releases/:release/export.json The release evidence report, as autotestx.release-report/1. ?environment= as on /report.

Each answers with Content-Disposition: attachment and a file name (exec_….junit.xml, run_….json). A suite run still in progress, or a run not yet finished, answers 409: export it once it has finished.

In the app, Download PDF sits at the top of the run report, of the suite run panel and of the release report (Test Suites → Recent suite runs → pick a release).

Report What the PDF holds
Run The test, its version, environment, release, start, duration, failure class and AI suggestion; then every step as executed with its screenshot, expected and actual, retries, fallback locators and self-healing; database checks with their rows; the run’s variables. A data-driven run has the rows table, then each row’s steps, with screenshots only for the steps that did not pass.
Suite run The result with its counts, environment, release, commit, branch, build and what started it; every test with status, steps, duration, why it failed or was skipped, and a link to its run.
Release The recommendation with its blockers and risks, totals, likely causes, the suites that count, suites that did not run, failing tests marked Regression, New or Known, and tests fixed since the release before.

Every page’s header names the report, the environment, the release and the timezone; its footer the report’s id, “Generated

A video or a trace can’t go in a PDF: the run PDF says whether they were kept and links to the run. Values are as the run page shows them, secrets masked. The server prints the PDF with the Chromium the runner already uses, two at a time.

One <testsuite> per suite run, one <testcase> per test:

<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="AutoTestX" tests="3" failures="1" errors="0" skipped="1" time="184.200">
<testsuite name="Checkout smoke" timestamp="2026-10-27T09:14:02" hostname="Staging"
tests="3" failures="1" errors="0" skipped="1" time="184.200">
<properties>
<property name="atx.executionId" value="exec_…"/>
<property name="atx.url" value="https://app.autotestx.com/#/suites/exec_…"/>
<property name="atx.trigger" value="api"/>
<property name="atx.status" value="failed"/>
<property name="atx.release" value="v2.14.0"/>
<property name="atx.commit" value="9f2c1ab"/>
</properties>
<testcase name="Sign in" classname="Checkout smoke" time="3.000">
<system-out>PASS 1. Open 0.500s
PASS 2. Sign in 1.200s
Report: https://app.autotestx.com/#/runs/run_…</system-out>
</testcase>
<testcase name="Place an order" classname="Checkout smoke" time="14.800">
<failure message="Step 7 'Order total': Text did not match" type="assertion">Expected: 42.00
Actual: 40.00
AssertionError: Text did not match
Failure class: Wrong value
Report: https://app.autotestx.com/#/runs/run_…</failure>
<system-out>…</system-out>
</testcase>
<testcase name="Refund" classname="Checkout smoke" time="0.000">
<skipped message="Draft: not run with --ready"/>
</testcase>
</testsuite>
</testsuites>
AutoTestX JUnit
Run passed <testcase> with no <failure> or <error>
Run failed <failure>. message is the first failed step and its error; type is the failure class (assertion, locator, server-error, …). The body has expected and actual, the error, the class and the report link.
Run error (could not complete) <error>, with the same fields
Test skipped by the suite run (a Draft under --ready, a Deprecated test) <skipped> with the reason
Test cancelled (the suite run was stopped before it started) <skipped message="Cancelled"/>. JUnit has no cancelled state.
Data-driven run One <testcase> per row, named Test [row 3], so 2 failed rows of 40 count as 2 failures, not 1

A soft assertion that failed the run is reported like any failure, naming its step.

  • <system-out> of each test lists every step as PASS 3. Save 0.412s (FAIL, WARN, SKIP, HEAL), then anything a green result would hide: steps that self-healed, steps that needed a retry, steps found by a fallback locator. The last line is the run report’s link.
  • Properties are on the <testsuite> only: the suite run’s id and link, what started it, its final status, and the release, commit, branch and build it was labelled with. Properties it doesn’t have are left out. A single run’s export carries atx.runId, atx.url and atx.testVersion.
  • timestamp is the start time in UTC, written without a zone (2026-10-27T09:14:02), as the JUnit schema requires. hostname is the environment’s name, or none.

No screenshot, and no value typed into the page or read from a data row: values are masked in the run already, but a CI log is the wrong place for them either way. Control characters and terminal colour codes in error messages are removed, so the file always parses.

Every JSON export has the same envelope:

{
"schema": "autotestx.run/1",
"generatedAt": "2026-10-27T09:20:11.402Z",
"generatedBy": { "kind": "user", "id": "usr_…", "name": "Vera" },
"server": { "url": "https://app.autotestx.com", "version": "0.1.0" },
"data": { … }
}
Field Meaning
schema What data is, and its version: autotestx.run/1, autotestx.execution/1, autotestx.release-report/1. The number changes only when a field is removed or changes meaning. A new field does not change it.
generatedBy.kind user for a person, token for an API token
data The record the API returns for it, unchanged, plus url (its page). autotestx.execution/1 adds runs: every run the suite run started, in run order.

atx run --json keeps writing the plain result, without the envelope, so pipelines that already read it don’t break.