or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-execution.mdcustom-runner.mdindex.mdtest-runner.mdworker-management.md
tile.json

custom-runner.mddocs/

0

# Custom Test Runner Framework

1

2

Abstract base classes and interfaces for building custom test runners with both callback and event-driven patterns. This framework enables extending Jest's test execution capabilities with custom test runners for different testing scenarios.

3

4

## Imports

5

6

```typescript

7

import {

8

CallbackTestRunner,

9

EmittingTestRunner,

10

type CallbackTestRunnerInterface,

11

type EmittingTestRunnerInterface,

12

type OnTestStart,

13

type OnTestFailure,

14

type OnTestSuccess,

15

type UnsubscribeFn

16

} from "jest-runner";

17

```

18

19

## Capabilities

20

21

### CallbackTestRunner Abstract Class

22

23

Base class for building custom test runners using callback-style interfaces for test lifecycle events.

24

25

```typescript { .api }

26

/**

27

* Abstract base class for callback-style test runners

28

* Suitable for runners that prefer explicit callback handling over events

29

*/

30

abstract class CallbackTestRunner extends BaseTestRunner implements CallbackTestRunnerInterface {

31

readonly supportsEventEmitters = false;

32

readonly isSerial?: boolean;

33

34

constructor(globalConfig: Config.GlobalConfig, context: TestRunnerContext);

35

36

/**

37

* Execute tests using callback-style event handling

38

* @param tests - Array of test objects to execute

39

* @param watcher - Test watcher for interrupt handling

40

* @param onStart - Callback invoked when each test starts

41

* @param onResult - Callback invoked when each test succeeds

42

* @param onFailure - Callback invoked when each test fails

43

* @param options - Execution options including serial mode

44

*/

45

abstract runTests(

46

tests: Array<Test>,

47

watcher: TestWatcher,

48

onStart: OnTestStart,

49

onResult: OnTestSuccess,

50

onFailure: OnTestFailure,

51

options: TestRunnerOptions

52

): Promise<void>;

53

}

54

```

55

56

**Usage Example:**

57

58

```typescript

59

import { CallbackTestRunner } from "jest-runner";

60

import type { Test, TestResult, SerializableError } from "@jest/test-result";

61

62

class CustomCallbackRunner extends CallbackTestRunner {

63

async runTests(

64

tests: Array<Test>,

65

watcher: TestWatcher,

66

onStart: OnTestStart,

67

onResult: OnTestSuccess,

68

onFailure: OnTestFailure,

69

options: TestRunnerOptions

70

): Promise<void> {

71

for (const test of tests) {

72

if (watcher.isInterrupted()) break;

73

74

try {

75

await onStart(test);

76

77

// Custom test execution logic

78

const result = await this.executeCustomTest(test);

79

80

await onResult(test, result);

81

} catch (error) {

82

await onFailure(test, this.formatError(error));

83

}

84

}

85

}

86

87

private async executeCustomTest(test: Test): Promise<TestResult> {

88

// Custom test execution implementation

89

return {

90

numPassingTests: 1,

91

numFailingTests: 0,

92

numPendingTests: 0,

93

numTodoTests: 0,

94

testFilePath: test.path,

95

// ... other TestResult properties

96

};

97

}

98

}

99

```

100

101

### EmittingTestRunner Abstract Class

102

103

Base class for building custom test runners using event-driven interfaces for real-time test progress reporting.

104

105

```typescript { .api }

106

/**

107

* Abstract base class for event-emitting test runners

108

* Suitable for runners that need real-time event broadcasting

109

*/

110

abstract class EmittingTestRunner extends BaseTestRunner implements EmittingTestRunnerInterface {

111

readonly supportsEventEmitters = true;

112

readonly isSerial?: boolean;

113

114

constructor(globalConfig: Config.GlobalConfig, context: TestRunnerContext);

115

116

/**

117

* Execute tests using event-driven progress reporting

118

* @param tests - Array of test objects to execute

119

* @param watcher - Test watcher for interrupt handling

120

* @param options - Execution options including serial mode

121

*/

122

abstract runTests(

123

tests: Array<Test>,

124

watcher: TestWatcher,

125

options: TestRunnerOptions

126

): Promise<void>;

127

128

/**

129

* Register event listener for test execution events

130

* @param eventName - Name of the event to listen for

131

* @param listener - Function to call when event is emitted

132

* @returns Unsubscribe function to remove the listener

133

*/

134

abstract on<Name extends keyof TestEvents>(

135

eventName: Name,

136

listener: (eventData: TestEvents[Name]) => void | Promise<void>

137

): UnsubscribeFn;

138

}

139

```

140

141

**Usage Example:**

142

143

```typescript

144

import { EmittingTestRunner } from "jest-runner";

145

import Emittery from "emittery";

146

147

class CustomEmittingRunner extends EmittingTestRunner {

148

private eventEmitter = new Emittery<TestEvents>();

149

150

async runTests(

151

tests: Array<Test>,

152

watcher: TestWatcher,

153

options: TestRunnerOptions

154

): Promise<void> {

155

for (const test of tests) {

156

if (watcher.isInterrupted()) break;

157

158

try {

159

await this.eventEmitter.emit('test-file-start', [test]);

160

161

// Custom test execution logic

162

const result = await this.executeCustomTest(test);

163

164

await this.eventEmitter.emit('test-file-success', [test, result]);

165

} catch (error) {

166

await this.eventEmitter.emit('test-file-failure', [test, error]);

167

}

168

}

169

}

170

171

on<Name extends keyof TestEvents>(

172

eventName: Name,

173

listener: (eventData: TestEvents[Name]) => void | Promise<void>

174

): UnsubscribeFn {

175

return this.eventEmitter.on(eventName, listener);

176

}

177

}

178

```

