GitHub - ctrf-io/ctrf-js: CTRF reference implementation in 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.

Installation

Quick 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))
}

// 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()
  .tool({ name: 'vitest', version: '1.0.0' })
  .environment({ os: 'linux', arch: 'x64' })
  .addTest(/* ... */)
  .build()

// TestBuilder - fluent API for constructing tests
const test = new TestBuilder()
  .name('User login test')
  .status('passed')
  .duration(1500)
  .suite(['Authentication', 'Login'])
  .tags(['smoke', 'critical'])
  .filePath('tests/auth/login.test.ts')
  .browser('chrome')
  .build()

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.0.0'
SUPPORTED_SPEC_VERSIONS // ['0.0.0']
TEST_STATUSES           // ['passed', 'failed', 'skipped', 'pending', 'other']
CTRF_NAMESPACE          // UUID namespace for deterministic IDs

Error Handling

import { validateStrict, ValidationError, ParseError } from 'ctrf'

try {
  validateStrict(report)
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Schema 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 schema for specific version
const v0_0Schema = getSchema('0.0.0')

// Get version info
const version = getCurrentSpecVersion()      // '0.0.0'
const supported = getSupportedSpecVersions() // ['0.0.0']

⚠️ Upgrading from v0.0.17?

Version 0.1.0 contains breaking changes. See the Migration Guide for detailed upgrade instructions.