assert-helpers
Version:
Common utilities and helpers to make testing assertions easier
655 lines (654 loc) • 23.5 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.wait = wait;
exports.isNode = isNode;
exports.isTTY = isTTY;
exports.bool = bool;
exports.useColors = useColors;
exports.color = color;
exports.inspect = inspect;
exports.log = log;
exports.logComparison = logComparison;
exports.equal = equal;
exports.gte = gte;
exports.lte = lte;
exports.gt = gt;
exports.lt = lt;
exports.undef = undef;
exports.nullish = nullish;
exports.deepEqual = deepEqual;
exports.contains = contains;
exports.notContains = notContains;
exports.errorEqual = errorEqual;
exports.returnViaCallback = returnViaCallback;
exports.completeViaCallback = completeViaCallback;
exports.errorViaCallback = errorViaCallback;
exports.returnErrorViaCallback = returnErrorViaCallback;
exports.throwErrorViaCallback = throwErrorViaCallback;
exports.expectViaCallback = expectViaCallback;
exports.expectErrorViaCallback = expectErrorViaCallback;
exports.expectThrowViaFunction = expectThrowViaFunction;
exports.expectErrorViaFunction = expectErrorViaFunction;
exports.expectFunctionToThrow = expectFunctionToThrow;
// Polyfill for older node versions
if (Array.prototype.includes == null) {
// Array.prototype.includes missing on Node.js v4, first added in Node.js v6
// eslint-disable-next-line no-extend-native
Array.prototype.includes = function (searchElement) {
return this.indexOf(searchElement) !== -1;
};
}
// String.prototype.includes included on Node.js v4
// Import
var assert_1 = require("assert");
var util_1 = require("util");
var ansi = __importStar(require("@bevry/ansi"));
var errlop_1 = __importDefault(require("errlop"));
var process_1 = require("process");
/**
* Alias for setTimeout with paramaters reversed.
* @param delay - The delay in milliseconds before executing the function
* @param fn - The function to execute after the delay
* @returns The timer ID returned by setTimeout
*/
function wait(delay, fn) {
return setTimeout(fn, delay);
}
/**
* Whether or not we are running in the node environment
* @returns True if running in Node.js, false otherwise
*/
function isNode() {
return Boolean(process_1.versions && process_1.versions.node);
}
/**
* Whether or not stdout and stderr are interactive.
* @returns True if both stdout and stderr are TTY, false otherwise
*/
function isTTY() {
return isNode() && (process_1.stdout === null || process_1.stdout === void 0 ? void 0 : process_1.stdout.isTTY) === true && (process_1.stderr === null || process_1.stderr === void 0 ? void 0 : process_1.stderr.isTTY) === true;
}
/**
* Convert a value to its boolean equivalent
* @param value - The value to convert to boolean
* @returns The boolean equivalent of the value, or null if undetermined
*/
function bool(value) {
// node.js 15 apparently sets COLOR to 0 (is TTY) 1 (not TTY) https://github.com/bevry/assert-helpers/commit/593c40a7211f460532077e67a38a452b707b4f9c
// however I was unable to reproduce in 2025 via console.log(process.env.COLOR) which outputs yes, which is what my shell env has set it to
var string = String(value).toLowerCase();
if (['no', 'n', 'false', '0'].includes(string)) {
return false;
}
if (['yes', 'y', 'true', '1'].includes(string)) {
return true;
}
// if (['null', ''].includes(string)) {
// return null
//}
// otherwise, it's something unknown, discard
return null;
}
/**
* Whether or not colors are desired on this environment
* @returns True if colors should be used, false otherwise
*/
function useColors() {
var _a, _b;
// handle strong technical capability
if (!isNode())
return false;
// disabled or enabled by strong user preference
if (process_1.argv.includes('--no-colors') || process_1.argv.includes('--no-color'))
return false;
if (process_1.argv.includes('--colors') || process_1.argv.includes('--color'))
return false;
// @todo support [--[no-]color[s]=<truthy/falsey>] arguments
// handle disabled by soft technical capability
// if (!isTTY()) return false <-- despite this making sense, for some reason, the wide convention is for env vars to take preference over TTY detection, probably as many want colors when piping
// handle soft user preference
return (_b = (_a = bool(process_1.env.COLOR)) !== null && _a !== void 0 ? _a : bool(process_1.env.COLORS)) !== null && _b !== void 0 ? _b : isTTY();
}
/**
* Applies the color to the value if desired
* @param value - The value to colorize
* @param color - The color function to apply
* @returns The colorized string if colors are enabled, otherwise the string value
*/
function color(value, color) {
return useColors() ? color(value) : String(value);
}
/**
* Checks to see if a value is an object
* https://github.com/bevry/typechecker/blob/69008d42927749d7e21cfe9816e478dd8d15ab88/source/index.js#L22-L30
* @param value - The value to check
* @returns True if the value is an object (but not null), false otherwise
*/
function isObject(value) {
// null is object, hence the extra check
return value !== null && typeof value === 'object';
}
/**
* Return a stringified version of the value with indentation and colors where applicable.
* Colors will be applied if the environment supports it (--no-colors not present and TTY).
* For the available options, refer to https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_inspect_object_options for Node.js
* @param value - The value to inspect and stringify
* @param opts - Additional options for the inspection
* @returns The stringified and optionally colorized representation of the value
*/
function inspect(value, opts) {
if (opts === void 0) { opts = {}; }
// If the terminal supports colours, and the user hasn't specified, then default to a sensible default
var colors = useColors();
var depth = 50;
// Inspect and return using our defaults
return (0, util_1.inspect)(value, __assign({ colors: colors, depth: depth }, opts));
}
/**
* Log the inspected values of each of the arguments to stdout
* @param args - The values to log
*/
function log() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (isNode() && process_1.env.ASSERT_SILENCE)
return;
for (var i = 0; i < args.length; ++i) {
console.log(inspect(args[i]));
}
}
/**
* Output a comparison of the failed result to stderr
* @param actual - The actual value that was received
* @param expected - The expected value
* @param error - The error that occurred during comparison
*/
function logComparison(actual, expected, error) {
if (isNode() && process_1.env.ASSERT_SILENCE)
return;
var lines = [
'------------------------------------',
'Comparison Error:',
color(error.stack || error.message || error, ansi.green),
'',
];
lines.push('Comparison Actual:', inspect(actual), '', 'Comparison Expected:', inspect(expected), '------------------------------------');
// Work for node
if (isNode() && process_1.stderr) {
process_1.stderr.write(lines.join('\n') + '\n');
}
// Work for browsers
else {
console.log(lines.join('\n'));
}
}
/**
* Same as assert.strictEqual in that it performs a strict equals check, but if a failure occurs it will output detailed information
* @param actual - The actual value to compare
* @param expected - The expected value to compare against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function equal(actual, expected, testName, next) {
if (testName === void 0) { testName = 'equal assertion'; }
try {
(0, assert_1.strictEqual)(actual, expected, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Is greater than or equal to
* @param actual - The actual value to compare
* @param expected - The expected value to compare against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function gte(actual, expected, testName, next) {
if (testName === void 0) { testName = 'is greater than or equal to assertion'; }
try {
(0, assert_1.strictEqual)(actual >= expected, true, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Is less than or equal to
* @param actual - The actual value to compare
* @param expected - The expected value to compare against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function lte(actual, expected, testName, next) {
if (testName === void 0) { testName = 'is less than or equal to assertion'; }
try {
(0, assert_1.strictEqual)(actual <= expected, true, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Is greater than
* @param actual - The actual value to compare
* @param expected - The expected value to compare against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function gt(actual, expected, testName, next) {
if (testName === void 0) { testName = 'is greater than assertion'; }
try {
(0, assert_1.strictEqual)(actual > expected, true, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Is less than
* @param actual - The actual value to compare
* @param expected - The expected value to compare against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function lt(actual, expected, testName, next) {
if (testName === void 0) { testName = 'is less than assertion'; }
try {
(0, assert_1.strictEqual)(actual < expected, true, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Ensure what is passed is undefined, otherwise fail and output what it is
* @param actual - The actual value to check
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function undef(actual, testName, next) {
if (testName === void 0) { testName = 'undef assertion'; }
try {
(0, assert_1.strictEqual)(typeof actual, 'undefined', testName);
}
catch (checkError) {
logComparison(actual, 'undefined', checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Ensure what is passed is undefined or null, otherwise fail and output what it is
* @param actual - The actual value to check
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function nullish(actual, testName, next) {
if (testName === void 0) { testName = 'nullish assertion'; }
try {
(0, assert_1.strictEqual)(typeof actual, 'undefined', testName);
}
catch (e1) {
try {
(0, assert_1.strictEqual)(actual, null, testName);
}
catch (e2) {
var error = new errlop_1.default(e2, e1);
logComparison(actual, 'nullish', error);
if (next) {
next(error);
return;
}
else {
throw error;
}
}
}
if (next)
next();
}
/**
* Same as assert.deepStrictEqual in that it performs a deep strict equals check, but if a failure occurs it will output detailed information
* @param actual - The actual value to compare
* @param expected - The expected value to compare against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function deepEqual(actual, expected, testName, next) {
if (testName === void 0) { testName = 'deep equal assertion'; }
try {
(0, assert_1.deepStrictEqual)(actual, expected, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Checks to see if the actual result contains the expected result .
* @param actual - The actual value to check for containment
* @param expected - The expected value that should be contained
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function contains(actual, expected, testName, next) {
if (testName === void 0) { testName = 'contains assertion'; }
if (testName == null)
testName = "Expected [".concat(actual, "] to contain [").concat(expected, "]");
try {
(0, assert_1.ok)(actual.includes(expected) === true, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Checks to see if the actual result does not contain the expected result .
* @param actual - The actual value to check for non-containment
* @param expected - The expected value that should not be contained
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function notContains(actual, expected, testName, next) {
if (testName === void 0) { testName = 'does not contain assertion'; }
if (testName == null)
testName = "Expected [".concat(actual, "] to not contain [").concat(expected, "]");
try {
(0, assert_1.ok)(actual.includes(expected) === false, testName);
}
catch (checkError) {
logComparison(actual, expected, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Checks to see if an error was as expected, if a failure occurs it will output detailed information
* @param actualError - The actual error that was thrown
* @param expectedError - The expected error to match against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function errorEqual(actualError, expectedError, testName, next) {
if (testName === void 0) { testName = 'error equal assertion'; }
try {
// needs to work with errlop codes, hence usage of stack, as errlop tostring doesn't include code for now, unfortunately
if (!actualError || !expectedError) {
equal(actualError || '', expectedError || '', testName + ' (empty check)');
}
else if (actualError.code === ((expectedError === null || expectedError === void 0 ? void 0 : expectedError.code) || expectedError)) {
equal(actualError.code, (expectedError === null || expectedError === void 0 ? void 0 : expectedError.code) || expectedError, testName + ' (.code check)');
}
else {
contains(actualError.toString(), expectedError.toString(), testName + ' (contains check)');
}
}
catch (checkError) {
logComparison(actualError, expectedError, checkError);
if (next) {
next(checkError);
return;
}
else {
throw checkError;
}
}
if (next)
next();
}
/**
* Generate a callback that will return the specified value.
* @param value - The value to return from the callback
* @returns A function that returns the specified value
*/
function returnViaCallback(value) {
return function () {
return value;
};
}
/**
* Generate a callback that will receive a completion callback whcih it will call with the specified result after the specified delay.
* @param value - The value to pass to the completion callback
* @param delay - The delay in milliseconds before calling the completion callback
* @returns A function that takes a completion callback and calls it with the value after the delay
*/
function completeViaCallback(value, delay) {
if (delay === void 0) { delay = 100; }
return function (complete) {
wait(delay, function () {
complete(null, value);
});
};
}
/**
* Generate a callback that will receive a completion callback which it will call with the passed error after the specified delay.
* @param error - The error to pass to the completion callback
* @param delay - The delay in milliseconds before calling the completion callback
* @returns A function that takes a completion callback and calls it with the error after the delay
*/
function errorViaCallback(error, delay) {
if (delay === void 0) { delay = 100; }
return function (complete) {
wait(delay, function () {
complete(error);
});
};
}
/**
* Generate a callback that return an error instance with the specified message/error.
* @param error - The error message or Error instance to return
* @returns A function that returns an Error instance
*/
function returnErrorViaCallback(error) {
if (error === void 0) { error = 'an error occurred'; }
return function () {
if (error instanceof Error) {
return error;
}
else {
return new Error(error);
}
};
}
/**
* Generate a callback that throw an error instance with the specified message/error.
* @param error - The error message or Error instance to throw
* @returns A function that throws an Error instance
*/
function throwErrorViaCallback(error) {
if (error === void 0) { error = 'an error occurred'; }
return function () {
if (error instanceof Error) {
throw error;
}
else {
throw new Error(error);
}
};
}
/**
* Generate a callback that will check the arguments it received with the arguments specified, if a failure occurs it will output detailed information.
* @param expected - The expected arguments to compare against
* @returns A function that compares received arguments with expected arguments
*/
function expectViaCallback() {
var expected = [];
for (var _i = 0; _i < arguments.length; _i++) {
expected[_i] = arguments[_i];
}
return function () {
var actual = [];
for (var _i = 0; _i < arguments.length; _i++) {
actual[_i] = arguments[_i];
}
return deepEqual(actual, expected);
};
}
/**
* Generate a callback that will check its error (the actual error) against the passed error (the expected error).
* If a failure occurs it will output detailed information.
* @param expected - The expected error to match against
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
* @returns A function that compares an actual error with the expected error
*/
function expectErrorViaCallback(expected, testName, next) {
if (testName === void 0) { testName = 'expect error via callback assertion'; }
return function (actual) {
return errorEqual(actual, expected, testName, next);
};
}
/**
* Expect the passed function to throw an error at some point.
* @param expected - The expected error to match against
* @param fn - The function that should throw an error
* @param testName - The name of the test for error reporting
* @param next - Optional callback to call with any error
*/
function expectThrowViaFunction(expected, fn, testName, next) {
if (testName === void 0) { testName = 'expect error via function assertion'; }
var actual = null;
try {
fn();
}
catch (error) {
actual = error;
}
errorEqual(actual, expected, testName, next);
}
/** @deprecated Use {@link expectErrorViaFunction} instead */
function expectErrorViaFunction() {
throw new Error('expectErrorViaFunction has been deprecated, use expectThrowViaFunction instead');
}
/** @deprecated Use {@link expectErrorViaFunction} instead */
function expectFunctionToThrow() {
throw new Error('expectFunctionToThrow has been deprecated, use expectThrowViaFunction instead');
}