Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions packages/core/src/lib/ArrayAssertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,15 +220,14 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
* @returns the assertion instance
*/
public toHaveSameMembers(expected: T[]): this {
const prettyValues = `[${expected.map(value => JSON.stringify(value)).join(", ")}]`;
const error = new AssertionError({
actual: this.actual,
expected,
message: `Expected array to have the same members as: ${prettyValues}`,
message: `Expected array to have the same members as <${prettify(expected)}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected array NOT to have the same members as: ${prettyValues}`,
message: `Expected array NOT to have the same members as <${prettify(expected)}>`,
});

return this.execute({
Expand All @@ -252,14 +251,14 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
* @returns the assertion instance
*/
public toContainAll(...values: T[]): this {
const prettyValues = `[${values.map(value => JSON.stringify(value)).join(", ")}]`;
const allValues = values.map(prettify).join(", ");
const error = new AssertionError({
actual: this.actual,
message: `Expected array to contain the following values: ${prettyValues}`,
message: `Expected array to contain all the values <${allValues}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected array NOT to contain the following values, but it does: ${prettyValues}`,
message: `Expected array NOT to contain all the values <${allValues}>`,
});

return this.execute({
Expand All @@ -281,14 +280,14 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
* @returns the assertion instance
*/
public toContainAny(...values: T[]): this {
const prettyValues = `[${values.map(value => JSON.stringify(value)).join(", ")}]`;
const allValues = values.map(prettify).join(", ");
const error = new AssertionError({
actual: this.actual,
message: `Expected array to contain at least one of the following values: ${prettyValues}`,
message: `Expected array to contain any of the values <${allValues}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected array NOT to contain one of the following values, but it does: ${prettyValues}`,
message: `Expected array NOT to contain any of the values <${allValues}>`,
});

return this.execute({
Expand All @@ -309,11 +308,11 @@ export class ArrayAssertion<T> extends Assertion<T[]> {
const error = new AssertionError({
actual: this.actual[index],
expected: value,
message: `Expected value at index ${index} of the array to be <${prettify(value)}>`,
message: `Expected value at index <${index}> to be <${prettify(value)}>`,
});
const invertedError = new AssertionError({
actual: this.actual[index],
message: `Expected value at index ${index} of the array NOT to be <${prettify(value)}>`,
message: `Expected value at index <${index}> NOT to be <${prettify(value)}>`,
});

return this.execute({
Expand Down
40 changes: 20 additions & 20 deletions packages/core/src/lib/NumberAssertion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Assertion } from "./Assertion";
import { isHighInclusiveOptions, isInclusiveOptions, isLowInclusiveOptions } from "./helpers/guards";
import { prettify } from "./helpers/messages";

import { AssertionError } from "assert";

Expand Down Expand Up @@ -277,62 +276,64 @@ export class NumberAssertion extends Assertion<number> {
* @returns the assertion instance
*/
public toBeBetween(options: BetweenOptions): this {
const [min, max] = options.range;

if (isInclusiveOptions(options)) {
const between = options.inclusive ? "strictly between" : "between";
const rangeText = options.inclusive ? `[${min}, ${max}]` : `(${min}, ${max})`;
const inclusiveError = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be ${between} <${prettify(options.range)}>` });
message: `Expected <${this.actual}> to be between ${rangeText} range` });
const inclusiveInvertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be ${between} <${prettify(options.range)}>` });
message: `Expected <${this.actual}> NOT to be between ${rangeText} range` });

return this.execute({
assertWhen: options.inclusive
? this.actual >= options.range[0] && this.actual <= options.range[1]
: this.actual > options.range[0] && this.actual < options.range[1],
? this.actual >= min && this.actual <= max
: this.actual > min && this.actual < max,
error: inclusiveError,
invertedError: inclusiveInvertedError,
});
}

if (isLowInclusiveOptions(options)) {
const withInclusion = options.lowInclusive ? ` with <${options.range[0]}> inclusion` : "";
const rangeText = options.lowInclusive ? `[${min}, ${max})` : `(${min}, ${max})`;
const lowInclusiveError = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be between <${prettify(options.range)}>${withInclusion}`,
message: `Expected <${this.actual}> to be between ${rangeText} range`,
});
const lowInclusiveErrorInvertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be between <${prettify(options.range)}>${withInclusion}`,
message: `Expected <${this.actual}> NOT to be between ${rangeText} range`,
});

return this.execute({
assertWhen: options.lowInclusive
? this.actual >= options.range[0] && this.actual < options.range[1]
: this.actual > options.range[0] && this.actual < options.range[1],
? this.actual >= min && this.actual < max
: this.actual > min && this.actual < max,
error: lowInclusiveError,
invertedError: lowInclusiveErrorInvertedError,
});
}

if (isHighInclusiveOptions(options)) {
const withInclusion = options.highInclusive ? ` with <${options.range[1]}> inclusion` : "";
const rangeText = options.highInclusive ? `(${min}, ${max}]` : `(${min}, ${max})`;
const highInclusiveError = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be between <${prettify(options.range)}>${withInclusion}`,
message: `Expected <${this.actual}> to be between ${rangeText} range`,
});
const highInclusiveErrorInvertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be between <${prettify(options.range)}>${withInclusion}`,
message: `Expected <${this.actual}> NOT to be between ${rangeText} range`,
});

return this.execute({
assertWhen: options.highInclusive
? this.actual > options.range[0] && this.actual <= options.range[1]
: this.actual > options.range[0] && this.actual < options.range[1],
? this.actual > min && this.actual <= max
: this.actual > min && this.actual < max,
error: highInclusiveError,
invertedError: highInclusiveErrorInvertedError,
});
Expand All @@ -341,16 +342,15 @@ public toBeBetween(options: BetweenOptions): this {
const error = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be between <${prettify(options.range)}>`,
message: `Expected <${this.actual}> to be between (${min}, ${max}) range`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be between <${prettify(options.range)}>`,
message: `Expected <${this.actual}> NOT to be between (${min}, ${max}) range`,
});

return this.execute({
assertWhen:
this.actual > options.range[0] && this.actual < options.range[1],
assertWhen: this.actual > min && this.actual < max,
error,
invertedError,
});
Expand Down
55 changes: 31 additions & 24 deletions packages/core/src/lib/ObjectAssertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
public toContainKey(key: keyof T): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected the object to contain the provided key <${String(key)}>`,
message: `Expected the object to contain the provided key <${prettify(key)}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected the object NOT to contain the provided key <${String(key)}>`,
message: `Expected the object NOT to contain the provided key <${prettify(key)}>`,
});

return this.execute({
Expand All @@ -86,14 +86,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @returns the assertion instance
*/
public toContainAllKeys(...keys: Array<keyof T>): this {
const allKeys = keys.map(prettify).join(", ");
const error = new AssertionError({
actual: Object.keys(this.actual),
expected: keys,
message: `Expected the object to contain all the provided keys <${prettify(keys)}>`,
message: `Expected the object to contain all the provided keys <${allKeys}>`,
});
const invertedError = new AssertionError({
actual: Object.keys(this.actual),
message: `Expected the object NOT to contain all the provided keys <${prettify(keys)}>`,
message: `Expected the object NOT to contain all the provided keys <${allKeys}>`,
});

return this.execute({
Expand All @@ -115,14 +116,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @returns the assertion instance
*/
public toContainAnyKeys(...keys: Array<keyof T>): this {
const allKeys = keys.map(prettify).join(", ");
const error = new AssertionError({
actual: Object.keys(this.actual),
expected: keys,
message: `Expected the object to contain at least one of the provided keys <${prettify(keys)}>`,
message: `Expected the object to contain at least one of the provided keys <${allKeys}>`,
});
const invertedError = new AssertionError({
actual: Object.keys(this.actual),
message: `Expected the object NOT to contain any of the provided keys <${prettify(keys)}>`,
message: `Expected the object NOT to contain any of the provided keys <${allKeys}>`,
});

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

const error = new AssertionError({
actual: sortedActual,
expected: sortedKeys,
message: `Expected the object to have exactly the keys <${prettify(sortedKeys)}>`,
message: `Expected the object to have exactly the keys <${allKeys}>`,
});
const invertedError = new AssertionError({
actual: sortedActual,
message: `Expected the object NOT to have the keys <${prettify(sortedKeys)}>`,
message: `Expected the object NOT to have the keys <${allKeys}>`,
});

return this.execute({
Expand Down Expand Up @@ -205,14 +208,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @returns the assertion instance
*/
public toContainAllValues(...values: Array<T[keyof T]>): this {
const allValues = values.map(prettify).join(", ");
const error = new AssertionError({
actual: Object.values(this.actual),
expected: values,
message: `Expected the object to contain all the provided values <${prettify(values)}>`,
message: `Expected the object to contain all the provided values <${allValues}>`,
});
const invertedError = new AssertionError({
actual: Object.values(this.actual),
message: `Expected the object NOT to contain all the provided values <${prettify(values)}>`,
message: `Expected the object NOT to contain all the provided values <${allValues}>`,
});

return this.execute({
Expand All @@ -237,14 +241,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @returns the assertion instance
*/
public toContainAnyValues(...values: Array<T[keyof T]>): this {
const allValues = values.map(prettify).join(", ");
const error = new AssertionError({
actual: Object.values(this.actual),
expected: values,
message: `Expected the object to contain at least one of the provided values <${prettify(values)}>`,
message: `Expected the object to contain at least one of the provided values <${allValues}>`,
});
const invertedError = new AssertionError({
actual: Object.values(this.actual),
message: `Expected the object NOT to contain any of the provided values <${prettify(values)}>`,
message: `Expected the object NOT to contain any of the provided values <${allValues}>`,
});

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

const error = new AssertionError({
actual: sortedActual,
expected: sorterdValues,
message: `Expected the object to have exactly the values <${prettify(sorterdValues)}>`,
message: `Expected the object to have exactly the values <${allValues}>`,
});
const invertedError = new AssertionError({
actual: sortedActual,
message: `Expected the object NOT to have the values <${prettify(sorterdValues)}>`,
message: `Expected the object NOT to have the values <${allValues}>`,
});

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

const invertedError = new AssertionError({
actual: Object.entries(this.actual),
message: `Expected the object NOT to contain all the provided entries <${prettify(entries)}>`,
message: `Expected the object NOT to contain all the provided entries <${allEntries}>`,
});
return this.execute({
assertWhen: entries
Expand All @@ -365,15 +372,16 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
* @returns the assertion instance
*/
public toContainAnyEntries(...entries: Entry<T>[]): this {
const allEntries = entries.map(prettify).join(", ");
const error = new AssertionError({
actual: Object.entries(this.actual),
expected: entries,
message: `Expected the object to contain at least one of the provided entries <${prettify(entries)}>`,
message: `Expected the object to contain at least one of the provided entries <${allEntries}>`,
});

const invertedError = new AssertionError({
actual: Object.entries(this.actual),
message: `Expected the object NOT to contain any of the provided entries <${prettify(entries)}>`,
message: `Expected the object NOT to contain any of the provided entries <${allEntries}>`,
});
return this.execute({
assertWhen: entries
Expand Down Expand Up @@ -401,15 +409,15 @@ export class ObjectAssertion<T extends Struct> extends Assertion<T> {
public toHaveEntries(...entries: Entry<T>[]): this {
const sortedActual = Object.entries(this.actual).sort();
const sortedEntries = [...entries].sort();
const prettyEntries = sortedEntries.map(entry => `[${prettify(entry)}]`).join(",");
const allEntries = sortedEntries.map(prettify).join(", ");
const error = new AssertionError({
actual: sortedActual,
expected: sortedEntries,
message: `Expected the object to have exactly the entries <${prettyEntries}>`,
message: `Expected the object to have exactly the entries <${allEntries}>`,
});
const invertedError = new AssertionError({
actual: Object.entries(this.actual),
message: `Expected the object NOT to have the entries <${prettyEntries}>`,
message: `Expected the object NOT to have the entries <${allEntries}>`,
});

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

const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the object NOT to be a partial match",
message: `Expected the object NOT to partially match <${prettify(other)}>`,
});
return this.execute({
assertWhen: Object.keys(other)
Expand Down
20 changes: 18 additions & 2 deletions packages/core/src/lib/helpers/messages.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
export function prettify<T>(value: T): string {
return typeof value === "object" && value !== null
const isClassObject = typeof value === "object"
&& value !== null
&& value.toString() !== "[object Object]";
const nonJsonValue = value === undefined
|| Number.isNaN(value)
|| typeof value === "symbol"
|| typeof value === "bigint";

if (Array.isArray(value)) {
return `[${value.map(prettify).join(",")}]`;
}

if (isClassObject || nonJsonValue) {
return String(value);
}

return typeof value === "function"
? value.toString()
: String(value);
: JSON.stringify(value);
}
Loading