The reference implementation in TypeScript for the Common Test Report Format (CTRF) specification.
CTRF is a community-driven open standard for test reporting.
By standardizing test results, reports can be validated, merged, compared, and analyzed consistently across languages and frameworks.
- CTRF Specification: https://github.com/ctrf-io/ctrf
The official specification defining the format and semantics - Discussions: https://github.com/orgs/ctrf-io/discussions
Community forum for questions, ideas, and support
Note
⭐ Starring the CTRF specification repository (https://github.com/ctrf-io/ctrf) helps support the standard.
npm install ctrfimport { ReportBuilder, TestBuilder, validateStrict } from "ctrf";
// Build a report using the fluent API
const report = new ReportBuilder()
.tool({ name: "jest", version: "29.0.0" })
.addTest(
new TestBuilder()
.name("should add numbers")
.status("passed")
.duration(150)
.build(),
)
.addTest(
new TestBuilder()
.name("should handle errors")
.status("failed")
.duration(200)
.message("Expected 5 but got 4")
.build(),
)
.build();
// Validate the report
validateStrict(report);📚 Full API Documentation: API Reference
Full TypeScript types are provided for all CTRF entities.
import type { CTRFReport, Test } from "ctrf";
const report: CTRFReport = {
/* ... */
};
const test: Test = {
/* ... */
};import {
isValid,
validate,
validateStrict,
isCTRFReport,
ValidationError,
} from "ctrf";
// Quick validation (returns boolean)
if (isValid(report)) {
console.log("Report is valid");
}
// Detailed validation (returns ValidationResult)
const result = validate(report);
if (!result.valid) {
result.errors?.forEach((err) => console.error(err.message));
}
// Validate against a published historical schema
const historicalResult = validate(report, { specVersion: "0.0.2" });
// `latest` is an alias for the current published specification
validateStrict(report, { specVersion: "latest" });
// Strict validation (throws on invalid)
try {
validateStrict(report);
} catch (error) {
if (error instanceof ValidationError) {
console.error("Invalid report:", error.errors);
}
}
// Type guards
if (isCTRFReport(data)) {
// data is typed as CTRFReport
}import { ReportBuilder, TestBuilder } from "ctrf";
// ReportBuilder - fluent API for constructing reports
const report = new ReportBuilder()
.runId("run-2026-08-13")
.tool({ name: "vitest", version: "1.0.0" })
.environment({ osPlatform: "linux", shardId: "shard-1-of-4" })
.addTest(/* ... */)
.build();
// TestBuilder - fluent API for constructing tests
const test = new TestBuilder()
.testId("authentication/login")
.executionId("execution-123")
.attemptId("attempt-1")
.name("User login test")
.status("passed")
.duration(1500)
.suite(["Authentication", "Login"])
.tags(["smoke", "critical"])
.filePath("tests/auth/login.test.ts")
.browser("chrome")
.build();retryAttempts is the ordered history of attempts completed before the final
attempt represented by the test object. It includes the initial attempt when a
retry occurred and excludes the final attempt. retries must equal the number
of history entries.
const flakyTest = new TestBuilder()
.name("eventually succeeds")
.attemptId("attempt-3")
.status("passed") // Final attempt
.duration(120)
.addAttemptHistoryEntry({
attempt: 1,
attemptId: "attempt-1",
status: "failed",
duration: 80,
})
.addAttemptHistoryEntry({
attempt: 2,
attemptId: "attempt-2",
status: "failed",
duration: 90,
})
.flaky()
.build();
// flakyTest.retries === 2
// flakyTest.retryAttempts contains attempts 1 and 2; the final attempt is 3.addRetryAttempt() remains available as a deprecated alias for
addAttemptHistoryEntry().
import { parse, stringify } from "ctrf";
// Parse from string
const parsed = parse(jsonString);
// Stringify with formatting
const json = stringify(report, { pretty: true, indent: 2 });import { filterTests, findTest } from "ctrf";
// Filter by criteria
const filtered = filterTests(report, {
status: "failed",
suite: "Authentication",
tags: ["smoke"],
});
// Find specific test
const test = findTest(report, { name: "login test" });
const testById = findTest(report, { id: "test-uuid" });import { merge } from "ctrf";
// Merge multiple reports into one
const merged = merge([report1, report2, report3], {
deduplicateTests: true, // Remove duplicate tests by ID
});import { generateTestId, generateReportId } from "ctrf";
// Generate deterministic test ID from properties
const testId = generateTestId({
name: "should add numbers",
suite: ["Math", "Addition"],
filePath: "tests/math.test.ts",
});
// Generate random report ID
const reportId = generateReportId();import { addInsights, isTestFlaky } from "ctrf";
// Enrich a report with insights from historical data
const enriched = addInsights(currentReport, historicalReports, {
baseline: baselineReport,
});
// Access insights
console.log(enriched.insights?.passRate); // { current: 0.95, baseline: 0.90, change: 0.05 }
console.log(enriched.insights?.flakyRate); // { current: 0.02, baseline: 0.05, change: -0.03 }
// Check if a test is flaky
const isFlaky = isTestFlaky(test);import { calculateSummary } from "ctrf";
// Calculate summary from tests
const summary = calculateSummary(tests);
// { tests: 10, passed: 8, failed: 1, skipped: 1, pending: 0, other: 0, ... }import {
REPORT_FORMAT,
CURRENT_SPEC_VERSION,
SUPPORTED_SPEC_VERSIONS,
TEST_STATUSES,
CTRF_NAMESPACE,
} from "ctrf";
REPORT_FORMAT; // 'CTRF'
CURRENT_SPEC_VERSION; // '0.2.0'
SUPPORTED_SPEC_VERSIONS; // ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.1.0', '0.2.0']
TEST_STATUSES; // ['passed', 'failed', 'skipped', 'pending', 'other']
CTRF_NAMESPACE; // UUID namespace for deterministic IDsimport { validateStrict, ValidationError, ParseError } from "ctrf";
try {
validateStrict(report);
} catch (error) {
if (error instanceof ValidationError) {
console.error("CTRF validation failed:", error.errors);
}
}
try {
const parsed = parse(jsonString);
} catch (error) {
if (error instanceof ParseError) {
console.error("Invalid JSON:", error.message);
}
}import {
schema,
getSchema,
getCurrentSpecVersion,
getSupportedSpecVersions,
} from "ctrf";
// Get the current JSON Schema
console.log(schema);
// Get schemas for a specific version or the latest published specification
const v0_0_2Schema = getSchema("0.0.2");
const latestSchema = getSchema("latest");
// Get version info
const version = getCurrentSpecVersion(); // '0.2.0'
const supported = getSupportedSpecVersions(); // ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.1.0', '0.2.0']