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

configuration.mddocs/

0

# Configuration and Extension

1

2

Library configuration options, prototype extension management, and custom assertion plugin system.

3

4

## Library Configuration

5

6

### should.config

7

8

Global configuration object that controls should.js behavior.

9

10

```javascript { .api }

11

/**

12

* Global configuration object for should.js behavior

13

*/

14

interface Config {

15

/** Whether .eql checks object prototypes for equality */

16

checkProtoEql: boolean;

17

/** Whether .eql treats +0 and -0 as equal */

18

plusZeroAndMinusZeroEqual: boolean;

19

/** Additional formatting options for should-format */

20

[key: string]: any;

21

}

22

23

const config: Config;

24

```

25

26

**Usage:**

27

```javascript

28

import should from 'should';

29

30

// Default configuration

31

console.log(should.config.checkProtoEql); // false

32

console.log(should.config.plusZeroAndMinusZeroEqual); // true

33

34

// Modify configuration

35

should.config.checkProtoEql = true;

36

should.config.plusZeroAndMinusZeroEqual = false;

37

38

// Test behavior with different settings

39

const objA = { value: 10 };

40

const objB = Object.create(null);

41

objB.value = 10;

42

43

// With checkProtoEql = false (default)

44

should.config.checkProtoEql = false;

45

objA.should.eql(objB); // Passes - prototypes ignored

46

47

// With checkProtoEql = true

48

should.config.checkProtoEql = true;

49

objA.should.not.eql(objB); // Different prototypes detected

50

51

// Zero equality behavior

52

should.config.plusZeroAndMinusZeroEqual = true;

53

(+0).should.eql(-0); // Passes

54

55

should.config.plusZeroAndMinusZeroEqual = false;

56

(+0).should.not.eql(-0); // Fails - treats +0 and -0 as different

57

```

58

59

## Prototype Extension Management

60

61

### should.extend()

62

63

Extend a prototype with the should property using a custom name.

64

65

```javascript { .api }

66

/**

67

* Extend given prototype with should property using specified name

68

* @param propertyName - Name of property to add (default: 'should')

69

* @param proto - Prototype to extend (default: Object.prototype)

70

* @returns Descriptor object to restore later with noConflict()

71

*/

72

extend(propertyName?: string, proto?: object): {

73

name: string;

74

descriptor: PropertyDescriptor | undefined;

75

proto: object;

76

};

77

```

78

79

**Usage:**

80

```javascript

81

import should from 'should';

82

83

// Default extension (already done automatically)

84

// Object.prototype.should = getter

85

86

// Custom property name

87

const mustDescriptor = should.extend('must', Object.prototype);

88

'test'.must.be.a.String(); // Now using 'must' instead of 'should'

89

90

// Custom prototype

91

class CustomClass {

92

constructor(value) {

93

this.value = value;

94

}

95

}

96

97

const customDescriptor = should.extend('expect', CustomClass.prototype);

98

const instance = new CustomClass(42);

99

instance.expect.have.property('value', 42);

100

101

// Multiple extensions

102

const checkDescriptor = should.extend('check', Array.prototype);

103

[1, 2, 3].check.have.length(3);

104

105

// Store descriptors for later cleanup

106

const extensions = {

107

must: should.extend('must'),

108

expect: should.extend('expect', CustomClass.prototype),

109

check: should.extend('check', Array.prototype)

110

};

111

```

112

113

### should.noConflict()

114

115

Remove should extension and restore previous property if it existed.

116

117

```javascript { .api }

118

/**

119

* Remove should extension and restore previous property descriptor

120

* @param descriptor - Descriptor returned from extend() (optional)

121

* @returns The should function

122

*/

123

noConflict(descriptor?: {

124

name: string;

125

descriptor: PropertyDescriptor | undefined;

126

proto: object;

127

}): typeof should;

128

```

129

130

**Usage:**

131

```javascript

132

// Remove default should extension

133

const cleanShould = should.noConflict();

134

135

// Now Object.prototype.should is removed

136

try {

137

'test'.should.be.a.String(); // TypeError: Cannot read property 'should'

138

} catch (error) {

139

console.log('should property removed');

140

}

141

142

// Use functional syntax instead

143

cleanShould('test').should.be.a.String(); // Works

144

145

// Remove specific extension

146

const mustDescriptor = should.extend('must');

147

'test'.must.be.a.String(); // Works

148

149

should.noConflict(mustDescriptor);

150

// Now 'must' property is removed

151

152

// Restore previous property if it existed

153

Object.prototype.previousProperty = 'original';

154

const prevDescriptor = should.extend('previousProperty');

155

'test'.previousProperty.be.a.String(); // should assertion

156

157

should.noConflict(prevDescriptor);

158

console.log('test'.previousProperty); // 'original' - restored

159

```

160

161

## Functional API Usage

162

163

### As-Function Import

164

165

Use should.js without prototype extension.

166

167

```javascript { .api }

168

/**

169

* Should.js as a function without prototype extension

170

* Import from 'should/as-function' to avoid Object.prototype modification

171

*/

172

declare function should(obj: any): should.Assertion;

173

```

