or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdconfiguration.mdindex.mdlaunchers.mdprogrammatic-api.mdreporters.md

reporters.mddocs/

0

# Reporters

1

2

Testem provides multiple output formats for test results including TAP, XUnit, dot notation, TeamCity integration, and an interactive development UI.

3

4

## Capabilities

5

6

### Built-in Reporters

7

8

Testem includes several built-in reporters for different output formats and integrations.

9

10

```javascript { .api }

11

const reporters = {

12

tap: 'TapReporter', // Test Anything Protocol output

13

xunit: 'XUnitReporter', // XUnit XML format for CI integration

14

dot: 'DotReporter', // Dot progress indicator

15

teamcity: 'TeamCityReporter', // TeamCity service messages

16

dev: 'DevReporter' // Interactive development UI (default for dev mode)

17

};

18

```

19

20

**Usage Examples:**

21

22

```bash

23

# Command line reporter selection

24

testem ci -R tap # TAP output

25

testem ci -R xunit # XUnit XML

26

testem ci -R dot # Dot notation

27

testem ci -R teamcity # TeamCity format

28

```

29

30

```javascript

31

// Configuration file

32

{

33

"reporter": "tap",

34

"report_file": "test-results.txt"

35

}

36

```

37

38

### TAP Reporter

39

40

Test Anything Protocol (TAP) output format, widely supported by CI systems and test tooling.

41

42

```javascript { .api }

43

/**

44

* TAP (Test Anything Protocol) reporter

45

* Outputs TAP-compliant test results

46

*/

47

class TapReporter {

48

constructor(silent?: boolean, out?: NodeJS.WritableStream);

49

}

50

51

// TAP configuration options

52

interface TapOptions {

53

tap_failed_tests_only?: boolean; // Only output failing tests

54

tap_quiet_logs?: boolean; // Only show logs for failed tests

55

tap_strict_spec_compliance?: boolean; // Strict TAP spec compliance

56

tap_log_processor?: (log: any) => string; // Custom log processing function

57

}

58

```

59

60

**Example TAP Output:**

61

62

```

63

ok 1 Chrome 91.0 - should add numbers correctly

64

ok 2 Chrome 91.0 - should handle edge cases

65

not ok 3 Firefox 89.0 - should validate input

66

---

67

message: "Expected 'invalid' to throw error"

68

severity: fail

69

data:

70

at: "test/validation.js:15:5"

71

...

72

73

1..3

74

# tests 3

75

# pass 2

76

# fail 1

77

78

# not ok

79

```

80

81

**Usage Examples:**

82

83

```javascript

84

// Basic TAP configuration

85

{

86

"reporter": "tap"

87

}

88

89

// Advanced TAP options

90

{

91

"reporter": "tap",

92

"tap_failed_tests_only": true,

93

"tap_quiet_logs": true,

94

"tap_strict_spec_compliance": true

95

}

96

97

// Custom log processor (testem.js only)

98

module.exports = {

99

reporter: 'tap',

100

tap_log_processor: function(log) {

101

return `[${new Date().toISOString()}] ${log}`;

102

}

103

};

104

```

105

106

### XUnit Reporter

107

108

XML format compatible with JUnit and other CI systems that expect XUnit-style test results.

109

110

```javascript { .api }

111

/**

112

* XUnit XML reporter for CI integration

113

* Generates JUnit-compatible XML output

114

*/

115

class XUnitReporter {

116

constructor(silent?: boolean, out?: NodeJS.WritableStream);

117

}

118

```

119

120

**Example XUnit Output:**

121

122

```xml

123

<?xml version="1.0" encoding="UTF-8" ?>

124

<testsuite name="Testem Tests" tests="3" failures="1" timestamp="2023-06-15T10:30:45Z" time="5.2">

125

<testcase classname="Chrome 91.0" name="should add numbers correctly" time="0.1"/>

126

<testcase classname="Chrome 91.0" name="should handle edge cases" time="0.2"/>

127

<testcase classname="Firefox 89.0" name="should validate input" time="0.3">

128

<failure name="should validate input" message="Expected 'invalid' to throw error">

129

<![CDATA[

130

AssertionError: Expected 'invalid' to throw error

131

at test/validation.js:15:5

132

]]>

133

</failure>

134

</testcase>

135

</testsuite>

136

```

137

138

**Usage Examples:**

139

140

