Appearance
Writing report files
EasyTrace reads the report files your test runner writes. Put them under .easytrace/ (and add .easytrace/ to .gitignore), then list them in easytrace.config.json.
Vitest
Add the JSON reporter on the command line, keeping the default one for the terminal:
bash
vitest run --reporter=default --reporter=json --outputFile.json=.easytrace/vitest.jsonOr in vitest.config.ts:
ts
export default defineConfig({
test: {
reporters: ['default', 'json'],
outputFile: { json: '.easytrace/vitest.json' },
},
});Add --coverage to upload coverage as well (Coverage). Vitest 2 and 3 are both supported. The JSON report records how many times a test was retried, but not the errors of intermediate attempts.
Jest
bash
jest --json --outputFile=.easytrace/jest.jsonRetries appear as failed attempts with Jest 29 or later (retryReasons). Jest doesn't put console output in its JSON report.
Playwright
Add the json reporter next to the ones you already use, in playwright.config.ts:
ts
export default defineConfig({
reporter: [['list'], ['json', { outputFile: '.easytrace/playwright.json' }]],
use: { trace: 'on-first-retry', screenshot: 'only-on-failure' },
});Playwright reports carry every attempt, steps, stdout/stderr and attachments. To upload screenshots, videos and traces, list the output folder as the suite's attachments (usually test-results).
Cypress
Use the mochawesome reporter with JSON output, one file per spec:
bash
cypress run --reporter mochawesome --reporter-options "reportDir=.easytrace/cypress,json=true,html=false,overwrite=false"List .easytrace/cypress/*.json as the suite's reports; all files of a suite form one run. Screenshots are matched by test name from the suite's attachments folder (e.g. cypress/screenshots).
JUnit XML
Any tool that writes JUnit XML works (--format junit is detected automatically). Retries are only visible with <flakyFailure> / <rerunFailure> elements (Maven Surefire style). Files referenced as [[ATTACHMENT|path]] in <system-out> are uploaded as attachments.
Monorepos
In a monorepo each package writes its own report into its own folder, and easytrace.config.json at the root picks them up with globs — one suite per package, or one suite for several:
json
{
"suites": {
"server": {
"reports": "apps/server/.easytrace/vitest.json",
"coverage": "apps/server/coverage/coverage-final.json"
},
"packages": { "reports": "packages/*/.easytrace/vitest.json" }
}
}Turborepo: no per-package scripts
With turbo you don't need to touch the packages. turbo run test -- <flags> appends the flags to every package's own test script (vitest run …), so one script at the root makes every package write its report:
json
{
"scripts": {
"test:json": "turbo run test --continue --force -- --coverage --reporter=default --reporter=json --outputFile.json=.easytrace/vitest.json",
"test:report": "easytrace run -- pnpm test:json",
"test:report:all": "easytrace run -- sh -c 'pnpm test:json; unit=$?; playwright test && exit $unit'"
}
}| Part | Why |
|---|---|
--continue | A failing package doesn't stop the others, so every suite has a report. |
--force | Turbo must really run the tests. A cache hit replays the output but doesn't rewrite the report file, and easytrace run skips report files older than the run. |
-- … | Everything after -- is appended to each package's vitest run. |
--coverage | Every package computes coverage; only suites that declare coverage upload it. Packages without @vitest/coverage-v8 need it installed at the root: pnpm add -Dw @vitest/coverage-v8@<your vitest major>. |
test:report:all runs Vitest then Playwright in one build: it fails if either failed, and uploads every suite once both are done.
Don't pass turbo options after the script
pnpm test:json --filter=web appends --filter=web after the --, so Vitest receives it instead of turbo. Filter with pnpm exec turbo run test --filter=web -- … instead.