174

175

**Usage:**

176

```javascript

177

// Import as function only

178

const should = require('should/as-function');

179

// or

180

import should from 'should/as-function';

181

182

// No prototype extension - Object.prototype.should doesn't exist

183

console.log(typeof ''.should); // 'undefined'

184

185

// Use functional syntax

186

should('test').be.a.String();

187

should(42).be.a.Number().and.be.above(0);

188

should({ name: 'john' }).have.property('name', 'john');

189

190

// Useful for testing null/undefined values

191

should(null).be.null();

192

should(undefined).be.undefined();

193

194

// Can still use all assertion methods

195

should([1, 2, 3])

196

.be.an.Array()

197

.and.have.length(3)

198

.and.containEql(2);

199

```

200

201

## Plugin System

202

203

### should.use()

204

205

Add custom assertion plugins to extend functionality.

206

207

```javascript { .api }

208

/**

209

* Use a plugin to extend should.js functionality

210

* @param plugin - Plugin function that receives (should, Assertion) parameters

211

* @returns The should function for chaining

212

*/

213

use(plugin: (should: any, Assertion: any) => void): typeof should;

214

```

215

216

**Usage:**

217

```javascript

218

// Basic plugin example

219

should.use(function(should, Assertion) {

220

// Add custom assertion method

221

Assertion.add('between', function(min, max) {

222

this.params = {

223

operator: `to be between ${min} and ${max}`

224

};

225

226

this.obj.should.be.a.Number();

227

this.obj.should.be.above(min - 1);

228

this.obj.should.be.below(max + 1);

229

});

230

});

231

232

// Use the custom assertion

233

(5).should.be.between(1, 10);

234

(15).should.not.be.between(1, 10);

235

236

// Advanced plugin with multiple methods

237

should.use(function(should, Assertion) {

238

// Email validation assertion

239

Assertion.add('email', function() {

240

this.params = { operator: 'to be a valid email' };

241

242

this.obj.should.be.a.String();

243

this.obj.should.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);

244

});

245

246

// Phone number validation

247

Assertion.add('phoneNumber', function() {

248

this.params = { operator: 'to be a valid phone number' };

249

250

this.obj.should.be.a.String();

251

this.obj.should.match(/^\(?[\d\s\-\+\(\)]{10,}$/);

252

});

253

254

// Credit card validation (simplified)

255

Assertion.add('creditCard', function() {

256

this.params = { operator: 'to be a valid credit card number' };

257

258

this.obj.should.be.a.String();

259

this.obj.should.match(/^\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}$/);

260

});

261

});

262

263

// Use custom validation assertions

264

'user@example.com'.should.be.email();

265

'123-456-7890'.should.be.phoneNumber();

266

'1234 5678 9012 3456'.should.be.creditCard();

267

```

268

269

### Complex Plugin Examples

270

271

#### HTTP Status Code Plugin

272

```javascript

273

should.use(function(should, Assertion) {

274

// HTTP status code categories

275

const statusCategories = {

276

informational: (code) => code >= 100 && code < 200,

277

success: (code) => code >= 200 && code < 300,

278

redirection: (code) => code >= 300 && code < 400,

279

clientError: (code) => code >= 400 && code < 500,

280

serverError: (code) => code >= 500 && code < 600

281

};

282

283

Object.keys(statusCategories).forEach(category => {

284

Assertion.add(category, function() {

285

this.params = { operator: `to be a ${category} status code` };

286

287

this.obj.should.be.a.Number();

288

statusCategories[category](this.obj).should.be.true();

289

});

290

});

291

292

// Specific status codes

293

const specificCodes = {

294

ok: 200,

295

created: 201,

296

badRequest: 400,

297

unauthorized: 401,

298

forbidden: 403,

299

notFound: 404,

300

internalServerError: 500

301

};

302

303

Object.keys(specificCodes).forEach(name => {

304

Assertion.add(name, function() {

305

this.params = { operator: `to be ${name} (${specificCodes[name]})` };

306

this.obj.should.equal(specificCodes[name]);

307

});

308

});

309

});

310

311

// Usage

312

(200).should.be.success();

313

(404).should.be.clientError();

314

(500).should.be.serverError();

315

316

(200).should.be.ok();

317

(404).should.be.notFound();

318

(401).should.be.unauthorized();

319

```

320

321

#### Date Range Plugin

322

