or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-assertions.mdconfiguration.mdcontainment-assertions.mderror-assertions.mdindex.mdnumber-assertions.mdpattern-matching.mdpromise-assertions.mdproperty-assertions.mdstring-assertions.mdtype-assertions.md

promise-assertions.mddocs/

0

# Promise Assertions

1

2

Methods for testing async functions, promise states, and asynchronous assertion chaining.

3

4

## Promise State Testing

5

6

### Promise()

7

8

Test that a value is a Promise.

9

10

```javascript { .api }

11

/**

12

* Assert that the value is a Promise

13

* @returns This assertion for chaining

14

*/

15

Promise(): Assertion;

16

```

17

18

**Usage:**

19

```javascript

20

import should from 'should';

21

22

// Basic Promise detection

23

Promise.resolve('value').should.be.a.Promise();

24

Promise.reject('error').should.be.a.Promise();

25

26

async function asyncFunc() {

27

return 'result';

28

}

29

asyncFunc().should.be.a.Promise();

30

31

// Function that returns a Promise

32

function createPromise() {

33

return new Promise((resolve) => {

34

setTimeout(() => resolve('done'), 100);

35

});

36

}

37

createPromise().should.be.a.Promise();

38

39

// Not a Promise

40

'string'.should.not.be.a.Promise();

41

123.should.not.be.a.Promise();

42

{}.should.not.be.a.Promise();

43

```

44

45

## Promise Resolution Testing

46

47

### fulfilled() / resolved()

48

49

Test that a Promise resolves successfully.

50

51

```javascript { .api }

52

/**

53

* Assert that the Promise resolves (fulfills) successfully

54

* @returns Promise<any> for async testing

55

*/

56

fulfilled(): Promise<any>;

57

resolved(): Promise<any>;

58

```

59

60

**Usage:**

61

```javascript

62

// Basic resolution testing

63

Promise.resolve('success').should.be.fulfilled();

64

Promise.resolve(42).should.be.resolved();

65

66

// Async function testing

67

async function successfulOperation() {

68

await new Promise(resolve => setTimeout(resolve, 10));

69

return 'completed';

70

}

71

successfulOperation().should.be.fulfilled();

72

73

// Testing with await

74

await Promise.resolve('value').should.be.fulfilled();

75

76

// Chain with other assertions using .finally

77

Promise.resolve('hello')

78

.should.be.fulfilled()

79

.finally.equal('hello');

80

```

81

82

### rejected()

83

84

Test that a Promise rejects.

85

86

```javascript { .api }

87

/**

88

* Assert that the Promise rejects

89

* @returns Promise<any> for async testing

90

*/

91

rejected(): Promise<any>;

92

```

93

94

**Usage:**

95

```javascript

96

// Basic rejection testing

97

Promise.reject(new Error('failed')).should.be.rejected();

98

99

// Async function that throws

100

async function failingOperation() {

101

throw new Error('Operation failed');

102

}

103

failingOperation().should.be.rejected();

104

105

// Testing with await

106

await Promise.reject('error').should.be.rejected();

107

108

// Network request simulation

109

async function fetchData(url) {

110

if (!url) {

111

throw new Error('URL is required');

112

}

113

// ... fetch logic

114

}

115

fetchData().should.be.rejected();

116

```

117

118

## Promise Value Testing

119

120

### fulfilledWith() / resolvedWith()

121

122

Test that a Promise resolves with a specific value.

123

124

```javascript { .api }

125

/**

126

* Assert that the Promise resolves with the specified value

127

* @param expected - The expected resolved value

128

* @returns Promise<any> for async testing

129

*/

130

fulfilledWith(expected: any): Promise<any>;

131

resolvedWith(expected: any): Promise<any>;

132

```

133

134

**Usage:**

135

```javascript

136

// Test specific resolved values

137

Promise.resolve('success').should.be.fulfilledWith('success');

138

Promise.resolve(42).should.be.resolvedWith(42);

139

140

// Object resolution

141

const user = { id: 1, name: 'john' };

142

Promise.resolve(user).should.be.fulfilledWith(user);

143

144

// Async function result testing

145

async function getUserById(id) {

146

// Simulate API call

147

await new Promise(resolve => setTimeout(resolve, 10));

148

return { id, name: `user_${id}` };

149

}

150

getUserById(123).should.be.fulfilledWith({ id: 123, name: 'user_123' });

151

152

// Array results

153

async function getUsers() {

154

return [

155

{ id: 1, name: 'john' },

156

{ id: 2, name: 'jane' }

157

];

158

}

159

getUsers().should.be.fulfilledWith([

160

{ id: 1, name: 'john' },

161

{ id: 2, name: 'jane' }

162

]);

163

164

// Testing with await

165

const result = await Promise.resolve('test').should.be.fulfilledWith('test');

166

```

