ctrf
v0.4.0
Published
CTRF reference implementation in TypeScript for creating and validating CTRF documents.
Maintainers
Readme
CTRF Reference Implementation TypeScript
The reference implementation in TypeScript for the Common Test Report Format (CTRF) specification.
CTRF Open Standard
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.
Installation
npm install ctrfQuick Start
import { 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)API Reference
📚 Full API Documentation: API Reference
Types
Full TypeScript types are provided for all CTRF entities.
import type { CTRFReport, Test } from 'ctrf'
const report: CTRFReport = { /* ... */ }
const test: Test = { /* ... */ }Validation
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
}Building Reports
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')
.name('User login test')
.status('passed')
.duration(1500)
.suite(['Authentication', 'Login'])
.tags(['smoke', 'critical'])
.filePath('tests/auth/login.test.ts')
.browser('chrome')
.build()Retry Histories
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')
.status('passed') // Final attempt
.duration(120)
.addAttemptHistoryEntry({ attempt: 1, status: 'failed', duration: 80 })
.addAttemptHistoryEntry({ 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().
Parsing Reports
import { parse, stringify } from 'ctrf'
// Parse from string
const parsed = parse(jsonString)
// Stringify with formatting
const json = stringify(report, { pretty: true, indent: 2 })Filtering & Querying
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' })Merging Reports
import { merge } from 'ctrf'
// Merge multiple reports into one
const merged = merge([report1, report2, report3], {
deduplicateTests: true, // Remove duplicate tests by ID
})ID Generation
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()Insights & Analytics
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)Summary Calculation
import { calculateSummary } from 'ctrf'
// Calculate summary from tests
const summary = calculateSummary(tests)
// { tests: 10, passed: 8, failed: 1, skipped: 1, pending: 0, other: 0, ... }Constants
import {
REPORT_FORMAT,
CURRENT_SPEC_VERSION,
SUPPORTED_SPEC_VERSIONS,
TEST_STATUSES,
CTRF_NAMESPACE,
} from 'ctrf'
REPORT_FORMAT // 'CTRF'
CURRENT_SPEC_VERSION // '0.1.0'
SUPPORTED_SPEC_VERSIONS // ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.1.0']
TEST_STATUSES // ['passed', 'failed', 'skipped', 'pending', 'other']
CTRF_NAMESPACE // UUID namespace for deterministic IDsError Handling
import { 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)
}
}Schema Access
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.1.0'
const supported = getSupportedSpecVersions() // ['0.0.1', '0.0.2', '0.0.3', '0.0.4', '0.1.0']