```javascript

323

should.use(function(should, Assertion) {

324

Assertion.add('dateAfter', function(date) {

325

this.params = { operator: `to be after ${date}` };

326

327

this.obj.should.be.a.Date();

328

this.obj.getTime().should.be.above(new Date(date).getTime());

329

});

330

331

Assertion.add('dateBefore', function(date) {

332

this.params = { operator: `to be before ${date}` };

333

334

this.obj.should.be.a.Date();

335

this.obj.getTime().should.be.below(new Date(date).getTime());

336

});

337

338

Assertion.add('dateWithin', function(startDate, endDate) {

339

this.params = {

340

operator: `to be between ${startDate} and ${endDate}`

341

};

342

343

this.obj.should.be.a.Date();

344

const time = this.obj.getTime();

345

const start = new Date(startDate).getTime();

346

const end = new Date(endDate).getTime();

347

348

time.should.be.above(start - 1);

349

time.should.be.below(end + 1);

350

});

351

352

Assertion.add('today', function() {

353

this.params = { operator: 'to be today' };

354

355

this.obj.should.be.a.Date();

356

const today = new Date();

357

const objDate = new Date(this.obj);

358

359

objDate.getFullYear().should.equal(today.getFullYear());

360

objDate.getMonth().should.equal(today.getMonth());

361

objDate.getDate().should.equal(today.getDate());

362

});

363

});

364

365

// Usage

366

const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);

367

const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000);

368

const now = new Date();

369

370

now.should.be.dateAfter(yesterday);

371

now.should.be.dateBefore(tomorrow);

372

now.should.be.dateWithin(yesterday, tomorrow);

373

now.should.be.today();

374

```

375

376

#### API Response Plugin

377

```javascript

378

should.use(function(should, Assertion) {

379

Assertion.add('apiResponse', function() {

380

this.params = { operator: 'to be a valid API response' };

381

382

this.obj.should.be.an.Object();

383

this.obj.should.have.properties('status', 'data');

384

this.obj.status.should.be.a.Number();

385

});

386

387

Assertion.add('successResponse', function() {

388

this.params = { operator: 'to be a successful API response' };

389

390

this.obj.should.be.apiResponse();

391

this.obj.status.should.be.within(200, 299);

392

});

393

394

Assertion.add('errorResponse', function() {

395

this.params = { operator: 'to be an error API response' };

396

397

this.obj.should.be.apiResponse();

398

this.obj.status.should.be.above(399);

399

this.obj.should.have.property('error');

400

});

401

402

Assertion.add('paginatedResponse', function() {

403

this.params = { operator: 'to be a paginated API response' };

404

405

this.obj.should.be.successResponse();

406

this.obj.should.have.property('pagination');

407

this.obj.pagination.should.have.properties('page', 'limit', 'total');

408

});

409

});

410

411

// Usage

412

const response = {

413

status: 200,

414

data: [{ id: 1, name: 'user1' }],

415

pagination: { page: 1, limit: 10, total: 50 }

416

};

417

418

response.should.be.apiResponse();

419

response.should.be.successResponse();

420

response.should.be.paginatedResponse();

421

422

const errorResponse = {

423

status: 404,

424

data: null,

425

error: { message: 'Not found' }

426

};

427

428

errorResponse.should.be.errorResponse();

429

```

430

431

## Advanced Configuration

432

433

### Environment-Specific Setup

434

```javascript

435

// Development environment

436

if (process.env.NODE_ENV === 'development') {

437

should.config.checkProtoEql = true; // Strict prototype checking

438

439

// Add development-only assertions

440

should.use(function(should, Assertion) {

441

Assertion.add('debug', function() {

442

console.log('Debug assertion:', this.obj);

443

return this;

444

});

445

});

446

}

447

448

// Test environment

449

if (process.env.NODE_ENV === 'test') {

450

// More lenient configuration for tests

451

should.config.plusZeroAndMinusZeroEqual = true;

452

453

// Test-specific utilities

454

should.use(function(should, Assertion) {

455

Assertion.add('testFixture', function(type) {

456

this.params = { operator: `to be a ${type} test fixture` };

457

458

this.obj.should.be.an.Object();

459

this.obj.should.have.property('_fixture', type);

460

});

461

});

462

}

463

```

464

465

### Framework Integration

466

```javascript

467

// Express.js response testing

468

should.use(function(should, Assertion) {

469

Assertion.add('expressResponse', function() {

470

this.params = { operator: 'to be an Express response object' };

471

472

this.obj.should.be.an.Object();

473

this.obj.should.have.properties('status', 'json', 'send');

474

this.obj.status.should.be.a.Function();

475

});

476

});

477

478

// React component testing

479

should.use(function(should, Assertion) {

480

Assertion.add('reactComponent', function() {

481

this.params = { operator: 'to be a React component' };

482

483

this.obj.should.be.a.Function();

484

// Additional React-specific checks

485

});

486

});

487

```

488

489

## Chaining and Integration

490

491

All configuration and extension features integrate with standard should.js chaining:

492

493

```javascript

494

// Chain configuration changes

495

should.config.checkProtoEql = true;

496

497

const obj1 = { value: 1 };

498

const obj2 = Object.create(null);

499

obj2.value = 1;

500

501

obj1.should.not.eql(obj2); // Prototype difference detected

502

503

// Chain custom assertions

504

'user@test.com'.should.be.email().and.be.a.String();

505

(404).should.be.clientError().and.be.notFound();

506

507

// Use extensions in complex chains

508

const apiResponse = { status: 200, data: [] };

509

apiResponse.should.be.successResponse()

510

.and.have.property('data')

511

.which.is.an.Array();

512

```