167

168

### rejectedWith()

169

170

Test that a Promise rejects with a specific error or message.

171

172

```javascript { .api }

173

/**

174

* Assert that the Promise rejects with the specified error/message

175

* @param error - Expected error (RegExp, string, Error, or Function)

176

* @param properties - Optional expected error properties

177

* @returns Promise<any> for async testing

178

*/

179

rejectedWith(error: RegExp | string | Error, properties?: object): Promise<any>;

180

rejectedWith(properties: object): Promise<any>;

181

```

182

183

**Usage:**

184

```javascript

185

// Test specific error messages

186

Promise.reject(new Error('Not found')).should.be.rejectedWith('Not found');

187

188

// Test with RegExp

189

Promise.reject(new Error('User ID 123 not found'))

190

.should.be.rejectedWith(/User ID \d+ not found/);

191

192

// Test specific error types

193

Promise.reject(new TypeError('Invalid input'))

194

.should.be.rejectedWith(TypeError);

195

196

// Test error properties

197

const customError = new Error('Database error');

198

customError.code = 500;

199

customError.table = 'users';

200

Promise.reject(customError).should.be.rejectedWith('Database error', {

201

code: 500,

202

table: 'users'

203

});

204

205

// Async function error testing

206

async function validateUser(user) {

207

if (!user.email) {

208

const error = new Error('Email is required');

209

error.field = 'email';

210

throw error;

211

}

212

}

213

validateUser({}).should.be.rejectedWith('Email is required', { field: 'email' });

214

215

// API error simulation

216

async function apiCall(endpoint) {

217

if (endpoint === '/forbidden') {

218

const error = new Error('Access denied');

219

error.statusCode = 403;

220

throw error;

221

}

222

}

223

apiCall('/forbidden').should.be.rejectedWith({ statusCode: 403 });

224

```

225

226

## Async Assertion Chaining

227

228

### finally

229

230

Chain assertions after promise resolution.

231

232

```javascript { .api }

233

/**

234

* Chain assertions after promise settles (resolved or rejected)

235

*/

236

finally: PromisedAssertion;

237

```

238

239

**Usage:**

240

```javascript

241

// Chain assertions on resolved value

242

Promise.resolve('hello world')

243

.should.be.fulfilled()

244

.finally.be.a.String()

245

.and.have.length(11)

246

.and.startWith('hello');

247

248

// Chain with object properties

249

Promise.resolve({ id: 123, name: 'john', active: true })

250

.should.be.fulfilled()

251

.finally.have.property('id', 123)

252

.and.have.property('name')

253

.which.is.a.String()

254

.and.have.property('active', true);

255

256

// Chain with array assertions

257

Promise.resolve([1, 2, 3, 4, 5])

258

.should.be.fulfilled()

259

.finally.be.an.Array()

260

.and.have.length(5)

261

.and.containEql(3);

262

263

// Complex chaining

264

async function fetchUserProfile(userId) {

265

return {

266

id: userId,

267

profile: {

268

name: 'John Doe',

269

email: 'john@example.com',

270

preferences: { theme: 'dark' }

271

},

272

roles: ['user', 'member']

273

};

274

}

275

276

fetchUserProfile(123)

277

.should.be.fulfilled()

278

.finally.have.property('profile')

279

.which.has.property('preferences')

280

.which.has.property('theme', 'dark');

281

```

282

283

### eventually

284

285

Alias for `finally` - chain assertions after promise settlement.

286

287

```javascript { .api }

288

/**

289

* Chain assertions after promise settles - alias for finally

290

*/

291

eventually: PromisedAssertion;

292

```

293

294

**Usage:**

295

```javascript

296

// Same functionality as finally

297

Promise.resolve(42)

298

.should.be.fulfilled()

299

.eventually.be.a.Number()

300

.and.be.above(40);

301

302

// Object property testing

303

Promise.resolve({ count: 5, items: ['a', 'b', 'c', 'd', 'e'] })

304

.should.be.fulfilled()

305

.eventually.have.property('count', 5)

306

.and.have.property('items')

307

.which.has.length(5);

308

```

