@assertive-ts/core
Version:
A type-safe fluent assertion library
517 lines (473 loc) • 13.7 kB
text/typescript
import { Assertion } from "./Assertion";
import { isHighInclusiveOptions, isInclusiveOptions, isLowInclusiveOptions } from "./helpers/guards";
import { AssertionError } from "assert";
export interface BaseBetweenOptions {
range: [number, number];
}
export interface CloseToOptions {
value: number;
withOffset: number;
}
export interface InclusiveBetweenOptions extends BaseBetweenOptions {
inclusive: boolean;
}
export interface LowInclusiveBetweenOptions extends BaseBetweenOptions {
lowInclusive: boolean;
}
export interface HighInclusiveBetweenOptions extends BaseBetweenOptions {
highInclusive: boolean;
}
export type BetweenOptions =
| BaseBetweenOptions
| InclusiveBetweenOptions
| LowInclusiveBetweenOptions
| HighInclusiveBetweenOptions;
/**
* Encapsulates assertion methods applicable to values of type number
*/
export class NumberAssertion extends Assertion<number> {
public constructor(actual: number) {
super(actual);
}
/**
* Check if the number is zero.
*
* @example
* ```
* expect(0).toBeZero();
* ```
*
* @returns the assertion instance
*/
public toBeZero(): this {
const error = new AssertionError({
actual: this.actual,
expected: 0,
message: `Expected <${this.actual}> to be zero`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be zero",
});
return this.execute({
assertWhen: this.actual === 0,
error,
invertedError,
});
}
/**
* Check if the number is positive. That is, when the number is greater than
* zero.
*
* @example
* ```
* expect(10).toBePositive();
* ```
*
* @returns the assertion instance
*/
public toBePositive(): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be positive`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be positive",
});
return this.execute({
assertWhen: this.actual > 0,
error,
invertedError,
});
}
/**
* Check if the number is negative. That is, when the number is less than
* zero.
*
* @example
* ```
* expect(-10).toBeNegative();
* ```
*
* @returns the assertion instance
*/
public toBeNegative(): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be negative`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be negative",
});
return this.execute({
assertWhen: this.actual < 0,
error,
invertedError,
});
}
/**
* Check if the number is finite. That is, when the number is not a
* JavaScript's `Infinity` value. Keep in mind that this includes
* positive and negative infinity.
*
* @example
* ```
* expect(0).toBeFinite();
* expect(-10).toBeFinite();
* expect(10).toBeFinite();
* ```
*
* @returns the assertion instance
*/
public toBeFinite(): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be finite`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be finite",
});
return this.execute({
assertWhen: isFinite(this.actual),
error,
invertedError,
});
}
/**
* Check if the number is `NaN`. That is only when the number is JavaScript's
* `NaN` value.
*
* @example
* ```
* expect(NaN).toBeNaN();
* expect(0/0).toBeNaN();
* ```
*
* @returns the assertion instance
*/
public toBeNaN(): this {
const error = new AssertionError({
actual: this.actual,
expected: NaN,
message: `Expected <${this.actual}> to be NaN`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be NaN",
});
return this.execute({
assertWhen: isNaN(this.actual),
error,
invertedError,
});
}
/**
* Check if the number is even. That is, when the number is divisible by 2.
*
* @example
* ```
* expect(50).toBeEven();
* expect(0).toBeEven();
* expect(-10).toBeEven();
* ```
*
* @returns the assertion instance
*/
public toBeEven(): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be even`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be even",
});
return this.execute({
assertWhen: this.actual % 2 === 0,
error,
invertedError,
});
}
/**
* Check if the number is odd. That is, when the number is not divisible by 2.
*
* @example
* ```
* expect(-13).toBeOdd();
* expect(17).toBeOdd();
* ```
*
* @returns the assertion instance
*/
public toBeOdd(): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be odd`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: "Expected the value NOT to be odd",
});
return this.execute({
assertWhen: this.actual % 2 === 1,
error,
invertedError,
});
}
/**
* Check if the number is between the specified bounds. By default, the
* bounds are exclusive, but the options allow to set the high, low, or both
* limits as inclusive
*
* @example
* ```
* expect(0).toBeBetween({
* range: [-1, 1],
* });
*
* expect(-1).toBeBetween({
* range: [-1, 1],
* lowInclusive: true,
* });
*
* expect(1).toBeBetween({
* range: [-1, 1],
* highInclusive: true,
* });
*
* expect(0).toBeBetween({
* range: [0, 0],
* inclusive: true,
* });
* ```
*
* @param options an object of type {@link BetweenOptions} where the `range`
* property defines the bounds, so it's always required. Use
* `inclusive: true` to make both limits inclusive. Or you can
* selectively make low or high limits inclusive using
* `lowInclusive: true` or `highInclusive: true`, respectively
* @returns the assertion instance
*/
public toBeBetween(options: BetweenOptions): this {
const [min, max] = options.range;
if (isInclusiveOptions(options)) {
const rangeText = options.inclusive ? `[${min}, ${max}]` : `(${min}, ${max})`;
const inclusiveError = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be between ${rangeText} range` });
const inclusiveInvertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be between ${rangeText} range` });
return this.execute({
assertWhen: options.inclusive
? this.actual >= min && this.actual <= max
: this.actual > min && this.actual < max,
error: inclusiveError,
invertedError: inclusiveInvertedError,
});
}
if (isLowInclusiveOptions(options)) {
const rangeText = options.lowInclusive ? `[${min}, ${max})` : `(${min}, ${max})`;
const lowInclusiveError = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be between ${rangeText} range`,
});
const lowInclusiveErrorInvertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be between ${rangeText} range`,
});
return this.execute({
assertWhen: options.lowInclusive
? this.actual >= min && this.actual < max
: this.actual > min && this.actual < max,
error: lowInclusiveError,
invertedError: lowInclusiveErrorInvertedError,
});
}
if (isHighInclusiveOptions(options)) {
const rangeText = options.highInclusive ? `(${min}, ${max}]` : `(${min}, ${max})`;
const highInclusiveError = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be between ${rangeText} range`,
});
const highInclusiveErrorInvertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be between ${rangeText} range`,
});
return this.execute({
assertWhen: options.highInclusive
? this.actual > min && this.actual <= max
: this.actual > min && this.actual < max,
error: highInclusiveError,
invertedError: highInclusiveErrorInvertedError,
});
}
const error = new AssertionError({
actual: this.actual,
expected: options,
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 (${min}, ${max}) range`,
});
return this.execute({
assertWhen: this.actual > min && this.actual < max,
error,
invertedError,
});
}
/**
* Check if the number value is close to the base value with certain offset.
* This checks both limits min and max which are inclusive.
*
* @example
* ```
* expect(-1).toBeCloseTo({
* value: 0,
* withOffset: 1,
* });
*
* expect(1).toBeCloseTo({
* value: 0,
* withOffset: 1,
* });
* ```
*
* @param options the object that contains the value (base number that value
* should be close) and withOffset (min and max offset value)
*
* @returns the assertion instance
*/
public toBeCloseTo(options: CloseToOptions): this {
const error = new AssertionError({
actual: this.actual,
expected: options,
message: `Expected <${this.actual}> to be close to <${options.value}> with offset <${options.withOffset}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be close to <${options.value}> with offset <${options.withOffset}>`,
});
const { value, withOffset } = options;
return this.execute({
assertWhen:
this.actual <= value + withOffset && this.actual >= value - withOffset,
error,
invertedError,
});
}
/**
* Check if the number value is greater than the defined value.
*
* @example
* ```
* expect(5).toBeGreaterThan(3);
* ```
*
* @param value the value that number should be greater than
* @returns the assertion instance
*/
public toBeGreaterThan(value: number): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be greater than <${value}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be greater than <${value}>`,
});
return this.execute({
assertWhen: this.actual > value,
error,
invertedError,
});
}
/**
* Check if the number value is greater than or equal to the defined value.
*
* @example
* ```
* expect(5).toBeGreaterThanOrEqual(3);
* expect(3).toBeGreaterThanOrEqual(3);
* ```
*
* @param value the value that number should be greater than or equal to
* @returns the assertion instance
*/
public toBeGreaterThanOrEqual(value: number): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be greater than or equal to <${value}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be greater than or equal to <${value}>`,
});
return this.execute({
assertWhen: this.actual >= value,
error,
invertedError,
});
}
/**
* Check if the number value is less than the defined value.
*
* @example
* ```
* expect(2).toBeLessThan(5);
* ```
*
* @example
* @param value the value that number should be less than
* @returns the assertion instance
*/
public toBeLessThan(value: number): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be less than <${value}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be less than <${value}>`,
});
return this.execute({
assertWhen: this.actual < value,
error,
invertedError,
});
}
/**
* Check if the number value is less than or equal to the defined value.
*
* @example
* ```
* expect(2).toBeLessThanOrEqual(5);
* expect(5).toBeLessThanOrEqual(5);
* ```
*
* @param value the value that number should be less than or equal to
* @returns the assertion instance
*/
public toBeLessThanOrEqual(value: number): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> to be less than or equal to <${value}>`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected <${this.actual}> NOT to be less than or equal to <${value}>`,
});
return this.execute({
assertWhen: this.actual <= value,
error,
invertedError,
});
}
}