```javascript

141

// XUnit with output file

142

{

143

"reporter": "xunit",

144

"report_file": "test-results.xml"

145

}

146

147

// CI integration

148

{

149

"reporter": "xunit",

150

"launch_in_ci": ["Chrome", "Firefox"]

151

}

152

```

153

154

### Dot Reporter

155

156

Minimalist dot-based progress indicator showing test execution status.

157

158

```javascript { .api }

159

/**

160

* Dot progress reporter

161

* Shows dots for passing tests, F for failures

162

*/

163

class DotReporter {

164

constructor(silent?: boolean, out?: NodeJS.WritableStream);

165

}

166

```

167

168

**Example Dot Output:**

169

170

```

171

..F...F..

172

173

Failures:

174

175

1) Chrome 91.0 - should validate input

176

Expected 'invalid' to throw error

177

at test/validation.js:15:5

178

179

2) Firefox 89.0 - should handle async operations

180

Timeout after 5000ms

181

at test/async.js:23:10

182

183

9 tests, 2 failures

184

```

185

186

**Usage Examples:**

187

188

```javascript

189

// Dot reporter for quick feedback

190

{

191

"reporter": "dot"

192

}

193

```

194

195

### TeamCity Reporter

196

197

TeamCity service message format for seamless integration with JetBrains TeamCity CI/CD platform.

198

199

```javascript { .api }

200

/**

201

* TeamCity service messages reporter

202

* Outputs TeamCity-compatible service messages

203

*/

204

class TeamCityReporter {

205

constructor(silent?: boolean, out?: NodeJS.WritableStream);

206

}

207

```

208

209

**Example TeamCity Output:**

210

211

```

212

##teamcity[testSuiteStarted name='testem.suite']

213

##teamcity[testStarted name='Chrome 91.0 - should add numbers correctly']

214

##teamcity[testFinished name='Chrome 91.0 - should add numbers correctly' duration='100']

215

##teamcity[testStarted name='Firefox 89.0 - should validate input']

216

##teamcity[testFailed name='Firefox 89.0 - should validate input' message='Expected |'invalid|' to throw error' details='AssertionError: Expected |'invalid|' to throw error|n at test/validation.js:15:5']

217

##teamcity[testFinished name='Firefox 89.0 - should validate input' duration='300']

218

##teamcity[testSuiteFinished name='testem.suite' duration='5200']

219

```

220

221

**Usage Examples:**

222

223

```javascript

224

// TeamCity integration

225

{

226

"reporter": "teamcity"

227

}

228

```

229

230

### Dev Reporter

231

232

Interactive text-based UI for development mode with real-time test feedback and keyboard controls.

233

234

```javascript { .api }

235

/**

236

* Interactive development reporter

237

* Provides TUI with test results and browser management

238

*/

239

class DevReporter {

240

constructor(silent?: boolean, out?: NodeJS.WritableStream);

241

}

242

```

243

244

**Features:**

245

246

- Real-time test results display

247

- Browser tab navigation

248

- File watching notifications

249

- Keyboard-controlled interface

250

- Split-panel view for test output

251

- Color-coded test status

252

253

**Keyboard Controls:**

254

255

- `ENTER` - Run tests

256

- `q` - Quit

257

- `←`/`→` - Navigate browser tabs

258

- `TAB` - Switch panels

259

- `↑`/`↓` - Scroll

260

- `SPACE` - Page down

261

- `b` - Page up

262

263

**Usage Examples:**

264

265

```javascript

266

// Dev reporter (default for development mode)

267

{

268

"reporter": "dev" // Usually automatic in dev mode

269

}

270

```

271

272

## Custom Reporters

273

274

### Creating Custom Reporters

275

276

Create custom reporters by implementing the reporter interface.

277

278

```javascript { .api }

279

/**

280

* Base reporter interface

281

*/

282

interface Reporter {

283

/**

284

* Called when test suite starts

285

* @param numLaunchers - Number of test launchers

286

*/

287

onStart(numLaunchers: number): void;

288

289

/**

290

* Called when a test starts

291

* @param launcher - Launcher information

292

* @param test - Test information

293

*/

294

onTestStart(launcher: LauncherInfo, test: TestInfo): void;

295

296

/**

297

* Called when a test completes

298

* @param launcher - Launcher information

299

* @param test - Test result

300

*/

301

onTestResult(launcher: LauncherInfo, test: TestResult): void;

302

303

/**

304

* Called when all tests complete

305

* @param results - Final test results

306

*/

307

onAllTestResults(results: TestResults): void;

308

}

309

310

interface LauncherInfo {

311

name: string; // Launcher name (e.g., "Chrome 91.0")

312

type: string; // Launcher type ("browser" or "process")

313

}

314

315

interface TestInfo {

316

name: string; // Test name

317

id: number; // Test ID

318

}

319

320

interface TestResult extends TestInfo {

321

passed: boolean; // Test passed

322

failed: boolean; // Test failed

323

error?: Error; // Test error

324

logs: string[]; // Test logs

325

runDuration: number; // Test duration in ms

326

}

327

```

