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
48 changes: 48 additions & 0 deletions package/src/lib/Assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ export interface Constructor<T> extends Function {
prototype: T;
}

export type DataType =
| "array"
| "bigint"
| "boolean"
| "function"
| "number"
| "object"
| "string"
| "symbol"
| "undefined";

export interface ExecuteOptions {
/**
* The condition for when the assertion should pass. The negation of this
Expand Down Expand Up @@ -429,6 +440,43 @@ export class Assertion<T> {
});
}

/**
* Checks if the value is of a specific data type. The supported data types
* are the same as the `typeof` operator, plus an additional `array` which
* allows desabiguation between `object` (which can also be an array).
*
* @example
* ```
* const arr = [1, 2, 3];
*
* expect(arr).toBeOfType("array");
* expect(arr[0]).toBeOfType("number");
* expect(arr[9]).toBeOfType("undefined");
* ```
*
* @param expected the expected data type
* @returns the assertion instance
*/
public toBeOfType(expected: DataType): this {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not overly fond of the name of this method, so I'm open to suggestions 🙂

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine. It reads naturally when used and it's pretty self explanatory. :)

const error = new AssertionError({
actual: typeof this.actual,
expected,
message: `Expected <${prettify(this.actual)}> to be of type <${expected}>`,
});
const invertedError = new AssertionError({
actual: typeof this.actual,
message: `Expected <${prettify(this.actual)}> NOT to be of type <${expected}>`,
});

return this.execute({
assertWhen: expected === "array"
? Array.isArray(this.actual)
: typeof this.actual === expected,
error,
invertedError,
});
}

/**
* Check first if the value is of some specific type, in which case returns
* an assertion instance for that specific type. The new assertion is built
Expand Down
57 changes: 56 additions & 1 deletion package/test/lib/Assertion.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Assertion } from "../../src/lib/Assertion";
import { Assertion, DataType } from "../../src/lib/Assertion";
import { StringAssertion } from "../../src/lib/StringAssertion";
import { UnsupportedOperationError } from "../../src/lib/errors/UnsupportedOperationError";
import { TypeFactories } from "../../src/lib/helpers/TypeFactories";
import { prettify } from "../../src/lib/helpers/messages";

import assert, { AssertionError } from "assert";

Expand Down Expand Up @@ -471,6 +472,60 @@ describe("[Unit] Assertion.test.ts", () => {
});
});

describe(".toBeOfType", () => {
context("when the type of the value is of the expected type", () => {
const variants: Array<[DataType, unknown]> = [
["array", [1, 2, 3]],
["bigint", BigInt(9)],
["boolean", true],
["function", () => undefined],
["number", 10],
["object", { foo: 1 }],
["string", "foo"],
["symbol", Symbol("id")],
["undefined", undefined],
];

variants.forEach(([expected, value]) => {
it(`[${expected}] returns the assertion instance`, () => {
const test = new Assertion(value);

assert.deepStrictEqual(test.toBeOfType(expected), test);
assert.throws(() => test.not.toBeOfType(expected), {
message: `Expected <${prettify(value)}> NOT to be of type <${expected}>`,
name: AssertionError.name,
});
});
});
});

context("when the type of the value is NOT of the expected type", () => {
const variants: Array<[DataType, unknown]> = [
["array", { x: [1, 2, 3] }],
["bigint", 9],
["boolean", "false"],
["function", { foo: () => undefined }],
["number", BigInt(10)],
["object", "foo"],
["string", Symbol("id")],
["symbol", undefined],
["undefined", null],
];

variants.forEach(([expected, value]) => {
it(`[${expected}] throws an assertion error`, () => {
const test = new Assertion(value);

assert.throws(() => test.toBeOfType(expected), {
message: `Expected <${prettify(value)}> to be of type <${expected}>`,
name: AssertionError.name,
});
assert.deepStrictEqual(test.not.toBeOfType(expected), test);
});
});
});
});

describe(".asType", () => {
context("when the type predicate is true", () => {
it("returns a new instance of the assertion passed to the type factory", () => {
Expand Down