or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-api.mdcore-zone-api.mdindex.mdpatching-system.mdtask-system.mdtesting-utilities.mdzone-specifications.md

testing-utilities.mddocs/

0

# Testing Utilities

1

2

Comprehensive testing framework with fakeAsync, async testing zones, and task tracking for testing asynchronous code in a controlled environment.

3

4

## Capabilities

5

6

### FakeAsync Testing

7

8

Testing utilities that allow control of time and microtask execution for deterministic async testing.

9

10

```typescript { .api }

11

/**

12

* Create a fakeAsync test function that controls time

13

* @param fn - Test function to wrap

14

* @param options - Configuration options

15

* @returns Wrapped test function

16

*/

17

function fakeAsync(fn: Function, options?: {flush?: boolean}): (...args: any[]) => any;

18

19

/**

20

* Advance virtual time by specified milliseconds

21

* @param millis - Milliseconds to advance (default: 0)

22

* @param ignoreNestedTimeout - Whether to ignore nested setTimeout calls

23

*/

24

function tick(millis?: number, ignoreNestedTimeout?: boolean): void;

25

26

/**

27

* Flush all pending macrotasks and microtasks

28

* @param maxTurns - Maximum number of task execution cycles

29

* @returns Number of tasks flushed

30

*/

31

function flush(maxTurns?: number): number;

32

33

/**

34

* Flush only pending microtasks

35

*/

36

function flushMicrotasks(): void;

37

38

/**

39

* Discard all pending periodic tasks (setInterval)

40

*/

41

function discardPeriodicTasks(): void;

42

43

/**

44

* Reset the fakeAsync zone to initial state

45

*/

46

function resetFakeAsyncZone(): void;

47

48

/**

49

* Wrap a function to automatically use ProxyZoneSpec for test isolation

50

* @param fn - Function to wrap with proxy zone behavior

51

* @returns Wrapped function that runs in proxy zone context

52

*/

53

function withProxyZone<T extends Function>(fn: T): T;

54

```

55

56

**Usage Examples:**

57

58

```typescript

59

import 'zone.js/testing';

60

61

describe('FakeAsync Tests', () => {

62

it('should control time', fakeAsync(() => {

63

let executed = false;

64

65

setTimeout(() => {

66

executed = true;

67

}, 1000);

68

69

// Time hasn't advanced yet

70

expect(executed).toBe(false);

71

72

// Advance time by 1000ms

73

tick(1000);

74

75

// Now the timeout has executed

76

expect(executed).toBe(true);

77

}));

78

79

it('should handle promises', fakeAsync(() => {

80

let result = '';

81

82

Promise.resolve('async-value')

83

.then(value => result = value);

84

85

// Promise hasn't resolved yet

86

expect(result).toBe('');

87

88

// Flush microtasks

89

flushMicrotasks();

90

91

// Promise has resolved

92

expect(result).toBe('async-value');

93

}));

94

95

it('should handle mixed async operations', fakeAsync(() => {

96

const results: string[] = [];

97

98

// Schedule various async operations

99

setTimeout(() => results.push('timeout-100'), 100);

100

setTimeout(() => results.push('timeout-200'), 200);

101

102

Promise.resolve().then(() => results.push('promise-1'));

103

Promise.resolve().then(() => results.push('promise-2'));

104

105

setInterval(() => results.push('interval'), 50);

106

107

// Flush microtasks first

108

flushMicrotasks();

109

expect(results).toEqual(['promise-1', 'promise-2']);

110

111

// Advance time and check results

112

tick(50);

113

expect(results).toContain('interval');

114

115

tick(50); // Total: 100ms

116

expect(results).toContain('timeout-100');

117

118

tick(100); // Total: 200ms

119

expect(results).toContain('timeout-200');

120

121

// Clean up periodic tasks

122

discardPeriodicTasks();

123

}));

124

125

it('should use proxy zone for test isolation', withProxyZone(() => {

126

let result = '';

127

128

// This test runs in an isolated proxy zone

129

setTimeout(() => {

130

result = 'completed';

131

}, 100);

132

133

tick(100);

134

expect(result).toBe('completed');

135

136

// ProxyZoneSpec automatically handles cleanup

137

}));

138

});

139

```

140

141

### FakeAsyncTestZoneSpec

142

143

Zone specification for fakeAsync testing environment.

144

145

```typescript { .api }

146

/**

147

* Zone specification for controlling time in tests

148

*/

149

class FakeAsyncTestZoneSpec implements ZoneSpec {

150

/** Zone name identifier */

151

name: 'fakeAsync';

152

153

/**

154

* Advance virtual time by specified milliseconds

155

* @param millis - Milliseconds to advance

156

* @param doTick - Optional callback for each tick

157

*/

158

tick(millis?: number, doTick?: (elapsed: number) => void): void;

159

160

/**

161

* Flush all pending tasks

162

* @param maxTurns - Maximum execution cycles

163

* @returns Number of tasks flushed

164

*/

165

flush(maxTurns?: number): number;

166

167

/**

168

* Flush only microtasks

169

*/

170

flushMicrotasks(): void;

171

172

/**

173

* Get current fake system time

174

* @returns Current virtual time in milliseconds

175

*/

176

getFakeSystemTime(): number;

177

}

178

```

