@devant-net/reporter-core
v0.1.9
Published
Shared utilities for devq-cloud test reporters (HTTP queue, CI detection, stable IDs, test-case resolution, artifact upload).
Readme
@devant-net/reporter-core
Shared utilities for building test-runner reporters that stream results into Devant Cloud.
⚠️ Internal building block. Most users want one of the framework-specific packages instead:
@devant-net/playwright-reporter@devant-net/cypress-reporter(planned)@devant-net/pytest-reporter(planned)
What it gives you
DevqClient— typed wrapper around the/v1/runs/*,/v1/test-cases/*and artifact endpoints. Owns aRequestQueueso writes are non-blocking.RequestQueue— bounded-retry HTTP queue with exponential backoff + jitter.flush()lets the framework's exit hook block until every in-flight request settles, so results never get dropped on shutdown.detectCI()— populates theciblock on a run from environment variables. GitHub Actions, GitLab CI, CircleCI, Jenkins, Azure DevOps, plus a genericCI=truefallback.resolveTestCase()—@KEYtag → name match → auto-create. Same resolution order as the API expects.uploadArtifacts()— multipart upload with path-traversal safety (only files inside the framework'soutputDirare allowed).generateStableId()— SHA-256 hash offile_path + title_path + titlefor cross-run identity when@KEYtags aren't present.
Wire (Devant Cloud)
| Step | Endpoint |
|---|---|
| Create run | POST /v1/runs |
| Resolve test case | GET /v1/test-cases/by-key/:key → GET /v1/test-cases?search=… → POST /v1/test-cases |
| Submit results | POST /v1/runs/:id/results |
| Upload artifact | POST /v1/runs/:id/results/:rid/attempts/:aid/artifacts (multipart) |
| Complete run | POST /v1/runs/:id/complete |
Building a new reporter
import {
DevqClient,
detectCI,
extractKey,
} from "@devant-net/reporter-core";
const client = new DevqClient({
apiUrl: process.env.DEVQ_API_URL!,
apiToken: process.env.DEVQ_TOKEN!,
projectId: Number(process.env.DEVQ_PROJECT_ID),
});
// onBegin
const run = await client.createRun({
name: `<framework> — ${new Date().toISOString()}`,
framework: "<framework>",
ci: detectCI(),
});
// per-test
const tc = await client.resolveTestCase({
explicitKey: extractKey(...test.tags),
fullName: `${test.suite} > ${test.title}`,
});
const [submitted] = await client.submitResults(run.id, [{
test_case_id: tc.id,
status: "pass",
attempts: [{ attempt_no: 1, status: "pass", duration_ms: 123 }],
}]);
await client.uploadArtifacts(
{
runId: run.id,
resultId: submitted.result_id,
attemptId: submitted.attempt_ids[0]!.id,
attemptNo: 1,
outputDir: "/abs/path/to/test-results",
},
attachments,
);
// onEnd
await client.completeRun(run.id);
await client.flush();Why this exists separate from each framework reporter
The HTTP/retry/CI-detection logic is identical across Playwright, Cypress, pytest, etc. Only the hooks (when each framework calls into the reporter) and the type adapters (mapping framework status/step trees to Devant Cloud shapes) differ. Putting the shared parts in core means each new framework reporter is ~150 lines instead of ~500.
