@augment-vir/assert
Version:
A collection of assertions for test and production code alike.
1,468 lines • 52.1 kB
JavaScript
import { stringify, } from '@augment-vir/core';
import { AssertionError } from '../augments/assertion.error.js';
import { createWaitUntil } from '../guard-types/wait-until-function.js';
function hasValue(parent, value) {
if (typeof parent === 'string') {
return typeof value === 'string' && parent.includes(value);
}
/** Wrap this in a try/catch because `Reflect.ownKeys` can fail depending on what its input is. */
let hasValue = true;
try {
hasValue = Reflect.ownKeys(parent)
.map((key) => parent[key])
.includes(value);
}
catch {
return false;
}
return hasValue;
}
export function isIn(child, parent) {
if (typeof parent === 'string') {
return parent.includes(child);
}
else {
return hasValue(parent, child);
}
}
const assertions = {
/**
* Asserts that an object/array parent includes a child value through reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assert.hasValue({child}, child); // passes
* assert.hasValue({child: {a: 'a'}}, child); // fails
* assert.hasValue([child], child); // passes
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.lacksValue} : the opposite assertion.
* - {@link assert.hasValues} : the multi-value assertion.
*/
hasValue(parent, value, failureMessage) {
if (!hasValue(parent, value)) {
throw new AssertionError(`'${stringify(parent)}' does not have value '${stringify(value)}'.`, failureMessage);
}
},
/**
* Asserts that an object/array parent does _not_ include a child value through reference
* equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assert.lacksValue({child}, child); // fails
* assert.lacksValue({child: {a: 'a'}}, child); // passes
* assert.lacksValue([child], child); // fails
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.hasValue} : the opposite assertion.
* - {@link assert.lacksValues} : the multi-value assertion.
*/
lacksValue(parent, value, failureMessage) {
if (hasValue(parent, value)) {
throw new AssertionError(`'${stringify(parent)}' has value '${stringify(value)}'.`, failureMessage);
}
},
/**
* Asserts that an object/array parent includes all child values through reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* assert.hasValues({child, child2}, [
* child,
* child2,
* ]); // passes
* assert.hasValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // fails
* assert.hasValues(
* [child],
* [
* child,
* child2,
* ],
* ); // passes
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.lacksValues} : the opposite assertion.
* - {@link assert.hasValue} : the single-value assertion.
*/
hasValues(parent, values, failureMessage) {
let missingValues = [];
if (typeof parent === 'string') {
missingValues = values.filter((value) => {
return !(typeof value === 'string' && parent.includes(value));
});
}
else {
try {
const actualValues = Reflect.ownKeys(parent).map((key) => parent[key]);
missingValues = values.filter((value) => {
return !actualValues.includes(value);
});
}
catch {
throw new AssertionError(`'${stringify(parent)}' does not have values '${stringify(values)}'.`, failureMessage);
}
}
if (missingValues.length) {
throw new AssertionError(`'${stringify(parent)}' does not have values '${stringify(missingValues)}'.`, failureMessage);
}
},
/**
* Asserts that an object/array parent includes none of the provided child values through
* reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* assert.lacksValues({}, [
* child,
* child2,
* ]); // passes
* assert.lacksValues({child, child2}, [
* child,
* child2,
* ]); // fails
* assert.lacksValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // fails
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.lacksValues} : the opposite assertion.
* - {@link assert.hasValue} : the single-value assertion.
*/
lacksValues(parent, values, failureMessage) {
let includedValues = [];
if (typeof parent === 'string') {
includedValues = values.filter((value) => {
return typeof value === 'string' && parent.includes(value);
});
}
else {
try {
const actualValues = Reflect.ownKeys(parent).map((key) => parent[key]);
includedValues = values.filter((value) => {
return actualValues.includes(value);
});
}
catch {
// ignore error
}
}
if (includedValues.length) {
throw new AssertionError(`'${stringify(parent)}' has values '${stringify(includedValues)}'.`, failureMessage);
}
},
/**
* Asserts that child value is contained within a parent object, array, or string through
* reference equality.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assert.isIn(child, {child}); // passes
* assert.isIn('a', 'ab'); // passes
* assert.isIn(child, [child]); // passes
*
* assert.isIn(child, {child: {a: 'a'}}); // fails
* assert.isIn('a', 'bc'); // fails
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.isNotIn} : the opposite assertion.
*/
isIn(child, parent, failureMessage) {
if (!isIn(child, parent)) {
throw new AssertionError(`'${stringify(child)}'\n\nis not in\n\n${stringify(parent)}.`, failureMessage);
}
},
/**
* Asserts that child value is _not_ contained within a parent object, array, or string through
* reference equality.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assert.isNotIn(child, {child}); // fails
* assert.isNotIn('a', 'ab'); // fails
* assert.isNotIn(child, [child]); // fails
*
* assert.isNotIn(child, {child: {a: 'a'}}); // passes
* assert.isNotIn('a', 'bc'); // passes
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.isIn} : the opposite assertion.
*/
isNotIn(child, parent, failureMessage) {
if (isIn(child, parent)) {
throw new AssertionError(`'${stringify(child)}'\n\nis in\n\n${stringify(parent)}.`, failureMessage);
}
},
/**
* Asserts that a value is empty. Supports strings, Maps, Sets, objects, and arrays.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* assert.isEmpty({}); // passes
* assert.isEmpty(''); // passes
* assert.isEmpty([]); // passes
*
* assert.isEmpty('a'); // fails
* assert.isEmpty({a: 'a'}); // fails
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.isNotEmpty} : the opposite assertion.
*/
isEmpty(actual, failureMessage) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
}
if (typeof actual === 'string' && !actual) {
// eslint-disable-next-line sonarjs/no-gratuitous-expressions
if (!actual) {
return;
}
}
else if (Array.isArray(actual)) {
if (!actual.length) {
return;
}
}
else if (actual instanceof Map || actual instanceof Set) {
if (!actual.size) {
return;
}
}
else if (typeof actual === 'object' && !Object.keys(actual).length) {
return;
}
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
},
/**
* Asserts that a value is _not_ empty. Supports strings, Maps, Sets, objects, and arrays.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {assert} from '@augment-vir/assert';
*
* assert.isNotEmpty({}); // fails
* assert.isNotEmpty(''); // fails
* assert.isNotEmpty([]); // fails
*
* assert.isNotEmpty('a'); // passes
* assert.isNotEmpty({a: 'a'}); // passes
* ```
*
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assert.isEmpty} : the opposite assertion.
*/
isNotEmpty(actual, failureMessage) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
return;
}
if (typeof actual === 'string' && !actual) {
// eslint-disable-next-line sonarjs/no-gratuitous-expressions
if (!actual) {
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
}
}
else if (Array.isArray(actual)) {
if (!actual.length) {
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
}
}
else if (actual instanceof Map || actual instanceof Set) {
if (!actual.size) {
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
}
}
else if (typeof actual === 'object' && !Object.keys(actual).length) {
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
}
},
};
export const valueGuards = {
assert: assertions,
check: {
/**
* Checks that an object/array parent includes a child value through reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* check.hasValue({child}, child); // returns `true`
* check.hasValue({child: {a: 'a'}}, child); // returns `false`
* check.hasValue([child], child); // returns `true`
* ```
*
* @see
* - {@link check.lacksValue} : the opposite check.
* - {@link check.hasValues} : the multi-value check.
*/
hasValue(parent, value) {
return hasValue(parent, value);
},
/**
* Checks that an object/array parent does _not_ include a child value through reference
* equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* check.lacksValue({child}, child); // returns `false`
* check.lacksValue({child: {a: 'a'}}, child); // returns `true`
* check.lacksValue([child], child); // returns `false`
* ```
*
* @see
* - {@link check.hasValue} : the opposite check.
* - {@link check.lacksValues} : the multi-value check.
*/
lacksValue(parent, value) {
return !hasValue(parent, value);
},
/**
* Checks that an object/array parent includes all child values through reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* check.hasValues({child, child2}, [
* child,
* child2,
* ]); // returns `true`
* check.hasValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // returns `false`
* check.hasValues(
* [child],
* [
* child,
* child2,
* ],
* ); // returns `true`
* ```
*
* @see
* - {@link check.lacksValues} : the opposite check.
* - {@link check.hasValue} : the single-value check.
*/
hasValues(parent, values) {
return values.every((value) => hasValue(parent, value));
},
/**
* Checks that an object/array parent includes none of the provided child values through
* reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* check.lacksValues({}, [
* child,
* child2,
* ]); // returns `true`
* check.lacksValues({child, child2}, [
* child,
* child2,
* ]); // returns `false`
* check.lacksValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // returns `false`
* ```
*
* @see
* - {@link check.lacksValues} : the opposite check.
* - {@link check.hasValue} : the single-value check.
*/
lacksValues(parent, values) {
return values.every((value) => !hasValue(parent, value));
},
/**
* Checks that child value is contained within a parent object, array, or string through
* reference equality.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* check.isIn(child, {child}); // returns `true`
* check.isIn('a', 'ab'); // returns `true`
* check.isIn(child, [child]); // returns `true`
*
* check.isIn(child, {child: {a: 'a'}}); // returns `false`
* check.isIn('a', 'bc'); // returns `false`
* ```
*
* @see
* - {@link check.isNotIn} : the opposite check.
*/
isIn(child, parent) {
return isIn(child, parent);
},
/**
* Checks that child value is _not_ contained within a parent object, array, or string
* through reference equality.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* check.isNotIn(child, {child}); // returns `false`
* check.isNotIn('a', 'ab'); // returns `false`
* check.isNotIn(child, [child]); // returns `false`
*
* check.isNotIn(child, {child: {a: 'a'}}); // returns `true`
* check.isNotIn('a', 'bc'); // returns `true`
* ```
*
* @see
* - {@link check.isIn} : the opposite check.
*/
isNotIn(child, parent) {
return !isIn(child, parent);
},
/**
* Checks that a value is empty. Supports strings, Maps, Sets, objects, and arrays.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* check.isEmpty({}); // returns `true`
* check.isEmpty(''); // returns `true`
* check.isEmpty([]); // returns `true`
*
* check.isEmpty('a'); // returns `false`
* check.isEmpty({a: 'a'}); // returns `false`
* ```
*
* @see
* - {@link check.isNotEmpty} : the opposite check.
*/
isEmpty(actual) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
return false;
}
if (typeof actual === 'string') {
return !actual;
}
else if (Array.isArray(actual)) {
return !actual.length;
}
else if (actual instanceof Map) {
return !actual.size;
}
else if (actual instanceof Set) {
return !actual.size;
}
else {
return !Object.keys(actual).length;
}
},
/**
* Checks that a value is _not_ empty. Supports strings, Maps, Sets, objects, and arrays.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {check} from '@augment-vir/assert';
*
* check.isNotEmpty({}); // returns `false`
* check.isNotEmpty(''); // returns `false`
* check.isNotEmpty([]); // returns `false`
*
* check.isNotEmpty('a'); // returns `true`
* check.isNotEmpty({a: 'a'}); // returns `true`
* ```
*
* @see
* - {@link check.isEmpty} : the opposite check.
*/
isNotEmpty(actual) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
return true;
}
if (typeof actual === 'string') {
return !!actual;
}
else if (Array.isArray(actual)) {
return !!actual.length;
}
else if (actual instanceof Map) {
return !!actual.size;
}
else if (actual instanceof Set) {
return !!actual.size;
}
else {
return !!Object.keys(actual).length;
}
},
},
assertWrap: {
/**
* Asserts that an object/array parent includes a child value through reference equality.
* Returns the parent value if the assertion passes.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assertWrap.hasValue({child}, child); // returns `{child}`;
* assertWrap.hasValue({child: {a: 'a'}}, child); // throws an error
* assertWrap.hasValue([child], child); // returns `[child]`;
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.lacksValue} : the opposite assertion.
* - {@link assertWrap.hasValues} : the multi-value assertion.
*/
hasValue(parent, value, failureMessage) {
if (!hasValue(parent, value)) {
throw new AssertionError(`'${stringify(parent)}' does not have value '${stringify(value)}'.`, failureMessage);
}
return parent;
},
/**
* Asserts that an object/array parent does _not_ include a child value through reference
* equality. Returns the parent value if the assertion passes.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assertWrap.lacksValue({child}, child); // throws an error
* assertWrap.lacksValue({child: {a: 'a'}}, child); // returns `{child: {a: 'a'}}`;
* assertWrap.lacksValue([child], child); // throws an error
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.hasValue} : the opposite assertion.
* - {@link assertWrap.lacksValues} : the multi-value assertion.
*/
lacksValue(parent, value, failureMessage) {
if (hasValue(parent, value)) {
throw new AssertionError(`'${stringify(parent)}' has value '${stringify(value)}'.`, failureMessage);
}
return parent;
},
/**
* Asserts that an object/array parent includes all child values through reference equality.
* Returns the parent value if the assertion passes.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* assertWrap.hasValues({child, child2}, [
* child,
* child2,
* ]); // returns `{child, child2}`;
* assertWrap.hasValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // throws an error
* assertWrap.hasValues(
* [child],
* [
* child,
* child2,
* ],
* ); // returns `[child]`;
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.lacksValues} : the opposite assertion.
* - {@link assertWrap.hasValue} : the single-value assertion.
*/
hasValues(parent, values, failureMessage) {
let missingValues = [];
if (typeof parent === 'string') {
missingValues = values.filter((value) => {
return !(typeof value === 'string' && parent.includes(value));
});
}
else {
try {
const actualValues = Reflect.ownKeys(parent).map((key) => parent[key]);
missingValues = values.filter((value) => {
return !actualValues.includes(value);
});
}
catch {
throw new AssertionError(`'${stringify(parent)}' does not have values '${stringify(values)}'.`, failureMessage);
}
}
if (missingValues.length) {
throw new AssertionError(`'${stringify(parent)}' does not have values '${stringify(missingValues)}'.`, failureMessage);
}
return parent;
},
/**
* Asserts that an object/array parent includes none of the provided child values through
* reference equality. Returns the parent value if the assertion passes.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* assertWrap.lacksValues({}, [
* child,
* child2,
* ]); // returns `{}`;
* assertWrap.lacksValues({child, child2}, [
* child,
* child2,
* ]); // throws an error
* assertWrap.lacksValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // throws an error
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.lacksValues} : the opposite assertion.
* - {@link assertWrap.hasValue} : the single-value assertion.
*/
lacksValues(parent, values, failureMessage) {
let includedValues = [];
if (typeof parent === 'string') {
includedValues = values.filter((value) => {
return typeof value === 'string' && parent.includes(value);
});
}
else {
try {
const actualValues = Reflect.ownKeys(parent).map((key) => parent[key]);
includedValues = values.filter((value) => {
return actualValues.includes(value);
});
}
catch {
// ignore error
}
}
if (includedValues.length) {
throw new AssertionError(`'${stringify(parent)}' has values '${stringify(includedValues)}'.`, failureMessage);
}
return parent;
},
/**
* Asserts that child value is contained within a parent object, array, or string through
* reference equality. Returns the child value if the assertion passes.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assertWrap.isIn(child, {child}); // returns `child`;
* assertWrap.isIn('a', 'ab'); // returns `'a'`;
* assertWrap.isIn(child, [child]); // returns `child`;
*
* assertWrap.isIn(child, {child: {a: 'a'}}); // throws an error
* assertWrap.isIn('a', 'bc'); // throws an error
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.isNotIn} : the opposite assertion.
*/
isIn(child, parent, failureMessage) {
if (!isIn(child, parent)) {
throw new AssertionError(`'${stringify(child)}'\n\nis not in\n\n${stringify(parent)}.`, failureMessage);
}
return child;
},
/**
* Asserts that child value is _not_ contained within a parent object, array, or string
* through reference equality. Returns the child value if the assertion passes.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* assertWrap.isNotIn(child, {child}); // throws an error
* assertWrap.isNotIn('a', 'ab'); // throws an error
* assertWrap.isNotIn(child, [child]); // throws an error
*
* assertWrap.isNotIn(child, {child: {a: 'a'}}); // returns `child`;
* assertWrap.isNotIn('a', 'bc'); // returns `'a'`;
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.isIn} : the opposite assertion.
*/
isNotIn(child, parent, failureMessage) {
if (isIn(child, parent)) {
throw new AssertionError(`'${stringify(child)}'\n\nis in\n\n${stringify(parent)}.`, failureMessage);
}
return child;
},
/**
* Asserts that a value is empty. Supports strings, Maps, Sets, objects, and arrays. Returns
* the value if the assertion passes.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* assertWrap.isEmpty({}); // returns `{}`;
* assertWrap.isEmpty(''); // returns `''`;
* assertWrap.isEmpty([]); // returns `[]`;
*
* assertWrap.isEmpty('a'); // throws an error
* assertWrap.isEmpty({a: 'a'}); // throws an error
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.isNotEmpty} : the opposite assertion.
*/
isEmpty(actual, failureMessage) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
}
if (typeof actual === 'string' && !actual) {
// eslint-disable-next-line sonarjs/no-gratuitous-expressions
if (!actual) {
return actual;
}
}
else if (Array.isArray(actual)) {
if (!actual.length) {
return actual;
}
}
else if (actual instanceof Map || actual instanceof Set) {
if (!actual.size) {
return actual;
}
}
else if (typeof actual === 'object' && !Object.keys(actual).length) {
return actual;
}
throw new AssertionError(`'${stringify(actual)}' is not empty.`, failureMessage);
},
/**
* Asserts that a value is _not_ empty. Supports strings, Maps, Sets, objects, and arrays.
* Returns the value if the assertion passes.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {assertWrap} from '@augment-vir/assert';
*
* assertWrap.isNotEmpty({}); // throws an error
* assertWrap.isNotEmpty(''); // throws an error
* assertWrap.isNotEmpty([]); // throws an error
*
* assertWrap.isNotEmpty('a'); // returns `'a'`;
* assertWrap.isNotEmpty({a: 'a'}); // returns `{a: 'a'}`;
* ```
*
* @returns The value if the assertion passes.
* @throws {@link AssertionError} If the assertion fails.
* @see
* - {@link assertWrap.isEmpty} : the opposite assertion.
*/
isNotEmpty(actual, failureMessage) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
return actual;
}
if (typeof actual === 'string' && !actual) {
// eslint-disable-next-line sonarjs/no-gratuitous-expressions
if (!actual) {
throw new AssertionError(`'${stringify(actual)}' is empty.`, failureMessage);
}
}
else if (Array.isArray(actual)) {
if (!actual.length) {
throw new AssertionError(`'${stringify(actual)}' is empty.`, failureMessage);
}
}
else if (actual instanceof Map || actual instanceof Set) {
if (!actual.size) {
throw new AssertionError(`'${stringify(actual)}' is empty.`, failureMessage);
}
}
else if (typeof actual === 'object' && !Object.keys(actual).length) {
throw new AssertionError(`'${stringify(actual)}' is empty.`, failureMessage);
}
return actual;
},
},
checkWrap: {
/**
* Checks that an object/array parent includes a child value through reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* checkWrap.hasValue({child}, child); // returns `{child}`
* checkWrap.hasValue({child: {a: 'a'}}, child); // returns `undefined`
* checkWrap.hasValue([child], child); // returns `[child]`
* ```
*
* @see
* - {@link checkWrap.lacksValue} : the opposite check.
* - {@link checkWrap.hasValues} : the multi-value check.
*/
hasValue(parent, value) {
if (hasValue(parent, value)) {
return parent;
}
else {
return undefined;
}
},
/**
* Checks that an object/array parent does _not_ include a child value through reference
* equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* checkWrap.lacksValue({child}, child); // returns `undefined`
* checkWrap.lacksValue({child: {a: 'a'}}, child); // returns `{child: {a: 'a'}}`
* checkWrap.lacksValue([child], child); // returns `undefined`
* ```
*
* @see
* - {@link checkWrap.hasValue} : the opposite check.
* - {@link checkWrap.lacksValues} : the multi-value check.
*/
lacksValue(parent, value) {
if (hasValue(parent, value)) {
return undefined;
}
else {
return parent;
}
},
/**
* Checks that an object/array parent includes all child values through reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* checkWrap.hasValues({child, child2}, [
* child,
* child2,
* ]); // returns `{child, child2}`
* checkWrap.hasValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // returns `undefined`
* checkWrap.hasValues(
* [child],
* [
* child,
* child2,
* ],
* ); // returns `[child]`
* ```
*
* @see
* - {@link checkWrap.lacksValues} : the opposite check.
* - {@link checkWrap.hasValue} : the single-value check.
*/
hasValues(parent, values) {
if (values.every((value) => hasValue(parent, value))) {
return parent;
}
else {
return undefined;
}
},
/**
* Checks that an object/array parent includes none of the provided child values through
* reference equality.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* checkWrap.lacksValues({}, [
* child,
* child2,
* ]); // returns `{}`
* checkWrap.lacksValues({child, child2}, [
* child,
* child2,
* ]); // returns `undefined`
* checkWrap.lacksValues({child: {a: 'a'}, child2}, [
* child,
* child2,
* ]); // returns `undefined`
* ```
*
* @see
* - {@link checkWrap.lacksValues} : the opposite check.
* - {@link checkWrap.hasValue} : the single-value check.
*/
lacksValues(parent, values) {
if (values.every((value) => hasValue(parent, value))) {
return undefined;
}
else {
return parent;
}
},
/**
* Checks that child value is contained within a parent object, array, or string through
* reference equality.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* checkWrap.isIn(child, {child}); // returns `child`
* checkWrap.isIn('a', 'ab'); // returns `'a'`
* checkWrap.isIn(child, [child]); // returns `child`
*
* checkWrap.isIn(child, {child: {a: 'a'}}); // returns `undefined`
* checkWrap.isIn('a', 'bc'); // returns `undefined`
* ```
*
* @see
* - {@link checkWrap.isNotIn} : the opposite check.
*/
isIn(child, parent) {
if (isIn(child, parent)) {
return child;
}
else {
return undefined;
}
},
/**
* Checks that child value is _not_ contained within a parent object, array, or string
* through reference equality.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* checkWrap.isNotIn(child, {child}); // returns `undefined`
* checkWrap.isNotIn('a', 'ab'); // returns `undefined`
* checkWrap.isNotIn(child, [child]); // returns `undefined`
*
* checkWrap.isNotIn(child, {child: {a: 'a'}}); // returns `child`
* checkWrap.isNotIn('a', 'bc'); // returns `'a'`
* ```
*
* @see
* - {@link checkWrap.isIn} : the opposite check.
*/
isNotIn(child, parent) {
if (isIn(child, parent)) {
return undefined;
}
else {
return child;
}
},
/**
* Checks that a value is empty. Supports strings, Maps, Sets, objects, and arrays.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* checkWrap.isEmpty({}); // returns `{}`
* checkWrap.isEmpty(''); // returns `''`
* checkWrap.isEmpty([]); // returns `[]`
*
* checkWrap.isEmpty('a'); // returns `undefined`
* checkWrap.isEmpty({a: 'a'}); // returns `undefined`
* ```
*
* @see
* - {@link checkWrap.isNotEmpty} : the opposite check.
*/
isEmpty(actual) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
return undefined;
}
if (typeof actual === 'string') {
if (!actual) {
return actual;
}
}
else if (Array.isArray(actual)) {
if (!actual.length) {
return actual;
}
}
else if (actual instanceof Map || actual instanceof Set) {
if (!actual.size) {
return actual;
}
}
else if (typeof actual === 'object' && !Object.keys(actual).length) {
return actual;
}
return undefined;
},
/**
* Checks that a value is _not_ empty. Supports strings, Maps, Sets, objects, and arrays.
*
* Type guards the value.
*
* @example
*
* ```ts
* import {checkWrap} from '@augment-vir/assert';
*
* checkWrap.isNotEmpty({}); // returns `undefined`
* checkWrap.isNotEmpty(''); // returns `undefined`
* checkWrap.isNotEmpty([]); // returns `undefined`
*
* checkWrap.isNotEmpty('a'); // returns `'a'`
* checkWrap.isNotEmpty({a: 'a'}); // returns `{a: 'a'}`
* ```
*
* @see
* - {@link checkWrap.isEmpty} : the opposite check.
*/
isNotEmpty(actual) {
if (typeof actual !== 'string' && typeof actual !== 'object') {
return actual;
}
if (typeof actual === 'string') {
if (!actual) {
return undefined;
}
}
else if (Array.isArray(actual)) {
if (!actual.length) {
return undefined;
}
}
else if (actual instanceof Map || actual instanceof Set) {
if (!actual.size) {
return undefined;
}
}
else if (typeof actual === 'object' && !Object.keys(actual).length) {
return undefined;
}
return actual;
},
},
waitUntil: {
/**
* Repeatedly calls a callback until its output is an object/array parent includes a child
* value through reference equality. Once the callback output passes, it is returned. If the
* attempts time out, an error is thrown.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {waitUntil} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* await waitUntil.hasValue(child, () => {
* return {child};
* }); // returns `{child}`;
* await waitUntil.hasValue(child, () => {
* return {child: {a: 'a'}};
* }); // throws an error
* await waitUntil.hasValue(child, () => [child]); // returns `[child]`;
* ```
*
* @returns The callback output once it passes.
* @throws {@link AssertionError} On timeout.
* @see
* - {@link waitUntil.lacksValue} : the opposite assertion.
* - {@link waitUntil.hasValues} : the multi-value assertion.
*/
hasValue: createWaitUntil(assertions.hasValue),
/**
* Repeatedly calls a callback until its output is an object/array parent does _not_ include
* a child value through reference equality. Once the callback output passes, it is
* returned. If the attempts time out, an error is thrown.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {waitUntil} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* await waitUntil.lacksValue(child, () => {
* return {child};
* }); // throws an error
* await waitUntil.lacksValue(child, () => {
* return {child: {a: 'a'}};
* }); // returns `{child: {a: 'a'}}`;
* await waitUntil.lacksValue(child, () => [child]); // throws an error
* ```
*
* @returns The callback output once it passes.
* @throws {@link AssertionError} On timeout.
* @see
* - {@link waitUntil.hasValue} : the opposite assertion.
* - {@link waitUntil.lacksValues} : the multi-value assertion.
*/
lacksValue: createWaitUntil(assertions.lacksValue),
/**
* Repeatedly calls a callback until its output is an object/array parent includes all child
* values through reference equality. Once the callback output passes, it is returned. If
* the attempts time out, an error is thrown.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {waitUntil} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* await waitUntil.hasValues(
* [
* child,
* child2,
* ],
* () => {
* return {child, child2};
* },
* ); // returns `{child, child2}`;
* await waitUntil.hasValues(
* [
* child,
* child2,
* ],
* () => {
* return {child: {a: 'a'}, child2};
* },
* ); // throws an error
* await waitUntil.hasValues(
* [
* child,
* child2,
* ],
* () => [child],
* ); // returns `[child]`;
* ```
*
* @returns The callback output once it passes.
* @throws {@link AssertionError} On timeout.
* @see
* - {@link waitUntil.lacksValues} : the opposite assertion.
* - {@link waitUntil.hasValue} : the single-value assertion.
*/
hasValues: createWaitUntil(assertions.hasValues),
/**
* Repeatedly calls a callback until its output is an object/array parent includes none of
* the provided child values through reference equality. Once the callback output passes, it
* is returned. If the attempts time out, an error is thrown.
*
* Performs no type guarding.
*
* @example
*
* ```ts
* import {waitUntil} from '@augment-vir/assert';
*
* const child = {a: 'a'};
* const child2 = {b: 'b'};
*
* await waitUntil.lacksValues(
* [
* child,
* child2,
* ],
* () => {
* return {};
* },
* ); // returns `{}`;
* await waitUntil.lacksValues(
* [
* child,
* child2,
* ],
* () => {
* return {child, child2};
* },
* ); // throws an error
* await waitUntil.lacksValues(
* [
* child,
* child2,
* ],
* () => {
* return {child: {a: 'a'}, child2};
* },
* ); // throws an error
* ```
*
* @returns The callback output once it passes.
* @throws {@link AssertionError} On timeout.
* @see
* - {@link waitUntil.lacksValues} : the opposite assertion.
* - {@link waitUntil.hasValue} : the single-value assertion.
*/
lacksValues: createWaitUntil(assertions.lacksValues),
/**
* Repeatedly calls a callback until its output is child value is contained within a parent
* object, array, or string through reference equality. Once the callback output passes, it
* is returned. If the attempts time out, an error is thrown.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {waitUntil} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* await waitUntil.isIn({child}, () => child); // returns `child`
* await waitUntil.isIn('ab', () => 'a'); // returns `'a'`
* await waitUntil.isIn(child, () => [child]); // returns `child`
*
* await waitUntil.isIn({child: {a: 'a'}}, () => child); // throws an error
* await waitUntil.isIn('bc', () => 'a'); // throws an error
* ```
*
* @returns The callback output once it passes.
* @throws {@link AssertionError} On timeout.
* @see
* - {@link waitUntil.isNotIn} : the opposite assertion.
*/
isIn: createWaitUntil(assertions.isIn),
/**
* Repeatedly calls a callback until its output is child value is _not_ contained within a
* parent object, array, or string through reference equality. Once the callback output
* passes, it is returned. If the attempts time out, an error is thrown.
*
* Type guards the child when possible.
*
* @example
*
* ```ts
* import {waitUntil} from '@augment-vir/assert';
*
* const child = {a: 'a'};
*
* await waitUntil.isNotIn({child}, () => child); // throws an error
* await waitUntil.isNotIn('ab', () => 'a'); // throws an error
* await waitUntil.isNotIn([child], () => child); // throws an error
*
* await waitUntil.isNotIn({child: {a: 'a'}}, () => child); // returns `child`;
* await waitUntil.isNotIn('bc', () => 'a'); // returns `'a'`;
* ```
*
* @returns The callback output once it passes.
* @throws {@link AssertionError} On timeout.
* @see
* - {@link waitUntil.isIn} : the opposite assertion.
*/
isNotIn: createWaitUntil(assertions.isNotIn),
/**
* Repeatedly calls a callback until its output is a value is empty. Supports strings, Maps,
* Sets, objects, and arrays. Once the callback output passes, it is retur