179

180

**Usage Examples:**

181

182

```typescript

183

import 'zone.js/testing';

184

185

// Direct use of FakeAsyncTestZoneSpec

186

const fakeAsyncZone = Zone.current.fork(new FakeAsyncTestZoneSpec());

187

188

fakeAsyncZone.run(() => {

189

let value = 0;

190

191

setTimeout(() => value = 1, 100);

192

setTimeout(() => value = 2, 200);

193

194

// Control time directly on the zone spec

195

const zoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');

196

197

zoneSpec.tick(100);

198

expect(value).toBe(1);

199

200

zoneSpec.tick(100); // Total: 200ms

201

expect(value).toBe(2);

202

});

203

```

204

205

### Async Testing

206

207

Testing utilities for asynchronous operations that need to complete naturally.

208

209

```typescript { .api }

210

/**

211

* Zone specification for async testing

212

*/

213

class AsyncTestZoneSpec implements ZoneSpec {

214

/** Zone name identifier */

215

name: 'async-test';

216

217

/**

218

* Wait for all pending asynchronous operations to complete

219

* @returns Promise that resolves when all async operations are done

220

*/

221

whenStable(): Promise<any>;

222

223

/**

224

* Check if there are any pending asynchronous operations

225

* @returns true if async operations are pending

226

*/

227

hasPendingAsyncTasks(): boolean;

228

229

/**

230

* Check if there are any pending microtasks

231

* @returns true if microtasks are pending

232

*/

233

hasPendingMicrotasks(): boolean;

234

235

/**

236

* Check if there are any pending macrotasks

237

* @returns true if macrotasks are pending

238

*/

239

hasPendingMacrotasks(): boolean;

240

}

241

```

242

243

**Usage Examples:**

244

245

```typescript

246

import 'zone.js/testing';

247

248

describe('Async Tests', () => {

249

let asyncZone: Zone;

250

let asyncZoneSpec: AsyncTestZoneSpec;

251

252

beforeEach(() => {

253

asyncZoneSpec = new AsyncTestZoneSpec();

254

asyncZone = Zone.current.fork(asyncZoneSpec);

255

});

256

257

it('should wait for async operations', async () => {

258

let completed = false;

259

260

asyncZone.run(() => {

261

setTimeout(() => {

262

completed = true;

263

}, 100);

264

265

fetch('/api/data').then(() => {

266

// Some async operation

267

});

268

});

269

270

// Wait for all async operations to complete

271

await asyncZoneSpec.whenStable();

272

273

expect(completed).toBe(true);

274

});

275

276

it('should check pending tasks', () => {

277

asyncZone.run(() => {

278

setTimeout(() => {}, 100);

279

280

expect(asyncZoneSpec.hasPendingMacrotasks()).toBe(true);

281

expect(asyncZoneSpec.hasPendingAsyncTasks()).toBe(true);

282

});

283

});

284

});

285

```

286

287

### Task Tracking

288

289

Zone specification for monitoring and tracking task execution.

290

291

```typescript { .api }

292

/**

293

* Zone specification for tracking task execution

294

*/

295

class TaskTrackingZoneSpec implements ZoneSpec {

296

/** Zone name identifier */

297

name: 'task-tracking';

298

299

/** Array of scheduled microtasks */

300

readonly microTasks: Task[];

301

302

/** Array of scheduled macrotasks */

303

readonly macroTasks: Task[];

304

305

/** Array of scheduled event tasks */

306

readonly eventTasks: Task[];

307

308

/**

309

* Clear all tracked tasks

310

*/

311

clear(): void;

312

313

/**

314

* Get tasks by type

315

* @param type - Task type to filter by

316

* @returns Array of tasks of the specified type

317

*/

318

getTasksByType(type: TaskType): Task[];

319

320

/**

321

* Get tasks by source

322

* @param source - Source identifier to filter by

323

* @returns Array of tasks from the specified source

324

*/

325

getTasksBySource(source: string): Task[];

326

}

327

```

328

329

**Usage Examples:**

330

331

```typescript

332

import 'zone.js/testing';

333

334

describe('Task Tracking Tests', () => {

335

let trackingZone: Zone;

336

let trackingSpec: TaskTrackingZoneSpec;

337

338

beforeEach(() => {

339

trackingSpec = new TaskTrackingZoneSpec();

340

trackingZone = Zone.current.fork(trackingSpec);

341

});

342

343

it('should track scheduled tasks', () => {

344

trackingZone.run(() => {

345

setTimeout(() => {}, 100);

346

setTimeout(() => {}, 200);

347

348

Promise.resolve().then(() => {});

349

350

document.addEventListener('click', () => {});

351

});

352

353

expect(trackingSpec.macroTasks).toHaveLength(2);

354

expect(trackingSpec.microTasks).toHaveLength(1);

355

expect(trackingSpec.eventTasks).toHaveLength(1);

356

357

// Check specific task sources

358

const timerTasks = trackingSpec.getTasksBySource('setTimeout');

359

expect(timerTasks).toHaveLength(2);

360

361

const promiseTasks = trackingSpec.getTasksByType('microTask');

362

expect(promiseTasks).toHaveLength(1);

363

});

364

365

it('should clear tracked tasks', () => {

366

trackingZone.run(() => {

367

setTimeout(() => {}, 100);

368

Promise.resolve().then(() => {});

369

});

370

371

expect(trackingSpec.macroTasks).toHaveLength(1);

372

expect(trackingSpec.microTasks).toHaveLength(1);

373

374

trackingSpec.clear();

375

376

expect(trackingSpec.macroTasks).toHaveLength(0);

377

expect(trackingSpec.microTasks).toHaveLength(0);

378

});

379

});

380

```