328

329

**Custom Reporter Example:**

330

331

```javascript

332

// custom-reporter.js

333

class CustomReporter {

334

constructor(silent, out) {

335

this.out = out || process.stdout;

336

this.silent = silent;

337

this.results = [];

338

}

339

340

onStart(numLaunchers) {

341

if (!this.silent) {

342

this.out.write(`Starting tests on ${numLaunchers} launchers...\n`);

343

}

344

}

345

346

onTestResult(launcher, test) {

347

this.results.push({ launcher, test });

348

349

if (!this.silent) {

350

const status = test.passed ? '✓' : '✗';

351

const duration = `(${test.runDuration}ms)`;

352

this.out.write(`${status} ${launcher.name} - ${test.name} ${duration}\n`);

353

}

354

}

355

356

onAllTestResults(results) {

357

const passed = this.results.filter(r => r.test.passed).length;

358

const total = this.results.length;

359

360

this.out.write(`\nResults: ${passed}/${total} tests passed\n`);

361

362

// Output JSON summary

363

const summary = {

364

total,

365

passed,

366

failed: total - passed,

367

results: this.results

368

};

369

370

this.out.write(`\n${JSON.stringify(summary, null, 2)}\n`);

371

}

372

}

373

374

module.exports = CustomReporter;

375

```

376

377

### Registering Custom Reporters

378

379

```javascript

380

// testem.js

381

const CustomReporter = require('./custom-reporter');

382

383

module.exports = {

384

src_files: ['src/**/*.js', 'test/**/*.js'],

385

reporter_options: {

386

'custom': CustomReporter

387

}

388

};

389

```

390

391

## Reporter Configuration

392

393

### Output File Configuration

394

395

Direct reporter output to files for CI integration and archival.

396

397

```javascript { .api }

398

interface ReporterConfig {

399

reporter: string; // Reporter name

400

report_file?: string; // Output file path

401

reporter_options?: object; // Reporter-specific options

402

}

403

```

404

405

**Usage Examples:**

406

407

```javascript

408

// Output to file

409

{

410

"reporter": "tap",

411

"report_file": "results/test-output.tap"

412

}

413

414

// XUnit with timestamped filename

415

{

416

"reporter": "xunit",

417

"report_file": "results/junit-{{timestamp}}.xml"

418

}

419

420

// Multiple output files (testem.js only)

421

module.exports = {

422

reporter: 'tap',

423

after_tests: function(config, data, callback) {

424

// Custom post-processing

425

const fs = require('fs');

426

const results = JSON.stringify(data.results, null, 2);

427

fs.writeFileSync('results/detailed-results.json', results);

428

callback();

429

}

430

};

431

```

432

433

### CI Integration Examples

434

435

### Jenkins Integration

436

437

```xml

438

<!-- Jenkins pipeline -->

439

<project>

440

<builders>

441

<hudson.tasks.Shell>

442

<command>testem ci -R xunit</command>

443

</hudson.tasks.Shell>

444

</builders>

445

<publishers>

446

<hudson.tasks.junit.JUnitResultArchiver>

447

<testResults>test-results.xml</testResults>

448

</hudson.tasks.junit.JUnitResultArchiver>

449

</publishers>

450

</project>

451

```

452

453

### GitHub Actions Integration

454

455

```yaml

456

# .github/workflows/test.yml

457

name: Tests

458

on: [push, pull_request]

459

jobs:

460

test:

461

runs-on: ubuntu-latest

462

steps:

463

- uses: actions/checkout@v2

464

- uses: actions/setup-node@v2

465

- run: npm install

466

- run: testem ci -R tap

467

- uses: dorny/test-reporter@v1

468

if: always()

469

with:

470

name: Test Results

471

path: test-results.tap

472

reporter: java-junit

473

```