Publish Test Results
Config, version check, and pattern rules per claude/rules/skills.md preamble.
Step 1: Resolve TMS Provider
Read outputs.tms.provider from sparq.config.json.
- If set: use that provider; confirm with user
- If missing: ask "Which TMS are you publishing to? (testrail / qase / zephyr)"
Step 2: Locate Test Output
Check common output paths in order:
playwright-report/results.json (Playwright JSON reporter)
test-results/*.json (Playwright blob reporter output)
test-results/**/*.xml (JUnit XML — Playwright or Cypress)
cypress/results/**/*.xml (Cypress JUnit XML)
If none found: ask the user to provide the path. Example prompt:
"I couldn't find test output at the default paths. Where is your test results file? (e.g., my-results/output.xml)"
Step 3: Detect Format
- File extension
.json + contains "suites" or "stats" key → Playwright JSON
- File extension
.xml + root element <testsuites> or <testsuite> → JUnit XML
Step 4: Parse Results
Playwright JSON (results.json):
- Iterate
suites[].specs[].tests[]
- Extract:
title (full test title), status (passed/failed/skipped/timedOut), duration (ms), error.message (if failed)
JUnit XML:
- Parse
<testcase> elements
- Extract:
name attribute (test title), presence of <failure> child (failed), <skipped> child (skipped), time attribute (seconds → ms), <failure> text (error message)
timedOut detected from failure message containing "Timeout"
Step 5: Extract TC IDs
For each test result, apply regex TC-[A-Z0-9]+-[A-Z0-9]+-\d+ to the test title.
- Match found → associate result with that TC ID
- No match → group under "Untracked Tests" (separate run or appended section)
Step 6: Create Test Run in TMS
Create a test run named: SparQ Results — {date} {branch} (branch from git rev-parse --abbrev-ref HEAD if available, else omit).
- Include only the case IDs found in the parsed results (matched TC IDs resolved to TMS case IDs via
tmsId frontmatter in .sparq/test-cases/)
- If no
tmsId mappings exist, include all cases in the project/suite (provider default)
Step 7: Post Results per Test Case
Map SparQ/Playwright statuses to TMS statuses per <tms_tools> section. Post in bulk where supported (TestRail add_results_for_cases), per-result otherwise (Qase, Zephyr).
Step 8: Report
Emit summary:
[sparq] Results published:
Provider: TestRail (project: {projectId})
Run: {runName} — {runUrl}
Posted: {N} passed, {N} failed, {N} skipped
Unmatched (no TC ID): {N} tests (see "Untracked Tests" run)
Status Mapping
passed → PASS (TestRail: 1, Qase: passed, Zephyr: PASS)
failed / timedOut → FAIL (TestRail: 5, Qase: failed, Zephyr: FAIL)
skipped → NOT_EXECUTED (TestRail: 3 (Skipped), Qase: skipped, Zephyr: NOT_EXECUTED)
TestRail
- Create run:
mcp__testrail__add_run(project_id, name, case_ids[])
- Batch results:
mcp__testrail__add_results_for_cases(run_id, results[]) — preferred for performance
- Per-result fallback:
mcp__testrail__add_result_for_case(run_id, case_id, status_id, comment)
comment field: include duration (ms) and error message truncated to 500 chars
Qase
- Create run:
mcp__qase__create_run(project_code, title, description?)
- Per result:
mcp__qase__create_result(project_code, run_id, case_id, status, time_ms, comment?)
- No batch API — loop per test result
Zephyr Scale
- Create cycle:
mcp__zephyr__create_test_cycle(project_key, name, description?)
- Per result:
mcp__zephyr__add_test_result(project_key, test_cycle_key, test_case_key, status, comment?)
References
claude/skills/sparq-shared/references/tms-abstraction.md
claude/skills/sparq-shared/references/mcp-tool-inventory.md
claude/skills/sparq-shared/references/degradation-strategy.md
Usage
/sparq:publish-results
→ reads sparq.config.json (outputs.tms.provider: "testrail")
→ detects playwright-report/results.json (Playwright JSON format)
→ parses 42 test results, extracts 38 TC IDs
→ creates TestRail run "SparQ Results — 2026-02-20 feature/login"
→ posts 35 passed, 2 failed, 1 skipped via mcp__testrail__add_results_for_cases
→ output: https://team.testrail.io/index.php?/runs/view/42
→ 4 untracked tests (no TC ID) listed in summary
/sparq:publish-results
→ outputs.tms.provider: "qase"
→ detects test-results/junit.xml (JUnit XML format)
→ creates Qase run, posts results per mcp__qase__create_result
→ output: https://app.qase.io/run/PROJ/1
1---2name: sparq-publish-results3description: Publishing E2E test execution results from Playwright/Cypress back to TestRail, Qase, or Zephyr Scale TMS after CI runs. Use when: (1) posting pass/fail results from a completed CI run to a TMS, (2) creating a test run record in TestRail or Qase from Playwright JSON or JUnit XML output, (3) syncing CI results back to the test management system.4---56# Publish Test Results78Config, version check, and pattern rules per `claude/rules/skills.md` preamble.910<overview>11Reads Playwright JSON reporter output (`playwright-report/results.json` or `test-results/*.json`) or JUnit XML (`test-results/**/*.xml`), maps test titles to TC IDs embedded as `TC-{feature}-{ABBR}-{NNN}` (regex: `TC-[A-Z0-9]+-[A-Z0-9]+-\d+`), creates a test run in the configured TMS, and posts pass/fail/skip per matched test case. Tests with no TC ID are grouped into an "Untracked Tests" run. Supports TestRail, Qase, and Zephyr Scale. Falls back to a local CSV file when MCP is unavailable.12</overview>1314<workflow>1516### Step 1: Resolve TMS Provider1718Read `outputs.tms.provider` from `sparq.config.json`.19- If set: use that provider; confirm with user20- If missing: ask "Which TMS are you publishing to? (testrail / qase / zephyr)"2122### Step 2: Locate Test Output2324Check common output paths in order:251. `playwright-report/results.json` (Playwright JSON reporter)262. `test-results/*.json` (Playwright blob reporter output)273. `test-results/**/*.xml` (JUnit XML — Playwright or Cypress)284. `cypress/results/**/*.xml` (Cypress JUnit XML)2930If none found: ask the user to provide the path. Example prompt:31> "I couldn't find test output at the default paths. Where is your test results file? (e.g., `my-results/output.xml`)"3233### Step 3: Detect Format3435- File extension `.json` + contains `"suites"` or `"stats"` key → Playwright JSON36- File extension `.xml` + root element `<testsuites>` or `<testsuite>` → JUnit XML3738### Step 4: Parse Results3940**Playwright JSON** (`results.json`):41- Iterate `suites[].specs[].tests[]`42- Extract: `title` (full test title), `status` (`passed`/`failed`/`skipped`/`timedOut`), `duration` (ms), `error.message` (if failed)4344**JUnit XML**:45- Parse `<testcase>` elements46- Extract: `name` attribute (test title), presence of `<failure>` child (failed), `<skipped>` child (skipped), `time` attribute (seconds → ms), `<failure>` text (error message)47- `timedOut` detected from failure message containing "Timeout"4849### Step 5: Extract TC IDs5051For each test result, apply regex `TC-[A-Z0-9]+-[A-Z0-9]+-\d+` to the test title.52- Match found → associate result with that TC ID53- No match → group under "Untracked Tests" (separate run or appended section)5455### Step 6: Create Test Run in TMS5657Create a test run named: `SparQ Results — {date} {branch}` (branch from `git rev-parse --abbrev-ref HEAD` if available, else omit).58- Include only the case IDs found in the parsed results (matched TC IDs resolved to TMS case IDs via `tmsId` frontmatter in `.sparq/test-cases/`)59- If no `tmsId` mappings exist, include all cases in the project/suite (provider default)6061### Step 7: Post Results per Test Case6263Map SparQ/Playwright statuses to TMS statuses per `<tms_tools>` section. Post in bulk where supported (TestRail `add_results_for_cases`), per-result otherwise (Qase, Zephyr).6465### Step 8: Report6667Emit summary:68```69[sparq] Results published:70 Provider: TestRail (project: {projectId})71 Run: {runName} — {runUrl}72 Posted: {N} passed, {N} failed, {N} skipped73 Unmatched (no TC ID): {N} tests (see "Untracked Tests" run)74```7576</workflow>7778<tms_tools>7980### Status Mapping8182- `passed` → PASS (TestRail: 1, Qase: `passed`, Zephyr: `PASS`)83- `failed` / `timedOut` → FAIL (TestRail: 5, Qase: `failed`, Zephyr: `FAIL`)84- `skipped` → NOT_EXECUTED (TestRail: 3 (Skipped), Qase: `skipped`, Zephyr: `NOT_EXECUTED`)8586### TestRail8788- Create run: `mcp__testrail__add_run(project_id, name, case_ids[])`89- Batch results: `mcp__testrail__add_results_for_cases(run_id, results[])` — preferred for performance90- Per-result fallback: `mcp__testrail__add_result_for_case(run_id, case_id, status_id, comment)`91- `comment` field: include duration (ms) and error message truncated to 500 chars9293### Qase9495- Create run: `mcp__qase__create_run(project_code, title, description?)`96- Per result: `mcp__qase__create_result(project_code, run_id, case_id, status, time_ms, comment?)`97- No batch API — loop per test result9899### Zephyr Scale100101- Create cycle: `mcp__zephyr__create_test_cycle(project_key, name, description?)`102- Per result: `mcp__zephyr__add_test_result(project_key, test_cycle_key, test_case_key, status, comment?)`103104</tms_tools>105106<fallback>107When MCP is unavailable (per `degradation-strategy.md`):1081. Log: `"[sparq] {Provider} MCP unavailable — generating local CSV fallback"`1092. Write `.sparq/results/{YYYY-MM-DD}-results.csv` with columns:110 - `TC ID`, `Test Title`, `Status`, `Duration (ms)`, `Error Message`1113. Instruct user: "Import this CSV manually into your TMS. TestRail supports bulk result import via the API or CSV upload in a test run."1124. Report fallback file path to user113</fallback>114115<done_criteria>116- [ ] TMS provider resolved from config or user input117- [ ] Test output file located and format detected (Playwright JSON or JUnit XML)118- [ ] All test results parsed: title, status, duration, error (if any)119- [ ] TC IDs extracted from test titles via regex `TC-[A-Z0-9]+-[A-Z0-9]+-\d+`120- [ ] Test run created in TMS (or fallback CSV generated at `.sparq/results/{date}-results.csv`)121- [ ] Pass/fail/skip posted per matched test case with correct TMS status codes122- [ ] Run URL reported to user (or CSV path if fallback)123- [ ] Untracked tests (no TC ID) reported with count124</done_criteria>125126## References127128- `claude/skills/sparq-shared/references/tms-abstraction.md`129- `claude/skills/sparq-shared/references/mcp-tool-inventory.md`130- `claude/skills/sparq-shared/references/degradation-strategy.md`131132## Usage133134```135/sparq:publish-results136→ reads sparq.config.json (outputs.tms.provider: "testrail")137→ detects playwright-report/results.json (Playwright JSON format)138→ parses 42 test results, extracts 38 TC IDs139→ creates TestRail run "SparQ Results — 2026-02-20 feature/login"140→ posts 35 passed, 2 failed, 1 skipped via mcp__testrail__add_results_for_cases141→ output: https://team.testrail.io/index.php?/runs/view/42142→ 4 untracked tests (no TC ID) listed in summary143144/sparq:publish-results145→ outputs.tms.provider: "qase"146→ detects test-results/junit.xml (JUnit XML format)147→ creates Qase run, posts results per mcp__qase__create_result148→ output: https://app.qase.io/run/PROJ/1149```