381

382

### Proxy Zone

383

384

Zone specification for test isolation and delegation control.

385

386

```typescript { .api }

387

/**

388

* Zone specification for test isolation and delegation

389

*/

390

class ProxyZoneSpec implements ZoneSpec {

391

/** Zone name identifier */

392

name: 'proxy';

393

394

/**

395

* Set the delegate zone specification

396

* @param delegateSpec - Zone spec to delegate operations to

397

*/

398

setDelegate(delegateSpec: ZoneSpec): void;

399

400

/**

401

* Get the current delegate zone specification

402

* @returns Current delegate zone spec

403

*/

404

getDelegate(): ZoneSpec;

405

406

/**

407

* Reset delegation to default behavior

408

*/

409

resetDelegate(): void;

410

411

/**

412

* Assert that no tasks are pending

413

* @param ignoreLeisure - Whether to ignore leisure tasks

414

*/

415

assertNoPendingTasks(ignoreLeisure?: boolean): void;

416

}

417

```

418

419

**Usage Examples:**

420

421

```typescript

422

import 'zone.js/testing';

423

424

describe('Proxy Zone Tests', () => {

425

let proxyZone: Zone;

426

let proxySpec: ProxyZoneSpec;

427

428

beforeEach(() => {

429

proxySpec = new ProxyZoneSpec();

430

proxyZone = Zone.current.fork(proxySpec);

431

});

432

433

it('should isolate tests', () => {

434

// Set up custom delegate for this test

435

const customDelegate = {

436

name: 'custom-test',

437

onHandleError: (delegate, current, target, error) => {

438

console.log('Custom error handling:', error);

439

return true; // Handle the error

440

}

441

};

442

443

proxySpec.setDelegate(customDelegate);

444

445

proxyZone.run(() => {

446

// Test code that might throw errors

447

throw new Error('Test error');

448

// Error will be handled by custom delegate

449

});

450

451

// Reset for next test

452

proxySpec.resetDelegate();

453

});

454

455

it('should assert no pending tasks', () => {

456

proxyZone.run(() => {

457

// Synchronous operations only

458

const result = 1 + 1;

459

expect(result).toBe(2);

460

});

461

462

// Should not throw - no pending tasks

463

proxySpec.assertNoPendingTasks();

464

});

465

466

it('should detect pending tasks', () => {

467

proxyZone.run(() => {

468

setTimeout(() => {}, 100);

469

});

470

471

// Should throw - there are pending tasks

472

expect(() => {

473

proxySpec.assertNoPendingTasks();

474

}).toThrow();

475

});

476

});

477

```

478

479

### Long Stack Trace

480

481

Zone specification for enhanced error stack traces across async boundaries.

482

483

```typescript { .api }

484

/**

485

* Enable long stack traces across async boundaries

486

* @param Zone - Zone constructor

487

*/

488

function patchLongStackTrace(Zone: ZoneType): void;

489

490

/**

491

* Zone specification for enhanced stack traces

492

*/

493

class LongStackTraceZoneSpec implements ZoneSpec {

494

/** Zone name identifier */

495

name: 'long-stack-trace';

496

497

/**

498

* Get enhanced stack trace for an error

499

* @param error - Error to get stack trace for

500

* @returns Enhanced stack trace string

501

*/

502

getLongStackTrace(error: Error): string;

503

}

504

```

505

506

**Usage Examples:**

507

508

```typescript

509

import 'zone.js/testing';

510

511

// Enable long stack traces

512

Zone.__load_patch('longStackTraceZone', (global, Zone) => {

513

patchLongStackTrace(Zone);

514

});

515

516

const longStackZone = Zone.current.fork(new LongStackTraceZoneSpec());

517

518

longStackZone.run(() => {

519

function asyncFunction() {

520

return new Promise((resolve, reject) => {

521

setTimeout(() => {

522

someOtherAsyncFunction()

523

.then(resolve)

524

.catch(reject);

525

}, 100);

526

});

527

}

528

529

function someOtherAsyncFunction() {

530

return Promise.reject(new Error('Async error'));

531

}

532

533

asyncFunction().catch(error => {

534

// Error will have enhanced stack trace showing

535

// the full async call chain

536

console.log(error.stack);

537

});

538

});

539

```