309

310

## Practical Async Testing Examples

311

312

### API Testing

313

```javascript

314

class APIClient {

315

async get(endpoint) {

316

if (endpoint === '/users/404') {

317

const error = new Error('User not found');

318

error.statusCode = 404;

319

throw error;

320

}

321

322

if (endpoint === '/users/1') {

323

return {

324

id: 1,

325

name: 'John Doe',

326

email: 'john@example.com',

327

createdAt: '2023-01-01T00:00:00Z'

328

};

329

}

330

331

return [];

332

}

333

334

async post(endpoint, data) {

335

if (!data.name) {

336

const error = new Error('Name is required');

337

error.field = 'name';

338

error.statusCode = 400;

339

throw error;

340

}

341

342

return {

343

id: Date.now(),

344

...data,

345

createdAt: new Date().toISOString()

346

};

347

}

348

}

349

350

const client = new APIClient();

351

352

// Test successful API calls

353

client.get('/users/1')

354

.should.be.fulfilled()

355

.finally.have.properties('id', 'name', 'email')

356

.and.have.property('id', 1);

357

358

// Test API errors

359

client.get('/users/404')

360

.should.be.rejectedWith('User not found', { statusCode: 404 });

361

362

client.post('/users', {})

363

.should.be.rejectedWith('Name is required', {

364

field: 'name',

365

statusCode: 400

366

});

367

368

// Test successful creation

369

client.post('/users', { name: 'Jane Doe', email: 'jane@example.com' })

370

.should.be.fulfilled()

371

.finally.have.property('name', 'Jane Doe')

372

.and.have.property('id')

373

.which.is.a.Number();

374

```

375

376

### Database Operations

377

```javascript

378

class UserRepository {

379

async findById(id) {

380

if (typeof id !== 'number') {

381

throw new TypeError('User ID must be a number');

382

}

383

384

if (id <= 0) {

385

throw new RangeError('User ID must be positive');

386

}

387

388

if (id === 999) {

389

return null; // User not found

390

}

391

392

return {

393

id,

394

name: `User ${id}`,

395

active: true

396

};

397

}

398

399

async create(userData) {

400

if (!userData.name) {

401

const error = new Error('User name is required');

402

error.code = 'VALIDATION_ERROR';

403

throw error;

404

}

405

406

return {

407

id: Math.floor(Math.random() * 1000),

408

...userData,

409

createdAt: new Date(),

410

active: true

411

};

412

}

413

}

414

415

const userRepo = new UserRepository();

416

417

// Test successful queries

418

userRepo.findById(1)

419

.should.be.fulfilled()

420

.finally.have.properties('id', 'name', 'active')

421

.and.have.property('active', true);

422

423

// Test not found scenario

424

userRepo.findById(999)

425

.should.be.fulfilledWith(null);

426

427

// Test validation errors

428

userRepo.findById('invalid')

429

.should.be.rejectedWith(TypeError);

430

431

userRepo.findById(-1)

432

.should.be.rejectedWith(RangeError);

433

434

userRepo.create({})

435

.should.be.rejectedWith('User name is required', { code: 'VALIDATION_ERROR' });

436

437

// Test successful creation

438

userRepo.create({ name: 'John Doe', email: 'john@example.com' })

439

.should.be.fulfilled()

440

.finally.have.property('name', 'John Doe')

441

.and.have.property('id')

442

.which.is.a.Number()

443

.and.be.above(0);

444

```

445

446

### File Operations

447

