Skip to content

Commit df8a260

Browse files
authored
fix(messages): Improve prettify(..) helper and assertion messages (#111)
1 parent 9a18f36 commit df8a260

File tree

11 files changed

+247
-172
lines changed

11 files changed

+247
-172
lines changed

packages/core/src/lib/ArrayAssertion.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,14 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
220220
* @returns the assertion instance
221221
*/
222222
public toHaveSameMembers(expected: T[]): this {
223-
const prettyValues = `[${expected.map(value => JSON.stringify(value)).join(", ")}]`;
224223
const error = new AssertionError({
225224
actual: this.actual,
226225
expected,
227-
message: `Expected array to have the same members as: ${prettyValues}`,
226+
message: `Expected array to have the same members as <${prettify(expected)}>`,
228227
});
229228
const invertedError = new AssertionError({
230229
actual: this.actual,
231-
message: `Expected array NOT to have the same members as: ${prettyValues}`,
230+
message: `Expected array NOT to have the same members as <${prettify(expected)}>`,
232231
});
233232

234233
return this.execute({
@@ -252,14 +251,14 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
252251
* @returns the assertion instance
253252
*/
254253
public toContainAll(...values: T[]): this {
255-
const prettyValues = `[${values.map(value => JSON.stringify(value)).join(", ")}]`;
254+
const allValues = values.map(prettify).join(", ");
256255
const error = new AssertionError({
257256
actual: this.actual,
258-
message: `Expected array to contain the following values: ${prettyValues}`,
257+
message: `Expected array to contain all the values <${allValues}>`,
259258
});
260259
const invertedError = new AssertionError({
261260
actual: this.actual,
262-
message: `Expected array NOT to contain the following values, but it does: ${prettyValues}`,
261+
message: `Expected array NOT to contain all the values <${allValues}>`,
263262
});
264263

265264
return this.execute({
@@ -281,14 +280,14 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
281280
* @returns the assertion instance
282281
*/
283282
public toContainAny(...values: T[]): this {
284-
const prettyValues = `[${values.map(value => JSON.stringify(value)).join(", ")}]`;
283+
const allValues = values.map(prettify).join(", ");
285284
const error = new AssertionError({
286285
actual: this.actual,
287-
message: `Expected array to contain at least one of the following values: ${prettyValues}`,
286+
message: `Expected array to contain any of the values <${allValues}>`,
288287
});
289288
const invertedError = new AssertionError({
290289
actual: this.actual,
291-
message: `Expected array NOT to contain one of the following values, but it does: ${prettyValues}`,
290+
message: `Expected array NOT to contain any of the values <${allValues}>`,
292291
});
293292

294293
return this.execute({
@@ -309,11 +308,11 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
309308
const error = new AssertionError({
310309
actual: this.actual[index],
311310
expected: value,
312-
message: `Expected value at index ${index} of the array to be <${prettify(value)}>`,
311+
message: `Expected value at index <${index}> to be <${prettify(value)}>`,
313312
});
314313
const invertedError = new AssertionError({
315314
actual: this.actual[index],
316-
message: `Expected value at index ${index} of the array NOT to be <${prettify(value)}>`,
315+
message: `Expected value at index <${index}> NOT to be <${prettify(value)}>`,
317316
});
318317

319318
return this.execute({

packages/core/src/lib/NumberAssertion.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Assertion } from "./Assertion";
22
import { isHighInclusiveOptions, isInclusiveOptions, isLowInclusiveOptions } from "./helpers/guards";
3-
import { prettify } from "./helpers/messages";
43

54
import { AssertionError } from "assert";
65

@@ -277,62 +276,64 @@ export class NumberAssertion extends Assertion<number> {
277276
* @returns the assertion instance
278277
*/
279278
public toBeBetween(options: BetweenOptions): this {
279+
const [min, max] = options.range;
280+
280281
if (isInclusiveOptions(options)) {
281-
const between = options.inclusive ? "strictly between" : "between";
282+
const rangeText = options.inclusive ? `[${min}, ${max}]` : `(${min}, ${max})`;
282283
const inclusiveError = new AssertionError({
283284
actual: this.actual,
284285
expected: options,
285-
message: `Expected <${this.actual}> to be ${between} <${prettify(options.range)}>` });
286+
message: `Expected <${this.actual}> to be between ${rangeText} range` });
286287
const inclusiveInvertedError = new AssertionError({
287288
actual: this.actual,
288-
message: `Expected <${this.actual}> NOT to be ${between} <${prettify(options.range)}>` });
289+
message: `Expected <${this.actual}> NOT to be between ${rangeText} range` });
289290

290291
return this.execute({
291292
assertWhen: options.inclusive
292-
? this.actual >= options.range[0] && this.actual <= options.range[1]
293-
: this.actual > options.range[0] && this.actual < options.range[1],
293+
? this.actual >= min && this.actual <= max
294+
: this.actual > min && this.actual < max,
294295
error: inclusiveError,
295296
invertedError: inclusiveInvertedError,
296297
});
297298
}
298299

299300
if (isLowInclusiveOptions(options)) {
300-
const withInclusion = options.lowInclusive ? ` with <${options.range[0]}> inclusion` : "";
301+
const rangeText = options.lowInclusive ? `[${min}, ${max})` : `(${min}, ${max})`;
301302
const lowInclusiveError = new AssertionError({
302303
actual: this.actual,
303304
expected: options,
304-
message: `Expected <${this.actual}> to be between <${prettify(options.range)}>${withInclusion}`,
305+
message: `Expected <${this.actual}> to be between ${rangeText} range`,
305306
});
306307
const lowInclusiveErrorInvertedError = new AssertionError({
307308
actual: this.actual,
308-
message: `Expected <${this.actual}> NOT to be between <${prettify(options.range)}>${withInclusion}`,
309+
message: `Expected <${this.actual}> NOT to be between ${rangeText} range`,
309310
});
310311

311312
return this.execute({
312313
assertWhen: options.lowInclusive
313-
? this.actual >= options.range[0] && this.actual < options.range[1]
314-
: this.actual > options.range[0] && this.actual < options.range[1],
314+
? this.actual >= min && this.actual < max
315+
: this.actual > min && this.actual < max,
315316
error: lowInclusiveError,
316317
invertedError: lowInclusiveErrorInvertedError,
317318
});
318319
}
319320

320321
if (isHighInclusiveOptions(options)) {
321-
const withInclusion = options.highInclusive ? ` with <${options.range[1]}> inclusion` : "";
322+
const rangeText = options.highInclusive ? `(${min}, ${max}]` : `(${min}, ${max})`;
322323
const highInclusiveError = new AssertionError({
323324
actual: this.actual,
324325
expected: options,
325-
message: `Expected <${this.actual}> to be between <${prettify(options.range)}>${withInclusion}`,
326+
message: `Expected <${this.actual}> to be between ${rangeText} range`,
326327
});
327328
const highInclusiveErrorInvertedError = new AssertionError({
328329
actual: this.actual,
329-
message: `Expected <${this.actual}> NOT to be between <${prettify(options.range)}>${withInclusion}`,
330+
message: `Expected <${this.actual}> NOT to be between ${rangeText} range`,
330331
});
331332

332333
return this.execute({
333334
assertWhen: options.highInclusive
334-
? this.actual > options.range[0] && this.actual <= options.range[1]
335-
: this.actual > options.range[0] && this.actual < options.range[1],
335+
? this.actual > min && this.actual <= max
336+
: this.actual > min && this.actual < max,
336337
error: highInclusiveError,
337338
invertedError: highInclusiveErrorInvertedError,
338339
});
@@ -341,16 +342,15 @@ public toBeBetween(options: BetweenOptions): this {
341342
const error = new AssertionError({
342343
actual: this.actual,
343344
expected: options,
344-
message: `Expected <${this.actual}> to be between <${prettify(options.range)}>`,
345+
message: `Expected <${this.actual}> to be between (${min}, ${max}) range`,
345346
});
346347
const invertedError = new AssertionError({
347348
actual: this.actual,
348-
message: `Expected <${this.actual}> NOT to be between <${prettify(options.range)}>`,
349+
message: `Expected <${this.actual}> NOT to be between (${min}, ${max}) range`,
349350
});
350351

351352
return this.execute({
352-
assertWhen:
353-
this.actual > options.range[0] && this.actual < options.range[1],
353+
assertWhen: this.actual > min && this.actual < max,
354354
error,
355355
invertedError,
356356
});

packages/core/src/lib/ObjectAssertion.ts

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
6060
public toContainKey(key: keyof T): this {
6161
const error = new AssertionError({
6262
actual: this.actual,
63-
message: `Expected the object to contain the provided key <${String(key)}>`,
63+
message: `Expected the object to contain the provided key <${prettify(key)}>`,
6464
});
6565
const invertedError = new AssertionError({
6666
actual: this.actual,
67-
message: `Expected the object NOT to contain the provided key <${String(key)}>`,
67+
message: `Expected the object NOT to contain the provided key <${prettify(key)}>`,
6868
});
6969

7070
return this.execute({
@@ -86,14 +86,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
8686
* @returns the assertion instance
8787
*/
8888
public toContainAllKeys(...keys: Array<keyof T>): this {
89+
const allKeys = keys.map(prettify).join(", ");
8990
const error = new AssertionError({
9091
actual: Object.keys(this.actual),
9192
expected: keys,
92-
message: `Expected the object to contain all the provided keys <${prettify(keys)}>`,
93+
message: `Expected the object to contain all the provided keys <${allKeys}>`,
9394
});
9495
const invertedError = new AssertionError({
9596
actual: Object.keys(this.actual),
96-
message: `Expected the object NOT to contain all the provided keys <${prettify(keys)}>`,
97+
message: `Expected the object NOT to contain all the provided keys <${allKeys}>`,
9798
});
9899

99100
return this.execute({
@@ -115,14 +116,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
115116
* @returns the assertion instance
116117
*/
117118
public toContainAnyKeys(...keys: Array<keyof T>): this {
119+
const allKeys = keys.map(prettify).join(", ");
118120
const error = new AssertionError({
119121
actual: Object.keys(this.actual),
120122
expected: keys,
121-
message: `Expected the object to contain at least one of the provided keys <${prettify(keys)}>`,
123+
message: `Expected the object to contain at least one of the provided keys <${allKeys}>`,
122124
});
123125
const invertedError = new AssertionError({
124126
actual: Object.keys(this.actual),
125-
message: `Expected the object NOT to contain any of the provided keys <${prettify(keys)}>`,
127+
message: `Expected the object NOT to contain any of the provided keys <${allKeys}>`,
126128
});
127129

128130
return this.execute({
@@ -146,15 +148,16 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
146148
public toHaveKeys(...keys: Array<keyof T>): this {
147149
const sortedActual = Object.keys(this.actual).sort();
148150
const sortedKeys = [...keys].sort();
151+
const allKeys = sortedKeys.map(prettify).join(", ");
149152

150153
const error = new AssertionError({
151154
actual: sortedActual,
152155
expected: sortedKeys,
153-
message: `Expected the object to have exactly the keys <${prettify(sortedKeys)}>`,
156+
message: `Expected the object to have exactly the keys <${allKeys}>`,
154157
});
155158
const invertedError = new AssertionError({
156159
actual: sortedActual,
157-
message: `Expected the object NOT to have the keys <${prettify(sortedKeys)}>`,
160+
message: `Expected the object NOT to have the keys <${allKeys}>`,
158161
});
159162

160163
return this.execute({
@@ -205,14 +208,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
205208
* @returns the assertion instance
206209
*/
207210
public toContainAllValues(...values: Array<T[keyof T]>): this {
211+
const allValues = values.map(prettify).join(", ");
208212
const error = new AssertionError({
209213
actual: Object.values(this.actual),
210214
expected: values,
211-
message: `Expected the object to contain all the provided values <${prettify(values)}>`,
215+
message: `Expected the object to contain all the provided values <${allValues}>`,
212216
});
213217
const invertedError = new AssertionError({
214218
actual: Object.values(this.actual),
215-
message: `Expected the object NOT to contain all the provided values <${prettify(values)}>`,
219+
message: `Expected the object NOT to contain all the provided values <${allValues}>`,
216220
});
217221

218222
return this.execute({
@@ -237,14 +241,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
237241
* @returns the assertion instance
238242
*/
239243
public toContainAnyValues(...values: Array<T[keyof T]>): this {
244+
const allValues = values.map(prettify).join(", ");
240245
const error = new AssertionError({
241246
actual: Object.values(this.actual),
242247
expected: values,
243-
message: `Expected the object to contain at least one of the provided values <${prettify(values)}>`,
248+
message: `Expected the object to contain at least one of the provided values <${allValues}>`,
244249
});
245250
const invertedError = new AssertionError({
246251
actual: Object.values(this.actual),
247-
message: `Expected the object NOT to contain any of the provided values <${prettify(values)}>`,
252+
message: `Expected the object NOT to contain any of the provided values <${allValues}>`,
248253
});
249254

250255
return this.execute({
@@ -271,15 +276,16 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
271276
public toHaveValues(...values: Array<T[keyof T]>): this {
272277
const sortedActual = Object.values(this.actual).sort();
273278
const sorterdValues = [...values].sort();
279+
const allValues = sorterdValues.map(prettify).join(", ");
274280

275281
const error = new AssertionError({
276282
actual: sortedActual,
277283
expected: sorterdValues,
278-
message: `Expected the object to have exactly the values <${prettify(sorterdValues)}>`,
284+
message: `Expected the object to have exactly the values <${allValues}>`,
279285
});
280286
const invertedError = new AssertionError({
281287
actual: sortedActual,
282-
message: `Expected the object NOT to have the values <${prettify(sorterdValues)}>`,
288+
message: `Expected the object NOT to have the values <${allValues}>`,
283289
});
284290

285291
return this.execute({
@@ -331,15 +337,16 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
331337
* @returns the assertion instance
332338
*/
333339
public toContainAllEntries(...entries: Entry<T>[]): this {
340+
const allEntries = entries.map(prettify).join(", ");
334341
const error = new AssertionError({
335342
actual: Object.entries(this.actual),
336343
expected: entries,
337-
message: `Expected the object to contain all the provided entries <${prettify(entries)}>`,
344+
message: `Expected the object to contain all the provided entries <${allEntries}>`,
338345
});
339346

340347
const invertedError = new AssertionError({
341348
actual: Object.entries(this.actual),
342-
message: `Expected the object NOT to contain all the provided entries <${prettify(entries)}>`,
349+
message: `Expected the object NOT to contain all the provided entries <${allEntries}>`,
343350
});
344351
return this.execute({
345352
assertWhen: entries
@@ -365,15 +372,16 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
365372
* @returns the assertion instance
366373
*/
367374
public toContainAnyEntries(...entries: Entry<T>[]): this {
375+
const allEntries = entries.map(prettify).join(", ");
368376
const error = new AssertionError({
369377
actual: Object.entries(this.actual),
370378
expected: entries,
371-
message: `Expected the object to contain at least one of the provided entries <${prettify(entries)}>`,
379+
message: `Expected the object to contain at least one of the provided entries <${allEntries}>`,
372380
});
373381

374382
const invertedError = new AssertionError({
375383
actual: Object.entries(this.actual),
376-
message: `Expected the object NOT to contain any of the provided entries <${prettify(entries)}>`,
384+
message: `Expected the object NOT to contain any of the provided entries <${allEntries}>`,
377385
});
378386
return this.execute({
379387
assertWhen: entries
@@ -401,15 +409,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
401409
public toHaveEntries(...entries: Entry<T>[]): this {
402410
const sortedActual = Object.entries(this.actual).sort();
403411
const sortedEntries = [...entries].sort();
404-
const prettyEntries = sortedEntries.map(entry => `[${prettify(entry)}]`).join(",");
412+
const allEntries = sortedEntries.map(prettify).join(", ");
405413
const error = new AssertionError({
406414
actual: sortedActual,
407415
expected: sortedEntries,
408-
message: `Expected the object to have exactly the entries <${prettyEntries}>`,
416+
message: `Expected the object to have exactly the entries <${allEntries}>`,
409417
});
410418
const invertedError = new AssertionError({
411419
actual: Object.entries(this.actual),
412-
message: `Expected the object NOT to have the entries <${prettyEntries}>`,
420+
message: `Expected the object NOT to have the entries <${allEntries}>`,
413421
});
414422

415423
return this.execute({
@@ -433,13 +441,12 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
433441
public toPartiallyMatch(other: Partial<T>): this {
434442
const error = new AssertionError({
435443
actual: this.actual,
436-
expected: other,
437-
message: "Expected the object to be a partial match",
444+
message: `Expected the object to partially match <${prettify(other)}>`,
438445
});
439446

440447
const invertedError = new AssertionError({
441448
actual: this.actual,
442-
message: "Expected the object NOT to be a partial match",
449+
message: `Expected the object NOT to partially match <${prettify(other)}>`,
443450
});
444451
return this.execute({
445452
assertWhen: Object.keys(other)
Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
export function prettify<T>(value: T): string {
2-
return typeof value === "object" && value !== null
2+
const isClassObject = typeof value === "object"
3+
&& value !== null
4+
&& value.toString() !== "[object Object]";
5+
const nonJsonValue = value === undefined
6+
|| Number.isNaN(value)
7+
|| typeof value === "symbol"
8+
|| typeof value === "bigint";
9+
10+
if (Array.isArray(value)) {
11+
return `[${value.map(prettify).join(",")}]`;
12+
}
13+
14+
if (isClassObject || nonJsonValue) {
15+
return String(value);
16+
}
17+
18+
return typeof value === "function"
319
? value.toString()
4-
: String(value);
20+
: JSON.stringify(value);
521
}

0 commit comments

Comments
 (0)