Parses JUnit-format XML reports (the de-facto interchange format every CI ingests - Jenkins, GitHub Actions, GitLab, Buildkite, CircleCI) into structured, machine-readable per-suite and per-case metrics tables (passed / failed / errored / skipped, time, classname, message, stack), groups failures by classname for trend analysis, and distinguishes "new failures vs flakes" by cross-referencing the `flakyFailure` and `rerunFailure` rerun elements. Use when the downstream consumer is a dashboard, script, or aggregator - not when the goal is a human-readable prose summary (use test-run-summary-author for that). Single-run, in-XML aggregation only; for cross-run cross-environment roll-ups, use a cross-run test-suite aggregator.
90
90%
Does it follow best practices?
Impact
—
Average score across 10 eval scenarios
Passed
No findings from the security scan
Companion detail for junit-xml-analysis. The Python parse_junit.py in SKILL.md is the runnable core; this file holds the Node.js equivalent and the CI workflow.
import { XMLParser } from 'fast-xml-parser';
import { readFileSync } from 'node:fs';
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' });
const xml = parser.parse(readFileSync(path, 'utf8'));
const suites = xml.testsuites
? (Array.isArray(xml.testsuites.testsuite) ? xml.testsuites.testsuite : [xml.testsuites.testsuite])
: [xml.testsuite];
for (const suite of suites) {
const cases = Array.isArray(suite.testcase) ? suite.testcase : [suite.testcase];
// ...
}Handle both root shapes (<testsuites> or a bare <testsuite>) and single-element collapsing (one testcase = bare object, multiple = array), which is common in JS XML libraries.
# .github/workflows/test-analytics.yml
- name: Run tests (any framework, JUnit XML reporter enabled)
run: npm test -- --reporters=default,jest-junit
env:
JEST_JUNIT_OUTPUT_FILE: junit.xml
- name: Analyze JUnit XML
if: always()
run: python scripts/parse_junit.py junit.xml > analytics.json
- name: Upload analytics
if: always()
uses: actions/upload-artifact@v4
with:
name: junit-analytics
path: |
junit.xml
analytics.jsonif: always() is critical - JUnit XML matters most on failed runs.