179

180

### Test Runner Interfaces

181

182

Type definitions for implementing custom test runners without extending the abstract base classes.

183

184

```typescript { .api }

185

/**

186

* Interface for callback-style test runners

187

*/

188

interface CallbackTestRunnerInterface {

189

readonly isSerial?: boolean;

190

readonly supportsEventEmitters?: boolean;

191

192

runTests(

193

tests: Array<Test>,

194

watcher: TestWatcher,

195

onStart: OnTestStart,

196

onResult: OnTestSuccess,

197

onFailure: OnTestFailure,

198

options: TestRunnerOptions

199

): Promise<void>;

200

}

201

202

/**

203

* Interface for event-emitting test runners

204

*/

205

interface EmittingTestRunnerInterface {

206

readonly isSerial?: boolean;

207

readonly supportsEventEmitters: true;

208

209

runTests(

210

tests: Array<Test>,

211

watcher: TestWatcher,

212

options: TestRunnerOptions

213

): Promise<void>;

214

215

on<Name extends keyof TestEvents>(

216

eventName: Name,

217

listener: (eventData: TestEvents[Name]) => void | Promise<void>

218

): UnsubscribeFn;

219

}

220

```

221

222

### Runner Implementation Examples

223

224

Complete examples showing how to implement both callback and event-driven test runners.

225

226

**Callback Runner Implementation:**

227

228

```typescript

229

class ESLintTestRunner extends CallbackTestRunner {

230

async runTests(

231

tests: Array<Test>,

232

watcher: TestWatcher,

233

onStart: OnTestStart,

234

onResult: OnTestSuccess,

235

onFailure: OnTestFailure,

236

options: TestRunnerOptions

237

): Promise<void> {

238

// Access inherited properties

239

const { collectCoverage } = this._globalConfig;

240

const { changedFiles } = this._context;

241

242

for (const test of tests) {

243

if (watcher.isInterrupted()) break;

244

245

await onStart(test);

246

247

try {

248

// Run ESLint on the test file

249

const results = await this.runESLint(test.path);

250

251

const testResult: TestResult = {

252

numFailingTests: results.errorCount,

253

numPassingTests: results.errorCount === 0 ? 1 : 0,

254

numPendingTests: 0,

255

numTodoTests: 0,

256

testFilePath: test.path,

257

console: [],

258

leaks: false,

259

// Map ESLint messages to test output

260

testResults: results.messages.map(msg => ({

261

title: `ESLint: ${msg.ruleId || 'Error'}`,

262

status: msg.severity === 2 ? 'failed' : 'passed',

263

ancestorTitles: [],

264

fullName: `${test.path}:${msg.line}:${msg.column}`,

265

})),

266

};

267

268

await onResult(test, testResult);

269

} catch (error) {

270

await onFailure(test, {

271

message: error.message,

272

stack: error.stack,

273

type: error.constructor.name,

274

});

275

}

276

}

277

}

278

}

279

```

280

281

**Event-Driven Runner Implementation:**

282

283

```typescript

284

class PuppeteerTestRunner extends EmittingTestRunner {

285

private eventEmitter = new Emittery<TestEvents>();

286

287

async runTests(

288

tests: Array<Test>,

289

watcher: TestWatcher,

290

options: TestRunnerOptions

291

): Promise<void> {

292

const browser = await puppeteer.launch();

293

294

try {

295

for (const test of tests) {

296

if (watcher.isInterrupted()) break;

297

298

await this.eventEmitter.emit('test-file-start', [test]);

299

300

try {

301

const page = await browser.newPage();

302

const result = await this.runBrowserTest(test, page);

303

await page.close();

304

305

await this.eventEmitter.emit('test-file-success', [test, result]);

306

} catch (error) {

307

await this.eventEmitter.emit('test-file-failure', [test, error]);

308

}

309

}

310

} finally {

311

await browser.close();

312

}

313

}

314

315

on<Name extends keyof TestEvents>(

316

eventName: Name,

317

listener: (eventData: TestEvents[Name]) => void | Promise<void>

318

): UnsubscribeFn {

319

return this.eventEmitter.on(eventName, listener);

320

}

321

}

322

```

323

324

## Types

325

326

```typescript { .api }

327

type OnTestStart = (test: Test) => Promise<void>;

328

type OnTestFailure = (test: Test, serializableError: SerializableError) => Promise<void>;

329

type OnTestSuccess = (test: Test, testResult: TestResult) => Promise<void>;

330

type UnsubscribeFn = () => void;

331

type JestTestRunner = CallbackTestRunner | EmittingTestRunner;

332

333

abstract class BaseTestRunner {

334

readonly isSerial?: boolean;

335

abstract readonly supportsEventEmitters: boolean;

336

337

protected readonly _globalConfig: Config.GlobalConfig;

338

protected readonly _context: TestRunnerContext;

339

340

constructor(globalConfig: Config.GlobalConfig, context: TestRunnerContext);

341

}

342

```