@assertive-ts/core
Version:
A type-safe fluent assertion library
279 lines (278 loc) • 8.09 kB
TypeScript
/// <reference types="node" />
import { type TypeFactory } from "./helpers/TypeFactories";
import { AssertionError } from "assert";
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
* condition is also used for the `.not` case of the assertion
*/
assertWhen: boolean;
/**
* The assertion error to throw when the condition is not fullfiled
*/
error: AssertionError;
/**
* The assertion error to throw given the condition was inverted (`.not`),
* and it is also not fullfilled
*/
invertedError: AssertionError;
}
/**
* Base class for all assertions.
*
* @param T the type of the `actual` value
*/
export declare class Assertion<T> {
protected readonly actual: T;
protected readonly inverted: boolean;
readonly not: this;
constructor(actual: T);
/**
* A convenience method to normalize the assertion instance. If it was
* inverted with `.not`, it'll return it back to the previous non-inverted
* state. Otherwise, it returns the same instance.
*
* @returns the normalized assertion instance
*/
protected normalized(): this;
/**
* A convenience method to execute the assertion. The inversion logic for
* `.not` is already embedded in this method, so this should always be used
* in assertions to keep the negation system working
*
* @param options the execution options for the assertion
* @returns the Assertion instance if no error was thrown
*/
protected execute(options: ExecuteOptions): this;
/**
* Check if the value matches the given predicate.
*
* @param matcher a matcher predicate
* @returns the assertion instance
*/
toMatch(matcher: (actual: T) => boolean): this;
/**
* Check if the value exists. This means that the value should be neither
* `null` nor `undefined`.
*
* @example
* ```
* expect(planetEarth).toExist();
* ```
*
* @returns the assertion instance
*/
toExist(): this;
/**
* Check if the value is `undefined`
*
* @example
* ```
* expect(myUndefinedValue).toBeUndefined()
* ```
*
* @returns the assertion instance
*/
toBeUndefined(): this;
/**
* Check if the value is `null`.
*
* @example
* ```
* expect(myNullValue).toBeNull();
* ```
*
* @returns the assertion instance
*/
toBeNull(): this;
/**
* Check if the value is present. This means that the value should not be
* `undefined`.
*
* @example
* ```
* expect(PI).toBePresent();
* ```
*
* @returns the assertion instance
*/
toBePresent(): this;
/**
* Check if the value is a truthy value. There are six falsy values in
* JavaScript: `null`, `undefined`, `0`, `""`, `false`, `NaN`. Everything
* else is truthy.
*
* @example
* ```
* expect("hello world").toBeTruthy();
* expect(128).toBeTruthy();
* ```
*
* @returns the assertion instance
*/
toBeTruthy(): this;
/**
* Check if the value is a falsy value. There are six falsy values in
* JavaScript: `null`, `undefined`, `0`, `""`, `false`, `NaN`. Everything
* else is truthy.
*
* @example
* ```
* expect(0).toBeFalsy();
* expect("").toBeFalsy();
* ```
* @returns the assertion instance
*/
toBeFalsy(): this;
/**
* Check if the value is an instance of the provided constructor.
*
* @example
* ```
* expect(pontiac).toBeInstanceOf(Car);
*
* expect(today).toBeInstanceOf(Date);
* ```
*
* @param Expected the constructor the value should be an instance
* @returns the assertion instance
*/
toBeInstanceOf(Expected: Constructor<unknown>): this;
/**
* Check if the value is deep strict equal to another value.
*
* @example
* ```
* expect(3 + 2).toBeEqual(5);
* expect({ a: { b: 1 } }).toBeEqual({ a: { b: 1 } });
* expect(today).toBeEqual(new Date(today.toISOString()));
* ```
*
* @param expected the value to compare for deep equality
* @returns the assertion instance
*/
toBeEqual(expected: T): this;
/**
* Check if the value is shallow equal to another value.
*
* @example
* ```
* expect(3 + 2).toBeSimilar(5);
* expect({ a: 1 }).toBeSimilar({ a: 1 });
*
* expect({ a: { b: 1 } }).not.toBeSimilar({ a: {b: 1} });
* ```
*
* @param expected the value to compare for shallow equality
* @returns the assertion instance
*/
toBeSimilar(expected: T): this;
/**
* Check if the value is the same as another value.
*
* @example
* ```
* const x = { a: 1 };
* const y = x;
*
* expect(x).toBeSame(x);
* expect(x).toBeSame(y);
*
* expect(x).not.toBeSame({ ...x });
* ```
*
* @param expected the value to compare for referential equality
* @returns the assertion instance
*/
toBeSame(expected: T): this;
/**
* Alias of `.toBeSame(..)` assertion.
*
* @example
* ```
* const x = { a: 1 };
* const y = x;
*
* expect(x).toBeSameAs(x);
* expect(x).toBeSameAs(y);
*
* expect(x).not.toBeSameAs({ ...x });
* ```
*
* @param expected the value to compare for referential equality
* @returns the assertion instance
*/
toBeSameAs(expected: T): this;
/**
* Another alias of `.toBeSame(..)` assertion.
*
* @example
* ```
* const x = { a: 1 };
* const y = x;
*
* expect(x).toBe(x);
* expect(x).toBe(y);
*
* expect(x).not.toBe({ ...x });
* ```
*
* @param expected the value to compare for referential equality
* @returns the assertion instance
*/
toBe(expected: T): this;
/**
* 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
*/
toBeOfType(expected: DataType): this;
/**
* 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
* from a factory that should extend from the base {@link Assertion} class.
*
* We provide some basic factories in `TypeFactories`. If you need some
* other factory for a custom assertion for instance, you can easily create
* one from a Factory reference and a predicate.
*
* @example
* ```
* expect(unknownValue)
* .asType(TypeFactories.STRING)
* .toStartWith("/api/");
*
* expect(uuid)
* .asType({
* Factory: UUIDAssertion, // a custom UUID assertion
* predicate: (value): value is UUID => {
* return typeof value === "string" && UUID.PATTER.test(value);
* },
* })
* .isValid();
* ```
*
* @typeParam S the type of the factory's value
* @typeParam A the type of the assertion factory
* @param typeFactory a factory to assert the type and create an assertion
* @returns a more specific assertion based on the factory type
*/
asType<S, A extends Assertion<S>>(typeFactory: TypeFactory<S, A>): A;
private proxyInverter;
}