```javascript

448

const fs = require('fs').promises;

449

const path = require('path');

450

451

class FileManager {

452

async readConfig(filename) {

453

try {

454

const content = await fs.readFile(filename, 'utf8');

455

return JSON.parse(content);

456

} catch (error) {

457

if (error.code === 'ENOENT') {

458

const notFoundError = new Error(`Config file not found: ${filename}`);

459

notFoundError.code = 'CONFIG_NOT_FOUND';

460

throw notFoundError;

461

}

462

throw error;

463

}

464

}

465

466

async writeConfig(filename, config) {

467

if (!config || typeof config !== 'object') {

468

throw new TypeError('Config must be an object');

469

}

470

471

const content = JSON.stringify(config, null, 2);

472

await fs.writeFile(filename, content, 'utf8');

473

return { success: true, filename };

474

}

475

}

476

477

const fileManager = new FileManager();

478

479

// Test file reading

480

fileManager.readConfig('existing-config.json')

481

.should.be.fulfilled()

482

.finally.be.an.Object();

483

484

// Test missing file

485

fileManager.readConfig('missing-config.json')

486

.should.be.rejectedWith(/Config file not found/, { code: 'CONFIG_NOT_FOUND' });

487

488

// Test invalid config writing

489

fileManager.writeConfig('test.json', null)

490

.should.be.rejectedWith(TypeError, 'Config must be an object');

491

492

// Test successful writing

493

fileManager.writeConfig('test.json', { setting: 'value' })

494

.should.be.fulfilled()

495

.finally.have.properties('success', 'filename')

496

.and.have.property('success', true);

497

```

498

499

### Timeout and Retry Logic

500

```javascript

501

class RetryableOperation {

502

constructor(maxRetries = 3) {

503

this.maxRetries = maxRetries;

504

this.attempt = 0;

505

}

506

507

async execute() {

508

this.attempt++;

509

510

if (this.attempt <= 2) {

511

const error = new Error(`Attempt ${this.attempt} failed`);

512

error.attempt = this.attempt;

513

error.retryable = true;

514

throw error;

515

}

516

517

return {

518

success: true,

519

attempt: this.attempt,

520

message: 'Operation completed'

521

};

522

}

523

524

async executeWithTimeout(timeoutMs) {

525

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

526

const timer = setTimeout(() => {

527

const error = new Error('Operation timed out');

528

error.code = 'TIMEOUT';

529

reject(error);

530

}, timeoutMs);

531

532

this.execute()

533

.then(result => {

534

clearTimeout(timer);

535

resolve(result);

536

})

537

.catch(error => {

538

clearTimeout(timer);

539

reject(error);

540

});

541

});

542

}

543

}

544

545

// Test retry logic

546

const operation = new RetryableOperation();

547

548

// First attempts should fail

549

operation.execute()

550

.should.be.rejectedWith('Attempt 1 failed', {

551

attempt: 1,

552

retryable: true

553

});

554

555

// After retries, should succeed

556

const retryOperation = new RetryableOperation();

557

retryOperation.attempt = 2; // Skip to final attempt

558

retryOperation.execute()

559

.should.be.fulfilled()

560

.finally.have.properties('success', 'attempt', 'message')

561

.and.have.property('success', true);

562

563

// Test timeout

564

const timeoutOperation = new RetryableOperation();

565

timeoutOperation.executeWithTimeout(1) // Very short timeout

566

.should.be.rejectedWith('Operation timed out', { code: 'TIMEOUT' });

567

```

568

569

## Advanced Promise Testing

570

571

### Promise.all() Testing

572

```javascript

573

const promises = [

574

Promise.resolve(1),

575

Promise.resolve(2),

576

Promise.resolve(3)

577

];

578

579

Promise.all(promises)

580

.should.be.fulfilled()

581

.finally.eql([1, 2, 3]);

582

583

// Test Promise.all() with rejection

584

const mixedPromises = [

585

Promise.resolve('success'),

586

Promise.reject(new Error('failed')),

587

Promise.resolve('also success')

588

];

589

590

Promise.all(mixedPromises)

591

.should.be.rejectedWith('failed');

592

```

593

594

### Promise.race() Testing

595

```javascript

596

const racePromises = [

597

new Promise(resolve => setTimeout(() => resolve('slow'), 100)),

598

Promise.resolve('fast')

599

];

600

601

Promise.race(racePromises)

602

.should.be.fulfilledWith('fast');

603

```

604

605

## Negation and Error Cases

606

607

```javascript

608

// Negation examples

609

Promise.resolve('value').should.not.be.rejected();

610

Promise.reject('error').should.not.be.fulfilled();

611

612

'not a promise'.should.not.be.a.Promise();

613

Promise.resolve(42).should.not.be.fulfilledWith(43);

614

Promise.reject('wrong').should.not.be.rejectedWith('different error');

615

616

// Testing async functions that shouldn't throw

617

async function safeAsyncOperation() {

618

return 'safe result';

619

}

620

621

safeAsyncOperation().should.not.be.rejected();

622

safeAsyncOperation().should.be.fulfilledWith('safe result');

623

```