diff --git a/README.md b/README.md index 2508e39..bdd83da 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ $ npm install assertthat ## Quick Start -First you need to add a reference to assertthat to your application. +First you need to add a reference to `assertthat` to your application. ```javascript const { assert } = require('assertthat'); @@ -32,269 +32,304 @@ If you use TypeScript, use the following code instead: import { assert } from 'assertthat'; ``` -Now you are able to use the various constraints. +Now you are able to use various assertions, e.g. to compare two values for equality: ```javascript -const add = function(first, second) { - return first + second; -}; - -const actual = add(23, 42), +const actual = 23 + 42, expected = 65; assert.that(actual).is.equalTo(expected); ``` -## Available constraints - -Please note that any constraint can be negated using the `not` keyword. - -### atLeast +## Using common assertions -Asserts that `actual` is greater than or equal to `expected`. +While most assertions work for any type, a few are only available for specific types. The former are called *common assertions*. No matter which assertions you use, they can always be negated by using the `not` modifier, such as: ```javascript -assert.that(actual).is.atLeast(expected); -assert.that(actual).is.not.atLeast(expected); +assert.that(actual).is.not.equalTo(expected); ``` -### atMost +### Asserting for equality -Asserts that `actual` is less than or equal to `expected`. +To compare two values for equality, use the `equalTo` assertion. It works with value types and reference types, using a deep equality comparison on the latter, i.e. you can use it with objects, arrays, and other reference types, without having to care about the identity of these values: ```javascript -assert.that(actual).is.atMost(expected); -assert.that(actual).is.not.atMost(expected); +assert.that(actual).is.equalTo(expected); ``` -### between +### Asserting for identity -Asserts that `actual` is between `expectedLow` and `expectedHigh`. +If you actually care about identity, use the `identicalTo` assertion. On value types this will compare by value, but on reference types it will use their respective identity. ```javascript -assert.that(actual).is.between(expectedLow, expectedHigh); -assert.that(actual).is.not.between(expectedLow, expectedHigh); +assert.that(actual).is.identicalTo(expected); ``` -### containing +### Asserting for JSON structures -Asserts that `actual` contains `expected`. +From time to time you may want to assert that two values result in the same JSON structure. For these cases use `sameJsonAs`: ```javascript -assert.that(actual).is.containing(expected); -assert.that(actual).is.not.containing(expected); +assert.that(actual).is.sameJsonAs(expected); ``` -### containingAnyOf +### Asserting for literal values -Asserts that `actual` contains any of `expected`. +There are special assertions for the literal values `true`, `false`, `null`, and `undefined`. Although you may use `equalTo` here as well, their usage is more straight-forward and intuitive: ```javascript -assert.that(actual).is.containingAnyOf(expected); -assert.that(actual).is.not.containingAnyOf(expected); +assert.that(actual).is.true(); +assert.that(actual).is.false(); +assert.that(actual).is.null(); +assert.that(actual).is.undefined(); ``` -### containingAllOf +### Asserting for a specific type -Asserts that `actual` contains all of `expected`. +Sometimes you may want to assert that a value is of a given type. For that, use the `ofType` assertion, and specify the expected type. The supported values are `array`, `boolean`, `error`, `function`, `map`, `null`, `number`, `object`, `result`, `set`, `string`, `symbol`, and `undefined`: ```javascript -assert.that(actual).is.containingAllOf(expected); -assert.that(actual).is.not.containingAllOf(expected); +assert.that(actual).is.ofType('function'); ``` -### endingWith +## Using special assertions -Asserts that `actual` ends with `expected`. +In contrast to the common assertions, special assertions are only available for specific types. + +### Asserting for arrays + +To assert that an array is empty, use the `empty` assertion: ```javascript -assert.that(actual).is.endingWith(expected); -assert.that(actual).is.not.endingWith(expected); +assert.that(actual).is.empty(); ``` -### equalTo - -Asserts that `actual` is equal to `expected`. +Alternatively, you may verify whether one or more elements are contained by using the `containing`, `containingAnyOf`, and `containingAllOf` assertions: ```javascript -assert.that(actual).is.equalTo(expected); -assert.that(actual).is.not.equalTo(expected); +// The given element must be contained. +assert.that(actual).is.containing(23); + +// At least one of the given elements must be contained. +assert.that(actual).is.containingAnyOf([ 23, 42 ]); + +// All of the given elements must be contained. +assert.that(actual).is.containingAllOf([ 23, 42 ]); ``` -### false +### Asserting for functions -Asserts that `actual` is false. +You may want to ensure that a function throws an exception. For that, use the `throwing` assertion. Please note that you have to wrap the function you would like to verify into a callback: ```javascript -assert.that(actual).is.false(); -assert.that(actual).is.not.false(); +assert.that(() => { + actual(); +}).is.throwing(); ``` -### falsy +If the function to check is asynchronous, use `throwingAsync` instead, and make sure to use `async` and `await` where needed: -Asserts that `actual` is falsy. +```javascript +await assert.that(async () => { + await actual(); +}).is.throwingAsync(); +``` + +Sometimes you may want to check for a specific exception. For that, provide the expected exception's message as a string or as a regular expression, or hand over a predicate function to check for anything else, such as the error code or the stack trace: ```javascript -assert.that(actual).is.falsy(); -assert.that(actual).is.not.falsy(); +// Using a string. +assert.that(() => { + actual(); +}).is.throwing('File not found.'); + +// Using a regular expression. +assert.that(() => { + actual(); +}).is.throwing(/^File/u); + +// Using a predicate. +assert.that(() => { + actual(); +}).is.throwing(ex => ex.code === 'ENOENT'); ``` -### greaterThan +If you are using TypeScript, you may want to specify the type of the expected exception so that you get a correctly typed parameter in the predicate function: -Asserts that `actual` is greater than `expected`. +```typescript +assert.that(() => { + actual(); +}).is.throwing((ex): boolean => ex.code === 'ENOENT'); +``` + +### Asserting for maps + +To assert that a map is empty, use the `empty` assertion: ```javascript -assert.that(actual).is.greaterThan(expected); -assert.that(actual).is.not.greaterThan(expected); +assert.that(actual).is.empty(); ``` -### instanceOf +Alternatively, you may use the `atLeast` assertion to assert that a map contains at least everything another map does: + +```javascript +assert.that(actual).is.atLeast(new Map([ + [ name, 'the native web' ], + [ website, 'https://www.thenativeweb.io' ] +])); +``` -Asserts that `actual` is an instance of `expected`. +The same is true for asserting that a map contains at most everything another map does, using the `atMost` assertion: ```javascript -assert.that(actual).is.instanceOf(expected); -assert.that(actual).is.not.instanceOf(expected); +assert.that(actual).is.atMost(new Map([ + [ name, 'the native web' ], + [ website, 'https://www.thenativeweb.io' ] +])); ``` -### lessThan +### Asserting for numbers -Asserts that `actual` is less than `expected`. +If you want to verify that a number is `NaN`, use the respective assertion: ```javascript -assert.that(actual).is.lessThan(expected); -assert.that(actual).is.not.lessThan(expected); +assert.that(actual).is.NaN(); ``` -### matching - -Asserts that `actual` matches `expected` where `expected` is a regular expression. +Alternatively, you may want to verify the relation between two numbers by comparing them: ```javascript -assert.that(actual).is.matching(expected); -assert.that(actual).is.not.matching(expected); +// actual > 23 +assert.that(actual).is.greaterThan(23); + +// actual < 23 +assert.that(actual).is.lessThan(23); + +// actual >= 23 +assert.that(actual).is.atLeast(23); + +// actual <= 23 +assert.that(actual).is.atMost(23); ``` -### NaN +### Asserting for objects -Asserts that `actual` is NaN. +To check that an object is empty, use the `empty` assertion: ```javascript -assert.that(actual).is.NaN(); -assert.that(actual).is.not.NaN(); +assert.that(actual).is.empty(); ``` -### null - -Asserts that `actual` is null. +Alternatively, you may use the `atLeast` assertion to assert that an object contains at least everything another object does: ```javascript -assert.that(actual).is.null(); -assert.that(actual).is.not.null(); +assert.that(actual).is.atLeast({ + name: 'the native web', + website: 'https://www.thenativeweb.io' +}); ``` -### ofType +The same is true for asserting that an object contains at most everything another object does, using the `atMost` assertion: -Asserts that `actual` is of type `expected`. +```javascript +assert.that(actual).is.atMost({ + name: 'the native web', + website: 'https://www.thenativeweb.io' +}); +``` + +Finally, to check that an object is an instance of a specific type, use the `instanceOf` assertion, and provide the constructor function or class: ```javascript -assert.that(actual).is.ofType(expected); -assert.that(actual).is.not.ofType(expected); +assert.that(actual).is.instanceOf(Person); ``` -### sameAs +### Asserting for results -Asserts that `actual` is identical to `expected`. +If you are using the `Result` type from the [defekt](https://www.npmjs.com/package/defekt) module, you may use `assertthat` to verify that a result is either a value or an error. For that, use the respective assertions: ```javascript -assert.that(actual).is.sameAs(expected); -assert.that(actual).is.not.sameAs(expected); +assert.that(actual).is.aValue(); +assert.that(actual).is.anError(); ``` -### sameJsonAs - -Asserts that `actual` is stringified as the same JSON as `expected`. +If you are interested in a specific error, use the `anErrorWithMessage` assertion instead of `anError`: ```javascript -assert.that(actual).is.sameJsonAs(expected); -assert.that(actual).is.not.sameJsonAs(expected); +assert.that(actual).is.anErrorWithMessage('File not found.'); ``` -### startingWith +### Asserting for sets -Asserts that `actual` starts with `expected`. +To assert that a set is empty, use the `empty` assertion: ```javascript -assert.that(actual).is.startingWith(expected); -assert.that(actual).is.not.startingWith(expected); +assert.that(actual).is.empty(); ``` -### throwing +Alternatively, you may use the `atLeast` assertion to assert that a set contains at least everything another set does: -Asserts that `f` throws an exception. +```javascript +assert.that(actual).is.atLeast(new Set([ 23, 42 ])); +``` + +The same is true for asserting that a set contains at most everything another set does, using the `atMost` assertion: ```javascript -assert.that(() => { - f(); -}).is.throwing(); -assert.that(() => { - f(); -}).is.not.throwing(); +assert.that(actual).is.atMost(new Set([ 23, 42 ])); ``` -Alternatively, asserts that `f` throws an exception with the `expected` message. For the `expected` message you can either specify a string, a regular expression or a predicate. +Finally, you may verify whether one or more elements are contained by using the `containing`, `containingAnyOf`, and `containingAllOf` assertions: ```javascript -assert.that(() => { - f(); -}).is.throwing(expected); -assert.that(() => { - f(); -}).is.not.throwing(expected); +// The given element must be contained. +assert.that(actual).is.containing(23); + +// At least one of the given elements must be contained. +assert.that(actual).is.containingAnyOf([ 23, 42 ]); + +// All of the given elements must be contained. +assert.that(actual).is.containingAllOf([ 23, 42 ]); ``` -### throwingAsync +### Asserting for strings -Asserts that `f` throws an exception. +To assert that a string is empty, use the `empty` assertion: ```javascript -await assert.that(async () => { - await f(); -}).is.throwingAsync(); -await assert.that(async () => { - await f(); -}).is.not.throwingAsync(); +assert.that(actual).is.empty(); ``` -Alternatively, asserts that `f` throws an exception with the `expected` message. For the `expected` message you can either specify a string, a regular expression or a predicate. +If you want to verify that it is starting or ending with another string, use `startingWith` and `endingWith` respectively: ```javascript -await assert.that(async () => { - await f(); -}).is.throwingAsync(expected); -await assert.that(async () => { - await f(); -}).is.not.throwingAsync(expected); +assert.that(actual).is.startingWith('the'); +assert.that(actual).is.endingWith('web'); ``` -### true - -Asserts that `actual` is true. +To verify that a string matches a regular expression, use the `matching` assertion: ```javascript -assert.that(actual).is.true(); -assert.that(actual).is.not.true(); +assert.that(actual).is.matching(/native/u); ``` -### undefined - -Asserts that `actual` is undefined. +Finally, you may verify whether one or more substrings are contained by using the `containing`, `containingAnyOf`, and `containingAllOf` assertions: ```javascript -assert.that(actual).is.undefined(); -assert.that(actual).is.not.undefined(); +// The given string must be contained. +assert.that(actual).is.containing('native'); + +// At least one of the given strings must be contained. +assert.that(actual).is.containingAnyOf([ 'native', 'web' ]); + +// All of the given strings must be contained. +assert.that(actual).is.containingAllOf([ 'native', 'web' ]); ``` +## Caveats + +Most assertions build upon an internal comparison using a diff-algorithm. To avoid infinite recursion, all asserted values are first dispelled (i.e. recursions in them are detected and removed). These recursions can in principle be compared by value across arrays and objects. However, this does not work with `Set`s and `Map`s, since a `Map` can have reference types as keys and the element in `Set` can not be uniquely identified in a reproducible way. So comparisons of `Set`s and `Map`s that contain recursions might not work as expected. + ## Running quality assurance To run quality assurance for this module use [roboter](https://www.npmjs.com/package/roboter): diff --git a/lib/assertions/forAny/AssertThatForAny.ts b/lib/assertions/forAny/AssertThatForAny.ts new file mode 100644 index 0000000..fa5ab52 --- /dev/null +++ b/lib/assertions/forAny/AssertThatForAny.ts @@ -0,0 +1,11 @@ +import { CommonAssertions } from './CommonAssertions'; + +type AssertThatForAny = (actual: TAny) => { + is: CommonAssertions & { + not: CommonAssertions; + }; +}; + +export type { + AssertThatForAny +}; diff --git a/lib/assertions/forAny/CommonAssertions.ts b/lib/assertions/forAny/CommonAssertions.ts new file mode 100644 index 0000000..97c8446 --- /dev/null +++ b/lib/assertions/forAny/CommonAssertions.ts @@ -0,0 +1,21 @@ +import { Type } from '../../types/Type'; + +interface CommonAssertions { + equalTo: (expected: TAny) => void; + identicalTo: (expected: TAny) => void; + sameJsonAs: (expected: TAny) => void; + + falsy: () => void; + truthy: () => void; + + null: () => void; + undefined: () => void; + true: () => void; + false: () => void; + + ofType: (expected: Type) => void; +} + +export type { + CommonAssertions +}; diff --git a/lib/assertions/forAny/assertActualIsEqualToExpected.ts b/lib/assertions/forAny/assertActualIsEqualToExpected.ts new file mode 100644 index 0000000..b0a0a62 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsEqualToExpected.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertActualIsEqualToExpected = function ( + actual: any, + expected: any +): Result { + const diff = compare(dispel(actual), dispel(expected)); + + if (isEqualDiff(diff)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The values are not equal.', + expected: prettyPrint(expected), + actual: prettyPrint(actual), + diff: prettyPrintDiff(diff) + })); +}; + +export { + assertActualIsEqualToExpected +}; diff --git a/lib/assertions/forAny/assertActualIsFalse.ts b/lib/assertions/forAny/assertActualIsFalse.ts new file mode 100644 index 0000000..0592283 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsFalse.ts @@ -0,0 +1,21 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { error, Result, value } from 'defekt'; + +const assertActualIsFalse = function ( + actual: any +): Result { + const diff = compare(actual, false); + + if (diff.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is not false.' + })); +}; + +export { + assertActualIsFalse +}; diff --git a/lib/assertions/forAny/assertActualIsFalsy.ts b/lib/assertions/forAny/assertActualIsFalsy.ts new file mode 100644 index 0000000..86dea84 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsFalsy.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsFalsy = function ( + actual: any +): Result { + if (!actual) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is not falsy.', + actual: prettyPrint(actual) + })); +}; + +export { + assertActualIsFalsy +}; diff --git a/lib/assertions/forAny/assertActualIsIdenticalToExpected.ts b/lib/assertions/forAny/assertActualIsIdenticalToExpected.ts new file mode 100644 index 0000000..654117f --- /dev/null +++ b/lib/assertions/forAny/assertActualIsIdenticalToExpected.ts @@ -0,0 +1,19 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertActualIsIdenticalToExpected = function ( + actual: any, + expected: any +): Result { + if (actual === expected) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The values are not identical.' + })); +}; + +export { + assertActualIsIdenticalToExpected +}; diff --git a/lib/assertions/forAny/assertActualIsNotEqualToExpected.ts b/lib/assertions/forAny/assertActualIsNotEqualToExpected.ts new file mode 100644 index 0000000..642abb9 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotEqualToExpected.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotEqualToExpected = function ( + actual: any, + expected: any +): Result { + const diff = compare(dispel(actual), dispel(expected)); + + if (!isEqualDiff(diff)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The values are equal.', + expected: `Not to equal:\n${prettyPrint(expected)}` + })); +}; + +export { + assertActualIsNotEqualToExpected +}; diff --git a/lib/assertions/forAny/assertActualIsNotFalse.ts b/lib/assertions/forAny/assertActualIsNotFalse.ts new file mode 100644 index 0000000..f976fa0 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotFalse.ts @@ -0,0 +1,21 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotFalse = function ( + actual: any +): Result { + const diff = compare(actual, true); + + if (diff.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is false.' + })); +}; + +export { + assertActualIsNotFalse +}; diff --git a/lib/assertions/forAny/assertActualIsNotFalsy.ts b/lib/assertions/forAny/assertActualIsNotFalsy.ts new file mode 100644 index 0000000..76d2b04 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotFalsy.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotFalsy = function ( + actual: any +): Result { + if (actual) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is falsy.', + actual: prettyPrint(actual) + })); +}; + +export { + assertActualIsNotFalsy +}; diff --git a/lib/assertions/forAny/assertActualIsNotIdenticalToExpected.ts b/lib/assertions/forAny/assertActualIsNotIdenticalToExpected.ts new file mode 100644 index 0000000..4a27254 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotIdenticalToExpected.ts @@ -0,0 +1,19 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotIdenticalToExpected = function ( + actual: any, + expected: any +): Result { + if (actual !== expected) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The values are identical.' + })); +}; + +export { + assertActualIsNotIdenticalToExpected +}; diff --git a/lib/assertions/forAny/assertActualIsNotNull.ts b/lib/assertions/forAny/assertActualIsNotNull.ts new file mode 100644 index 0000000..ea320b3 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotNull.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotNull = function ( + actual: any +): Result { + if (actual !== null) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is null.' + })); +}; + +export { + assertActualIsNotNull +}; diff --git a/lib/assertions/forAny/assertActualIsNotOfType.ts b/lib/assertions/forAny/assertActualIsNotOfType.ts new file mode 100644 index 0000000..d3f0ba7 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotOfType.ts @@ -0,0 +1,149 @@ +import { isArray } from '../../types/isArray'; +import { isBoolean } from '../../types/isBoolean'; +import { isFunction } from '../../types/isFunction'; +import { isMap } from '../../types/isMap'; +import { isNull } from '../../types/isNull'; +import { isNumber } from '../../types/isNumber'; +import { isObject } from '../../types/isObject'; +import { isSet } from '../../types/isSet'; +import { isString } from '../../types/isString'; +import { isSymbol } from '../../types/isSymbol'; +import { isUndefined } from '../../types/isUndefined'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { Type } from '../../types/Type'; +import { AssertionFailed, InvalidOperation } from '../../errors'; +import { error, isError, isResult, Result, value } from 'defekt'; + +const assertActualIsNotOfType = function ( + actual: any, + expected: Type +): Result { + switch (expected) { + case 'array': { + if (isArray(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type array.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'boolean': { + if (isBoolean(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type boolean.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'error': { + if (isError(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type error.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'function': { + if (isFunction(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type function.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'map': { + if (isMap(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type map.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'null': { + if (isNull(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type null.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'number': { + if (isNumber(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type number.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'result': { + if (isResult(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type result.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'set': { + if (isSet(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type set.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'string': { + if (isString(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type string.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'symbol': { + if (isSymbol(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type symbol.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'undefined': { + if (isUndefined(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type undefined.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'object': { + if (isObject(actual)) { + return error(new AssertionFailed({ + message: 'The value is of type object.', + actual: prettyPrint(actual) + })); + } + break; + } + default: { + throw new InvalidOperation(); + } + } + + return value(); +}; + +export { + assertActualIsNotOfType +}; diff --git a/lib/assertions/forAny/assertActualIsNotSameJsonAsExpected.ts b/lib/assertions/forAny/assertActualIsNotSameJsonAsExpected.ts new file mode 100644 index 0000000..08306da --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotSameJsonAsExpected.ts @@ -0,0 +1,23 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotSameJsonAsExpected = function ( + actual: any, + expected: any +): Result { + const actualJson = JSON.stringify(actual); + const expectedJson = JSON.stringify(expected); + + if (actualJson !== expectedJson) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The values have the same JSON representation.', + expected: `Not to equal:\n${expectedJson}` + })); +}; + +export { + assertActualIsNotSameJsonAsExpected +}; diff --git a/lib/assertions/forAny/assertActualIsNotTrue.ts b/lib/assertions/forAny/assertActualIsNotTrue.ts new file mode 100644 index 0000000..213e89e --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotTrue.ts @@ -0,0 +1,21 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotTrue = function ( + actual: any +): Result { + const diff = compare(actual, false); + + if (diff.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is true.' + })); +}; + +export { + assertActualIsNotTrue +}; diff --git a/lib/assertions/forAny/assertActualIsNotTruthy.ts b/lib/assertions/forAny/assertActualIsNotTruthy.ts new file mode 100644 index 0000000..ba13acc --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotTruthy.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotTruthy = function ( + actual: any +): Result { + if (!actual) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is truthy.', + actual: prettyPrint(actual) + })); +}; + +export { + assertActualIsNotTruthy +}; diff --git a/lib/assertions/forAny/assertActualIsNotUndefined.ts b/lib/assertions/forAny/assertActualIsNotUndefined.ts new file mode 100644 index 0000000..d216d1b --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNotUndefined.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNotUndefined = function ( + actual: any +): Result { + if (actual !== undefined) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is undefined.' + })); +}; + +export { + assertActualIsNotUndefined +}; diff --git a/lib/assertions/forAny/assertActualIsNull.ts b/lib/assertions/forAny/assertActualIsNull.ts new file mode 100644 index 0000000..667a67f --- /dev/null +++ b/lib/assertions/forAny/assertActualIsNull.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsNull = function ( + actual: any +): Result { + if (actual === null) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is not null.', + actual: prettyPrint(actual) + })); +}; + +export { + assertActualIsNull +}; diff --git a/lib/assertions/forAny/assertActualIsOfType.ts b/lib/assertions/forAny/assertActualIsOfType.ts new file mode 100644 index 0000000..08a3436 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsOfType.ts @@ -0,0 +1,149 @@ +import { isArray } from '../../types/isArray'; +import { isBoolean } from '../../types/isBoolean'; +import { isFunction } from '../../types/isFunction'; +import { isMap } from '../../types/isMap'; +import { isNull } from '../../types/isNull'; +import { isNumber } from '../../types/isNumber'; +import { isObject } from '../../types/isObject'; +import { isSet } from '../../types/isSet'; +import { isString } from '../../types/isString'; +import { isSymbol } from '../../types/isSymbol'; +import { isUndefined } from '../../types/isUndefined'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { Type } from '../../types/Type'; +import { AssertionFailed, InvalidOperation } from '../../errors'; +import { error, isError, isResult, Result, value } from 'defekt'; + +const assertActualIsOfType = function ( + actual: any, + expected: Type +): Result { + switch (expected) { + case 'array': { + if (!isArray(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type array.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'boolean': { + if (!isBoolean(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type boolean.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'error': { + if (!isError(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type error.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'function': { + if (!isFunction(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type function.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'map': { + if (!isMap(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type map.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'null': { + if (!isNull(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type null.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'number': { + if (!isNumber(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type number.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'result': { + if (!isResult(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type result.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'set': { + if (!isSet(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type set.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'string': { + if (!isString(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type string.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'symbol': { + if (!isSymbol(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type symbol.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'undefined': { + if (!isUndefined(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type undefined.', + actual: prettyPrint(actual) + })); + } + break; + } + case 'object': { + if (!isObject(actual)) { + return error(new AssertionFailed({ + message: 'The value is not of type object.', + actual: prettyPrint(actual) + })); + } + break; + } + default: { + throw new InvalidOperation(); + } + } + + return value(); +}; + +export { + assertActualIsOfType +}; diff --git a/lib/assertions/forAny/assertActualIsSameJsonAsExpected.ts b/lib/assertions/forAny/assertActualIsSameJsonAsExpected.ts new file mode 100644 index 0000000..b968cbd --- /dev/null +++ b/lib/assertions/forAny/assertActualIsSameJsonAsExpected.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertActualIsSameJsonAsExpected = function ( + actual: any, + expected: any +): Result { + const actualJson = JSON.stringify(actual); + const expectedJson = JSON.stringify(expected); + + if (actualJson === expectedJson) { + return value(); + } + + const diff = compare(actualJson, expectedJson); + + return error(new AssertionFailed({ + message: 'The values do not have the same JSON representation.', + expected: expectedJson, + actual: actualJson, + diff: prettyPrintDiff(diff) + })); +}; + +export { + assertActualIsSameJsonAsExpected +}; diff --git a/lib/assertions/forAny/assertActualIsTrue.ts b/lib/assertions/forAny/assertActualIsTrue.ts new file mode 100644 index 0000000..1d14326 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsTrue.ts @@ -0,0 +1,21 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { error, Result, value } from 'defekt'; + +const assertActualIsTrue = function ( + actual: any +): Result { + const diff = compare(actual, true); + + if (diff.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is not true.' + })); +}; + +export { + assertActualIsTrue +}; diff --git a/lib/assertions/forAny/assertActualIsTruthy.ts b/lib/assertions/forAny/assertActualIsTruthy.ts new file mode 100644 index 0000000..ea87f8c --- /dev/null +++ b/lib/assertions/forAny/assertActualIsTruthy.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsTruthy = function ( + actual: any +): Result { + if (actual) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is not truthy.', + actual: prettyPrint(actual) + })); +}; + +export { + assertActualIsTruthy +}; diff --git a/lib/assertions/forAny/assertActualIsUndefined.ts b/lib/assertions/forAny/assertActualIsUndefined.ts new file mode 100644 index 0000000..2e95596 --- /dev/null +++ b/lib/assertions/forAny/assertActualIsUndefined.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertActualIsUndefined = function ( + actual: any +): Result { + if (actual === undefined) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The value is not undefined.', + actual: prettyPrint(actual) + })); +}; + +export { + assertActualIsUndefined +}; diff --git a/lib/assertions/forAny/assertions.ts b/lib/assertions/forAny/assertions.ts new file mode 100644 index 0000000..0d9f1f5 --- /dev/null +++ b/lib/assertions/forAny/assertions.ts @@ -0,0 +1,103 @@ +import { assertActualIsEqualToExpected } from './assertActualIsEqualToExpected'; +import { assertActualIsFalse } from './assertActualIsFalse'; +import { assertActualIsFalsy } from './assertActualIsFalsy'; +import { assertActualIsIdenticalToExpected } from './assertActualIsIdenticalToExpected'; +import { assertActualIsNotEqualToExpected } from './assertActualIsNotEqualToExpected'; +import { assertActualIsNotFalse } from './assertActualIsNotFalse'; +import { assertActualIsNotFalsy } from './assertActualIsNotFalsy'; +import { assertActualIsNotIdenticalToExpected } from './assertActualIsNotIdenticalToExpected'; +import { assertActualIsNotNull } from './assertActualIsNotNull'; +import { assertActualIsNotOfType } from './assertActualIsNotOfType'; +import { assertActualIsNotSameJsonAsExpected } from './assertActualIsNotSameJsonAsExpected'; +import { assertActualIsNotTrue } from './assertActualIsNotTrue'; +import { assertActualIsNotTruthy } from './assertActualIsNotTruthy'; +import { assertActualIsNotUndefined } from './assertActualIsNotUndefined'; +import { assertActualIsNull } from './assertActualIsNull'; +import { assertActualIsOfType } from './assertActualIsOfType'; +import { assertActualIsSameJsonAsExpected } from './assertActualIsSameJsonAsExpected'; +import { assertActualIsTrue } from './assertActualIsTrue'; +import { assertActualIsTruthy } from './assertActualIsTruthy'; +import { assertActualIsUndefined } from './assertActualIsUndefined'; +import { CommonAssertions } from './CommonAssertions'; +import { report } from '../../report'; + +const getAssertionsForAny = function (actual: TAny): CommonAssertions { + return { + equalTo (expected: TAny): void { + report(assertActualIsEqualToExpected(actual, expected)); + }, + identicalTo (expected: TAny): void { + report(assertActualIsIdenticalToExpected(actual, expected)); + }, + sameJsonAs (expected: TAny): void { + report(assertActualIsSameJsonAsExpected(actual, expected)); + }, + + falsy (): void { + report(assertActualIsFalsy(actual)); + }, + truthy (): void { + report(assertActualIsTruthy(actual)); + }, + + null (): void { + report(assertActualIsNull(actual)); + }, + undefined (): void { + report(assertActualIsUndefined(actual)); + }, + false (): void { + report(assertActualIsFalse(actual)); + }, + true (): void { + report(assertActualIsTrue(actual)); + }, + + ofType (expected): void { + report(assertActualIsOfType(actual, expected)); + } + }; +}; + +const getNegatedAssertionsForAny = function (actual: TAny): CommonAssertions { + return { + equalTo (expected: TAny): void { + report(assertActualIsNotEqualToExpected(actual, expected)); + }, + identicalTo (expected: TAny): void { + report(assertActualIsNotIdenticalToExpected(actual, expected)); + }, + sameJsonAs (expected: TAny): void { + report(assertActualIsNotSameJsonAsExpected(actual, expected)); + }, + + falsy (): void { + report(assertActualIsNotFalsy(actual)); + }, + truthy (): void { + report(assertActualIsNotTruthy(actual)); + }, + + null (): void { + report(assertActualIsNotNull(actual)); + }, + undefined (): void { + report(assertActualIsNotUndefined(actual)); + }, + false (): void { + report(assertActualIsNotFalse(actual)); + }, + true (): void { + report(assertActualIsNotTrue(actual)); + }, + + ofType (expected): void { + report(assertActualIsNotOfType(actual, expected)); + } + }; +}; + +export { + getAssertionsForAny, + getNegatedAssertionsForAny +}; diff --git a/lib/assertions/forArrays/ArrayAssertions.ts b/lib/assertions/forArrays/ArrayAssertions.ts new file mode 100644 index 0000000..a1151f0 --- /dev/null +++ b/lib/assertions/forArrays/ArrayAssertions.ts @@ -0,0 +1,10 @@ +interface ArrayAssertions { + containing: (item: TContent) => void; + containingAllOf: (iterable: Iterable) => void; + containingAnyOf: (iterable: Iterable) => void; + empty: () => void; +} + +export type { + ArrayAssertions +}; diff --git a/lib/assertions/forArrays/AssertThatForArray.ts b/lib/assertions/forArrays/AssertThatForArray.ts new file mode 100644 index 0000000..a9aa822 --- /dev/null +++ b/lib/assertions/forArrays/AssertThatForArray.ts @@ -0,0 +1,12 @@ +import { ArrayAssertions } from './ArrayAssertions'; +import { CommonAssertions } from '../forAny/CommonAssertions'; + +type AssertThatForArray = (actual: TContent[]) => { + is: CommonAssertions & ArrayAssertions & { + not: CommonAssertions & ArrayAssertions; + }; +}; + +export type { + AssertThatForArray +}; diff --git a/lib/assertions/forArrays/assertArrayIsContainingAllOfIterable.ts b/lib/assertions/forArrays/assertArrayIsContainingAllOfIterable.ts new file mode 100644 index 0000000..7028f68 --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsContainingAllOfIterable.ts @@ -0,0 +1,35 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsContainingAllOfIterable = function ( + actual: TContent[], + iterable: Iterable +): Result { + const setFromActual = new Set(dispel(actual)); + const setFromExpected = new Set(dispel(iterable)); + + const diff = compareSets(setFromActual, setFromExpected); + + if (isEqualDiff(diff)) { + return value(); + } + + if (diff.omissions.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The array does not contain all expected items.', + actual: prettyPrint(actual), + expected: `To contain all of:\n${prettyPrint(iterable)}`, + diff: `Missing these items:\n${prettyPrint(diff.omissions)}` + })); +}; + +export { + assertArrayIsContainingAllOfIterable +}; diff --git a/lib/assertions/forArrays/assertArrayIsContainingAnyOfIterable.ts b/lib/assertions/forArrays/assertArrayIsContainingAnyOfIterable.ts new file mode 100644 index 0000000..8c572a4 --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsContainingAnyOfIterable.ts @@ -0,0 +1,27 @@ +import { assertArrayIsContainingItem } from './assertArrayIsContainingItem'; +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsContainingAnyOfIterable = function ( + actual: TContent[], + iterable: Iterable +): Result { + for (const item of iterable) { + const result = assertArrayIsContainingItem(actual, item); + + if (result.hasValue()) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The array does not contain any of the expected items.', + actual: prettyPrint(actual), + expected: `To contain any of:\n${prettyPrint(iterable)}` + })); +}; + +export { + assertArrayIsContainingAnyOfIterable +}; diff --git a/lib/assertions/forArrays/assertArrayIsContainingItem.ts b/lib/assertions/forArrays/assertArrayIsContainingItem.ts new file mode 100644 index 0000000..713eaaa --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsContainingItem.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsContainingItem = function ( + actual: TContent[], + item: TContent +): Result { + for (const actualItem of actual) { + const diff = compare(dispel(actualItem), dispel(item)); + + if (isEqualDiff(diff)) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The array does not contain the expected item.', + actual: prettyPrint(actual), + expected: `To contain:\n${prettyPrint(item)}` + })); +}; + +export { + assertArrayIsContainingItem +}; diff --git a/lib/assertions/forArrays/assertArrayIsEmpty.ts b/lib/assertions/forArrays/assertArrayIsEmpty.ts new file mode 100644 index 0000000..84c99b5 --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsEmpty.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsEmpty = function ( + actual: TContent[] +): Result { + if (actual.length === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The array is not empty.', + actual: prettyPrint(actual) + })); +}; + +export { + assertArrayIsEmpty +}; diff --git a/lib/assertions/forArrays/assertArrayIsNotContainingAllOfIterable.ts b/lib/assertions/forArrays/assertArrayIsNotContainingAllOfIterable.ts new file mode 100644 index 0000000..68d237e --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsNotContainingAllOfIterable.ts @@ -0,0 +1,27 @@ +import { assertArrayIsContainingItem } from './assertArrayIsContainingItem'; +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsNotContainingAllOfIterable = function ( + actual: TContent[], + iterable: Iterable +): Result { + for (const item of iterable) { + const result = assertArrayIsContainingItem(actual, item); + + if (result.hasError()) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The array contains all items in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain all of:\n${prettyPrint(iterable)}` + })); +}; + +export { + assertArrayIsNotContainingAllOfIterable +}; diff --git a/lib/assertions/forArrays/assertArrayIsNotContainingAnyOfIterable.ts b/lib/assertions/forArrays/assertArrayIsNotContainingAnyOfIterable.ts new file mode 100644 index 0000000..7cd3178 --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsNotContainingAnyOfIterable.ts @@ -0,0 +1,33 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsNotContainingAnyOfIterable = function ( + actual: TContent[], + iterable: Iterable +): Result { + const setFromActual = new Set(dispel(actual)); + const setFromExpected = new Set(dispel(iterable)); + + const diff = compareSets(setFromActual, setFromExpected); + + if (!isEqualDiff(diff) && diff.equal.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The array does contains one or more of the items in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain any of:\n${prettyPrint(iterable)}`, + diff: `These items are contained, but should not be:\n${prettyPrint( + isEqualDiff(diff) ? setFromExpected : diff.equal + )}` + })); +}; + +export { + assertArrayIsNotContainingAnyOfIterable +}; diff --git a/lib/assertions/forArrays/assertArrayIsNotContainingItem.ts b/lib/assertions/forArrays/assertArrayIsNotContainingItem.ts new file mode 100644 index 0000000..4a3606c --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsNotContainingItem.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsNotContainingItem = function ( + actual: TContent[], + item: TContent +): Result { + for (const actualItem of actual) { + const diff = compare(dispel(actualItem), dispel(item)); + + if (isEqualDiff(diff)) { + return error(new AssertionFailed({ + message: 'The array contains the item.', + expected: `To not contain:\n${prettyPrint(item)}`, + actual: prettyPrint(actual) + })); + } + } + + return value(); +}; + +export { + assertArrayIsNotContainingItem +}; diff --git a/lib/assertions/forArrays/assertArrayIsNotEmpty.ts b/lib/assertions/forArrays/assertArrayIsNotEmpty.ts new file mode 100644 index 0000000..e37a47f --- /dev/null +++ b/lib/assertions/forArrays/assertArrayIsNotEmpty.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertArrayIsNotEmpty = function ( + actual: TContent[] +): Result { + if (actual.length > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The array is empty.' + })); +}; + +export { + assertArrayIsNotEmpty +}; diff --git a/lib/assertions/forArrays/assertions.ts b/lib/assertions/forArrays/assertions.ts new file mode 100644 index 0000000..de276da --- /dev/null +++ b/lib/assertions/forArrays/assertions.ts @@ -0,0 +1,49 @@ +import { ArrayAssertions } from './ArrayAssertions'; +import { assertArrayIsContainingAllOfIterable } from './assertArrayIsContainingAllOfIterable'; +import { assertArrayIsContainingAnyOfIterable } from './assertArrayIsContainingAnyOfIterable'; +import { assertArrayIsContainingItem } from './assertArrayIsContainingItem'; +import { assertArrayIsEmpty } from './assertArrayIsEmpty'; +import { assertArrayIsNotContainingAllOfIterable } from './assertArrayIsNotContainingAllOfIterable'; +import { assertArrayIsNotContainingAnyOfIterable } from './assertArrayIsNotContainingAnyOfIterable'; +import { assertArrayIsNotContainingItem } from './assertArrayIsNotContainingItem'; +import { assertArrayIsNotEmpty } from './assertArrayIsNotEmpty'; +import { report } from '../../report'; + +const getAssertionsForArray = function (actual: TContent[]): ArrayAssertions { + return { + containing (item: TContent): void { + report(assertArrayIsContainingItem(actual, item)); + }, + containingAllOf (iterable: Iterable): void { + report(assertArrayIsContainingAllOfIterable(actual, iterable)); + }, + containingAnyOf (iterable: Iterable): void { + report(assertArrayIsContainingAnyOfIterable(actual, iterable)); + }, + empty (): void { + report(assertArrayIsEmpty(actual)); + } + }; +}; + +const getNegatedAssertionsForArray = function (actual: TContent[]): ArrayAssertions { + return { + containing (item: TContent): void { + report(assertArrayIsNotContainingItem(actual, item)); + }, + containingAllOf (iterable: Iterable): void { + report(assertArrayIsNotContainingAllOfIterable(actual, iterable)); + }, + containingAnyOf (iterable: Iterable): void { + report(assertArrayIsNotContainingAnyOfIterable(actual, iterable)); + }, + empty (): void { + report(assertArrayIsNotEmpty(actual)); + } + }; +}; + +export { + getAssertionsForArray, + getNegatedAssertionsForArray +}; diff --git a/lib/assertions/forFunctions/AssertThatForFunction.ts b/lib/assertions/forFunctions/AssertThatForFunction.ts new file mode 100644 index 0000000..1a23bd9 --- /dev/null +++ b/lib/assertions/forFunctions/AssertThatForFunction.ts @@ -0,0 +1,14 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { FunctionAssertions } from './FunctionAssertions'; + +/* eslint-disable @typescript-eslint/ban-types */ +type AssertThatForFunction = (actual: Function) => { + is: CommonAssertions & FunctionAssertions & { + not: CommonAssertions & FunctionAssertions; + }; +}; +/* eslint-enable @typescript-eslint/ban-types */ + +export type { + AssertThatForFunction +}; diff --git a/lib/assertions/forFunctions/FunctionAssertions.ts b/lib/assertions/forFunctions/FunctionAssertions.ts new file mode 100644 index 0000000..d73c3f3 --- /dev/null +++ b/lib/assertions/forFunctions/FunctionAssertions.ts @@ -0,0 +1,8 @@ +interface FunctionAssertions { + throwing: (expected?: string | RegExp | ((ex: TError) => boolean)) => void; + throwingAsync: (expected?: string | RegExp | ((ex: TError) => boolean)) => Promise; +} + +export type { + FunctionAssertions +}; diff --git a/lib/assertions/forFunctions/assertFunctionIsNotThrowing.ts b/lib/assertions/forFunctions/assertFunctionIsNotThrowing.ts new file mode 100644 index 0000000..b9acb99 --- /dev/null +++ b/lib/assertions/forFunctions/assertFunctionIsNotThrowing.ts @@ -0,0 +1,50 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +/* eslint-disable @typescript-eslint/ban-types */ +const assertFunctionIsNotThrowing = function ( + actual: Function, + expected?: string | RegExp | ((ex: TError) => boolean) +): Result { + try { + actual(); + // eslint-disable-next-line @typescript-eslint/no-implicit-any-catch + } catch (ex: any) { + if (expected === undefined) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + actual: `Error message:\n${ex.message}` + })); + } + + if (expected instanceof RegExp && expected.test(ex.message)) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The message should not have matched:\n${expected.toString()}`, + actual: `Error message:\n${ex.message}` + })); + } + if (typeof expected === 'function' && expected(ex)) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The exception should not have fulfilled a predicate.`, + actual: `Error message:\n${ex.message}` + })); + } + + if (typeof expected === 'string' && ex.message === expected) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The message should not have been:\n${expected}`, + actual: `Error message:\n${ex.message}` + })); + } + } + + return value(); +}; +/* eslint-enable @typescript-eslint/ban-types */ + +export { + assertFunctionIsNotThrowing +}; diff --git a/lib/assertions/forFunctions/assertFunctionIsNotThrowingAsync.ts b/lib/assertions/forFunctions/assertFunctionIsNotThrowingAsync.ts new file mode 100644 index 0000000..0fccf2a --- /dev/null +++ b/lib/assertions/forFunctions/assertFunctionIsNotThrowingAsync.ts @@ -0,0 +1,58 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +/* eslint-disable @typescript-eslint/ban-types */ +const assertFunctionIsNotThrowingAsync = async function ( + actual: Function, + expected?: string | RegExp | ((ex: TError) => boolean) +): Promise> { + try { + const promise = actual(); + + if (!(promise instanceof Promise)) { + return error(new AssertionFailed({ + message: 'The function did not return a Promise.' + })); + } + + await promise; + // eslint-disable-next-line @typescript-eslint/no-implicit-any-catch + } catch (ex: any) { + if (expected === undefined) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + actual: `Error message:\n${ex.message}` + })); + } + + if (expected instanceof RegExp && expected.test(ex.message)) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The message should not have matched:\n${expected.toString()}`, + actual: `Error message:\n${ex.message}` + })); + } + if (typeof expected === 'function' && expected(ex)) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The exception should not have fulfilled a predicate.`, + actual: `Error message:\n${ex.message}` + })); + } + + if (typeof expected === 'string' && ex.message === expected) { + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The message should not have been:\n${expected}`, + actual: `Error message:\n${ex.message}` + })); + } + } + + return value(); +}; +/* eslint-enable @typescript-eslint/ban-types */ + +export { + assertFunctionIsNotThrowingAsync +}; diff --git a/lib/assertions/forFunctions/assertFunctionIsThrowing.ts b/lib/assertions/forFunctions/assertFunctionIsThrowing.ts new file mode 100644 index 0000000..a843ffb --- /dev/null +++ b/lib/assertions/forFunctions/assertFunctionIsThrowing.ts @@ -0,0 +1,59 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +/* eslint-disable @typescript-eslint/ban-types */ +const assertFunctionIsThrowing = function ( + actual: Function, + expected?: string | RegExp | ((ex: TError) => boolean) +): Result { + try { + actual(); + // eslint-disable-next-line @typescript-eslint/no-implicit-any-catch + } catch (ex: any) { + if (expected === undefined) { + return value(); + } + + if (expected instanceof RegExp) { + if (expected.test(ex.message)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The message should have matched:\n${expected.toString()}`, + actual: `Error message:\n${ex.message}` + })); + } + if (typeof expected === 'function') { + if (expected(ex)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The exception should have fulfilled a predicate.`, + actual: `Error message:\n${ex.message}` + })); + } + + if (ex.message === expected) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The message should have been:\n${expected}`, + actual: `Error message:\n${ex.message}` + })); + } + + return error(new AssertionFailed({ + message: 'The function did not throw an exception.' + })); +}; +/* eslint-enable @typescript-eslint/ban-types */ + +export { + assertFunctionIsThrowing +}; diff --git a/lib/assertions/forFunctions/assertFunctionIsThrowingAsync.ts b/lib/assertions/forFunctions/assertFunctionIsThrowingAsync.ts new file mode 100644 index 0000000..e146562 --- /dev/null +++ b/lib/assertions/forFunctions/assertFunctionIsThrowingAsync.ts @@ -0,0 +1,67 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +/* eslint-disable @typescript-eslint/ban-types */ +const assertFunctionIsThrowingAsync = async function ( + actual: Function, + expected?: string | RegExp | ((ex: TError) => boolean) +): Promise> { + try { + const promise = actual(); + + if (!(promise instanceof Promise)) { + return error(new AssertionFailed({ + message: 'The function did not return a Promise.' + })); + } + + await promise; + // eslint-disable-next-line @typescript-eslint/no-implicit-any-catch + } catch (ex: any) { + if (expected === undefined) { + return value(); + } + + if (expected instanceof RegExp) { + if (expected.test(ex.message)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The message should have matched:\n${expected.toString()}`, + actual: `Error message:\n${ex.message}` + })); + } + if (typeof expected === 'function') { + if (expected(ex)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The exception should have fulfilled a predicate.`, + actual: `Error message:\n${ex.message}` + })); + } + + if (ex.message === expected) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The message should have been:\n${expected}`, + actual: `Error message:\n${ex.message}` + })); + } + + return error(new AssertionFailed({ + message: 'The function did not throw an asynchronous exception.' + })); +}; +/* eslint-enable @typescript-eslint/ban-types */ + +export { + assertFunctionIsThrowingAsync +}; diff --git a/lib/assertions/forFunctions/assertions.ts b/lib/assertions/forFunctions/assertions.ts new file mode 100644 index 0000000..771d8de --- /dev/null +++ b/lib/assertions/forFunctions/assertions.ts @@ -0,0 +1,47 @@ +import { assertFunctionIsNotThrowing } from './assertFunctionIsNotThrowing'; +import { assertFunctionIsNotThrowingAsync } from './assertFunctionIsNotThrowingAsync'; +import { assertFunctionIsThrowing } from './assertFunctionIsThrowing'; +import { assertFunctionIsThrowingAsync } from './assertFunctionIsThrowingAsync'; +import { FunctionAssertions } from './FunctionAssertions'; +import { report } from '../../report'; + +const getAssertionsForFunction = function ( +// eslint-disable-next-line @typescript-eslint/ban-types + actual: Function +): FunctionAssertions { + return { + throwing ( + expected?: string | RegExp | ((ex: TError) => boolean) + ): void { + report(assertFunctionIsThrowing(actual, expected)); + }, + async throwingAsync ( + expected?: string | RegExp | ((ex: TError) => boolean) + ): Promise { + report(await assertFunctionIsThrowingAsync(actual, expected)); + } + }; +}; + +const getNegatedAssertionsForFunction = function ( +// eslint-disable-next-line @typescript-eslint/ban-types + actual: Function +): FunctionAssertions { + return { + throwing ( + expected?: string | RegExp | ((ex: TError) => boolean) + ): void { + report(assertFunctionIsNotThrowing(actual, expected)); + }, + async throwingAsync ( + expected?: string | RegExp | ((ex: TError) => boolean) + ): Promise { + report(await assertFunctionIsNotThrowingAsync(actual, expected)); + } + }; +}; + +export { + getAssertionsForFunction, + getNegatedAssertionsForFunction +}; diff --git a/lib/assertions/forMaps/AssertThatForMap.ts b/lib/assertions/forMaps/AssertThatForMap.ts new file mode 100644 index 0000000..c0c46b0 --- /dev/null +++ b/lib/assertions/forMaps/AssertThatForMap.ts @@ -0,0 +1,12 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { MapAssertions } from './MapAssertions'; + +type AssertThatForMap = (actual: Map) => { + is: CommonAssertions> & MapAssertions & { + not: CommonAssertions> & MapAssertions; + }; +}; + +export type { + AssertThatForMap +}; diff --git a/lib/assertions/forMaps/MapAssertions.ts b/lib/assertions/forMaps/MapAssertions.ts new file mode 100644 index 0000000..d8cea0f --- /dev/null +++ b/lib/assertions/forMaps/MapAssertions.ts @@ -0,0 +1,9 @@ +interface MapAssertions { + atLeast: (expected: Map) => void; + atMost: (expected: Map) => void; + empty: () => void; +} + +export type { + MapAssertions +}; diff --git a/lib/assertions/forMaps/assertMapIsAtLeastMap.ts b/lib/assertions/forMaps/assertMapIsAtLeastMap.ts new file mode 100644 index 0000000..3d89698 --- /dev/null +++ b/lib/assertions/forMaps/assertMapIsAtLeastMap.ts @@ -0,0 +1,39 @@ +import { AssertionFailed } from '../../errors'; +import { compareMaps } from '../../comparisons/forMaps/compareMaps'; +import { dispel } from '../../dispel/dispel'; +import { findMapDiffOmissions } from '../../diffs/forMaps/findMapDiffOmissions'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertMapIsAtLeastMap = function ( + actual: Map, + expected: Map +): Result { + const diff = compareMaps( + dispel(actual), + dispel(expected) + ); + + if (isEqualDiff(diff)) { + return value(); + } + + const diffWithOnlyOmissions = findMapDiffOmissions(diff); + + if (diffWithOnlyOmissions.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The expected map is not entirely contained in the actual map.', + actual: prettyPrint(actual), + expected: `To entirely contain:\n${prettyPrint(expected)}`, + diff: `The following sub-map shows relevant changes between actual and expected:\n${prettyPrintDiff(diffWithOnlyOmissions)}` + })); +}; + +export { + assertMapIsAtLeastMap +}; diff --git a/lib/assertions/forMaps/assertMapIsAtMostMap.ts b/lib/assertions/forMaps/assertMapIsAtMostMap.ts new file mode 100644 index 0000000..8d0a9a8 --- /dev/null +++ b/lib/assertions/forMaps/assertMapIsAtMostMap.ts @@ -0,0 +1,39 @@ +import { AssertionFailed } from '../../errors'; +import { compareMaps } from '../../comparisons/forMaps/compareMaps'; +import { dispel } from '../../dispel/dispel'; +import { findMapDiffAdditions } from '../../diffs/forMaps/findMapDiffAdditions'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertMapIsAtMostMap = function ( + actual: Map, + expected: Map +): Result { + const diff = compareMaps( + dispel(actual), + dispel(expected) + ); + + if (isEqualDiff(diff)) { + return value(); + } + + const diffWithOnlyAdditions = findMapDiffAdditions(diff); + + if (diffWithOnlyAdditions.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The actual map is not entirely contained in the expected map.', + actual: prettyPrint(actual), + expected: `To be entirely contained in:\n${prettyPrint(expected)}`, + diff: `The following sub-map shows relevant changes between actual and expected:\n${prettyPrintDiff(diffWithOnlyAdditions)}` + })); +}; + +export { + assertMapIsAtMostMap +}; diff --git a/lib/assertions/forMaps/assertMapIsEmpty.ts b/lib/assertions/forMaps/assertMapIsEmpty.ts new file mode 100644 index 0000000..2950ac2 --- /dev/null +++ b/lib/assertions/forMaps/assertMapIsEmpty.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertMapIsEmpty = function ( + actual: Map +): Result { + if (actual.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The map is not empty.', + actual: prettyPrint(actual) + })); +}; + +export { + assertMapIsEmpty +}; diff --git a/lib/assertions/forMaps/assertMapIsNotAtLeastMap.ts b/lib/assertions/forMaps/assertMapIsNotAtLeastMap.ts new file mode 100644 index 0000000..a3b21d1 --- /dev/null +++ b/lib/assertions/forMaps/assertMapIsNotAtLeastMap.ts @@ -0,0 +1,30 @@ +import { AssertionFailed } from '../../errors'; +import { compareMaps } from '../../comparisons/forMaps/compareMaps'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertMapIsNotAtLeastMap = function ( + actual: Map, + expected: Map +): Result { + const diff = compareMaps( + dispel(actual), + dispel(expected) + ); + + if (!isEqualDiff(diff) && (diff.omissions.size > 0 || diff.changes.size > 0)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The expected map is entirely contained in the actual map.', + actual: prettyPrint(actual), + expected: `To not entirely contain:\n${prettyPrint(expected)}` + })); +}; + +export { + assertMapIsNotAtLeastMap +}; diff --git a/lib/assertions/forMaps/assertMapIsNotAtMostMap.ts b/lib/assertions/forMaps/assertMapIsNotAtMostMap.ts new file mode 100644 index 0000000..f60eee1 --- /dev/null +++ b/lib/assertions/forMaps/assertMapIsNotAtMostMap.ts @@ -0,0 +1,30 @@ +import { AssertionFailed } from '../../errors'; +import { compareMaps } from '../../comparisons/forMaps/compareMaps'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertMapIsNotAtMostMap = function ( + actual: Map, + expected: Map +): Result { + const diff = compareMaps( + dispel(actual), + dispel(expected) + ); + + if (!isEqualDiff(diff) && (diff.additions.size > 0 || diff.changes.size > 0)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The actual map is entirely contained in the expected map.', + actual: prettyPrint(actual), + expected: `To not be entirely contained in:\n${prettyPrint(expected)}` + })); +}; + +export { + assertMapIsNotAtMostMap +}; diff --git a/lib/assertions/forMaps/assertMapIsNotEmpty.ts b/lib/assertions/forMaps/assertMapIsNotEmpty.ts new file mode 100644 index 0000000..62fb6e9 --- /dev/null +++ b/lib/assertions/forMaps/assertMapIsNotEmpty.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertMapIsNotEmpty = function ( + actual: Map +): Result { + if (actual.size > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The map is empty.' + })); +}; + +export { + assertMapIsNotEmpty +}; diff --git a/lib/assertions/forMaps/assertions.ts b/lib/assertions/forMaps/assertions.ts new file mode 100644 index 0000000..d5688be --- /dev/null +++ b/lib/assertions/forMaps/assertions.ts @@ -0,0 +1,45 @@ +import { assertMapIsAtLeastMap } from './assertMapIsAtLeastMap'; +import { assertMapIsAtMostMap } from './assertMapIsAtMostMap'; +import { assertMapIsEmpty } from './assertMapIsEmpty'; +import { assertMapIsNotAtLeastMap } from './assertMapIsNotAtLeastMap'; +import { assertMapIsNotAtMostMap } from './assertMapIsNotAtMostMap'; +import { assertMapIsNotEmpty } from './assertMapIsNotEmpty'; +import { MapAssertions } from './MapAssertions'; +import { report } from '../../report'; + +const getAssertionsForMap = function ( + actual: Map +): MapAssertions { + return { + atLeast (expected): void { + report(assertMapIsAtLeastMap(actual, expected)); + }, + atMost (expected): void { + report(assertMapIsAtMostMap(actual, expected)); + }, + empty (): void { + report(assertMapIsEmpty(actual)); + } + }; +}; + +const getNegatedAssertionsForMap = function ( + actual: Map +): MapAssertions { + return { + atLeast (expected): void { + report(assertMapIsNotAtLeastMap(actual, expected)); + }, + atMost (expected): void { + report(assertMapIsNotAtMostMap(actual, expected)); + }, + empty (): void { + report(assertMapIsNotEmpty(actual)); + } + }; +}; + +export { + getAssertionsForMap, + getNegatedAssertionsForMap +}; diff --git a/lib/assertions/forNumbers/AssertThatForNumber.ts b/lib/assertions/forNumbers/AssertThatForNumber.ts new file mode 100644 index 0000000..ad8611b --- /dev/null +++ b/lib/assertions/forNumbers/AssertThatForNumber.ts @@ -0,0 +1,12 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { NumberAssertions } from './NumberAssertions'; + +type AssertThatForNumber = (actual: number) => { + is: CommonAssertions & NumberAssertions & { + not: CommonAssertions & NumberAssertions; + }; +}; + +export type { + AssertThatForNumber +}; diff --git a/lib/assertions/forNumbers/NumberAssertions.ts b/lib/assertions/forNumbers/NumberAssertions.ts new file mode 100644 index 0000000..6905ab2 --- /dev/null +++ b/lib/assertions/forNumbers/NumberAssertions.ts @@ -0,0 +1,11 @@ +interface NumberAssertions { + greaterThan: (expected: number) => void; + lessThan: (expected: number) => void; + atLeast: (expected: number) => void; + atMost: (expected: number) => void; + NaN: () => void; +} + +export type { + NumberAssertions +}; diff --git a/lib/assertions/forNumbers/assertNumberIsAtLeastNumber.ts b/lib/assertions/forNumbers/assertNumberIsAtLeastNumber.ts new file mode 100644 index 0000000..04b741a --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsAtLeastNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsAtLeastNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (isEqualDiff(diff) || diff.difference > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is less than the expected number.', + actual: prettyPrint(actual), + expected: `To be at least:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsAtLeastNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsAtMostNumber.ts b/lib/assertions/forNumbers/assertNumberIsAtMostNumber.ts new file mode 100644 index 0000000..3a7561f --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsAtMostNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsAtMostNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (isEqualDiff(diff) || diff.difference < 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is greater than the expected number.', + actual: prettyPrint(actual), + expected: `To be at most:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsAtMostNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsGreaterThanNumber.ts b/lib/assertions/forNumbers/assertNumberIsGreaterThanNumber.ts new file mode 100644 index 0000000..da3b6ee --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsGreaterThanNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsGreaterThanNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (!isEqualDiff(diff) && diff.difference > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To be greater than:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsGreaterThanNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsLessThanNumber.ts b/lib/assertions/forNumbers/assertNumberIsLessThanNumber.ts new file mode 100644 index 0000000..577b966 --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsLessThanNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsLessThanNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (!isEqualDiff(diff) && diff.difference < 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To be less than:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsLessThanNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsNaN.ts b/lib/assertions/forNumbers/assertNumberIsNaN.ts new file mode 100644 index 0000000..6ef0e2f --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsNaN.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsNaN = function ( + actual: number +): Result { + if (Number.isNaN(actual)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is not Nan.', + actual: prettyPrint(actual) + })); +}; + +export { + assertNumberIsNaN +}; diff --git a/lib/assertions/forNumbers/assertNumberIsNotAtLeastNumber.ts b/lib/assertions/forNumbers/assertNumberIsNotAtLeastNumber.ts new file mode 100644 index 0000000..914d32f --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsNotAtLeastNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsNotAtLeastNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (!isEqualDiff(diff) && diff.difference < 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is greater than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To not be at least:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsNotAtLeastNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsNotAtMostNumber.ts b/lib/assertions/forNumbers/assertNumberIsNotAtMostNumber.ts new file mode 100644 index 0000000..970edc9 --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsNotAtMostNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsNotAtMostNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (!isEqualDiff(diff) && diff.difference > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To not be at most:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsNotAtMostNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsNotGreaterThanNumber.ts b/lib/assertions/forNumbers/assertNumberIsNotGreaterThanNumber.ts new file mode 100644 index 0000000..b9962bf --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsNotGreaterThanNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsNotGreaterThanNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (isEqualDiff(diff) || diff.difference < 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is greater than the expected number.', + actual: prettyPrint(actual), + expected: `To not be greater than:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsNotGreaterThanNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsNotLessThanNumber.ts b/lib/assertions/forNumbers/assertNumberIsNotLessThanNumber.ts new file mode 100644 index 0000000..7f82c4c --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsNotLessThanNumber.ts @@ -0,0 +1,26 @@ +import { AssertionFailed } from '../../errors'; +import { compareNumbers } from '../../comparisons/forNumbers/compareNumbers'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsNotLessThanNumber = function ( + actual: number, + expected: number +): Result { + const diff = compareNumbers(actual, expected); + + if (isEqualDiff(diff) || diff.difference > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is less than the expected number.', + actual: prettyPrint(actual), + expected: `To not be less than:\n${prettyPrint(expected)}` + })); +}; + +export { + assertNumberIsNotLessThanNumber +}; diff --git a/lib/assertions/forNumbers/assertNumberIsNotNaN.ts b/lib/assertions/forNumbers/assertNumberIsNotNaN.ts new file mode 100644 index 0000000..d43a245 --- /dev/null +++ b/lib/assertions/forNumbers/assertNumberIsNotNaN.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertNumberIsNotNaN = function ( + actual: number +): Result { + if (!Number.isNaN(actual)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The number is NaN.' + })); +}; + +export { + assertNumberIsNotNaN +}; diff --git a/lib/assertions/forNumbers/assertions.ts b/lib/assertions/forNumbers/assertions.ts new file mode 100644 index 0000000..8cde4f7 --- /dev/null +++ b/lib/assertions/forNumbers/assertions.ts @@ -0,0 +1,57 @@ +import { assertNumberIsAtLeastNumber } from './assertNumberIsAtLeastNumber'; +import { assertNumberIsAtMostNumber } from './assertNumberIsAtMostNumber'; +import { assertNumberIsGreaterThanNumber } from './assertNumberIsGreaterThanNumber'; +import { assertNumberIsLessThanNumber } from './assertNumberIsLessThanNumber'; +import { assertNumberIsNaN } from './assertNumberIsNaN'; +import { assertNumberIsNotAtLeastNumber } from './assertNumberIsNotAtLeastNumber'; +import { assertNumberIsNotAtMostNumber } from './assertNumberIsNotAtMostNumber'; +import { assertNumberIsNotGreaterThanNumber } from './assertNumberIsNotGreaterThanNumber'; +import { assertNumberIsNotLessThanNumber } from './assertNumberIsNotLessThanNumber'; +import { assertNumberIsNotNaN } from './assertNumberIsNotNaN'; +import { NumberAssertions } from './NumberAssertions'; +import { report } from '../../report'; + +const getAssertionsForNumber = function (actual: number): NumberAssertions { + return { + greaterThan (expected): void { + report(assertNumberIsGreaterThanNumber(actual, expected)); + }, + lessThan (expected): void { + report(assertNumberIsLessThanNumber(actual, expected)); + }, + atLeast (expected): void { + report(assertNumberIsAtLeastNumber(actual, expected)); + }, + atMost (expected): void { + report(assertNumberIsAtMostNumber(actual, expected)); + }, + NaN (): void { + report(assertNumberIsNaN(actual)); + } + }; +}; + +const getNegatedAssertionsForNumber = function (actual: number): NumberAssertions { + return { + greaterThan (expected): void { + report(assertNumberIsNotGreaterThanNumber(actual, expected)); + }, + lessThan (expected): void { + report(assertNumberIsNotLessThanNumber(actual, expected)); + }, + atLeast (expected): void { + report(assertNumberIsNotAtLeastNumber(actual, expected)); + }, + atMost (expected): void { + report(assertNumberIsNotAtMostNumber(actual, expected)); + }, + NaN (): void { + report(assertNumberIsNotNaN(actual)); + } + }; +}; + +export { + getAssertionsForNumber, + getNegatedAssertionsForNumber +}; diff --git a/lib/assertions/forObjects/AssertThatForObject.ts b/lib/assertions/forObjects/AssertThatForObject.ts new file mode 100644 index 0000000..4b12450 --- /dev/null +++ b/lib/assertions/forObjects/AssertThatForObject.ts @@ -0,0 +1,12 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { ObjectAssertions } from './ObjectAssertions'; + +type AssertThatForObject = (actual: object) => { + is: CommonAssertions & ObjectAssertions & { + not: CommonAssertions & ObjectAssertions; + }; +}; + +export type { + AssertThatForObject +}; diff --git a/lib/assertions/forObjects/ObjectAssertions.ts b/lib/assertions/forObjects/ObjectAssertions.ts new file mode 100644 index 0000000..ff5ee62 --- /dev/null +++ b/lib/assertions/forObjects/ObjectAssertions.ts @@ -0,0 +1,11 @@ +interface ObjectAssertions { + atLeast: (expected: object) => void; + atMost: (expected: object) => void; + // eslint-disable-next-line @typescript-eslint/ban-types + instanceOf: (expected: Function) => void; + empty: () => void; +} + +export type { + ObjectAssertions +}; diff --git a/lib/assertions/forObjects/assertObjectIsAtLeastObject.ts b/lib/assertions/forObjects/assertObjectIsAtLeastObject.ts new file mode 100644 index 0000000..dade34c --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsAtLeastObject.ts @@ -0,0 +1,39 @@ +import { AssertionFailed } from '../../errors'; +import { compareObjects } from '../../comparisons/forObjects/compareObjects'; +import { dispel } from '../../dispel/dispel'; +import { findObjectDiffOmissions } from '../../diffs/forObjects/findObjectDiffOmissions'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsAtLeastObject = function ( + actual: object, + expected: object +): Result { + const diff = compareObjects( + dispel(actual), + dispel(expected) + ); + + if (isEqualDiff(diff)) { + return value(); + } + + const diffWithOnlyOmissions = findObjectDiffOmissions(diff); + + if (diffWithOnlyOmissions.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The expected object is not entirely contained in the actual object.', + actual: prettyPrint(actual), + expected: `To entirely contain:\n${prettyPrint(expected)}`, + diff: `The following sub-object shows relevant changes between actual and expected:\n${prettyPrintDiff(diffWithOnlyOmissions)}` + })); +}; + +export { + assertObjectIsAtLeastObject +}; diff --git a/lib/assertions/forObjects/assertObjectIsAtMostObject.ts b/lib/assertions/forObjects/assertObjectIsAtMostObject.ts new file mode 100644 index 0000000..2464b28 --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsAtMostObject.ts @@ -0,0 +1,39 @@ +import { AssertionFailed } from '../../errors'; +import { compareObjects } from '../../comparisons/forObjects/compareObjects'; +import { dispel } from '../../dispel/dispel'; +import { findObjectDiffAdditions } from '../../diffs/forObjects/findObjectDiffAdditions'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsAtMostObject = function ( + actual: object, + expected: object +): Result { + const diff = compareObjects( + dispel(actual), + dispel(expected) + ); + + if (isEqualDiff(diff)) { + return value(); + } + + const diffWithOnlyAdditions = findObjectDiffAdditions(diff); + + if (diffWithOnlyAdditions.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The actual object is not entirely contained in the expected object.', + actual: prettyPrint(actual), + expected: `To be entirely contained in:\n${prettyPrint(expected)}`, + diff: `The following sub-object shows relevant changes between actual and expected:\n${prettyPrintDiff(diffWithOnlyAdditions)}` + })); +}; + +export { + assertObjectIsAtMostObject +}; diff --git a/lib/assertions/forObjects/assertObjectIsEmpty.ts b/lib/assertions/forObjects/assertObjectIsEmpty.ts new file mode 100644 index 0000000..e995544 --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsEmpty.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsEmpty = function ( + actual: object +): Result { + if (Object.keys(actual).length === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The object is not empty.', + actual: prettyPrint(actual) + })); +}; + +export { + assertObjectIsEmpty +}; diff --git a/lib/assertions/forObjects/assertObjectIsInstanceOfClass.ts b/lib/assertions/forObjects/assertObjectIsInstanceOfClass.ts new file mode 100644 index 0000000..ed0f7d9 --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsInstanceOfClass.ts @@ -0,0 +1,23 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsInstanceOfClass = function ( + actual: object, + // eslint-disable-next-line @typescript-eslint/ban-types + expected: Function +): Result { + if (actual instanceof expected) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The object is not an instance of the expected class.', + actual: prettyPrint(actual), + expected: `To be an instance of:\n${expected.name}` + })); +}; + +export { + assertObjectIsInstanceOfClass +}; diff --git a/lib/assertions/forObjects/assertObjectIsNotAtLeastObject.ts b/lib/assertions/forObjects/assertObjectIsNotAtLeastObject.ts new file mode 100644 index 0000000..9aaab10 --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsNotAtLeastObject.ts @@ -0,0 +1,36 @@ +import { AssertionFailed } from '../../errors'; +import { compareObjects } from '../../comparisons/forObjects/compareObjects'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsNotAtLeastObject = function ( + actual: object, + expected: object +): Result { + const diff = compareObjects( + dispel(actual), + dispel(expected) + ); + + if ( + !isEqualDiff(diff) && + ( + Object.keys(diff.omissions).length > 0 || + Object.keys(diff.changes).length > 0 + ) + ) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The expected object is entirely contained in the actual object.', + actual: prettyPrint(actual), + expected: `To not entirely contain:\n${prettyPrint(expected)}` + })); +}; + +export { + assertObjectIsNotAtLeastObject +}; diff --git a/lib/assertions/forObjects/assertObjectIsNotAtMostObject.ts b/lib/assertions/forObjects/assertObjectIsNotAtMostObject.ts new file mode 100644 index 0000000..80cabba --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsNotAtMostObject.ts @@ -0,0 +1,36 @@ +import { AssertionFailed } from '../../errors'; +import { compareObjects } from '../../comparisons/forObjects/compareObjects'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsNotAtMostObject = function ( + actual: object, + expected: object +): Result { + const diff = compareObjects( + dispel(actual), + dispel(expected) + ); + + if ( + !isEqualDiff(diff) && + ( + Object.keys(diff.additions).length > 0 || + Object.keys(diff.changes).length > 0 + ) + ) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The actual object is entirely contained in the expected object.', + actual: prettyPrint(actual), + expected: `To not be entirely contained in:\n${prettyPrint(expected)}` + })); +}; + +export { + assertObjectIsNotAtMostObject +}; diff --git a/lib/assertions/forObjects/assertObjectIsNotEmpty.ts b/lib/assertions/forObjects/assertObjectIsNotEmpty.ts new file mode 100644 index 0000000..b523f98 --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsNotEmpty.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsNotEmpty = function ( + actual: object +): Result { + if (Object.keys(actual).length > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The object is empty.' + })); +}; + +export { + assertObjectIsNotEmpty +}; diff --git a/lib/assertions/forObjects/assertObjectIsNotInstanceOfClass.ts b/lib/assertions/forObjects/assertObjectIsNotInstanceOfClass.ts new file mode 100644 index 0000000..9933643 --- /dev/null +++ b/lib/assertions/forObjects/assertObjectIsNotInstanceOfClass.ts @@ -0,0 +1,23 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertObjectIsNotInstanceOfClass = function ( + actual: object, + // eslint-disable-next-line @typescript-eslint/ban-types + expected: Function +): Result { + if (!(actual instanceof expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The object is an instance of the class.', + actual: prettyPrint(actual), + expected: `To not be an instance of:\n${expected.name}` + })); +}; + +export { + assertObjectIsNotInstanceOfClass +}; diff --git a/lib/assertions/forObjects/assertions.ts b/lib/assertions/forObjects/assertions.ts new file mode 100644 index 0000000..505a475 --- /dev/null +++ b/lib/assertions/forObjects/assertions.ts @@ -0,0 +1,49 @@ +import { assertObjectIsAtLeastObject } from './assertObjectIsAtLeastObject'; +import { assertObjectIsAtMostObject } from './assertObjectIsAtMostObject'; +import { assertObjectIsEmpty } from './assertObjectIsEmpty'; +import { assertObjectIsInstanceOfClass } from './assertObjectIsInstanceOfClass'; +import { assertObjectIsNotAtLeastObject } from './assertObjectIsNotAtLeastObject'; +import { assertObjectIsNotAtMostObject } from './assertObjectIsNotAtMostObject'; +import { assertObjectIsNotEmpty } from './assertObjectIsNotEmpty'; +import { assertObjectIsNotInstanceOfClass } from './assertObjectIsNotInstanceOfClass'; +import { ObjectAssertions } from './ObjectAssertions'; +import { report } from '../../report'; + +const getAssertionsForObject = function (actual: object): ObjectAssertions { + return { + atLeast (expected): void { + report(assertObjectIsAtLeastObject(actual, expected)); + }, + atMost (expected): void { + report(assertObjectIsAtMostObject(actual, expected)); + }, + instanceOf (expected): void { + report(assertObjectIsInstanceOfClass(actual, expected)); + }, + empty (): void { + report(assertObjectIsEmpty(actual)); + } + }; +}; + +const getNegatedAssertionsForObject = function (actual: object): ObjectAssertions { + return { + atLeast (expected): void { + report(assertObjectIsNotAtLeastObject(actual, expected)); + }, + atMost (expected): void { + report(assertObjectIsNotAtMostObject(actual, expected)); + }, + instanceOf (expected): void { + report(assertObjectIsNotInstanceOfClass(actual, expected)); + }, + empty (): void { + report(assertObjectIsNotEmpty(actual)); + } + }; +}; + +export { + getAssertionsForObject, + getNegatedAssertionsForObject +}; diff --git a/lib/assertions/forResults/AssertThatForResult.ts b/lib/assertions/forResults/AssertThatForResult.ts new file mode 100644 index 0000000..88e954f --- /dev/null +++ b/lib/assertions/forResults/AssertThatForResult.ts @@ -0,0 +1,13 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { Result } from 'defekt'; +import { ResultAssertions } from './ResultAssertions'; + +type AssertThatForResult = (actual: Result) => { + is: CommonAssertions> & ResultAssertions & { + not: CommonAssertions> & ResultAssertions; + }; +}; + +export type { + AssertThatForResult +}; diff --git a/lib/assertions/forResults/ResultAssertions.ts b/lib/assertions/forResults/ResultAssertions.ts new file mode 100644 index 0000000..b2bdff2 --- /dev/null +++ b/lib/assertions/forResults/ResultAssertions.ts @@ -0,0 +1,9 @@ +interface ResultAssertions { + aValue: () => void; + anError: () => void; + anErrorWithMessage: (expected: string) => void; +} + +export type { + ResultAssertions +}; diff --git a/lib/assertions/forResults/assertResultIsAValue.ts b/lib/assertions/forResults/assertResultIsAValue.ts new file mode 100644 index 0000000..fa953c3 --- /dev/null +++ b/lib/assertions/forResults/assertResultIsAValue.ts @@ -0,0 +1,21 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +// eslint-disable-next-line @typescript-eslint/naming-convention +const assertResultIsAValue = function ( + actual: Result +): Result { + if (actual.hasValue()) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The result is an error.', + actual: prettyPrint(actual) + })); +}; + +export { + assertResultIsAValue +}; diff --git a/lib/assertions/forResults/assertResultIsAnError.ts b/lib/assertions/forResults/assertResultIsAnError.ts new file mode 100644 index 0000000..2941347 --- /dev/null +++ b/lib/assertions/forResults/assertResultIsAnError.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertResultIsAnError = function ( + actual: Result +): Result { + if (actual.hasError()) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The result is a value.', + actual: prettyPrint(actual) + })); +}; + +export { + assertResultIsAnError +}; diff --git a/lib/assertions/forResults/assertResultIsAnErrorWithMessage.ts b/lib/assertions/forResults/assertResultIsAnErrorWithMessage.ts new file mode 100644 index 0000000..86f253e --- /dev/null +++ b/lib/assertions/forResults/assertResultIsAnErrorWithMessage.ts @@ -0,0 +1,33 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrintString } from '../../prettyPrint/forStrings/prettyPrintString'; +import { error, Result, value } from 'defekt'; + +const assertResultIsAnErrorWithMessage = function ( + actual: Result, + expected: string +): Result { + if (!actual.hasError()) { + return error(new AssertionFailed({ + message: 'The result is a value.', + expected: `To be an error with message ${prettyPrintString(expected)}.` + })); + } + + const diff = compare(actual.error.message, expected); + + if (isEqualDiff(diff)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The error does not have the expected message.', + expected: `To have the message ${prettyPrintString(expected)}`, + actual: prettyPrintString(actual.error.message) + })); +}; + +export { + assertResultIsAnErrorWithMessage +}; diff --git a/lib/assertions/forResults/assertResultIsNotAValue.ts b/lib/assertions/forResults/assertResultIsNotAValue.ts new file mode 100644 index 0000000..34029c2 --- /dev/null +++ b/lib/assertions/forResults/assertResultIsNotAValue.ts @@ -0,0 +1,21 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +// eslint-disable-next-line @typescript-eslint/naming-convention +const assertResultIsNotAValue = function ( + actual: Result +): Result { + if (!actual.hasValue()) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The result is a value.', + actual: prettyPrint(actual) + })); +}; + +export { + assertResultIsNotAValue +}; diff --git a/lib/assertions/forResults/assertResultIsNotAnError.ts b/lib/assertions/forResults/assertResultIsNotAnError.ts new file mode 100644 index 0000000..dde955b --- /dev/null +++ b/lib/assertions/forResults/assertResultIsNotAnError.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertResultIsNotAnError = function ( + actual: Result +): Result { + if (!actual.hasError()) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The result is an error.', + actual: prettyPrint(actual) + })); +}; + +export { + assertResultIsNotAnError +}; diff --git a/lib/assertions/forResults/assertResultIsNotAnErrorWithMessage.ts b/lib/assertions/forResults/assertResultIsNotAnErrorWithMessage.ts new file mode 100644 index 0000000..71b6386 --- /dev/null +++ b/lib/assertions/forResults/assertResultIsNotAnErrorWithMessage.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrintString } from '../../prettyPrint/forStrings/prettyPrintString'; +import { error, Result, value } from 'defekt'; + +const assertResultIsNotAnErrorWithMessage = function ( + actual: Result, + expected: string +): Result { + if (!actual.hasError()) { + return value(); + } + + const diff = compare(actual.error.message, expected); + + if (!isEqualDiff(diff)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The error has the expected message.', + expected: `To not have the message ${prettyPrintString(expected)}` + })); +}; + +export { + assertResultIsNotAnErrorWithMessage +}; diff --git a/lib/assertions/forResults/assertions.ts b/lib/assertions/forResults/assertions.ts new file mode 100644 index 0000000..7e304de --- /dev/null +++ b/lib/assertions/forResults/assertions.ts @@ -0,0 +1,46 @@ +import { assertResultIsAnError } from './assertResultIsAnError'; +import { assertResultIsAnErrorWithMessage } from './assertResultIsAnErrorWithMessage'; +import { assertResultIsAValue } from './assertResultIsAValue'; +import { assertResultIsNotAnError } from './assertResultIsNotAnError'; +import { assertResultIsNotAnErrorWithMessage } from './assertResultIsNotAnErrorWithMessage'; +import { assertResultIsNotAValue } from './assertResultIsNotAValue'; +import { report } from '../../report'; +import { Result } from 'defekt'; +import { ResultAssertions } from './ResultAssertions'; + +const getAssertionsForResult = function ( + actual: Result +): ResultAssertions { + return { + aValue (): void { + report(assertResultIsAValue(actual)); + }, + anError (): void { + report(assertResultIsAnError(actual)); + }, + anErrorWithMessage (expected): void { + report(assertResultIsAnErrorWithMessage(actual, expected)); + } + }; +}; + +const getNegatedAssertionsForResult = function ( + actual: Result +): ResultAssertions { + return { + aValue (): void { + report(assertResultIsNotAValue(actual)); + }, + anError (): void { + report(assertResultIsNotAnError(actual)); + }, + anErrorWithMessage (expected): void { + report(assertResultIsNotAnErrorWithMessage(actual, expected)); + } + }; +}; + +export { + getAssertionsForResult, + getNegatedAssertionsForResult +}; diff --git a/lib/assertions/forSets/AssertThatForSet.ts b/lib/assertions/forSets/AssertThatForSet.ts new file mode 100644 index 0000000..047f9c7 --- /dev/null +++ b/lib/assertions/forSets/AssertThatForSet.ts @@ -0,0 +1,12 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { SetAssertions } from './SetAssertions'; + +type AssertThatForSet = (actual: Set) => { + is: CommonAssertions> & SetAssertions & { + not: CommonAssertions> & SetAssertions; + }; +}; + +export type { + AssertThatForSet +}; diff --git a/lib/assertions/forSets/SetAssertions.ts b/lib/assertions/forSets/SetAssertions.ts new file mode 100644 index 0000000..88857ac --- /dev/null +++ b/lib/assertions/forSets/SetAssertions.ts @@ -0,0 +1,12 @@ +interface SetAssertions { + containing: (item: TContent) => void; + containingAllOf: (iterable: Iterable) => void; + containingAnyOf: (iterable: Iterable) => void; + atLeast: (expected: Set) => void; + atMost: (expected: Set) => void; + empty: () => void; +} + +export type { + SetAssertions +}; diff --git a/lib/assertions/forSets/assertSetIsAtLeastSet.ts b/lib/assertions/forSets/assertSetIsAtLeastSet.ts new file mode 100644 index 0000000..9dab531 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsAtLeastSet.ts @@ -0,0 +1,39 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { findSetDiffOmissions } from '../../diffs/forSets/findSetDiffOmissions'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { error, Result, value } from 'defekt'; + +const assertSetIsAtLeastSet = function ( + actual: Set, + expected: Set +): Result { + const diff = compareSets( + dispel(actual), + dispel(expected) + ); + + if (isEqualDiff(diff)) { + return value(); + } + + const diffWithOnlyOmissions = findSetDiffOmissions(diff); + + if (diffWithOnlyOmissions.cost === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The expected set is not entirely contained in the actual set.', + actual: prettyPrint(actual), + expected: `To entirely contain:\n${prettyPrint(expected)}`, + diff: `The following sub-set shows relevant changes between actual and expected:\n${prettyPrintDiff(diffWithOnlyOmissions)}` + })); +}; + +export { + assertSetIsAtLeastSet +}; diff --git a/lib/assertions/forSets/assertSetIsAtMostSet.ts b/lib/assertions/forSets/assertSetIsAtMostSet.ts new file mode 100644 index 0000000..730f39b --- /dev/null +++ b/lib/assertions/forSets/assertSetIsAtMostSet.ts @@ -0,0 +1,43 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../prettyPrint/typeAware/prettyPrintDiff'; +import { SetDiff } from '../../diffs/forSets/SetDiff'; +import { error, Result, value } from 'defekt'; + +const assertSetIsAtMostSet = function ( + actual: Set, + expected: Set +): Result { + const diff = compareSets( + dispel(actual), + dispel(expected) + ); + + if (isEqualDiff(diff)) { + return value(); + } + + if (diff.additions.size === 0) { + return value(); + } + + const cleanedDiff: SetDiff = { + ...diff, + omissions: new Set(), + equal: new Set() + }; + + return error(new AssertionFailed({ + message: 'The actual set is not entirely contained in the expected set.', + actual: prettyPrint(actual), + expected: `To be entirely contained in:\n${prettyPrint(expected)}`, + diff: `The following sub-set shows relevant changes between actual and expected:\n${prettyPrintDiff(cleanedDiff)}` + })); +}; + +export { + assertSetIsAtMostSet +}; diff --git a/lib/assertions/forSets/assertSetIsContainingAllOfIterable.ts b/lib/assertions/forSets/assertSetIsContainingAllOfIterable.ts new file mode 100644 index 0000000..a508ce0 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsContainingAllOfIterable.ts @@ -0,0 +1,34 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsContainingAllOfIterable = function ( + actual: Set, + iterable: Iterable +): Result { + const setFromExpected = new Set(dispel(iterable)); + + const diff = compareSets(actual, setFromExpected); + + if (isEqualDiff(diff)) { + return value(); + } + + if (diff.omissions.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The set does not contain all expected items.', + actual: prettyPrint(actual), + expected: `To contain all of:\n${prettyPrint(iterable)}`, + diff: `Missing these items:\n${prettyPrint(diff.omissions)}` + })); +}; + +export { + assertSetIsContainingAllOfIterable +}; diff --git a/lib/assertions/forSets/assertSetIsContainingAnyOfIterable.ts b/lib/assertions/forSets/assertSetIsContainingAnyOfIterable.ts new file mode 100644 index 0000000..091b296 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsContainingAnyOfIterable.ts @@ -0,0 +1,27 @@ +import { AssertionFailed } from '../../errors'; +import { assertSetIsContainingItem } from './assertSetIsContainingItem'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsContainingAnyOfIterable = function ( + actual: Set, + iterable: Iterable +): Result { + for (const item of iterable) { + const result = assertSetIsContainingItem(actual, item); + + if (result.hasValue()) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The set does not contain any of the expected items.', + actual: prettyPrint(actual), + expected: `To contain any of:\n${prettyPrint(iterable)}` + })); +}; + +export { + assertSetIsContainingAnyOfIterable +}; diff --git a/lib/assertions/forSets/assertSetIsContainingItem.ts b/lib/assertions/forSets/assertSetIsContainingItem.ts new file mode 100644 index 0000000..cef9131 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsContainingItem.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsContainingItem = function ( + actual: Set, + item: TContent +): Result { + for (const actualItem of actual) { + const diff = compare(dispel(actualItem), dispel(item)); + + if (isEqualDiff(diff)) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The set does not contain the expected item.', + actual: prettyPrint(actual), + expected: `To contain:\n${prettyPrint(item)}` + })); +}; + +export { + assertSetIsContainingItem +}; diff --git a/lib/assertions/forSets/assertSetIsEmpty.ts b/lib/assertions/forSets/assertSetIsEmpty.ts new file mode 100644 index 0000000..e24e7fc --- /dev/null +++ b/lib/assertions/forSets/assertSetIsEmpty.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsEmpty = function ( + actual: Set +): Result { + if (actual.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The set is not empty.', + actual: prettyPrint(actual) + })); +}; + +export { + assertSetIsEmpty +}; diff --git a/lib/assertions/forSets/assertSetIsNotAtLeastSet.ts b/lib/assertions/forSets/assertSetIsNotAtLeastSet.ts new file mode 100644 index 0000000..8d0d66a --- /dev/null +++ b/lib/assertions/forSets/assertSetIsNotAtLeastSet.ts @@ -0,0 +1,30 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsNotAtLeastSet = function ( + actual: Set, + expected: Set +): Result { + const diff = compareSets( + dispel(actual), + dispel(expected) + ); + + if (!isEqualDiff(diff) && diff.omissions.size > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The expected set is entirely contained in the actual set.', + actual: prettyPrint(actual), + expected: `To not entirely contain:\n${prettyPrint(expected)}` + })); +}; + +export { + assertSetIsNotAtLeastSet +}; diff --git a/lib/assertions/forSets/assertSetIsNotAtMostSet.ts b/lib/assertions/forSets/assertSetIsNotAtMostSet.ts new file mode 100644 index 0000000..592fd9f --- /dev/null +++ b/lib/assertions/forSets/assertSetIsNotAtMostSet.ts @@ -0,0 +1,30 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsNotAtMostSet = function ( + actual: Set, + expected: Set +): Result { + const diff = compareSets( + dispel(actual), + dispel(expected) + ); + + if (!isEqualDiff(diff) && diff.additions.size > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The actual set is entirely contained in the expected set.', + actual: prettyPrint(actual), + expected: `To not be entirely contained in:\n${prettyPrint(expected)}` + })); +}; + +export { + assertSetIsNotAtMostSet +}; diff --git a/lib/assertions/forSets/assertSetIsNotContainingAllOfIterable.ts b/lib/assertions/forSets/assertSetIsNotContainingAllOfIterable.ts new file mode 100644 index 0000000..585b5c5 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsNotContainingAllOfIterable.ts @@ -0,0 +1,27 @@ +import { AssertionFailed } from '../../errors'; +import { assertSetIsContainingItem } from './assertSetIsContainingItem'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsNotContainingAllOfIterable = function ( + actual: Set, + iterable: Iterable +): Result { + for (const item of iterable) { + const result = assertSetIsContainingItem(actual, item); + + if (result.hasError()) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The set contains all items in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain all of:\n${prettyPrint(iterable)}` + })); +}; + +export { + assertSetIsNotContainingAllOfIterable +}; diff --git a/lib/assertions/forSets/assertSetIsNotContainingAnyOfIterable.ts b/lib/assertions/forSets/assertSetIsNotContainingAnyOfIterable.ts new file mode 100644 index 0000000..5d2db37 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsNotContainingAnyOfIterable.ts @@ -0,0 +1,32 @@ +import { AssertionFailed } from '../../errors'; +import { compareSets } from '../../comparisons/forSets/compareSets'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsNotContainingAnyOfIterable = function ( + actual: Set, + iterable: Iterable +): Result { + const setFromExpected = new Set(iterable); + + const diff = compareSets(dispel(actual), dispel(setFromExpected)); + + if (!isEqualDiff(diff) && diff.equal.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The set does contains one or more of the items in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain any of:\n${prettyPrint(iterable)}`, + diff: `These items are contained, but should not be:\n${prettyPrint( + isEqualDiff(diff) ? setFromExpected : diff.equal + )}` + })); +}; + +export { + assertSetIsNotContainingAnyOfIterable +}; diff --git a/lib/assertions/forSets/assertSetIsNotContainingItem.ts b/lib/assertions/forSets/assertSetIsNotContainingItem.ts new file mode 100644 index 0000000..6b14327 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsNotContainingItem.ts @@ -0,0 +1,29 @@ +import { AssertionFailed } from '../../errors'; +import { compare } from '../../comparisons/typeAware/compare'; +import { dispel } from '../../dispel/dispel'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertSetIsNotContainingItem = function ( + actual: Set, + item: TContent +): Result { + for (const actualItem of actual) { + const diff = compare(dispel(actualItem), dispel(item)); + + if (isEqualDiff(diff)) { + return error(new AssertionFailed({ + message: 'The set contains the item.', + expected: `To not contain:\n${prettyPrint(item)}`, + actual: prettyPrint(actual) + })); + } + } + + return value(); +}; + +export { + assertSetIsNotContainingItem +}; diff --git a/lib/assertions/forSets/assertSetIsNotEmpty.ts b/lib/assertions/forSets/assertSetIsNotEmpty.ts new file mode 100644 index 0000000..2d481b8 --- /dev/null +++ b/lib/assertions/forSets/assertSetIsNotEmpty.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertSetIsNotEmpty = function ( + actual: Set +): Result { + if (actual.size > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The set is empty.' + })); +}; + +export { + assertSetIsNotEmpty +}; diff --git a/lib/assertions/forSets/assertions.ts b/lib/assertions/forSets/assertions.ts new file mode 100644 index 0000000..44ae29e --- /dev/null +++ b/lib/assertions/forSets/assertions.ts @@ -0,0 +1,65 @@ +import { assertSetIsAtLeastSet } from './assertSetIsAtLeastSet'; +import { assertSetIsAtMostSet } from './assertSetIsAtMostSet'; +import { assertSetIsContainingAllOfIterable } from './assertSetIsContainingAllOfIterable'; +import { assertSetIsContainingAnyOfIterable } from './assertSetIsContainingAnyOfIterable'; +import { assertSetIsContainingItem } from './assertSetIsContainingItem'; +import { assertSetIsEmpty } from './assertSetIsEmpty'; +import { assertSetIsNotAtLeastSet } from './assertSetIsNotAtLeastSet'; +import { assertSetIsNotAtMostSet } from './assertSetIsNotAtMostSet'; +import { assertSetIsNotContainingAllOfIterable } from './assertSetIsNotContainingAllOfIterable'; +import { assertSetIsNotContainingAnyOfIterable } from './assertSetIsNotContainingAnyOfIterable'; +import { assertSetIsNotContainingItem } from './assertSetIsNotContainingItem'; +import { assertSetIsNotEmpty } from './assertSetIsNotEmpty'; +import { report } from '../../report'; +import { SetAssertions } from './SetAssertions'; + +const getAssertionsForSet = function (actual: Set): SetAssertions { + return { + containing (item): void { + report(assertSetIsContainingItem(actual, item)); + }, + containingAllOf (iterable): void { + report(assertSetIsContainingAllOfIterable(actual, iterable)); + }, + containingAnyOf (iterable): void { + report(assertSetIsContainingAnyOfIterable(actual, iterable)); + }, + atLeast (expected): void { + report(assertSetIsAtLeastSet(actual, expected)); + }, + atMost (expected): void { + report(assertSetIsAtMostSet(actual, expected)); + }, + empty (): void { + report(assertSetIsEmpty(actual)); + } + }; +}; + +const getNegatedAssertionsForSet = function (actual: Set): SetAssertions { + return { + containing (item): void { + report(assertSetIsNotContainingItem(actual, item)); + }, + containingAllOf (iterable): void { + report(assertSetIsNotContainingAllOfIterable(actual, iterable)); + }, + containingAnyOf (iterable): void { + report(assertSetIsNotContainingAnyOfIterable(actual, iterable)); + }, + atLeast (expected): void { + report(assertSetIsNotAtLeastSet(actual, expected)); + }, + atMost (expected): void { + report(assertSetIsNotAtMostSet(actual, expected)); + }, + empty (): void { + report(assertSetIsNotEmpty(actual)); + } + }; +}; + +export { + getAssertionsForSet, + getNegatedAssertionsForSet +}; diff --git a/lib/assertions/forStrings/AssertThatForString.ts b/lib/assertions/forStrings/AssertThatForString.ts new file mode 100644 index 0000000..373be10 --- /dev/null +++ b/lib/assertions/forStrings/AssertThatForString.ts @@ -0,0 +1,12 @@ +import { CommonAssertions } from '../forAny/CommonAssertions'; +import { StringAssertions } from './StringAssertions'; + +type AssertThatForString = (actual: string) => { + is: CommonAssertions & StringAssertions & { + not: CommonAssertions & StringAssertions; + }; +}; + +export type { + AssertThatForString +}; diff --git a/lib/assertions/forStrings/StringAssertions.ts b/lib/assertions/forStrings/StringAssertions.ts new file mode 100644 index 0000000..94b3c8e --- /dev/null +++ b/lib/assertions/forStrings/StringAssertions.ts @@ -0,0 +1,13 @@ +interface StringAssertions { + containing: (expected: string) => void; + startingWith: (expected: string) => void; + endingWith: (expected: string) => void; + containingAllOf: (expected: Iterable) => void; + containingAnyOf: (expected: Iterable) => void; + empty: () => void; + matching: (expected: RegExp) => void; +} + +export type { + StringAssertions +}; diff --git a/lib/assertions/forStrings/assertStringIsContainingAllOfIterable.ts b/lib/assertions/forStrings/assertStringIsContainingAllOfIterable.ts new file mode 100644 index 0000000..eb84609 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsContainingAllOfIterable.ts @@ -0,0 +1,34 @@ +import { AssertionFailed } from '../../errors'; +import { assertStringIsContainingString } from './assertStringIsContainingString'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsContainingAllOfIterable = function ( + actual: string, + iterable: Iterable +): Result { + const missingItems = new Set(); + + for (const expectedItem of iterable) { + const result = assertStringIsContainingString(actual, expectedItem); + + if (result.hasError()) { + missingItems.add(expectedItem); + } + } + + if (missingItems.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string does not contain all expected sub-strings.', + actual: prettyPrint(actual), + expected: `To contain all of:\n${prettyPrint(iterable)}`, + diff: `Missing these sub-strings:\n${prettyPrint(missingItems)}` + })); +}; + +export { + assertStringIsContainingAllOfIterable +}; diff --git a/lib/assertions/forStrings/assertStringIsContainingAnyOfIterable.ts b/lib/assertions/forStrings/assertStringIsContainingAnyOfIterable.ts new file mode 100644 index 0000000..8a38967 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsContainingAnyOfIterable.ts @@ -0,0 +1,27 @@ +import { AssertionFailed } from '../../errors'; +import { assertStringIsContainingString } from './assertStringIsContainingString'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsContainingAnyOfIterable = function ( + actual: string, + iterable: Iterable +): Result { + for (const item of iterable) { + const result = assertStringIsContainingString(actual, item); + + if (result.hasValue()) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The string does not contain any of the expected sub-strings.', + actual: prettyPrint(actual), + expected: `To contain any of:\n${prettyPrint(iterable)}` + })); +}; + +export { + assertStringIsContainingAnyOfIterable +}; diff --git a/lib/assertions/forStrings/assertStringIsContainingString.ts b/lib/assertions/forStrings/assertStringIsContainingString.ts new file mode 100644 index 0000000..b3c9f20 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsContainingString.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsContainingString = function ( + actual: string, + expected: string +): Result { + if (actual.includes(expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is not containing the expected sub-string.', + actual: prettyPrint(actual), + expected: `To contain:\n${prettyPrint(expected)}` + })); +}; + +export { + assertStringIsContainingString +}; diff --git a/lib/assertions/forStrings/assertStringIsEmpty.ts b/lib/assertions/forStrings/assertStringIsEmpty.ts new file mode 100644 index 0000000..ced44d2 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsEmpty.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsEmpty = function ( + actual: string +): Result { + if (actual.length === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is not empty.', + actual: prettyPrint(actual) + })); +}; + +export { + assertStringIsEmpty +}; diff --git a/lib/assertions/forStrings/assertStringIsEndingWithString.ts b/lib/assertions/forStrings/assertStringIsEndingWithString.ts new file mode 100644 index 0000000..dd4b34c --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsEndingWithString.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsEndingWithString = function ( + actual: string, + expected: string +): Result { + if (actual.endsWith(expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is not ending with the expected sub-string.', + actual: prettyPrint(actual), + expected: `To end with:\n${prettyPrint(expected)}` + })); +}; + +export { + assertStringIsEndingWithString +}; diff --git a/lib/assertions/forStrings/assertStringIsMatchingRegExp.ts b/lib/assertions/forStrings/assertStringIsMatchingRegExp.ts new file mode 100644 index 0000000..a95f08b --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsMatchingRegExp.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsMatchingRegExp = function ( + actual: string, + expected: RegExp +): Result { + if (expected.test(actual)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is not matching the expected regex.', + actual: prettyPrint(actual), + expected: `To match:\n${expected.toString()}` + })); +}; + +export { + assertStringIsMatchingRegExp +}; diff --git a/lib/assertions/forStrings/assertStringIsNotContainingAllOfIterable.ts b/lib/assertions/forStrings/assertStringIsNotContainingAllOfIterable.ts new file mode 100644 index 0000000..e0a3103 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotContainingAllOfIterable.ts @@ -0,0 +1,27 @@ +import { AssertionFailed } from '../../errors'; +import { assertStringIsContainingString } from './assertStringIsContainingString'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotContainingAllOfIterable = function ( + actual: string, + iterable: Iterable +): Result { + for (const item of iterable) { + const result = assertStringIsContainingString(actual, item); + + if (result.hasError()) { + return value(); + } + } + + return error(new AssertionFailed({ + message: 'The string contains all sub-strings in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain all of:\n${prettyPrint(iterable)}` + })); +}; + +export { + assertStringIsNotContainingAllOfIterable +}; diff --git a/lib/assertions/forStrings/assertStringIsNotContainingAnyOfIterable.ts b/lib/assertions/forStrings/assertStringIsNotContainingAnyOfIterable.ts new file mode 100644 index 0000000..0eeaa1d --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotContainingAnyOfIterable.ts @@ -0,0 +1,34 @@ +import { AssertionFailed } from '../../errors'; +import { assertStringIsNotContainingString } from './assertStringIsNotContainingString'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotContainingAnyOfIterable = function ( + actual: string, + iterable: Iterable +): Result { + const containedItems = new Set(); + + for (const item of iterable) { + const result = assertStringIsNotContainingString(actual, item); + + if (result.hasError()) { + containedItems.add(item); + } + } + + if (containedItems.size === 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string does contain one or more of the sub-strings in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain any of:\n${prettyPrint(iterable)}`, + diff: `These sub-strings are contained, but should not be:\n${prettyPrint(containedItems)}` + })); +}; + +export { + assertStringIsNotContainingAnyOfIterable +}; diff --git a/lib/assertions/forStrings/assertStringIsNotContainingString.ts b/lib/assertions/forStrings/assertStringIsNotContainingString.ts new file mode 100644 index 0000000..84fdeaf --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotContainingString.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotContainingString = function ( + actual: string, + expected: string +): Result { + if (!actual.includes(expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is containing the sub-string.', + actual: prettyPrint(actual), + expected: `To not contain:\n${prettyPrint(expected)}` + })); +}; + +export { + assertStringIsNotContainingString +}; diff --git a/lib/assertions/forStrings/assertStringIsNotEmpty.ts b/lib/assertions/forStrings/assertStringIsNotEmpty.ts new file mode 100644 index 0000000..5ddb670 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotEmpty.ts @@ -0,0 +1,18 @@ +import { AssertionFailed } from '../../errors'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotEmpty = function ( + actual: string +): Result { + if (actual.length > 0) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is empty.' + })); +}; + +export { + assertStringIsNotEmpty +}; diff --git a/lib/assertions/forStrings/assertStringIsNotEndingWithString.ts b/lib/assertions/forStrings/assertStringIsNotEndingWithString.ts new file mode 100644 index 0000000..11f45a0 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotEndingWithString.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotEndingWithString = function ( + actual: string, + expected: string +): Result { + if (!actual.endsWith(expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is ending with the sub-string.', + actual: prettyPrint(actual), + expected: `To not end with:\n${prettyPrint(expected)}` + })); +}; + +export { + assertStringIsNotEndingWithString +}; diff --git a/lib/assertions/forStrings/assertStringIsNotMatchingRegExp.ts b/lib/assertions/forStrings/assertStringIsNotMatchingRegExp.ts new file mode 100644 index 0000000..0d8b519 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotMatchingRegExp.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotMatchingRegExp = function ( + actual: string, + expected: RegExp +): Result { + if (!expected.test(actual)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is matching the regex.', + actual: prettyPrint(actual), + expected: `To not match:\n${expected.toString()}` + })); +}; + +export { + assertStringIsNotMatchingRegExp +}; diff --git a/lib/assertions/forStrings/assertStringIsNotStartingWithString.ts b/lib/assertions/forStrings/assertStringIsNotStartingWithString.ts new file mode 100644 index 0000000..4418829 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsNotStartingWithString.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsNotStartingWithString = function ( + actual: string, + expected: string +): Result { + if (!actual.startsWith(expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is starting with the sub-string.', + actual: prettyPrint(actual), + expected: `To not start with:\n${prettyPrint(expected)}` + })); +}; + +export { + assertStringIsNotStartingWithString +}; diff --git a/lib/assertions/forStrings/assertStringIsStartingWithString.ts b/lib/assertions/forStrings/assertStringIsStartingWithString.ts new file mode 100644 index 0000000..5e6b101 --- /dev/null +++ b/lib/assertions/forStrings/assertStringIsStartingWithString.ts @@ -0,0 +1,22 @@ +import { AssertionFailed } from '../../errors'; +import { prettyPrint } from '../../prettyPrint/typeAware/prettyPrint'; +import { error, Result, value } from 'defekt'; + +const assertStringIsStartingWithString = function ( + actual: string, + expected: string +): Result { + if (actual.startsWith(expected)) { + return value(); + } + + return error(new AssertionFailed({ + message: 'The string is not starting with the expected sub-string.', + actual: prettyPrint(actual), + expected: `To start with:\n${prettyPrint(expected)}` + })); +}; + +export { + assertStringIsStartingWithString +}; diff --git a/lib/assertions/forStrings/assertions.ts b/lib/assertions/forStrings/assertions.ts new file mode 100644 index 0000000..a4ebeb0 --- /dev/null +++ b/lib/assertions/forStrings/assertions.ts @@ -0,0 +1,73 @@ +import { assertStringIsContainingAllOfIterable } from './assertStringIsContainingAllOfIterable'; +import { assertStringIsContainingAnyOfIterable } from './assertStringIsContainingAnyOfIterable'; +import { assertStringIsContainingString } from './assertStringIsContainingString'; +import { assertStringIsEmpty } from './assertStringIsEmpty'; +import { assertStringIsEndingWithString } from './assertStringIsEndingWithString'; +import { assertStringIsMatchingRegExp } from './assertStringIsMatchingRegExp'; +import { assertStringIsNotContainingAllOfIterable } from './assertStringIsNotContainingAllOfIterable'; +import { assertStringIsNotContainingAnyOfIterable } from './assertStringIsNotContainingAnyOfIterable'; +import { assertStringIsNotContainingString } from './assertStringIsNotContainingString'; +import { assertStringIsNotEmpty } from './assertStringIsNotEmpty'; +import { assertStringIsNotEndingWithString } from './assertStringIsNotEndingWithString'; +import { assertStringIsNotMatchingRegExp } from './assertStringIsNotMatchingRegExp'; +import { assertStringIsNotStartingWithString } from './assertStringIsNotStartingWithString'; +import { assertStringIsStartingWithString } from './assertStringIsStartingWithString'; +import { report } from '../../report'; +import { StringAssertions } from './StringAssertions'; + +const getAssertionsForString = function (actual: string): StringAssertions { + return { + containing (expected): void { + report(assertStringIsContainingString(actual, expected)); + }, + startingWith (expected): void { + report(assertStringIsStartingWithString(actual, expected)); + }, + endingWith (expected): void { + report(assertStringIsEndingWithString(actual, expected)); + }, + containingAllOf (expected): void { + report(assertStringIsContainingAllOfIterable(actual, expected)); + }, + containingAnyOf (expected): void { + report(assertStringIsContainingAnyOfIterable(actual, expected)); + }, + empty (): void { + report(assertStringIsEmpty(actual)); + }, + matching (expected: RegExp): void { + report(assertStringIsMatchingRegExp(actual, expected)); + } + }; +}; + +const getNegatedAssertionsForString = function (actual: string): StringAssertions { + return { + containing (expected): void { + report(assertStringIsNotContainingString(actual, expected)); + }, + startingWith (expected): void { + report(assertStringIsNotStartingWithString(actual, expected)); + }, + endingWith (expected): void { + report(assertStringIsNotEndingWithString(actual, expected)); + }, + containingAllOf (expected): void { + report(assertStringIsNotContainingAllOfIterable(actual, expected)); + }, + containingAnyOf (expected): void { + report(assertStringIsNotContainingAnyOfIterable(actual, expected)); + }, + empty (): void { + report(assertStringIsNotEmpty(actual)); + }, + matching (expected: RegExp): void { + report(assertStringIsNotMatchingRegExp(actual, expected)); + } + }; +}; + +export { + getAssertionsForString, + getNegatedAssertionsForString +}; diff --git a/lib/assertthat.ts b/lib/assertthat.ts index 503f1e6..788d5fe 100644 --- a/lib/assertthat.ts +++ b/lib/assertthat.ts @@ -1,148 +1,74 @@ -import { atLeast } from './constraints/atLeast'; -import { atMost } from './constraints/atMost'; -import { between } from './constraints/between'; -import { containing } from './constraints/containing'; -import { containingAllOf } from './constraints/containingAllOf'; -import { containingAnyOf } from './constraints/containingAnyOf'; -import { endingWith } from './constraints/endingWith'; -import { equalTo } from './constraints/equalTo'; -import { falsy } from './constraints/falsy'; -import { greaterThan } from './constraints/greaterThan'; -import { instanceOf } from './constraints/instanceOf'; -import { isFalse } from './constraints/false'; -import { isNan } from './constraints/nan'; -import { isNull } from './constraints/null'; -import { isTrue } from './constraints/true'; -import { isUndefined } from './constraints/undefined'; -import { lessThan } from './constraints/lessThan'; -import { matching } from './constraints/matching'; -import { ofType } from './constraints/ofType'; -import { sameAs } from './constraints/sameAs'; -import { sameJsonAs } from './constraints/sameJsonAs'; -import { startingWith } from './constraints/startingWith'; -import { throwing } from './constraints/throwing'; -import { throwingAsync } from './constraints/throwingAsync'; +import { AssertThatForAny } from './assertions/forAny/AssertThatForAny'; +import { AssertThatForArray } from './assertions/forArrays/AssertThatForArray'; +import { AssertThatForFunction } from './assertions/forFunctions/AssertThatForFunction'; +import { AssertThatForMap } from './assertions/forMaps/AssertThatForMap'; +import { AssertThatForNumber } from './assertions/forNumbers/AssertThatForNumber'; +import { AssertThatForObject } from './assertions/forObjects/AssertThatForObject'; +import { AssertThatForResult } from './assertions/forResults/AssertThatForResult'; +import { AssertThatForSet } from './assertions/forSets/AssertThatForSet'; +import { AssertThatForString } from './assertions/forStrings/AssertThatForString'; +import { isArray } from './types/isArray'; +import { isFunction } from './types/isFunction'; +import { isMap } from './types/isMap'; +import { isNumber } from './types/isNumber'; +import { isObject } from './types/isObject'; +import { isResult } from 'defekt'; +import { isSet } from './types/isSet'; +import { isString } from './types/isString'; +import { getAssertionsForAny, getNegatedAssertionsForAny } from './assertions/forAny/assertions'; +import { getAssertionsForArray, getNegatedAssertionsForArray } from './assertions/forArrays/assertions'; +import { getAssertionsForFunction, getNegatedAssertionsForFunction } from './assertions/forFunctions/assertions'; +import { getAssertionsForMap, getNegatedAssertionsForMap } from './assertions/forMaps/assertions'; +import { getAssertionsForNumber, getNegatedAssertionsForNumber } from './assertions/forNumbers/assertions'; +import { getAssertionsForObject, getNegatedAssertionsForObject } from './assertions/forObjects/assertions'; +import { getAssertionsForResult, getNegatedAssertionsForResult } from './assertions/forResults/assertions'; +import { getAssertionsForSet, getNegatedAssertionsForSet } from './assertions/forSets/assertions'; +import { getAssertionsForString, getNegatedAssertionsForString } from './assertions/forStrings/assertions'; -const assert = { - that (actual: any): { - is: { - atLeast: (expected: number | [] | Record) => void; - atMost: (expected: number | [] | Record) => void; - between: (expectedLower: number | [] | Record, expectedUpper: number | [] | Record) => void; - containing: (expected: any) => void; - containingAnyOf: (expected: any[]) => void; - containingAllOf: (expected: any[]) => void; - endingWith: (expected: string) => void; - equalTo: (expected: any) => void; - false: () => void; - falsy: () => void; - greaterThan: (expected: number | [] | Record) => void; - instanceOf: (expected: new(...args: any[]) => Record) => void; - lessThan: (expected: number | [] | Record) => void; - matching: (expected: RegExp) => void; - NaN: () => void; - null: () => void; - ofType: (expected: string) => void; - sameAs: (expected: any) => void; - sameJsonAs: (expected: any) => void; - startingWith: (expected: string) => void; - throwing: (expected?: string | RegExp | ((ex: TError) => boolean)) => void; - throwingAsync: (expected?: string | RegExp | ((ex: TError) => boolean)) => Promise; - true: () => void; - undefined: () => void; +type AssertThat = + AssertThatForSet & + AssertThatForMap & + AssertThatForArray & + AssertThatForResult & + AssertThatForNumber & + AssertThatForString & + AssertThatForFunction & + AssertThatForObject & + AssertThatForAny; + +// eslint-disable-next-line consistent-this +const that: AssertThat = (actual: any): any => ({ + is: { + ...getAssertionsForAny(actual), - not: { - atLeast: (expected: number | [] | Record) => void; - atMost: (expected: number | [] | Record) => void; - between: (expectedLower: number | [] | Record, expectedUpper: number | [] | Record) => void; - containing: (expected: any) => void; - containingAnyOf: (expected: any[]) => void; - containingAllOf: (expected: any[]) => void; - endingWith: (expected: string) => void; - equalTo: (expected: any) => void; - false: () => void; - falsy: () => void; - greaterThan: (expected: number | [] | Record) => void; - instanceOf: (expected: new(...args: any[]) => Record) => void; - lessThan: (expected: number | [] | Record) => void; - matching: (expected: RegExp) => void; - NaN: () => void; - null: () => void; - ofType: (expected: string) => void; - sameAs: (expected: any) => void; - sameJsonAs: (expected: any) => void; - startingWith: (expected: string) => void; - throwing: (expected?: string | RegExp | ((ex: TError) => boolean)) => void; - throwingAsync: (expected?: string | RegExp | ((ex: TError) => boolean)) => Promise; - true: () => void; - undefined: () => void; - }; - }; - } { - const is = { - atLeast: atLeast(actual), - atMost: atMost(actual), - between: between(actual), - containing: containing(actual), - containingAnyOf: containingAnyOf(actual), - containingAllOf: containingAllOf(actual), - endingWith: endingWith(actual), - equalTo: equalTo(actual), - false: isFalse(actual), - falsy: falsy(actual), - greaterThan: greaterThan(actual), - instanceOf: instanceOf(actual), - lessThan: lessThan(actual), - matching: matching(actual), - NaN: isNan(actual), - null: isNull(actual), - ofType: ofType(actual), - sameAs: sameAs(actual), - sameJsonAs: sameJsonAs(actual), - startingWith: startingWith(actual), - throwing (expected?: string | RegExp | ((ex: TError) => boolean)): void { - throwing(actual)(expected); - }, - async throwingAsync (expected?: string | RegExp | ((ex: TError) => boolean)): Promise { - await throwingAsync(actual)(expected); - }, - true: isTrue(actual), - undefined: isUndefined(actual), + ...isSet(actual) ? getAssertionsForSet(actual) : {}, + ...isMap(actual) ? getAssertionsForMap(actual) : {}, + ...isArray(actual) ? getAssertionsForArray(actual) : {}, + ...isResult(actual) ? getAssertionsForResult(actual) : {}, + ...isNumber(actual) ? getAssertionsForNumber(actual) : {}, + ...isString(actual) ? getAssertionsForString(actual) : {}, + ...isFunction(actual) ? getAssertionsForFunction(actual) : {}, + ...isObject(actual) ? getAssertionsForObject(actual) : {}, - not: { - atLeast: atLeast.negated(actual), - atMost: atMost.negated(actual), - between: between.negated(actual), - containing: containing.negated(actual), - containingAnyOf: containingAnyOf.negated(actual), - containingAllOf: containingAllOf.negated(actual), - endingWith: endingWith.negated(actual), - equalTo: equalTo.negated(actual), - false: isFalse.negated(actual), - falsy: falsy.negated(actual), - greaterThan: greaterThan.negated(actual), - instanceOf: instanceOf.negated(actual), - lessThan: lessThan.negated(actual), - matching: matching.negated(actual), - NaN: isNan.negated(actual), - null: isNull.negated(actual), - ofType: ofType.negated(actual), - sameAs: sameAs.negated(actual), - sameJsonAs: sameJsonAs.negated(actual), - startingWith: startingWith.negated(actual), - throwing (expected?: string | RegExp | ((ex: TError) => boolean)): void { - throwing.negated(actual)(expected); - }, - async throwingAsync (expected?: string | RegExp | ((ex: TError) => boolean)): Promise { - await throwingAsync.negated(actual)(expected); - }, - true: isTrue.negated(actual), - undefined: isUndefined.negated(actual) - } - }; + not: { + ...getNegatedAssertionsForAny(actual), - return { is }; + ...isSet(actual) ? getNegatedAssertionsForSet(actual) : {}, + ...isMap(actual) ? getNegatedAssertionsForMap(actual) : {}, + ...isArray(actual) ? getNegatedAssertionsForArray(actual) : {}, + ...isResult(actual) ? getNegatedAssertionsForResult(actual) : {}, + ...isNumber(actual) ? getNegatedAssertionsForNumber(actual) : {}, + ...isString(actual) ? getNegatedAssertionsForString(actual) : {}, + ...isFunction(actual) ? getNegatedAssertionsForFunction(actual) : {}, + ...isObject(actual) ? getNegatedAssertionsForObject(actual) : {} + } } +}); + +const assert = { + that }; -export { assert }; +export { + assert +}; diff --git a/lib/comparisons/forArrays/compareArrays.ts b/lib/comparisons/forArrays/compareArrays.ts new file mode 100644 index 0000000..7878ea6 --- /dev/null +++ b/lib/comparisons/forArrays/compareArrays.ts @@ -0,0 +1,128 @@ +import { compare } from '../typeAware/compare'; +import { simplifyDiff } from '../../diffs/forArrays/simplifyDiff'; +import { size } from '../../size/typeAware/size'; +import { AdditionDiffSegment, OmissionDiffSegment } from '../../diffs/forArrays/ArrayDiffSegment'; +import { arrayDiff, ArrayDiff } from '../../diffs/forArrays/ArrayDiff'; +import { EqualDiff, equalDiff } from '../../diffs/EqualDiff'; + +const compareArrays = function ( + actual: TContent[], + expected: TContent[] +): ArrayDiff | EqualDiff { + const results: Map> = new Map(); + + results.set('-1|-1', arrayDiff({ + segments: [], + cost: 0 + })); + + for (let indexActual = 0; indexActual < actual.length; indexActual++) { + const segment: AdditionDiffSegment = { + addition: [], + cost: 0 + }; + + for (let index = 0; index <= indexActual; index++) { + segment.addition.push(actual[index]); + segment.cost += size(actual[index]); + } + + results.set(`${indexActual}|-1`, arrayDiff({ + segments: [ segment ], + cost: segment.cost + })); + } + + for (let indexExpected = 0; indexExpected < expected.length; indexExpected++) { + const segment: OmissionDiffSegment = { + omission: [], + cost: 0 + }; + + for (let index = 0; index <= indexExpected; index++) { + segment.omission.push(expected[index]); + segment.cost += size(expected[index]); + } + + results.set(`-1|${indexExpected}`, arrayDiff({ + segments: [ segment ], + cost: segment.cost + })); + } + + for (const [ indexActual, elementActual ] of actual.entries()) { + for (const [ indexExpected, elementExpected ] of expected.entries()) { + const maybeEqualPreviousDiff = results.get(`${indexActual - 1}|${indexExpected - 1}`)!; + const maybeEqualCurrentDiff = compare(elementActual, elementExpected); + const maybeEqualCost = maybeEqualPreviousDiff.cost + maybeEqualCurrentDiff.cost; + + const omissionPreviousDiff = results.get(`${indexActual}|${indexExpected - 1}`)!; + const omissionCost = omissionPreviousDiff.cost + size(elementExpected); + + const additionPreviousDiff = results.get(`${indexActual - 1}|${indexExpected}`)!; + const additionCost = additionPreviousDiff.cost + size(elementActual); + + if (maybeEqualCost <= omissionCost && maybeEqualCost <= additionCost) { + if (maybeEqualCurrentDiff.cost === 0) { + results.set(`${indexActual}|${indexExpected}`, arrayDiff({ + segments: [ + ...maybeEqualPreviousDiff.segments, + { + equal: [ elementActual ], + cost: 0 + } + ], + cost: maybeEqualCost + })); + } else { + results.set(`${indexActual}|${indexExpected}`, arrayDiff({ + segments: [ + ...maybeEqualPreviousDiff.segments, + { + change: [ maybeEqualCurrentDiff ], + cost: maybeEqualCurrentDiff.cost + } + ], + cost: maybeEqualCost + })); + } + } else if (omissionCost <= additionCost) { + results.set(`${indexActual}|${indexExpected}`, arrayDiff({ + segments: [ + ...omissionPreviousDiff.segments, + { + omission: [ elementExpected ], + cost: size(elementExpected) + } + ], + cost: omissionCost + })); + } else { + results.set(`${indexActual}|${indexExpected}`, arrayDiff({ + segments: [ + ...additionPreviousDiff.segments, + { + addition: [ elementActual ], + cost: size(elementActual) + } + ], + cost: additionCost + })); + } + + results.delete(`${indexActual - 1}|${indexExpected - 1}`); + } + } + + const diff = results.get(`${actual.length - 1}|${expected.length - 1}`)!; + + if (diff.cost === 0) { + return equalDiff({ value: actual }); + } + + return simplifyDiff(results.get(`${actual.length - 1}|${expected.length - 1}`)!); +}; + +export { + compareArrays +}; diff --git a/lib/comparisons/forBooleans/compareBooleans.ts b/lib/comparisons/forBooleans/compareBooleans.ts new file mode 100644 index 0000000..b8b8901 --- /dev/null +++ b/lib/comparisons/forBooleans/compareBooleans.ts @@ -0,0 +1,22 @@ +import { unequalBooleanCost } from '../../constants/costs'; +import { booleanDiff, BooleanDiff } from '../../diffs/forBooleans/BooleanDiff'; +import { equalDiff, EqualDiff } from '../../diffs/EqualDiff'; + +const compareBooleans = function ( + actual: boolean, + expected: boolean +): BooleanDiff | EqualDiff { + if (actual === expected) { + return equalDiff({ value: actual }); + } + + return booleanDiff({ + actual, + expected, + cost: unequalBooleanCost + }); +}; + +export { + compareBooleans +}; diff --git a/lib/comparisons/forErrors/compareErrors.ts b/lib/comparisons/forErrors/compareErrors.ts new file mode 100644 index 0000000..3efb53a --- /dev/null +++ b/lib/comparisons/forErrors/compareErrors.ts @@ -0,0 +1,35 @@ +import { compareObjects } from '../forObjects/compareObjects'; +import { equalDiff, EqualDiff, isEqualDiff } from '../../diffs/EqualDiff'; +import { errorDiff, ErrorDiff } from '../../diffs/forErrors/ErrorDiff'; + +const compareErrors = function ( + actual: Error, + expected: Error +): ErrorDiff | EqualDiff { + const actualErrorButWithoutTheShittiness = { + ...actual, + message: actual.message + }; + const expectedErrorButWithoutTheShittiness = { + ...expected, + message: expected.message + }; + + const objectDiff = compareObjects( + actualErrorButWithoutTheShittiness, + expectedErrorButWithoutTheShittiness + ); + + if (isEqualDiff(objectDiff)) { + return equalDiff({ value: actual }); + } + + return errorDiff({ + cost: objectDiff.cost, + objectDiff + }); +}; + +export { + compareErrors +}; diff --git a/lib/comparisons/forFunctions/compareFunctions.ts b/lib/comparisons/forFunctions/compareFunctions.ts new file mode 100644 index 0000000..a0e9a9c --- /dev/null +++ b/lib/comparisons/forFunctions/compareFunctions.ts @@ -0,0 +1,34 @@ +import { compareStrings } from '../forStrings/compareStrings'; +import { unequalFunctionCost } from '../../constants/costs'; +import { equalDiff, EqualDiff, isEqualDiff } from '../../diffs/EqualDiff'; +import { functionDiff, FunctionDiff } from '../../diffs/forFunctions/FunctionDiff'; + +const compareFunctions = function ( +/* eslint-disable @typescript-eslint/ban-types */ + actual: Function, + expected: Function +/* eslint-enable @typescript-eslint/ban-types */ +): FunctionDiff | EqualDiff { + const actualStringRepresentation = actual.toString(); + const expectedStringRepresentation = expected.toString(); + + const stringRepresentationDiff = compareStrings( + actualStringRepresentation, + expectedStringRepresentation + ); + + if (isEqualDiff(stringRepresentationDiff)) { + return equalDiff({ + value: actual + }); + } + + return functionDiff({ + cost: unequalFunctionCost, + stringRepresentationDiff + }); +}; + +export { + compareFunctions +}; diff --git a/lib/comparisons/forMaps/compareMaps.ts b/lib/comparisons/forMaps/compareMaps.ts new file mode 100644 index 0000000..9baebac --- /dev/null +++ b/lib/comparisons/forMaps/compareMaps.ts @@ -0,0 +1,55 @@ +import { compare } from '../typeAware/compare'; +import { size } from '../../size/typeAware/size'; +import { equalDiff, EqualDiff, isEqualDiff } from '../../diffs/EqualDiff'; +import { mapDiff, MapDiff } from '../../diffs/forMaps/MapDiff'; + +const compareMaps = function ( + actual: Map, + expected: Map +): MapDiff | EqualDiff { + const newDiff = mapDiff({ + cost: 0, + additions: new Map(), + omissions: new Map(), + changes: new Map(), + equal: new Map() + }); + + for (const [ actualKey, actualElement ] of actual.entries()) { + newDiff.additions.set(actualKey, actualElement); + newDiff.cost += size(actualElement); + } + + for (const [ expectedKey, expectedElement ] of expected.entries()) { + const actualKeyInBothMaps = [ ...newDiff.additions.keys() ].find( + (actualKey): boolean => compare(actualKey, expectedKey).cost === 0 + ); + + if (actualKeyInBothMaps !== undefined) { + newDiff.additions.delete(actualKeyInBothMaps); + newDiff.cost -= size(actual.get(actualKeyInBothMaps)); + + const subDiff = compare(actual.get(actualKeyInBothMaps), expectedElement); + + if (isEqualDiff(subDiff)) { + newDiff.equal.set(actualKeyInBothMaps, expectedElement); + } else { + newDiff.changes.set(actualKeyInBothMaps, subDiff); + newDiff.cost += subDiff.cost; + } + } else { + newDiff.omissions.set(expectedKey, expectedElement); + newDiff.cost += size(expectedElement); + } + } + + if (newDiff.cost === 0) { + return equalDiff({ value: actual }); + } + + return newDiff; +}; + +export { + compareMaps +}; diff --git a/lib/comparisons/forNumbers/compareNumbers.ts b/lib/comparisons/forNumbers/compareNumbers.ts new file mode 100644 index 0000000..65e6fe4 --- /dev/null +++ b/lib/comparisons/forNumbers/compareNumbers.ts @@ -0,0 +1,24 @@ +import { unequalNumberCost } from '../../constants/costs'; +import { equalDiff, EqualDiff } from '../../diffs/EqualDiff'; +import { numberDiff, NumberDiff } from '../../diffs/forNumbers/NumberDiff'; + +const compareNumbers = function ( + actual: number, expected: number +): NumberDiff | EqualDiff { + const difference = actual - expected; + + if (difference === 0) { + return equalDiff({ value: actual }); + } + + return numberDiff({ + actual, + expected, + difference, + cost: unequalNumberCost + }); +}; + +export { + compareNumbers +}; diff --git a/lib/comparisons/forObjects/compareObjects.ts b/lib/comparisons/forObjects/compareObjects.ts new file mode 100644 index 0000000..49304ca --- /dev/null +++ b/lib/comparisons/forObjects/compareObjects.ts @@ -0,0 +1,52 @@ +import { compare } from '../typeAware/compare'; +import { size } from '../../size/typeAware/size'; +import { equalDiff, EqualDiff } from '../../diffs/EqualDiff'; +import { objectDiff, ObjectDiff } from '../../diffs/forObjects/ObjectDiff'; + +const compareObjects = function ( + actual: any, + expected: any +): ObjectDiff | EqualDiff { + const newDiff = objectDiff({ + cost: 0, + additions: {}, + omissions: {}, + changes: {}, + equal: {} + }); + + for (const key of Object.keys(actual)) { + if (key in expected) { + const subDiff = compare(actual[key], expected[key]); + + if (subDiff.cost === 0) { + newDiff.equal[key] = actual[key]; + } else { + newDiff.changes[key] = subDiff; + newDiff.cost += subDiff.cost; + } + } else { + newDiff.additions[key] = actual[key]; + newDiff.cost += size(actual[key]); + } + } + + for (const key of Object.keys(expected)) { + if (key in actual) { + continue; + } + + newDiff.omissions[key] = expected[key]; + newDiff.cost += size(expected[key]); + } + + if (newDiff.cost === 0) { + return equalDiff({ value: actual }); + } + + return newDiff; +}; + +export { + compareObjects +}; diff --git a/lib/comparisons/forRecursions/compareRecursions.ts b/lib/comparisons/forRecursions/compareRecursions.ts new file mode 100644 index 0000000..b0e7f56 --- /dev/null +++ b/lib/comparisons/forRecursions/compareRecursions.ts @@ -0,0 +1,30 @@ +import { compareStrings } from '../forStrings/compareStrings'; +import { Recursion } from '../../types/Recursion'; +import { unequalRecursionCost } from '../../constants/costs'; +import { equalDiff, EqualDiff, isEqualDiff } from '../../diffs/EqualDiff'; +import { recursionDiff, RecursionDiff } from '../../diffs/forRecursions/RecursionDiff'; + +const compareRecursions = function ( + actual: Recursion, + expected: Recursion +): RecursionDiff | EqualDiff { + const recursionPathDiff = compareStrings( + actual.recursionPath, + expected.recursionPath + ); + + if (isEqualDiff(recursionPathDiff)) { + return equalDiff({ + value: actual + }); + } + + return recursionDiff({ + cost: unequalRecursionCost, + recursionPathDiff + }); +}; + +export { + compareRecursions +}; diff --git a/lib/comparisons/forResults/compareResults.ts b/lib/comparisons/forResults/compareResults.ts new file mode 100644 index 0000000..3d37dc0 --- /dev/null +++ b/lib/comparisons/forResults/compareResults.ts @@ -0,0 +1,51 @@ +import { compare } from '../typeAware/compare'; +import { Result } from 'defekt'; +import { unequalResultCost } from '../../constants/costs'; +import { equalDiff, EqualDiff } from '../../diffs/EqualDiff'; +import { expectedErrorGotValueResultDiff, expectedValueGotErrorResultDiff, ResultDiff, unequalErrorResultDiff, unequalValueResultDiff } from '../../diffs/forResults/ResultDiff'; + +const compareResults = function ( + actual: Result, + expected: Result +): ResultDiff | EqualDiff { + if (actual.hasValue()) { + if (expected.hasError()) { + return expectedErrorGotValueResultDiff({ + cost: unequalResultCost, + expected, + actual + }); + } + const diff = compare(actual.value, expected.value); + + if (diff.cost === 0) { + return equalDiff({ + value: actual + }); + } + + return unequalValueResultDiff({ diff, cost: diff.cost }); + } + + if (expected.hasValue()) { + return expectedValueGotErrorResultDiff({ + cost: unequalResultCost, + expected, + actual + }); + } + + const diff = compare(actual.error, expected.error); + + if (diff.cost === 0) { + return equalDiff({ + value: actual + }); + } + + return unequalErrorResultDiff({ diff, cost: diff.cost }); +}; + +export { + compareResults +}; diff --git a/lib/comparisons/forSets/compareSets.ts b/lib/comparisons/forSets/compareSets.ts new file mode 100644 index 0000000..56c5b95 --- /dev/null +++ b/lib/comparisons/forSets/compareSets.ts @@ -0,0 +1,46 @@ +import { compare } from '../typeAware/compare'; +import { size } from '../../size/typeAware/size'; +import { equalDiff, EqualDiff } from '../../diffs/EqualDiff'; +import { setDiff, SetDiff } from '../../diffs/forSets/SetDiff'; + +const compareSets = function ( + actual: Set, + expected: Set +): SetDiff | EqualDiff { + const newDiff = setDiff({ + cost: 0, + additions: new Set(), + omissions: new Set(), + equal: new Set() + }); + + for (const actualSubValue of actual) { + newDiff.additions.add(actualSubValue); + newDiff.cost += size(actualSubValue); + } + + for (const expectedElement of expected) { + const elementInBothSets = [ ...newDiff.additions ].find( + (addition): boolean => compare(addition, expectedElement).cost === 0 + ); + + if (elementInBothSets !== undefined) { + newDiff.additions.delete(elementInBothSets); + newDiff.equal.add(elementInBothSets); + newDiff.cost -= size(elementInBothSets); + } else { + newDiff.omissions.add(expectedElement); + newDiff.cost += size(expectedElement); + } + } + + if (newDiff.cost === 0) { + return equalDiff({ value: actual }); + } + + return newDiff; +}; + +export { + compareSets +}; diff --git a/lib/comparisons/forStrings/compareStrings.ts b/lib/comparisons/forStrings/compareStrings.ts new file mode 100644 index 0000000..df65658 --- /dev/null +++ b/lib/comparisons/forStrings/compareStrings.ts @@ -0,0 +1,102 @@ +import { ArrayDiff } from '../../diffs/forArrays/ArrayDiff'; +import { compareArrays } from '../forArrays/compareArrays'; +import { InvalidOperation } from '../../errors'; +import { unequalCharCost } from '../../constants/costs'; +import { + AdditionDiffSegment as AdditionStringDiffSegment, + OmissionDiffSegment as OmissionStringDiffSegment +} from '../../diffs/forStrings/StringDiffSegment'; +import { equalDiff, EqualDiff, isEqualDiff } from '../../diffs/EqualDiff'; +import { + isAdditionDiffSegment as isAdditionArrayDiffSegment, + isChangeDiffSegment as isChangeArrayDiffSegment, + isEqualDiffSegment as isEqualArrayDiffSegment, + isOmissionDiffSegment as isOmissionArrayDiffSegment +} from '../../diffs/forArrays/ArrayDiffSegment'; +import { isStringDiff, stringDiff, StringDiff } from '../../diffs/forStrings/StringDiff'; + +const convertArrayDiffToStringDiff = function (arrayDiff: ArrayDiff): StringDiff { + const convertedStringDiff: StringDiff = stringDiff({ + segments: [], + cost: 0 + }); + + for (const arrayDiffSegment of arrayDiff.segments) { + if (isEqualArrayDiffSegment(arrayDiffSegment)) { + convertedStringDiff.segments.push({ + equal: arrayDiffSegment.equal.join(''), + cost: arrayDiffSegment.cost + }); + } + if (isChangeArrayDiffSegment(arrayDiffSegment)) { + const replaceSegment = { + replace: '', + replaceWith: '', + cost: 0 + }; + + for (const change of arrayDiffSegment.change) { + if (!isStringDiff(change)) { + throw new InvalidOperation('Tried to convert ArrayDiff with sub-diff to StringDiff.'); + } + replaceSegment.replace += (change.segments[0] as AdditionStringDiffSegment).addition; + replaceSegment.replaceWith += (change.segments[1] as OmissionStringDiffSegment).omission; + replaceSegment.cost += change.cost; + } + convertedStringDiff.segments.push(replaceSegment); + } + if (isOmissionArrayDiffSegment(arrayDiffSegment)) { + convertedStringDiff.segments.push({ + omission: arrayDiffSegment.omission.join(''), + cost: arrayDiffSegment.cost + }); + } + if (isAdditionArrayDiffSegment(arrayDiffSegment)) { + convertedStringDiff.segments.push({ + addition: arrayDiffSegment.addition.join(''), + cost: arrayDiffSegment.cost + }); + } + convertedStringDiff.cost += arrayDiffSegment.cost; + } + + return convertedStringDiff; +}; + +const compareStrings = function ( + actual: string, + expected: string, + chunkDelimiter = '' +): StringDiff | EqualDiff { + if (actual === expected) { + return equalDiff({ + value: actual + }); + } + if (actual.length === 1 && expected.length === 1) { + return stringDiff({ + segments: [ + { addition: actual, cost: unequalCharCost / 2 }, + { omission: expected, cost: unequalCharCost / 2 } + ], + cost: unequalCharCost + }); + } + + const actualExploded = actual.split(chunkDelimiter); + const expectedExploded = expected.split(chunkDelimiter); + + const result = compareArrays(actualExploded, expectedExploded); + + if (isEqualDiff(result)) { + return equalDiff({ + value: actual + }); + } + + return convertArrayDiffToStringDiff(result); +}; + +export { + compareStrings +}; diff --git a/lib/comparisons/forSymbols/compareSymbols.ts b/lib/comparisons/forSymbols/compareSymbols.ts new file mode 100644 index 0000000..a33d6a1 --- /dev/null +++ b/lib/comparisons/forSymbols/compareSymbols.ts @@ -0,0 +1,32 @@ +import { compareStrings } from '../forStrings/compareStrings'; +import { unequalSymbolCost } from '../../constants/costs'; +import { equalDiff, EqualDiff, isEqualDiff } from '../../diffs/EqualDiff'; +import { symbolDiff, SymbolDiff } from '../../diffs/forSymbols/SymbolDiff'; + +const compareSymbols = function ( + actual: symbol, + expected: symbol +): SymbolDiff | EqualDiff { + const actualDescription = actual.description ?? ''; + const expectedDescription = expected.description ?? ''; + + const descriptionDiff = compareStrings( + actualDescription, + expectedDescription + ); + + if (isEqualDiff(descriptionDiff)) { + return equalDiff({ + value: actual + }); + } + + return symbolDiff({ + cost: unequalSymbolCost, + descriptionDiff + }); +}; + +export { + compareSymbols +}; diff --git a/lib/comparisons/typeAware/compare.ts b/lib/comparisons/typeAware/compare.ts new file mode 100644 index 0000000..76e5168 --- /dev/null +++ b/lib/comparisons/typeAware/compare.ts @@ -0,0 +1,85 @@ +import { compareArrays } from '../forArrays/compareArrays'; +import { compareBooleans } from '../forBooleans/compareBooleans'; +import { compareErrors } from '../forErrors/compareErrors'; +import { compareFunctions } from '../forFunctions/compareFunctions'; +import { compareMaps } from '../forMaps/compareMaps'; +import { compareNumbers } from '../forNumbers/compareNumbers'; +import { compareObjects } from '../forObjects/compareObjects'; +import { compareRecursions } from '../forRecursions/compareRecursions'; +import { compareResults } from '../forResults/compareResults'; +import { compareSets } from '../forSets/compareSets'; +import { compareStrings } from '../forStrings/compareStrings'; +import { compareSymbols } from '../forSymbols/compareSymbols'; +import { Diff } from '../../diffs/Diff'; +import { equalDiff } from '../../diffs/EqualDiff'; +import { incompatibleTypeDiff } from '../../diffs/IncompatibleTypeDiff'; +import { incompatibleTypesCost } from '../../constants/costs'; +import { isArray } from '../../types/isArray'; +import { isBoolean } from '../../types/isBoolean'; +import { isError } from '../../types/isError'; +import { isFunction } from '../../types/isFunction'; +import { isMap } from '../../types/isMap'; +import { isNull } from '../../types/isNull'; +import { isNumber } from '../../types/isNumber'; +import { isObject } from '../../types/isObject'; +import { isRecursion } from '../../types/Recursion'; +import { isResult } from 'defekt'; +import { isSet } from '../../types/isSet'; +import { isString } from '../../types/isString'; +import { isSymbol } from '../../types/isSymbol'; +import { isUndefined } from '../../types/isUndefined'; + +const compare = function (actual: any, expected: any): Diff { + if (isUndefined(actual) && isUndefined(expected)) { + return equalDiff({ value: actual }); + } + if (isNull(actual) && isNull(expected)) { + return equalDiff({ value: actual }); + } + if (isRecursion(actual) && isRecursion(expected)) { + return compareRecursions(actual, expected); + } + if (isError(actual) && isError(expected)) { + return compareErrors(actual, expected); + } + if (isSet(actual) && isSet(expected)) { + return compareSets(actual, expected); + } + if (isMap(actual) && isMap(expected)) { + return compareMaps(actual, expected); + } + if (isArray(actual) && isArray(expected)) { + return compareArrays(actual, expected); + } + if (isResult(actual) && isResult(expected)) { + return compareResults(actual, expected); + } + if (isNumber(actual) && isNumber(expected)) { + return compareNumbers(actual, expected); + } + if (isString(actual) && isString(expected)) { + return compareStrings(actual, expected); + } + if (isBoolean(actual) && isBoolean(expected)) { + return compareBooleans(actual, expected); + } + if (isSymbol(actual) && isSymbol(expected)) { + return compareSymbols(actual, expected); + } + if (isFunction(actual) && isFunction(expected)) { + return compareFunctions(actual, expected); + } + if (isObject(actual) && isObject(expected)) { + return compareObjects(actual, expected); + } + + return incompatibleTypeDiff({ + actual, + expected, + cost: incompatibleTypesCost + }); +}; + +export { + compare +}; diff --git a/lib/constants/costs.ts b/lib/constants/costs.ts new file mode 100644 index 0000000..fe26bec --- /dev/null +++ b/lib/constants/costs.ts @@ -0,0 +1,21 @@ +const unequalNumberCost = 1; +const unequalCharCost = 1; +const unequalBooleanCost = 1; +const unequalSymbolCost = 1; +const unequalRecursionCost = 1; +const unequalFunctionCost = 1; +const unequalResultCost = 1; + +const incompatibleTypesCost = 10; + +export { + unequalNumberCost, + unequalCharCost, + unequalBooleanCost, + unequalSymbolCost, + unequalRecursionCost, + unequalFunctionCost, + unequalResultCost, + + incompatibleTypesCost +}; diff --git a/lib/constants/maximumDepth.ts b/lib/constants/maximumDepth.ts new file mode 100644 index 0000000..b19021e --- /dev/null +++ b/lib/constants/maximumDepth.ts @@ -0,0 +1,5 @@ +const maximumDepth = 4; + +export { + maximumDepth +}; diff --git a/lib/constraints/atLeast.ts b/lib/constraints/atLeast.ts deleted file mode 100644 index 74e9f2a..0000000 --- a/lib/constraints/atLeast.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const atLeast = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (compare.greaterThanOrEqual(actual, expected)) { - return; - } - - fail('Expected %s to be at least %s.', [ actual, expected ]); - }; -}; - -atLeast.negated = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (!compare.greaterThanOrEqual(actual, expected)) { - return; - } - - fail('Expected %s not to be at least %s.', [ actual, expected ]); - }; -}; - -export { atLeast }; diff --git a/lib/constraints/atMost.ts b/lib/constraints/atMost.ts deleted file mode 100644 index fd0ae17..0000000 --- a/lib/constraints/atMost.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const atMost = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (compare.lessThanOrEqual(actual, expected)) { - return; - } - - fail('Expected %s to be at most %s.', [ actual, expected ]); - }; -}; - -atMost.negated = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (!compare.lessThanOrEqual(actual, expected)) { - return; - } - - fail('Expected %s to be at most %s.', [ actual, expected ]); - }; -}; - -export { atMost }; diff --git a/lib/constraints/between.ts b/lib/constraints/between.ts deleted file mode 100644 index dc66669..0000000 --- a/lib/constraints/between.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const between = function (actual: any): (expectedLower: number | [] | Record, expectedUpper: number | [] | Record) => void { - return function (expectedLower, expectedUpper): void { - if ( - compare.greaterThanOrEqual(actual, expectedLower) && - compare.lessThanOrEqual(actual, expectedUpper) - ) { - return; - } - - fail('Expected %s to be between %s and %s.', [ actual, expectedLower, expectedUpper ]); - }; -}; - -between.negated = function (actual: any): (expectedLower: number | [] | Record, expectedUpper: number | [] | Record) => void { - return function (expectedLower, expectedUpper): void { - if ( - !compare.greaterThanOrEqual(actual, expectedLower) || - !compare.lessThanOrEqual(actual, expectedUpper) - ) { - return; - } - - fail('Expected %s not to be between %s and %s.', [ actual, expectedLower, expectedUpper ]); - }; -}; - -export { between }; diff --git a/lib/constraints/containing.ts b/lib/constraints/containing.ts deleted file mode 100644 index 7ce87b4..0000000 --- a/lib/constraints/containing.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { fail } from '../fail'; - -const containing = function (actual: any): (expected: any) => void { - return function (expected): void { - if (actual.includes(expected)) { - return; - } - - fail('Expected %s to contain %s.', [ actual, expected ]); - }; -}; - -containing.negated = function (actual: any): (expected: any) => void { - return function (expected): void { - if (!actual.includes(expected)) { - return; - } - - fail('Expected %s not to contain %s.', [ actual, expected ]); - }; -}; - -export { containing }; diff --git a/lib/constraints/containingAllOf.ts b/lib/constraints/containingAllOf.ts deleted file mode 100644 index c1cc1a9..0000000 --- a/lib/constraints/containingAllOf.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { fail } from '../fail'; - -const containsAll = function (actual: any[], expected: any[]): boolean { - return expected.every((element: any): boolean => actual.includes(element)); -}; - -const containingAllOf = function (actual: any): (expected: any[]) => void { - return function (expected): void { - if (containsAll(actual, expected)) { - return; - } - - fail('Expected %s to contain all of %s.', [ actual, expected ]); - }; -}; - -containingAllOf.negated = function (actual: any): (expected: any[]) => void { - return function (expected): void { - if (!containsAll(actual, expected)) { - return; - } - - fail('Expected %s not to contain all of %s.', [ actual, expected ]); - }; -}; - -export { containingAllOf }; diff --git a/lib/constraints/containingAnyOf.ts b/lib/constraints/containingAnyOf.ts deleted file mode 100644 index c501d7a..0000000 --- a/lib/constraints/containingAnyOf.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { fail } from '../fail'; - -const containsAny = function (actual: any[], expected: any[]): boolean { - return expected.some((element: any): boolean => actual.includes(element)); -}; - -const containingAnyOf = function (actual: any): (expected: any[]) => void { - return function (expected): void { - if (containsAny(actual, expected)) { - return; - } - - fail('Expected %s to contain any of %s.', [ actual, expected ]); - }; -}; - -containingAnyOf.negated = function (actual: any): (expected: any[]) => void { - return function (expected): void { - if (!containsAny(actual, expected)) { - return; - } - - fail('Expected %s not to contain any of %s.', [ actual, expected ]); - }; -}; - -export { containingAnyOf }; diff --git a/lib/constraints/endingWith.ts b/lib/constraints/endingWith.ts deleted file mode 100644 index 677b06e..0000000 --- a/lib/constraints/endingWith.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { fail } from '../fail'; - -const endingWith = function (actual: any): (expected: string) => void { - return function (expected): void { - if ( - actual.length >= expected.length && - actual.lastIndexOf(expected) === actual.length - expected.length - ) { - return; - } - - fail('Expected %s to end with %s.', [ actual, expected ]); - }; -}; - -endingWith.negated = function (actual: any): (expected: string) => void { - return function (expected): void { - if ( - actual.length < expected.length || - actual.lastIndexOf(expected) !== actual.length - expected.length - ) { - return; - } - - fail('Expected %s not to end with %s.', [ actual, expected ]); - }; -}; - -export { endingWith }; diff --git a/lib/constraints/equalTo.ts b/lib/constraints/equalTo.ts deleted file mode 100644 index 1f00782..0000000 --- a/lib/constraints/equalTo.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const equalTo = function (actual: any): (expected: any) => void { - return function (expected): void { - if (compare.equal(actual, expected)) { - return; - } - - fail('Expected %s to equal %s.', [ actual, expected ]); - }; -}; - -equalTo.negated = function (actual: any): (expected: any) => void { - return function (expected): void { - if (!compare.equal(actual, expected)) { - return; - } - - fail('Expected %s not to equal %s.', [ actual, expected ]); - }; -}; - -export { equalTo }; diff --git a/lib/constraints/false.ts b/lib/constraints/false.ts deleted file mode 100644 index a16c01a..0000000 --- a/lib/constraints/false.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const isFalse = function (actual: any): () => void { - return function (): void { - if (compare.equal(actual, false)) { - return; - } - - fail('Expected %s to be false.', [ actual ]); - }; -}; - -isFalse.negated = function (actual: any): () => void { - return function (): void { - if (!compare.equal(actual, false)) { - return; - } - - fail('Expected %s not to be false.', [ actual ]); - }; -}; - -export { isFalse }; diff --git a/lib/constraints/falsy.ts b/lib/constraints/falsy.ts deleted file mode 100644 index f8bdbbe..0000000 --- a/lib/constraints/falsy.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const falsy = function (actual: any): () => void { - return function (): void { - if (compare.equal(actual, false) || compare.equal(actual, 0) || compare.equal(actual, '') || compare.equal(actual, null) || compare.equal(actual, undefined)) { - return; - } - - fail('Expected %s to be falsy.', [ actual ]); - }; -}; - -falsy.negated = function (actual: any): () => void { - return function (): void { - if (!compare.equal(actual, false) && !compare.equal(actual, 0) && !compare.equal(actual, '') && !compare.equal(actual, null) && !compare.equal(actual, undefined)) { - return; - } - - fail('Expected %s not to be falsy.', [ actual ]); - }; -}; - -export { falsy }; diff --git a/lib/constraints/greaterThan.ts b/lib/constraints/greaterThan.ts deleted file mode 100644 index 43aabe2..0000000 --- a/lib/constraints/greaterThan.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const greaterThan = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (compare.greaterThan(actual, expected)) { - return; - } - - fail('Expected %s to be greater than %s.', [ actual, expected ]); - }; -}; - -greaterThan.negated = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (!compare.greaterThan(actual, expected)) { - return; - } - - fail('Expected %s not to be greater than %s.', [ actual, expected ]); - }; -}; - -export { greaterThan }; diff --git a/lib/constraints/instanceOf.ts b/lib/constraints/instanceOf.ts deleted file mode 100644 index dacf747..0000000 --- a/lib/constraints/instanceOf.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { fail } from '../fail'; - -const instanceOf = function (actual: any): (expected: new(...args: any[]) => Record | Error) => void { - return function (expected): void { - if (actual instanceof expected) { - return; - } - - fail('Expected %s to be an instance of %s.', [ actual, expected ]); - }; -}; - -instanceOf.negated = function (actual: any): (expected: new(...args: any[]) => Record | Error) => void { - return function (expected): void { - if (!(actual instanceof expected)) { - return; - } - - fail('Expected %s not to be an instance of %s.', [ actual, expected ]); - }; -}; - -export { instanceOf }; diff --git a/lib/constraints/lessThan.ts b/lib/constraints/lessThan.ts deleted file mode 100644 index 90da31a..0000000 --- a/lib/constraints/lessThan.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const lessThan = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (compare.lessThan(actual, expected)) { - return; - } - - fail('Expected %s to be less than %s.', [ actual, expected ]); - }; -}; - -lessThan.negated = function (actual: any): (expected: number | [] | Record) => void { - return function (expected): void { - if (!compare.lessThan(actual, expected)) { - return; - } - - fail('Expected %s not to be less than %s.', [ actual, expected ]); - }; -}; - -export { lessThan }; diff --git a/lib/constraints/matching.ts b/lib/constraints/matching.ts deleted file mode 100644 index aad5272..0000000 --- a/lib/constraints/matching.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { fail } from '../fail'; - -const matching = function (actual: any): (expected: RegExp) => void { - return function (expected): void { - if (expected.test(actual)) { - return; - } - - fail('Expected %s to match %s.', [ actual, expected ]); - }; -}; - -matching.negated = function (actual: any): (expected: RegExp) => void { - return function (expected): void { - if (!expected.test(actual)) { - return; - } - - fail('Expected %s not to match %s.', [ actual, expected ]); - }; -}; - -export { matching }; diff --git a/lib/constraints/nan.ts b/lib/constraints/nan.ts deleted file mode 100644 index 7178c08..0000000 --- a/lib/constraints/nan.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { fail } from '../fail'; - -const isNan = function (actual: any): () => void { - return function (): void { - if (typeof actual === 'number' && Number.isNaN(actual)) { - return; - } - - fail('Expected %s to be NaN.', [ actual ]); - }; -}; - -isNan.negated = function (actual: any): () => void { - return function (): void { - if (!(typeof actual === 'number') || !Number.isNaN(actual)) { - return; - } - - fail('Expected %s not to be NaN.', [ actual ]); - }; -}; - -export { isNan }; diff --git a/lib/constraints/null.ts b/lib/constraints/null.ts deleted file mode 100644 index e1a1f7f..0000000 --- a/lib/constraints/null.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const isNull = function (actual: any): () => void { - return function (): void { - if (compare.equal(actual, null)) { - return; - } - - fail('Expected %s to be null.', [ actual ]); - }; -}; - -isNull.negated = function (actual: any): () => void { - return function (): void { - if (!compare.equal(actual, null)) { - return; - } - - fail('Expected %s not to be null.', [ actual ]); - }; -}; - -export { isNull }; diff --git a/lib/constraints/ofType.ts b/lib/constraints/ofType.ts deleted file mode 100644 index 6d1b213..0000000 --- a/lib/constraints/ofType.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { fail } from '../fail'; - -const ofType = function (actual: any): (expected: string) => void { - return function (expected): void { - if (Array.isArray(actual) && (expected === 'array' || expected === 'object')) { - return; - } - - /* eslint-disable valid-typeof */ - if (typeof actual === expected) { - return; - } - /* eslint-enable valid-typeof */ - - fail('Expected %s to be of type %s.', [ actual, expected ]); - }; -}; - -ofType.negated = function (actual: any): (expected: string) => void { - return function (expected): void { - if (Array.isArray(actual) && (expected !== 'array' && expected !== 'object')) { - return; - } - - /* eslint-disable valid-typeof */ - if (typeof actual !== expected) { - return; - } - /* eslint-enable valid-typeof */ - - fail('Expected %s not to be of type %s.', [ actual, expected ]); - }; -}; - -export { ofType }; diff --git a/lib/constraints/sameAs.ts b/lib/constraints/sameAs.ts deleted file mode 100644 index 2a9e1db..0000000 --- a/lib/constraints/sameAs.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const sameAs = function (actual: any): (expected: any) => void { - return function (expected): void { - if (compare.identity(actual, expected)) { - return; - } - - fail('Expected %s to be same as %s.', [ actual, expected ]); - }; -}; - -sameAs.negated = function (actual: any): (expected: any) => void { - return function (expected): void { - if (!compare.identity(actual, expected)) { - return; - } - - fail('Expected %s not to be same as %s.', [ actual, expected ]); - }; -}; - -export { sameAs }; diff --git a/lib/constraints/sameJsonAs.ts b/lib/constraints/sameJsonAs.ts deleted file mode 100644 index dc4d95f..0000000 --- a/lib/constraints/sameJsonAs.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const sameJsonAs = function (actual: any): (expected: any) => void { - return function (expected): void { - if (compare.equal(JSON.stringify(actual), JSON.stringify(expected))) { - return; - } - - fail('Expected %s to equal %s.', [ actual, expected ]); - }; -}; - -sameJsonAs.negated = function (actual: any): (expected: any) => void { - return function (expected): void { - if (!compare.equal(JSON.stringify(actual), JSON.stringify(expected))) { - return; - } - - fail('Expected %s not to equal %s.', [ actual, expected ]); - }; -}; - -export { sameJsonAs }; diff --git a/lib/constraints/startingWith.ts b/lib/constraints/startingWith.ts deleted file mode 100644 index 5b92eb8..0000000 --- a/lib/constraints/startingWith.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { fail } from '../fail'; - -const startingWith = function (actual: any): (expected: string) => void { - return function (expected): void { - if (actual.indexOf(expected) === 0) { - return; - } - - fail('Expected %s to start with %s.', [ actual, expected ]); - }; -}; - -startingWith.negated = function (actual: any): (expected: string) => void { - return function (expected): void { - if (actual.indexOf(expected) !== 0) { - return; - } - - fail('Expected %s not to start with %s.', [ actual, expected ]); - }; -}; - -export { startingWith }; diff --git a/lib/constraints/throwing.ts b/lib/constraints/throwing.ts deleted file mode 100644 index d40821b..0000000 --- a/lib/constraints/throwing.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { fail } from '../fail'; - -/* eslint-disable @typescript-eslint/no-implicit-any-catch */ -const throwing = function (actual: any): (expected?: string | RegExp | ((ex: TError) => boolean)) => void { - return function (expected): void { - try { - actual(); - } catch (ex: any) { - if (!expected && expected !== '') { - return; - } - - if (expected instanceof RegExp && expected.test(ex.message)) { - return; - } - if (typeof expected === 'function') { - if (expected(ex)) { - return; - } - - return fail('Expected %s to fulfill predicate.', [ ex.message ]); - } - - if (ex.message === expected) { - return; - } - - /* eslint-disable consistent-return */ - return fail('Expected %s to equal %s.', [ ex.message, expected ]); - /* eslint-enable consistent-return */ - } - - if (!expected) { - /* eslint-disable consistent-return */ - return fail('Expected an exception.', []); - /* eslint-enable consistent-return */ - } - - fail('Expected an exception with message %s.', [ expected ]); - }; -}; - -throwing.negated = function (actual: any): (expected?: string | RegExp | ((ex: TError) => boolean)) => void { - return function (expected): void { - try { - actual(); - } catch (ex: any) { - if (!expected && expected !== '') { - return fail(`Expected not to throw an exception (received: '${ex.message}').`, []); - } - - if (expected instanceof RegExp && expected.test(ex.message)) { - return fail('Expected not to throw an exception with message %s.', [ expected ]); - } - if (typeof expected === 'function') { - if (expected(ex)) { - return fail('Expected %s not to fulfill predicate.', [ ex.message ]); - } - - return; - } - - if (ex.message === expected) { - return fail('Expected not to throw an exception with message %s.', [ expected ]); - } - } - }; -}; -/* eslint-enable @typescript-eslint/no-implicit-any-catch */ - -export { throwing }; diff --git a/lib/constraints/throwingAsync.ts b/lib/constraints/throwingAsync.ts deleted file mode 100644 index b6a6da9..0000000 --- a/lib/constraints/throwingAsync.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { fail } from '../fail'; - -/* eslint-disable @typescript-eslint/no-implicit-any-catch */ -const throwingAsync = function (actual: any): (expected?: string | RegExp | ((ex: TError) => boolean)) => Promise { - return async function (expected): Promise { - try { - await actual(); - } catch (ex: any) { - if (!expected && expected !== '') { - return; - } - - if (expected instanceof RegExp && expected.test(ex.message)) { - return; - } - if (typeof expected === 'function') { - if (expected(ex)) { - return; - } - - return fail('Expected %s to fulfill predicate.', [ ex.message ]); - } - - if (ex.message === expected) { - return; - } - - /* eslint-disable consistent-return */ - return fail('Expected %s to equal %s.', [ ex.message, expected ]); - /* eslint-enable consistent-return */ - } - - if (!expected) { - /* eslint-disable consistent-return */ - return fail('Expected an exception.', []); - /* eslint-enable consistent-return */ - } - - fail('Expected an exception with message %s.', [ expected ]); - }; -}; - -throwingAsync.negated = function (actual: any): (expected?: string | RegExp | ((ex: TError) => boolean)) => Promise { - return async function (expected): Promise { - try { - await actual(); - } catch (ex: any) { - if (!expected && expected !== '') { - return fail(`Expected not to throw an exception (received: '${ex.message}').`, []); - } - - if (expected instanceof RegExp && expected.test(ex.message)) { - return fail('Expected not to throw an exception with message %s.', [ expected ]); - } - if (typeof expected === 'function') { - if (expected(ex)) { - return fail('Expected %s not to fulfill predicate.', [ ex.message ]); - } - - return; - } - - if (ex.message === expected) { - return fail('Expected not to throw an exception with message %s.', [ expected ]); - } - } - }; -}; -/* eslint-enable @typescript-eslint/no-implicit-any-catch */ - -export { throwingAsync }; diff --git a/lib/constraints/true.ts b/lib/constraints/true.ts deleted file mode 100644 index e07adc8..0000000 --- a/lib/constraints/true.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const isTrue = function (actual: any): () => void { - return function (): void { - if (compare.equal(actual, true)) { - return; - } - - fail('Expected %s to be true.', [ actual ]); - }; -}; - -isTrue.negated = function (actual: any): () => void { - return function (): void { - if (!compare.equal(actual, true)) { - return; - } - - fail('Expected %s not to be true.', [ actual ]); - }; -}; - -export { isTrue }; diff --git a/lib/constraints/undefined.ts b/lib/constraints/undefined.ts deleted file mode 100644 index c4294e8..0000000 --- a/lib/constraints/undefined.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { compare } from 'comparejs'; -import { fail } from '../fail'; - -const isUndefined = function (actual: any): () => void { - return function (): void { - if (compare.equal(actual, undefined)) { - return; - } - - fail('Expected %s to be undefined.', [ actual ]); - }; -}; - -isUndefined.negated = function (actual: any): () => void { - return function (): void { - if (!compare.equal(actual, undefined)) { - return; - } - - fail('Expected %s not to be undefined.', [ actual ]); - }; -}; - -export { isUndefined }; diff --git a/lib/diffs/Diff.ts b/lib/diffs/Diff.ts new file mode 100644 index 0000000..f6b3ba3 --- /dev/null +++ b/lib/diffs/Diff.ts @@ -0,0 +1,7 @@ +interface Diff { + cost: number; +} + +export type { + Diff +}; diff --git a/lib/diffs/EqualDiff.ts b/lib/diffs/EqualDiff.ts new file mode 100644 index 0000000..702dd75 --- /dev/null +++ b/lib/diffs/EqualDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from './Diff'; + +const equalDiffSymbol: unique symbol = Symbol('equal diff'); + +const equalDiff = function ( + parameters: Omit +): EqualDiff { + return { + kind: equalDiffSymbol, + cost: 0, + ...parameters + }; +}; + +interface EqualDiff extends Diff { + kind: typeof equalDiffSymbol; + value: any; +} + +const isEqualDiff = function (diff: any): diff is EqualDiff { + return 'kind' in diff && diff.kind === equalDiffSymbol; +}; + +export type { + EqualDiff +}; + +export { + isEqualDiff, + equalDiff +}; diff --git a/lib/diffs/IncompatibleTypeDiff.ts b/lib/diffs/IncompatibleTypeDiff.ts new file mode 100644 index 0000000..df1cac3 --- /dev/null +++ b/lib/diffs/IncompatibleTypeDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from './Diff'; + +const incompatibleTypeDiffSymbol: unique symbol = Symbol('incompatible type diff'); + +const incompatibleTypeDiff = function ( + parameters: Omit +): IncompatibleTypeDiff { + return { + kind: incompatibleTypeDiffSymbol, + ...parameters + }; +}; + +interface IncompatibleTypeDiff extends Diff { + kind: typeof incompatibleTypeDiffSymbol; + actual: any; + expected: any; +} + +const isIncompatibleTypeDiff = function (diff: any): diff is IncompatibleTypeDiff { + return 'kind' in diff && diff.kind === incompatibleTypeDiffSymbol; +}; + +export type { + IncompatibleTypeDiff +}; + +export { + incompatibleTypeDiff, + isIncompatibleTypeDiff +}; diff --git a/lib/diffs/findAdditions.ts b/lib/diffs/findAdditions.ts new file mode 100644 index 0000000..30b3ea7 --- /dev/null +++ b/lib/diffs/findAdditions.ts @@ -0,0 +1,30 @@ +import { Diff } from './Diff'; +import { findArrayDiffAdditions } from './forArrays/findArrayDiffAdditions'; +import { findMapDiffAdditions } from './forMaps/findMapDiffAdditions'; +import { findObjectDiffAdditions } from './forObjects/findObjectDiffAdditions'; +import { findSetDiffAdditions } from './forSets/findSetDiffAdditions'; +import { isArrayDiff } from './forArrays/ArrayDiff'; +import { isMapDiff } from './forMaps/MapDiff'; +import { isObjectDiff } from './forObjects/ObjectDiff'; +import { isSetDiff } from './forSets/SetDiff'; + +const findAdditions = function (diff: Diff): Diff { + if (isArrayDiff(diff)) { + return findArrayDiffAdditions(diff); + } + if (isObjectDiff(diff)) { + return findObjectDiffAdditions(diff); + } + if (isSetDiff(diff)) { + return findSetDiffAdditions(diff); + } + if (isMapDiff(diff)) { + return findMapDiffAdditions(diff); + } + + return diff; +}; + +export { + findAdditions +}; diff --git a/lib/diffs/findOmissions.ts b/lib/diffs/findOmissions.ts new file mode 100644 index 0000000..a23fd9f --- /dev/null +++ b/lib/diffs/findOmissions.ts @@ -0,0 +1,30 @@ +import { Diff } from './Diff'; +import { findArrayDiffOmissions } from './forArrays/findArrayDiffOmissions'; +import { findMapDiffOmissions } from './forMaps/findMapDiffOmissions'; +import { findObjectDiffOmissions } from './forObjects/findObjectDiffOmissions'; +import { findSetDiffOmissions } from './forSets/findSetDiffOmissions'; +import { isArrayDiff } from './forArrays/ArrayDiff'; +import { isMapDiff } from './forMaps/MapDiff'; +import { isObjectDiff } from './forObjects/ObjectDiff'; +import { isSetDiff } from './forSets/SetDiff'; + +const findOmissions = function (diff: Diff): Diff { + if (isArrayDiff(diff)) { + return findArrayDiffOmissions(diff); + } + if (isObjectDiff(diff)) { + return findObjectDiffOmissions(diff); + } + if (isSetDiff(diff)) { + return findSetDiffOmissions(diff); + } + if (isMapDiff(diff)) { + return findMapDiffOmissions(diff); + } + + return diff; +}; + +export { + findOmissions +}; diff --git a/lib/diffs/forArrays/ArrayDiff.ts b/lib/diffs/forArrays/ArrayDiff.ts new file mode 100644 index 0000000..4366d1f --- /dev/null +++ b/lib/diffs/forArrays/ArrayDiff.ts @@ -0,0 +1,31 @@ +import { ArrayDiffSegment } from './ArrayDiffSegment'; +import { Diff } from '../Diff'; + +const arrayDiffSymbol: unique symbol = Symbol('array diff'); + +const arrayDiff = function ( + parameters: Omit, 'kind'> +): ArrayDiff { + return { + kind: arrayDiffSymbol, + ...parameters + }; +}; + +interface ArrayDiff extends Diff { + kind: typeof arrayDiffSymbol; + segments: ArrayDiffSegment[]; +} + +const isArrayDiff = function (diff: any): diff is ArrayDiff { + return 'kind' in diff && diff.kind === arrayDiffSymbol; +}; + +export type { + ArrayDiff +}; + +export { + arrayDiff, + isArrayDiff +}; diff --git a/lib/diffs/forArrays/ArrayDiffSegment.ts b/lib/diffs/forArrays/ArrayDiffSegment.ts new file mode 100644 index 0000000..03a5d4a --- /dev/null +++ b/lib/diffs/forArrays/ArrayDiffSegment.ts @@ -0,0 +1,59 @@ +import { Diff } from '../Diff'; + +interface EqualDiffSegment extends Diff { + equal: TContent[]; +} +interface ChangeDiffSegment extends Diff { + change: Diff[]; +} +interface OmissionDiffSegment extends Diff { + omission: TContent[]; +} +interface AdditionDiffSegment extends Diff { + addition: TContent[]; +} + +type ArrayDiffSegment = + EqualDiffSegment | + ChangeDiffSegment | + OmissionDiffSegment | + AdditionDiffSegment; + +const isEqualDiffSegment = function ( + value: ArrayDiffSegment +): value is EqualDiffSegment { + return 'equal' in value; +}; + +const isChangeDiffSegment = function ( + value: ArrayDiffSegment +): value is ChangeDiffSegment { + return 'change' in value; +}; + +const isOmissionDiffSegment = function ( + value: ArrayDiffSegment +): value is OmissionDiffSegment { + return 'omission' in value; +}; + +const isAdditionDiffSegment = function ( + value: ArrayDiffSegment +): value is AdditionDiffSegment { + return 'addition' in value; +}; + +export type { + EqualDiffSegment, + ChangeDiffSegment, + OmissionDiffSegment, + AdditionDiffSegment, + ArrayDiffSegment +}; + +export { + isEqualDiffSegment, + isChangeDiffSegment, + isOmissionDiffSegment, + isAdditionDiffSegment +}; diff --git a/lib/diffs/forArrays/findArrayDiffAdditions.ts b/lib/diffs/forArrays/findArrayDiffAdditions.ts new file mode 100644 index 0000000..4ac7de6 --- /dev/null +++ b/lib/diffs/forArrays/findArrayDiffAdditions.ts @@ -0,0 +1,43 @@ +import { Diff } from '../Diff'; +import { findAdditions } from '../findAdditions'; +import { sum } from '../../utils/sum'; +import { arrayDiff, ArrayDiff } from './ArrayDiff'; +import { + ArrayDiffSegment, isAdditionDiffSegment, + isChangeDiffSegment, + isEqualDiffSegment +} from './ArrayDiffSegment'; + +const findArrayDiffAdditions = function (diff: ArrayDiff): ArrayDiff { + const filteredSegments = diff.segments. + map((segment: ArrayDiffSegment): ArrayDiffSegment => { + if (isChangeDiffSegment(segment)) { + const newSegmentContent = segment.change. + map((change): Diff => findAdditions(change)). + filter((change): boolean => change.cost > 0); + const newCost = sum(newSegmentContent.map((change): number => change.cost)); + + return { + change: newSegmentContent, + cost: newCost + }; + } + + return segment; + }). + filter( + (segment): boolean => + !isEqualDiffSegment(segment) && + !isAdditionDiffSegment(segment) && + segment.cost > 0 + ); + + return arrayDiff({ + cost: sum(filteredSegments.map((segment): number => segment.cost)), + segments: filteredSegments + }); +}; + +export { + findArrayDiffAdditions +}; diff --git a/lib/diffs/forArrays/findArrayDiffOmissions.ts b/lib/diffs/forArrays/findArrayDiffOmissions.ts new file mode 100644 index 0000000..151a62b --- /dev/null +++ b/lib/diffs/forArrays/findArrayDiffOmissions.ts @@ -0,0 +1,43 @@ +import { Diff } from '../Diff'; +import { findOmissions } from '../findOmissions'; +import { sum } from '../../utils/sum'; +import { arrayDiff, ArrayDiff } from './ArrayDiff'; +import { + ArrayDiffSegment, isAdditionDiffSegment, + isChangeDiffSegment, + isEqualDiffSegment +} from './ArrayDiffSegment'; + +const findArrayDiffOmissions = function (diff: ArrayDiff): ArrayDiff { + const filteredSegments = diff.segments. + map((segment: ArrayDiffSegment): ArrayDiffSegment => { + if (isChangeDiffSegment(segment)) { + const newSegmentContent = segment.change. + map((change): Diff => findOmissions(change)). + filter((change): boolean => change.cost > 0); + const newCost = sum(newSegmentContent.map((change): number => change.cost)); + + return { + change: newSegmentContent, + cost: newCost + }; + } + + return segment; + }). + filter( + (segment): boolean => + !isEqualDiffSegment(segment) && + !isAdditionDiffSegment(segment) && + segment.cost > 0 + ); + + return arrayDiff({ + cost: sum(filteredSegments.map((segment): number => segment.cost)), + segments: filteredSegments + }); +}; + +export { + findArrayDiffOmissions +}; diff --git a/lib/diffs/forArrays/simplifyDiff.ts b/lib/diffs/forArrays/simplifyDiff.ts new file mode 100644 index 0000000..ab7523c --- /dev/null +++ b/lib/diffs/forArrays/simplifyDiff.ts @@ -0,0 +1,43 @@ +import { arrayDiff, ArrayDiff } from './ArrayDiff'; +import { isAdditionDiffSegment, isChangeDiffSegment, isEqualDiffSegment, isOmissionDiffSegment } from './ArrayDiffSegment'; + +const simplifyDiff = function ( + diff: ArrayDiff +): ArrayDiff { + if (diff.segments.length === 0) { + return diff; + } + + const newDiff: ArrayDiff = arrayDiff({ + segments: [ diff.segments[0] ], + cost: diff.segments[0].cost + }); + + for (const currentSegment of diff.segments.slice(1)) { + const previousSegment = newDiff.segments.slice(-1)[0]; + + if (isEqualDiffSegment(currentSegment) && isEqualDiffSegment(previousSegment)) { + previousSegment.equal = [ ...previousSegment.equal, ...currentSegment.equal ]; + previousSegment.cost += currentSegment.cost; + } else if (isChangeDiffSegment(currentSegment) && isChangeDiffSegment(previousSegment)) { + previousSegment.change = [ ...previousSegment.change, ...currentSegment.change ]; + previousSegment.cost += currentSegment.cost; + } else if (isOmissionDiffSegment(currentSegment) && isOmissionDiffSegment(previousSegment)) { + previousSegment.omission = [ ...previousSegment.omission, ...currentSegment.omission ]; + previousSegment.cost += currentSegment.cost; + } else if (isAdditionDiffSegment(currentSegment) && isAdditionDiffSegment(previousSegment)) { + previousSegment.addition = [ ...previousSegment.addition, ...currentSegment.addition ]; + previousSegment.cost += currentSegment.cost; + } else { + newDiff.segments.push(currentSegment); + } + + newDiff.cost += currentSegment.cost; + } + + return newDiff; +}; + +export { + simplifyDiff +}; diff --git a/lib/diffs/forBooleans/BooleanDiff.ts b/lib/diffs/forBooleans/BooleanDiff.ts new file mode 100644 index 0000000..bc08a67 --- /dev/null +++ b/lib/diffs/forBooleans/BooleanDiff.ts @@ -0,0 +1,32 @@ +import { Diff } from '../Diff'; + +const booleanDiffSymbol: unique symbol = Symbol('boolean diff'); + +const booleanDiff = function ( + parameters: Omit +): BooleanDiff { + return { + kind: booleanDiffSymbol, + ...parameters + }; +}; + +interface BooleanDiff extends Diff { + kind: typeof booleanDiffSymbol; + cost: number; + actual?: boolean; + expected?: boolean; +} + +const isBooleanDiff = function (diff: any): diff is BooleanDiff { + return 'kind' in diff && diff.kind === booleanDiffSymbol; +}; + +export type { + BooleanDiff +}; + +export { + booleanDiff, + isBooleanDiff +}; diff --git a/lib/diffs/forErrors/ErrorDiff.ts b/lib/diffs/forErrors/ErrorDiff.ts new file mode 100644 index 0000000..a2ecb7a --- /dev/null +++ b/lib/diffs/forErrors/ErrorDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from '../Diff'; +import { ObjectDiff } from '../forObjects/ObjectDiff'; + +const errorDiffSymbol: unique symbol = Symbol('error diff'); + +const errorDiff = function ( + parameters: Omit +): ErrorDiff { + return { + kind: errorDiffSymbol, + ...parameters + }; +}; + +interface ErrorDiff extends Diff { + kind: typeof errorDiffSymbol; + objectDiff: ObjectDiff; +} + +const isErrorDiff = function (diff: any): diff is ErrorDiff { + return 'kind' in diff && diff.kind === errorDiffSymbol; +}; + +export type { + ErrorDiff +}; + +export { + isErrorDiff, + errorDiff +}; diff --git a/lib/diffs/forFunctions/FunctionDiff.ts b/lib/diffs/forFunctions/FunctionDiff.ts new file mode 100644 index 0000000..f1555c4 --- /dev/null +++ b/lib/diffs/forFunctions/FunctionDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from '../Diff'; +import { StringDiff } from '../forStrings/StringDiff'; + +const functionDiffSymbol: unique symbol = Symbol('function diff'); + +const functionDiff = function ( + parameters: Omit +): FunctionDiff { + return { + kind: functionDiffSymbol, + ...parameters + }; +}; + +interface FunctionDiff extends Diff { + kind: typeof functionDiffSymbol; + stringRepresentationDiff: StringDiff; +} + +const isFunctionDiff = function (diff: any): diff is FunctionDiff { + return 'kind' in diff && diff.kind === functionDiffSymbol; +}; + +export type { + FunctionDiff +}; + +export { + functionDiff, + isFunctionDiff +}; diff --git a/lib/diffs/forMaps/MapDiff.ts b/lib/diffs/forMaps/MapDiff.ts new file mode 100644 index 0000000..467d5e0 --- /dev/null +++ b/lib/diffs/forMaps/MapDiff.ts @@ -0,0 +1,33 @@ +import { Diff } from '../Diff'; + +const mapDiffSymbol: unique symbol = Symbol('map diff'); + +const mapDiff = function ( + parameters: Omit +): MapDiff { + return { + kind: mapDiffSymbol, + ...parameters + }; +}; + +interface MapDiff extends Diff { + kind: typeof mapDiffSymbol; + additions: Map; + omissions: Map; + changes: Map; + equal: Map; +} + +const isMapDiff = function (diff: any): diff is MapDiff { + return 'kind' in diff && diff.kind === mapDiffSymbol; +}; + +export type { + MapDiff +}; + +export { + isMapDiff, + mapDiff +}; diff --git a/lib/diffs/forMaps/findMapDiffAdditions.ts b/lib/diffs/forMaps/findMapDiffAdditions.ts new file mode 100644 index 0000000..bf09844 --- /dev/null +++ b/lib/diffs/forMaps/findMapDiffAdditions.ts @@ -0,0 +1,36 @@ +import { Diff } from '../Diff'; +import { findAdditions } from '../findAdditions'; +import { size } from '../../size/typeAware/size'; +import { sum } from '../../utils/sum'; +import { mapDiff, MapDiff } from './MapDiff'; + +const findMapDiffAdditions = function (diff: MapDiff): MapDiff { + const filteredChanges = new Map( + [ ...diff.changes.entries() ]. + map( + ([ key, change ]): [string, Diff] => [ key, findAdditions(change) ] + ). + filter(([ , change ]): boolean => change.cost > 0) + ); + + const keptAdditionsCost = sum( + [ ...diff.additions.values() ]. + map((addition): number => size(addition)) + ); + const keptChangesCost = sum( + [ ...filteredChanges.values() ]. + map((change): number => change.cost) + ); + + return mapDiff({ + cost: keptAdditionsCost + keptChangesCost, + additions: diff.additions, + changes: filteredChanges, + omissions: new Map(), + equal: new Map() + }); +}; + +export { + findMapDiffAdditions +}; diff --git a/lib/diffs/forMaps/findMapDiffOmissions.ts b/lib/diffs/forMaps/findMapDiffOmissions.ts new file mode 100644 index 0000000..fd7b22b --- /dev/null +++ b/lib/diffs/forMaps/findMapDiffOmissions.ts @@ -0,0 +1,36 @@ +import { Diff } from '../Diff'; +import { findOmissions } from '../findOmissions'; +import { size } from '../../size/typeAware/size'; +import { sum } from '../../utils/sum'; +import { mapDiff, MapDiff } from './MapDiff'; + +const findMapDiffOmissions = function (diff: MapDiff): MapDiff { + const filteredChanges = new Map( + [ ...diff.changes.entries() ]. + map( + ([ key, change ]): [string, Diff] => [ key, findOmissions(change) ] + ). + filter(([ , change ]): boolean => change.cost > 0) + ); + + const keptOmissionsCost = sum( + [ ...diff.omissions.values() ]. + map((omission): number => size(omission)) + ); + const keptChangesCost = sum( + [ ...filteredChanges.values() ]. + map((change): number => change.cost) + ); + + return mapDiff({ + cost: keptOmissionsCost + keptChangesCost, + omissions: diff.omissions, + changes: filteredChanges, + additions: new Map(), + equal: new Map() + }); +}; + +export { + findMapDiffOmissions +}; diff --git a/lib/diffs/forNumbers/NumberDiff.ts b/lib/diffs/forNumbers/NumberDiff.ts new file mode 100644 index 0000000..40ef479 --- /dev/null +++ b/lib/diffs/forNumbers/NumberDiff.ts @@ -0,0 +1,32 @@ +import { Diff } from '../Diff'; + +const numberDiffSymbol: unique symbol = Symbol('number diff'); + +const numberDiff = function ( + parameters: Omit +): NumberDiff { + return { + kind: numberDiffSymbol, + ...parameters + }; +}; + +interface NumberDiff extends Diff { + kind: typeof numberDiffSymbol; + actual?: number; + expected?: number; + difference: number; +} + +const isNumberDiff = function (diff: any): diff is NumberDiff { + return 'kind' in diff && diff.kind === numberDiffSymbol; +}; + +export type { + NumberDiff +}; + +export { + isNumberDiff, + numberDiff +}; diff --git a/lib/diffs/forObjects/ObjectDiff.ts b/lib/diffs/forObjects/ObjectDiff.ts new file mode 100644 index 0000000..a9aa4f9 --- /dev/null +++ b/lib/diffs/forObjects/ObjectDiff.ts @@ -0,0 +1,33 @@ +import { Diff } from '../Diff'; + +const objectDiffSymbol: unique symbol = Symbol('object diff'); + +const objectDiff = function ( + parameters: Omit +): ObjectDiff { + return { + kind: objectDiffSymbol, + ...parameters + }; +}; + +interface ObjectDiff extends Diff { + kind: typeof objectDiffSymbol; + additions: Record; + omissions: Record; + changes: Record; + equal: Record; +} + +const isObjectDiff = function (diff: any): diff is ObjectDiff { + return 'kind' in diff && diff.kind === objectDiffSymbol; +}; + +export type { + ObjectDiff +}; + +export { + isObjectDiff, + objectDiff +}; diff --git a/lib/diffs/forObjects/findObjectDiffAdditions.ts b/lib/diffs/forObjects/findObjectDiffAdditions.ts new file mode 100644 index 0000000..8e919bb --- /dev/null +++ b/lib/diffs/forObjects/findObjectDiffAdditions.ts @@ -0,0 +1,36 @@ +import { Diff } from '../Diff'; +import { findAdditions } from '../findAdditions'; +import { size } from '../../size/typeAware/size'; +import { sum } from '../../utils/sum'; +import { objectDiff, ObjectDiff } from './ObjectDiff'; + +const findObjectDiffAdditions = function (diff: ObjectDiff): ObjectDiff { + const filteredChanges = Object.fromEntries( + Object.entries(diff.changes). + map( + ([ key, change ]): [string, Diff] => [ key, findAdditions(change) ] + ). + filter(([ , change ]): boolean => change.cost > 0) + ); + + const keptAdditionsCost = sum( + Object.values(diff.additions). + map((addition): number => size(addition)) + ); + const keptChangesCost = sum( + Object.values(filteredChanges). + map((change): number => change.cost) + ); + + return objectDiff({ + cost: keptAdditionsCost + keptChangesCost, + additions: diff.additions, + changes: filteredChanges, + omissions: {}, + equal: {} + }); +}; + +export { + findObjectDiffAdditions +}; diff --git a/lib/diffs/forObjects/findObjectDiffOmissions.ts b/lib/diffs/forObjects/findObjectDiffOmissions.ts new file mode 100644 index 0000000..78fd3c4 --- /dev/null +++ b/lib/diffs/forObjects/findObjectDiffOmissions.ts @@ -0,0 +1,36 @@ +import { Diff } from '../Diff'; +import { findOmissions } from '../findOmissions'; +import { size } from '../../size/typeAware/size'; +import { sum } from '../../utils/sum'; +import { objectDiff, ObjectDiff } from './ObjectDiff'; + +const findObjectDiffOmissions = function (diff: ObjectDiff): ObjectDiff { + const filteredChanges = Object.fromEntries( + Object.entries(diff.changes). + map( + ([ key, change ]): [string, Diff] => [ key, findOmissions(change) ] + ). + filter(([ , change ]): boolean => change.cost > 0) + ); + + const keptOmissionsCost = sum( + Object.values(diff.omissions). + map((omission): number => size(omission)) + ); + const keptChangesCost = sum( + Object.values(filteredChanges). + map((change): number => change.cost) + ); + + return objectDiff({ + cost: keptOmissionsCost + keptChangesCost, + omissions: diff.omissions, + changes: filteredChanges, + additions: {}, + equal: {} + }); +}; + +export { + findObjectDiffOmissions +}; diff --git a/lib/diffs/forRecursions/RecursionDiff.ts b/lib/diffs/forRecursions/RecursionDiff.ts new file mode 100644 index 0000000..9c17879 --- /dev/null +++ b/lib/diffs/forRecursions/RecursionDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from '../Diff'; +import { StringDiff } from '../forStrings/StringDiff'; + +const recursionDiffSymbol: unique symbol = Symbol('recursion diff'); + +const recursionDiff = function ( + parameters: Omit +): RecursionDiff { + return { + kind: recursionDiffSymbol, + ...parameters + }; +}; + +interface RecursionDiff extends Diff { + kind: typeof recursionDiffSymbol; + recursionPathDiff: StringDiff; +} + +const isRecursionDiff = function (diff: any): diff is RecursionDiff { + return 'kind' in diff && diff.kind === recursionDiffSymbol; +}; + +export type { + RecursionDiff +}; + +export { + isRecursionDiff, + recursionDiff +}; diff --git a/lib/diffs/forResults/ResultDiff.ts b/lib/diffs/forResults/ResultDiff.ts new file mode 100644 index 0000000..3d76c62 --- /dev/null +++ b/lib/diffs/forResults/ResultDiff.ts @@ -0,0 +1,122 @@ +import { Diff } from '../Diff'; +import { Result } from 'defekt'; + +const unequalValueResultDiffSymbol: unique symbol = Symbol('unequal value result diff'); +const unequalErrorResultDiffSymbol: unique symbol = Symbol('unequal error result diff'); +const expectedValueGotErrorResultDiffSymbol: unique symbol = Symbol('expected value got error result diff'); +const expectedErrorGotValueResultDiffSymbol: unique symbol = Symbol('expected error got value result diff'); + +const unequalValueResultDiff = function ( + parameters: Omit +): UnequalValueResultDiff { + return { + kind: unequalValueResultDiffSymbol, + ...parameters + }; +}; + +const unequalErrorResultDiff = function ( + parameters: Omit +): UnequalErrorResultDiff { + return { + kind: unequalErrorResultDiffSymbol, + ...parameters + }; +}; + +const expectedValueGotErrorResultDiff = function ( + parameters: Omit +): ExpectedValueGotErrorResultDiff { + return { + kind: expectedValueGotErrorResultDiffSymbol, + ...parameters + }; +}; + +const expectedErrorGotValueResultDiff = function ( + parameters: Omit +): ExpectedErrorGotValueResultDiff { + return { + kind: expectedErrorGotValueResultDiffSymbol, + ...parameters + }; +}; + +interface UnequalValueResultDiff extends Diff { + kind: typeof unequalValueResultDiffSymbol; + diff: Diff; +} + +interface UnequalErrorResultDiff extends Diff { + kind: typeof unequalErrorResultDiffSymbol; + diff: Diff; +} + +interface ExpectedValueGotErrorResultDiff extends Diff { + kind: typeof expectedValueGotErrorResultDiffSymbol; + actual: Result; + expected: Result; +} + +interface ExpectedErrorGotValueResultDiff extends Diff { + kind: typeof expectedErrorGotValueResultDiffSymbol; + actual: Result; + expected: Result; +} + +type ResultDiff = + UnequalValueResultDiff | + UnequalErrorResultDiff | + ExpectedValueGotErrorResultDiff | + ExpectedErrorGotValueResultDiff; + +const isUnequalValueResultDiff = function ( + diff: any +): diff is UnequalValueResultDiff { + return 'kind' in diff && diff.kind === unequalValueResultDiffSymbol; +}; + +const isUnequalErrorResultDiff = function ( + diff: any +): diff is UnequalErrorResultDiff { + return 'kind' in diff && diff.kind === unequalErrorResultDiffSymbol; +}; + +const isExpectedValueGotErrorResultDiff = function ( + diff: any +): diff is ExpectedValueGotErrorResultDiff { + return 'kind' in diff && diff.kind === expectedValueGotErrorResultDiffSymbol; +}; + +const isExpectedErrorGotValueResultDiff = function ( + diff: any +): diff is ExpectedErrorGotValueResultDiff { + return 'kind' in diff && diff.kind === expectedErrorGotValueResultDiffSymbol; +}; + +const isResultDiff = function (diff: any): diff is ResultDiff { + return isUnequalValueResultDiff(diff) || + isUnequalErrorResultDiff(diff) || + isExpectedValueGotErrorResultDiff(diff) || + isExpectedErrorGotValueResultDiff(diff); +}; + +export type { + ExpectedValueGotErrorResultDiff, + ExpectedErrorGotValueResultDiff, + ResultDiff, + UnequalValueResultDiff, + UnequalErrorResultDiff +}; + +export { + expectedErrorGotValueResultDiff, + expectedValueGotErrorResultDiff, + isExpectedValueGotErrorResultDiff, + isExpectedErrorGotValueResultDiff, + isResultDiff, + isUnequalErrorResultDiff, + isUnequalValueResultDiff, + unequalErrorResultDiff, + unequalValueResultDiff +}; diff --git a/lib/diffs/forSets/SetDiff.ts b/lib/diffs/forSets/SetDiff.ts new file mode 100644 index 0000000..5c5360a --- /dev/null +++ b/lib/diffs/forSets/SetDiff.ts @@ -0,0 +1,32 @@ +import { Diff } from '../Diff'; + +const setDiffSymbol: unique symbol = Symbol('set diff'); + +const setDiff = function ( + parameters: Omit +): SetDiff { + return { + kind: setDiffSymbol, + ...parameters + }; +}; + +interface SetDiff extends Diff { + kind: typeof setDiffSymbol; + additions: Set; + omissions: Set; + equal: Set; +} + +const isSetDiff = function (diff: any): diff is SetDiff { + return 'kind' in diff && diff.kind === setDiffSymbol; +}; + +export type { + SetDiff +}; + +export { + isSetDiff, + setDiff +}; diff --git a/lib/diffs/forSets/findSetDiffAdditions.ts b/lib/diffs/forSets/findSetDiffAdditions.ts new file mode 100644 index 0000000..8411841 --- /dev/null +++ b/lib/diffs/forSets/findSetDiffAdditions.ts @@ -0,0 +1,20 @@ +import { sum } from '../../utils/sum'; +import { setDiff, SetDiff } from './SetDiff'; + +const findSetDiffAdditions = function (diff: SetDiff): SetDiff { + const keptAdditionsCost = sum( + [ ...diff.additions.values() ]. + map((addition): number => addition.cost) + ); + + return setDiff({ + cost: keptAdditionsCost, + additions: diff.additions, + omissions: new Set(), + equal: new Set() + }); +}; + +export { + findSetDiffAdditions +}; diff --git a/lib/diffs/forSets/findSetDiffOmissions.ts b/lib/diffs/forSets/findSetDiffOmissions.ts new file mode 100644 index 0000000..a044e7f --- /dev/null +++ b/lib/diffs/forSets/findSetDiffOmissions.ts @@ -0,0 +1,20 @@ +import { sum } from '../../utils/sum'; +import { setDiff, SetDiff } from './SetDiff'; + +const findSetDiffOmissions = function (diff: SetDiff): SetDiff { + const keptOmissionsCost = sum( + [ ...diff.omissions.values() ]. + map((omission): number => omission.cost) + ); + + return setDiff({ + cost: keptOmissionsCost, + omissions: diff.omissions, + additions: new Set(), + equal: new Set() + }); +}; + +export { + findSetDiffOmissions +}; diff --git a/lib/diffs/forStrings/StringDiff.ts b/lib/diffs/forStrings/StringDiff.ts new file mode 100644 index 0000000..db04ae9 --- /dev/null +++ b/lib/diffs/forStrings/StringDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from '../Diff'; +import { StringDiffSegment } from './StringDiffSegment'; + +const stringDiffSymbol: unique symbol = Symbol('string diff'); + +const stringDiff = function ( + parameters: Omit +): StringDiff { + return { + kind: stringDiffSymbol, + ...parameters + }; +}; + +interface StringDiff extends Diff { + kind: typeof stringDiffSymbol; + segments: StringDiffSegment[]; +} + +const isStringDiff = function (diff: any): diff is StringDiff { + return 'kind' in diff && diff.kind === stringDiffSymbol; +}; + +export type { + StringDiff +}; + +export { + stringDiff, + isStringDiff +}; diff --git a/lib/diffs/forStrings/StringDiffSegment.ts b/lib/diffs/forStrings/StringDiffSegment.ts new file mode 100644 index 0000000..d14d56b --- /dev/null +++ b/lib/diffs/forStrings/StringDiffSegment.ts @@ -0,0 +1,63 @@ +import { Diff } from '../Diff'; + +interface EqualDiffSegment extends Diff { + equal: string; +} + +interface ReplaceDiffSegment extends Diff { + replace: string; + replaceWith: string; +} + +interface OmissionDiffSegment extends Diff { + omission: string; +} + +interface AdditionDiffSegment extends Diff { + addition: string; +} + +type StringDiffSegment = + EqualDiffSegment | + ReplaceDiffSegment | + OmissionDiffSegment | + AdditionDiffSegment; + +const isEqualDiffSegment = function ( + value: StringDiffSegment +): value is EqualDiffSegment { + return 'equal' in value; +}; + +const isReplaceDiffSegment = function ( + value: StringDiffSegment +): value is ReplaceDiffSegment { + return 'replace' in value && 'replaceWith' in value; +}; + +const isOmissionDiffSegment = function ( + value: StringDiffSegment +): value is OmissionDiffSegment { + return 'omission' in value; +}; + +const isAdditionDiffSegment = function ( + value: StringDiffSegment +): value is AdditionDiffSegment { + return 'addition' in value; +}; + +export type { + EqualDiffSegment, + ReplaceDiffSegment, + OmissionDiffSegment, + AdditionDiffSegment, + StringDiffSegment +}; + +export { + isEqualDiffSegment, + isReplaceDiffSegment, + isOmissionDiffSegment, + isAdditionDiffSegment +}; diff --git a/lib/diffs/forSymbols/SymbolDiff.ts b/lib/diffs/forSymbols/SymbolDiff.ts new file mode 100644 index 0000000..b2ce324 --- /dev/null +++ b/lib/diffs/forSymbols/SymbolDiff.ts @@ -0,0 +1,31 @@ +import { Diff } from '../Diff'; +import { StringDiff } from '../forStrings/StringDiff'; + +const symbolDiffSymbol: unique symbol = Symbol('symbol diff'); + +const symbolDiff = function ( + parameters: Omit +): SymbolDiff { + return { + kind: symbolDiffSymbol, + ...parameters + }; +}; + +interface SymbolDiff extends Diff { + kind: typeof symbolDiffSymbol; + descriptionDiff: StringDiff; +} + +const isSymbolDiff = function (diff: any): diff is SymbolDiff { + return 'kind' in diff && diff.kind === symbolDiffSymbol; +}; + +export type { + SymbolDiff +}; + +export { + isSymbolDiff, + symbolDiff +}; diff --git a/lib/dispel/Ancestors.ts b/lib/dispel/Ancestors.ts new file mode 100644 index 0000000..eff2e69 --- /dev/null +++ b/lib/dispel/Ancestors.ts @@ -0,0 +1,11 @@ +interface Ancestor { + reference: object; + path: string; +} + +type Ancestors = Ancestor[]; + +export type { + Ancestor, + Ancestors +}; diff --git a/lib/dispel/dispel.ts b/lib/dispel/dispel.ts new file mode 100644 index 0000000..aa14365 --- /dev/null +++ b/lib/dispel/dispel.ts @@ -0,0 +1,39 @@ +import { Ancestors } from './Ancestors'; +import { dispelArray } from './dispelArray'; +import { dispelMap } from './dispelMap'; +import { dispelObject } from './dispelObject'; +import { dispelResult } from './dispelResult'; +import { dispelSet } from './dispelSet'; +import { isArray } from '../types/isArray'; +import { isMap } from '../types/isMap'; +import { isObject } from '../types/isObject'; +import { isResult } from 'defekt'; +import { isSet } from '../types/isSet'; + +const dispel = function ( + value: any, + path = '/', + ancestors: Ancestors = [] +): any { + if (isSet(value)) { + return dispelSet(value, path, ancestors); + } + if (isMap(value)) { + return dispelMap(value, path, ancestors); + } + if (isArray(value)) { + return dispelArray(value, path, ancestors); + } + if (isResult(value)) { + return dispelResult(value, path, ancestors); + } + if (isObject(value)) { + return dispelObject(value, path, ancestors); + } + + return value; +}; + +export { + dispel +}; diff --git a/lib/dispel/dispelArray.ts b/lib/dispel/dispelArray.ts new file mode 100644 index 0000000..2e6b1d5 --- /dev/null +++ b/lib/dispel/dispelArray.ts @@ -0,0 +1,36 @@ +import { Ancestors } from './Ancestors'; +import { dispel } from './dispel'; +import { Recursion, recursion } from '../types/Recursion'; + +const dispelArray = function ( + value: any[], + path = '/', + ancestors: Ancestors = [] +): any[] | Recursion { + for (const ancestor of ancestors) { + if (value === ancestor.reference) { + return recursion({ + recursionPath: ancestor.path + }); + } + } + + const newAncestors = [ + ...ancestors, + { + reference: value, + path + } + ]; + + const dispelledArray = value.map( + (subValue, index): any => + dispel(subValue, `${path}${index}/`, newAncestors) + ); + + return dispelledArray; +}; + +export { + dispelArray +}; diff --git a/lib/dispel/dispelMap.ts b/lib/dispel/dispelMap.ts new file mode 100644 index 0000000..ade34fa --- /dev/null +++ b/lib/dispel/dispelMap.ts @@ -0,0 +1,41 @@ +import { Ancestors } from './Ancestors'; +import { dispel } from './dispel'; +import { isScalar } from '../types/isScalar'; +import { v4 } from 'uuid'; +import { Recursion, recursion } from '../types/Recursion'; + +const dispelMap = function ( + value: Map, + path = '/', + ancestors: Ancestors = [] +): Map | Recursion { + for (const ancestor of ancestors) { + if (value === ancestor.reference) { + return recursion({ + recursionPath: ancestor.path + }); + } + } + + const newAncestors = [ + ...ancestors, + { + reference: value, + path + } + ]; + + const dispelledMap: Map = new Map(); + + for (const [ key, subValue ] of value.entries()) { + const newPath = isScalar(key) ? `${path}map-${String(key)}/` : `${path}map-${v4()}/`; + + dispelledMap.set(key, dispel(subValue, newPath, newAncestors)); + } + + return dispelledMap; +}; + +export { + dispelMap +}; diff --git a/lib/dispel/dispelObject.ts b/lib/dispel/dispelObject.ts new file mode 100644 index 0000000..16adbd9 --- /dev/null +++ b/lib/dispel/dispelObject.ts @@ -0,0 +1,37 @@ +import { Ancestors } from './Ancestors'; +import { dispel } from './dispel'; +import { Recursion, recursion } from '../types/Recursion'; + +const dispelObject = function ( + value: object, + path = '/', + ancestors: Ancestors = [] +): any[] | Recursion { + for (const ancestor of ancestors) { + if (value === ancestor.reference) { + return recursion({ + recursionPath: ancestor.path + }); + } + } + + const newAncestors = [ + ...ancestors, + { + reference: value, + path + } + ]; + + const dispelledObject: any = {}; + + for (const [ key, subValue ] of Object.entries(value)) { + dispelledObject[key] = dispel(subValue, `${path}${key}/`, newAncestors); + } + + return dispelledObject; +}; + +export { + dispelObject +}; diff --git a/lib/dispel/dispelResult.ts b/lib/dispel/dispelResult.ts new file mode 100644 index 0000000..a9c687c --- /dev/null +++ b/lib/dispel/dispelResult.ts @@ -0,0 +1,36 @@ +import { Ancestors } from './Ancestors'; +import { dispel } from './dispel'; +import { error, Result, value } from 'defekt'; +import { recursion, Recursion } from '../types/Recursion'; + +const dispelResult = function ( + result: Result, + path = '/', + ancestors: Ancestors = [] +): Result | Recursion { + for (const ancestor of ancestors) { + if (result === ancestor.reference) { + return recursion({ + recursionPath: ancestor.path + }); + } + } + + const newAncestors = [ + ...ancestors, + { + reference: result, + path + } + ]; + + if (result.hasValue()) { + return value(dispel(result.value, `${path}value/`, newAncestors)); + } + + return error(dispel(result.error, `${path}error/`, newAncestors)); +}; + +export { + dispelResult +}; diff --git a/lib/dispel/dispelSet.ts b/lib/dispel/dispelSet.ts new file mode 100644 index 0000000..b72b954 --- /dev/null +++ b/lib/dispel/dispelSet.ts @@ -0,0 +1,39 @@ +import { Ancestors } from './Ancestors'; +import { dispel } from './dispel'; +import { v4 } from 'uuid'; +import { recursion, Recursion } from '../types/Recursion'; + +const dispelSet = function ( + value: Set, + path = '/', + ancestors: Ancestors = [] +): Set | Recursion { + for (const ancestor of ancestors) { + if (value === ancestor.reference) { + return recursion({ + recursionPath: ancestor.path + }); + } + } + + const newAncestors = [ + ...ancestors, + { + reference: value, + path + } + ]; + const newPath = `${path}set-${v4()}/`; + + const dispelledSet = new Set(); + + for (const subValue of value) { + dispelledSet.add(dispel(subValue, newPath, newAncestors)); + } + + return dispelledSet; +}; + +export { + dispelSet +}; diff --git a/lib/errors.ts b/lib/errors.ts new file mode 100644 index 0000000..1879e6f --- /dev/null +++ b/lib/errors.ts @@ -0,0 +1,25 @@ +import { defekt } from 'defekt'; + +class InvalidOperation extends defekt({ code: 'InvalidOperation' }) {} + +class AssertionFailed extends defekt({ code: 'AssertionFailed' }) { + public constructor ({ message, expected, actual, diff }: { + message: string; + expected?: string; + actual?: string; + diff?: string; + }) { + super(message); + + this.data = { + expected, + actual, + diff + }; + } +} + +export { + AssertionFailed, + InvalidOperation +}; diff --git a/lib/fail.ts b/lib/fail.ts deleted file mode 100644 index 5f11dd4..0000000 --- a/lib/fail.ts +++ /dev/null @@ -1,11 +0,0 @@ -import assert from 'assert'; -import { format } from 'util'; -import { humanReadable } from './humanReadable'; - -const fail = function (message: string, values: any[]): void { - const humanReadableValues = values.map((value: any): string => humanReadable(value)); - - assert.fail(format(message, ...humanReadableValues)); -}; - -export { fail }; diff --git a/lib/formatErrorMessage.ts b/lib/formatErrorMessage.ts new file mode 100644 index 0000000..103530b --- /dev/null +++ b/lib/formatErrorMessage.ts @@ -0,0 +1,53 @@ +import chalk from 'chalk'; +import { propagateDiffSymbols } from './prettyPrint/utils/propagateDiffSymbols'; +import { source } from 'common-tags'; + +const formatErrorMessage = function ({ + message, + expected, + actual, + diff +}: { + message: string; + expected?: string; + actual?: string; + diff?: string; +}): string { + // Some test libraries (e.g. mocha) print the errors thrown in tests in red. + // Since our diff makes use of color, we want to guarantee that our error + // message is displayed as intended. To achieve this, we reset all color in + // the beginning. + let errorMessage = chalk.reset(message); + + if (expected) { + errorMessage += `\n\n${source` + --- EXPECTED -------- + + ${expected} + `}`; + } + if (actual) { + errorMessage += `\n\n${source` + --- ACTUAL ---------- + + ${actual} + `}`; + } + if (diff) { + errorMessage += `\n\n${source` + --- DIFF ------------ + ${chalk.green('+++ must be added')} + ${chalk.red('--- must be removed')} + `}\n${propagateDiffSymbols(source` + *** contains changes + + ${diff} + `)}`; + } + + return errorMessage; +}; + +export { + formatErrorMessage +}; diff --git a/lib/humanReadable.ts b/lib/humanReadable.ts deleted file mode 100644 index f40f92e..0000000 --- a/lib/humanReadable.ts +++ /dev/null @@ -1,48 +0,0 @@ -import stringifyObject from 'stringify-object'; - -const humanReadable = function (value: any): string { - if (typeof value === 'number') { - return `${value}`; - } - - if (typeof value === 'boolean') { - // eslint-disable-next-line @typescript-eslint/no-base-to-string - return `${value}`; - } - - if (typeof value === 'string') { - return `'${value}'`; - } - - if (typeof value === 'object') { - if (value === null) { - return 'null'; - } - - if (value instanceof Error) { - return `'Error'`; - } - - if (value instanceof RegExp) { - // eslint-disable-next-line @typescript-eslint/no-base-to-string - return value.toString(); - } - - return stringifyObject(value, { - indent: ' ', - singleQuotes: true - }); - } - - if (typeof value === 'function') { - return value.name ? value.name : '(anonymous)'; - } - - if (typeof value === 'undefined') { - return 'undefined'; - } - - throw new Error(`Unsupported type '${typeof value}'.`); -}; - -export { humanReadable }; diff --git a/lib/index.ts b/lib/index.ts new file mode 100644 index 0000000..ca14f79 --- /dev/null +++ b/lib/index.ts @@ -0,0 +1,7 @@ +import { assert } from './assertthat'; +import * as errors from './errors'; + +export { + assert, + errors +}; diff --git a/lib/prettyPrint/forArrays/prettyPrintArray.ts b/lib/prettyPrint/forArrays/prettyPrintArray.ts new file mode 100644 index 0000000..41846ca --- /dev/null +++ b/lib/prettyPrint/forArrays/prettyPrintArray.ts @@ -0,0 +1,33 @@ +import { formatNestedArray } from '../utils/formatNestedArray'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; + +const prettyPrintArray = function (array: any[], depth = 0): string { + if (array.length === 0) { + return '[]'; + } + + const content: string[][] = []; + + for (const item of array) { + content.push(prepareSimple( + prettyPrint(item, depth + 1), + depth + )); + } + + if (depth >= maximumDepth) { + return formatNestedArray`[ ${content} ]`; + } + + return formatNestedArray` + [ + ${content} + ] + `; +}; + +export { + prettyPrintArray +}; diff --git a/lib/prettyPrint/forArrays/prettyPrintArrayDiff.ts b/lib/prettyPrint/forArrays/prettyPrintArrayDiff.ts new file mode 100644 index 0000000..ffa3610 --- /dev/null +++ b/lib/prettyPrint/forArrays/prettyPrintArrayDiff.ts @@ -0,0 +1,65 @@ +import { ArrayDiff } from '../../diffs/forArrays/ArrayDiff'; +import { formatNestedArray } from '../utils/formatNestedArray'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { prepareAddition } from '../utils/prepareAddition'; +import { prepareChange } from '../utils/prepareChange'; +import { prepareOmission } from '../utils/prepareOmission'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; +import { prettyPrintDiff } from '../typeAware/prettyPrintDiff'; +import { propagateDiffSymbols } from '../utils/propagateDiffSymbols'; +import { isAdditionDiffSegment, isChangeDiffSegment, isEqualDiffSegment, isOmissionDiffSegment } from '../../diffs/forArrays/ArrayDiffSegment'; + +const prettyPrintArrayDiff = function (diff: ArrayDiff, depth = 0): string { + if (diff.segments.length === 0) { + return '[]'; + } + + const content: string[][] = []; + + for (const segment of diff.segments) { + if (isEqualDiffSegment(segment)) { + for (const value of segment.equal) { + content.push(prepareSimple( + prettyPrint(value, depth + 1), + depth + )); + } + } else if (isChangeDiffSegment(segment)) { + for (const subDiff of segment.change) { + content.push(prepareChange( + prettyPrintDiff(subDiff, depth + 1), + depth + )); + } + } else if (isOmissionDiffSegment(segment)) { + for (const value of segment.omission) { + content.push(prepareOmission( + prettyPrint(value, depth + 1), + depth + )); + } + } else if (isAdditionDiffSegment(segment)) { + for (const value of segment.addition) { + content.push(prepareAddition( + prettyPrint(value, depth + 1), + depth + )); + } + } + } + + if (depth >= maximumDepth) { + return formatNestedArray`[ ${content} ]`; + } + + return propagateDiffSymbols(formatNestedArray` + [ + ${content} + ] + `); +}; + +export { + prettyPrintArrayDiff +}; diff --git a/lib/prettyPrint/forBooleans/prettyPrintBoolean.ts b/lib/prettyPrint/forBooleans/prettyPrintBoolean.ts new file mode 100644 index 0000000..cca4f2f --- /dev/null +++ b/lib/prettyPrint/forBooleans/prettyPrintBoolean.ts @@ -0,0 +1,7 @@ +const prettyPrintBoolean = function (value: boolean): string { + return `${value}`; +}; + +export { + prettyPrintBoolean +}; diff --git a/lib/prettyPrint/forBooleans/prettyPrintBooleanDiff.ts b/lib/prettyPrint/forBooleans/prettyPrintBooleanDiff.ts new file mode 100644 index 0000000..63ea6b6 --- /dev/null +++ b/lib/prettyPrint/forBooleans/prettyPrintBooleanDiff.ts @@ -0,0 +1,15 @@ +import { BooleanDiff } from '../../diffs/forBooleans/BooleanDiff'; +import chalk from 'chalk'; +import { oneLine } from 'common-tags'; +import { prettyPrintBoolean } from './prettyPrintBoolean'; + +const prettyPrintBooleanDiff = function (diff: BooleanDiff): string { + return oneLine` + ${diff.actual !== undefined ? chalk.red(prettyPrintBoolean(diff.actual)) : ''} + ${diff.expected !== undefined ? chalk.green(prettyPrintBoolean(diff.expected)) : ''} + `; +}; + +export { + prettyPrintBooleanDiff +}; diff --git a/lib/prettyPrint/forErrors/prettyPrintError.ts b/lib/prettyPrint/forErrors/prettyPrintError.ts new file mode 100644 index 0000000..e55a8c0 --- /dev/null +++ b/lib/prettyPrint/forErrors/prettyPrintError.ts @@ -0,0 +1,15 @@ +import { prettyPrint } from '../typeAware/prettyPrint'; + +const prettyPrintError = function (error: Error, depth = 0): string { + const errorButWithoutTheShittiness = { + ...error, + message: error.message, + stack: error.stack + }; + + return `Error(${prettyPrint(errorButWithoutTheShittiness, depth)})`; +}; + +export { + prettyPrintError +}; diff --git a/lib/prettyPrint/forErrors/prettyPrintErrorDiff.ts b/lib/prettyPrint/forErrors/prettyPrintErrorDiff.ts new file mode 100644 index 0000000..f9209ae --- /dev/null +++ b/lib/prettyPrint/forErrors/prettyPrintErrorDiff.ts @@ -0,0 +1,10 @@ +import { ErrorDiff } from '../../diffs/forErrors/ErrorDiff'; +import { prettyPrintDiff } from '../typeAware/prettyPrintDiff'; + +const prettyPrintErrorDiff = function (diff: ErrorDiff, depth = 0): string { + return `Error(${prettyPrintDiff(diff.objectDiff, depth)})`; +}; + +export { + prettyPrintErrorDiff +}; diff --git a/lib/prettyPrint/forFunctions/prettyPrintFunction.ts b/lib/prettyPrint/forFunctions/prettyPrintFunction.ts new file mode 100644 index 0000000..c28e1d5 --- /dev/null +++ b/lib/prettyPrint/forFunctions/prettyPrintFunction.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line @typescript-eslint/ban-types +const prettyPrintFunction = function (value: Function): string { + return `function ${value.name} (...) { ... }`; +}; + +export { + prettyPrintFunction +}; diff --git a/lib/prettyPrint/forFunctions/prettyPrintFunctionDiff.ts b/lib/prettyPrint/forFunctions/prettyPrintFunctionDiff.ts new file mode 100644 index 0000000..f35fc88 --- /dev/null +++ b/lib/prettyPrint/forFunctions/prettyPrintFunctionDiff.ts @@ -0,0 +1,12 @@ +import { FunctionDiff } from '../../diffs/forFunctions/FunctionDiff'; +import { prettyPrintStringDiff } from '../forStrings/prettyPrintStringDiff'; + +const prettyPrintFunctionDiff = function ( + diff: FunctionDiff +): string { + return prettyPrintStringDiff(diff.stringRepresentationDiff).slice(1, -1); +}; + +export { + prettyPrintFunctionDiff +}; diff --git a/lib/prettyPrint/forMaps/prettyPrintMap.ts b/lib/prettyPrint/forMaps/prettyPrintMap.ts new file mode 100644 index 0000000..f524b0d --- /dev/null +++ b/lib/prettyPrint/forMaps/prettyPrintMap.ts @@ -0,0 +1,33 @@ +import { formatNestedArray } from '../utils/formatNestedArray'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; + +const prettyPrintMap = function (map: Map, depth = 0): string { + if (map.size === 0) { + return 'Map({})'; + } + + const content: string[][] = []; + + for (const [ key, value ] of map) { + content.push(prepareSimple( + `${prettyPrint(key, Number.POSITIVE_INFINITY)}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + + if (depth >= maximumDepth) { + return formatNestedArray`Map({ ${content} })`; + } + + return formatNestedArray` + Map({ + ${content} + }) + `; +}; + +export { + prettyPrintMap +}; diff --git a/lib/prettyPrint/forMaps/prettyPrintMapDiff.ts b/lib/prettyPrint/forMaps/prettyPrintMapDiff.ts new file mode 100644 index 0000000..08e0840 --- /dev/null +++ b/lib/prettyPrint/forMaps/prettyPrintMapDiff.ts @@ -0,0 +1,57 @@ +import { formatNestedArray } from '../utils/formatNestedArray'; +import { MapDiff } from '../../diffs/forMaps/MapDiff'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { prepareAddition } from '../utils/prepareAddition'; +import { prepareChange } from '../utils/prepareChange'; +import { prepareOmission } from '../utils/prepareOmission'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; +import { prettyPrintDiff } from '../typeAware/prettyPrintDiff'; +import { propagateDiffSymbols } from '../utils/propagateDiffSymbols'; + +const prettyPrintMapDiff = function (diff: MapDiff, depth = 0): string { + const content: string[][] = []; + + for (const [ key, value ] of diff.equal.entries()) { + content.push(prepareSimple( + `${prettyPrint(key, Number.POSITIVE_INFINITY)}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + for (const [ key, value ] of diff.changes.entries()) { + content.push(prepareChange( + `${prettyPrint(key, Number.POSITIVE_INFINITY)}: ${prettyPrintDiff(value, depth + 1)}`, + depth + )); + } + for (const [ key, value ] of diff.omissions.entries()) { + content.push(prepareOmission( + `${prettyPrint(key, Number.POSITIVE_INFINITY)}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + for (const [ key, value ] of diff.additions.entries()) { + content.push(prepareAddition( + `${prettyPrint(key, Number.POSITIVE_INFINITY)}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + + if (content.length === 0) { + return 'Map({})'; + } + + if (depth >= maximumDepth) { + return formatNestedArray`Map({ ${content} })`; + } + + return propagateDiffSymbols(formatNestedArray` + Map({ + ${content} + }) + `); +}; + +export { + prettyPrintMapDiff +}; diff --git a/lib/prettyPrint/forNull/prettyPrintNull.ts b/lib/prettyPrint/forNull/prettyPrintNull.ts new file mode 100644 index 0000000..1b755a5 --- /dev/null +++ b/lib/prettyPrint/forNull/prettyPrintNull.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const prettyPrintNull = function (value: null): string { + return 'null'; +}; + +export { + prettyPrintNull +}; diff --git a/lib/prettyPrint/forNumbers/prettyPrintNumber.ts b/lib/prettyPrint/forNumbers/prettyPrintNumber.ts new file mode 100644 index 0000000..4da129c --- /dev/null +++ b/lib/prettyPrint/forNumbers/prettyPrintNumber.ts @@ -0,0 +1,7 @@ +const prettyPrintNumber = function (value: number): string { + return `${value}`; +}; + +export { + prettyPrintNumber +}; diff --git a/lib/prettyPrint/forNumbers/prettyPrintNumberDiff.ts b/lib/prettyPrint/forNumbers/prettyPrintNumberDiff.ts new file mode 100644 index 0000000..1867adc --- /dev/null +++ b/lib/prettyPrint/forNumbers/prettyPrintNumberDiff.ts @@ -0,0 +1,15 @@ +import chalk from 'chalk'; +import { NumberDiff } from '../../diffs/forNumbers/NumberDiff'; +import { oneLine } from 'common-tags'; +import { prettyPrintNumber } from './prettyPrintNumber'; + +const prettyPrintNumberDiff = function (diff: NumberDiff): string { + return oneLine` + ${diff.actual !== undefined ? chalk.red(prettyPrintNumber(diff.actual)) : ''} + ${diff.expected !== undefined ? chalk.green(prettyPrintNumber(diff.expected)) : ''} (${prettyPrintNumber(diff.difference)}) + `; +}; + +export { + prettyPrintNumberDiff +}; diff --git a/lib/prettyPrint/forObjects/prettyPrintObject.ts b/lib/prettyPrint/forObjects/prettyPrintObject.ts new file mode 100644 index 0000000..e97ac49 --- /dev/null +++ b/lib/prettyPrint/forObjects/prettyPrintObject.ts @@ -0,0 +1,33 @@ +import { formatNestedArray } from '../utils/formatNestedArray'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; + +const prettyPrintObject = function (object: object, depth = 0): string { + if (Object.keys(object).length === 0) { + return '{}'; + } + + const content: string[][] = []; + + for (const [ key, value ] of Object.entries(object)) { + content.push(prepareSimple( + `${key}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + + if (depth >= maximumDepth) { + return formatNestedArray`{ ${content} }`; + } + + return formatNestedArray` + { + ${content} + } + `; +}; + +export { + prettyPrintObject +}; diff --git a/lib/prettyPrint/forObjects/prettyPrintObjectDiff.ts b/lib/prettyPrint/forObjects/prettyPrintObjectDiff.ts new file mode 100644 index 0000000..aeb0653 --- /dev/null +++ b/lib/prettyPrint/forObjects/prettyPrintObjectDiff.ts @@ -0,0 +1,57 @@ +import { formatNestedArray } from '../utils/formatNestedArray'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { ObjectDiff } from '../../diffs/forObjects/ObjectDiff'; +import { prepareAddition } from '../utils/prepareAddition'; +import { prepareChange } from '../utils/prepareChange'; +import { prepareOmission } from '../utils/prepareOmission'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; +import { prettyPrintDiff } from '../typeAware/prettyPrintDiff'; +import { propagateDiffSymbols } from '../utils/propagateDiffSymbols'; + +const prettyPrintObjectDiff = function (diff: ObjectDiff, depth = 0): string { + const content = []; + + for (const [ key, value ] of Object.entries(diff.equal)) { + content.push(prepareSimple( + `${key}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + for (const [ key, value ] of Object.entries(diff.changes)) { + content.push(prepareChange( + `${key}: ${prettyPrintDiff(value, depth + 1)}`, + depth + )); + } + for (const [ key, value ] of Object.entries(diff.omissions)) { + content.push(prepareOmission( + `${key}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + for (const [ key, value ] of Object.entries(diff.additions)) { + content.push(prepareAddition( + `${key}: ${prettyPrint(value, depth + 1)}`, + depth + )); + } + + if (content.length === 0) { + return '{}'; + } + + if (depth >= maximumDepth) { + return formatNestedArray`{ ${content} }`; + } + + return propagateDiffSymbols(formatNestedArray` + { + ${content} + } + `); +}; + +export { + prettyPrintObjectDiff +}; diff --git a/lib/prettyPrint/forRecursions/prettyPrintRecursion.ts b/lib/prettyPrint/forRecursions/prettyPrintRecursion.ts new file mode 100644 index 0000000..21eb68c --- /dev/null +++ b/lib/prettyPrint/forRecursions/prettyPrintRecursion.ts @@ -0,0 +1,9 @@ +import { Recursion } from '../../types/Recursion'; + +const prettyPrintRecursion = function (recursion: Recursion): string { + return `Recursion("${recursion.recursionPath}")`; +}; + +export { + prettyPrintRecursion +}; diff --git a/lib/prettyPrint/forRecursions/prettyPrintRecursionDiff.ts b/lib/prettyPrint/forRecursions/prettyPrintRecursionDiff.ts new file mode 100644 index 0000000..6528c76 --- /dev/null +++ b/lib/prettyPrint/forRecursions/prettyPrintRecursionDiff.ts @@ -0,0 +1,10 @@ +import { prettyPrintStringDiff } from '../forStrings/prettyPrintStringDiff'; +import { RecursionDiff } from '../../diffs/forRecursions/RecursionDiff'; + +const prettyPrintRecursionDiff = function (diff: RecursionDiff): string { + return `Recursion(${prettyPrintStringDiff(diff.recursionPathDiff)})`; +}; + +export { + prettyPrintRecursionDiff +}; diff --git a/lib/prettyPrint/forResults/prettyPrintResult.ts b/lib/prettyPrint/forResults/prettyPrintResult.ts new file mode 100644 index 0000000..8dba63f --- /dev/null +++ b/lib/prettyPrint/forResults/prettyPrintResult.ts @@ -0,0 +1,17 @@ +import { prettyPrint } from '../typeAware/prettyPrint'; +import { Result } from 'defekt'; + +const prettyPrintResult = function ( + value: Result, + depth = 0 +): string { + if (value.hasValue()) { + return `ValueResult(${prettyPrint(value.value, depth + 1)})`; + } + + return `ErrorResult(${prettyPrint(value.error, depth + 1)})`; +}; + +export { + prettyPrintResult +}; diff --git a/lib/prettyPrint/forResults/prettyPrintResultDiff.ts b/lib/prettyPrint/forResults/prettyPrintResultDiff.ts new file mode 100644 index 0000000..7370403 --- /dev/null +++ b/lib/prettyPrint/forResults/prettyPrintResultDiff.ts @@ -0,0 +1,25 @@ +import chalk from 'chalk'; +import { InvalidOperation } from '../../errors'; +import { prettyPrintDiff } from '../typeAware/prettyPrintDiff'; +import { isExpectedErrorGotValueResultDiff, isExpectedValueGotErrorResultDiff, isUnequalErrorResultDiff, isUnequalValueResultDiff, ResultDiff } from '../../diffs/forResults/ResultDiff'; + +const prettyPrintResultDiff = function (diff: ResultDiff, depth = 0): string { + if (isUnequalValueResultDiff(diff)) { + return `ValueResult(${prettyPrintDiff(diff.diff, depth + 1)})`; + } + if (isUnequalErrorResultDiff(diff)) { + return `ErrorResult(${prettyPrintDiff(diff.diff, depth + 1)})`; + } + if (isExpectedValueGotErrorResultDiff(diff)) { + return `${chalk.red('ErrorResult(...)')}\n${chalk.green('ValueResult(...)')}`; + } + if (isExpectedErrorGotValueResultDiff(diff)) { + return `${chalk.red('ValueResult(...)')}\n${chalk.green('ErrorResult(...)')}`; + } + + throw new InvalidOperation(); +}; + +export { + prettyPrintResultDiff +}; diff --git a/lib/prettyPrint/forSets/prettyPrintSet.ts b/lib/prettyPrint/forSets/prettyPrintSet.ts new file mode 100644 index 0000000..48113b6 --- /dev/null +++ b/lib/prettyPrint/forSets/prettyPrintSet.ts @@ -0,0 +1,11 @@ +import { prettyPrintArray } from '../forArrays/prettyPrintArray'; + +const prettyPrintSet = function (value: Set, depth = 0): string { + const prettyPrintedArray = prettyPrintArray([ ...value ], depth); + + return `Set(${prettyPrintedArray})`; +}; + +export { + prettyPrintSet +}; diff --git a/lib/prettyPrint/forSets/prettyPrintSetDiff.ts b/lib/prettyPrint/forSets/prettyPrintSetDiff.ts new file mode 100644 index 0000000..880cd4a --- /dev/null +++ b/lib/prettyPrint/forSets/prettyPrintSetDiff.ts @@ -0,0 +1,49 @@ +import { formatNestedArray } from '../utils/formatNestedArray'; +import { maximumDepth } from '../../constants/maximumDepth'; +import { prepareAddition } from '../utils/prepareAddition'; +import { prepareOmission } from '../utils/prepareOmission'; +import { prepareSimple } from '../utils/prepareSimple'; +import { prettyPrint } from '../typeAware/prettyPrint'; +import { propagateDiffSymbols } from '../utils/propagateDiffSymbols'; +import { SetDiff } from '../../diffs/forSets/SetDiff'; + +const prettyPrintSetDiff = function (diff: SetDiff, depth = 0): string { + const content = []; + + for (const value of diff.equal) { + content.push(prepareSimple( + prettyPrint(value, depth + 1), + depth + )); + } + for (const value of diff.omissions) { + content.push(prepareOmission( + prettyPrint(value, depth + 1), + depth + )); + } + for (const value of diff.additions) { + content.push(prepareAddition( + prettyPrint(value, depth + 1), + depth + )); + } + + if (content.length === 0) { + return `Set([])`; + } + + if (depth >= maximumDepth) { + return formatNestedArray`Set([ ${content} ])`; + } + + return propagateDiffSymbols(formatNestedArray` + Set([ + ${content} + ]) + `); +}; + +export { + prettyPrintSetDiff +}; diff --git a/lib/prettyPrint/forStrings/prettyPrintString.ts b/lib/prettyPrint/forStrings/prettyPrintString.ts new file mode 100644 index 0000000..7add14c --- /dev/null +++ b/lib/prettyPrint/forStrings/prettyPrintString.ts @@ -0,0 +1,7 @@ +const prettyPrintString = function (value: string): string { + return `"${value}"`; +}; + +export { + prettyPrintString +}; diff --git a/lib/prettyPrint/forStrings/prettyPrintStringDiff.ts b/lib/prettyPrint/forStrings/prettyPrintStringDiff.ts new file mode 100644 index 0000000..6d84ecb --- /dev/null +++ b/lib/prettyPrint/forStrings/prettyPrintStringDiff.ts @@ -0,0 +1,28 @@ +import chalk from 'chalk'; +import { StringDiff } from '../../diffs/forStrings/StringDiff'; +import { isAdditionDiffSegment, isEqualDiffSegment, isOmissionDiffSegment, isReplaceDiffSegment } from '../../diffs/forStrings/StringDiffSegment'; + +const prettyPrintStringDiff = function (diff: StringDiff): string { + let result = ''; + + for (const segment of diff.segments) { + if (isEqualDiffSegment(segment)) { + result += segment.equal; + } + if (isReplaceDiffSegment(segment)) { + result += `${chalk.red(segment.replace)}${chalk.green(segment.replaceWith)}`; + } + if (isOmissionDiffSegment(segment)) { + result += chalk.green(segment.omission); + } + if (isAdditionDiffSegment(segment)) { + result += chalk.red(segment.addition); + } + } + + return `"${result}"`; +}; + +export { + prettyPrintStringDiff +}; diff --git a/lib/prettyPrint/forSymbols/prettyPrintSymbol.ts b/lib/prettyPrint/forSymbols/prettyPrintSymbol.ts new file mode 100644 index 0000000..9908221 --- /dev/null +++ b/lib/prettyPrint/forSymbols/prettyPrintSymbol.ts @@ -0,0 +1,7 @@ +const prettyPrintSymbol = function (value: symbol): string { + return `Symbol("${value.description}")`; +}; + +export { + prettyPrintSymbol +}; diff --git a/lib/prettyPrint/forSymbols/prettyPrintSymbolDiff.ts b/lib/prettyPrint/forSymbols/prettyPrintSymbolDiff.ts new file mode 100644 index 0000000..95963c7 --- /dev/null +++ b/lib/prettyPrint/forSymbols/prettyPrintSymbolDiff.ts @@ -0,0 +1,12 @@ +import { prettyPrintStringDiff } from '../forStrings/prettyPrintStringDiff'; +import { SymbolDiff } from '../../diffs/forSymbols/SymbolDiff'; + +const prettyPrintSymbolDiff = function ( + diff: SymbolDiff +): string { + return `Symbol(${prettyPrintStringDiff(diff.descriptionDiff)})`; +}; + +export { + prettyPrintSymbolDiff +}; diff --git a/lib/prettyPrint/forUndefined/prettyPrintUndefined.ts b/lib/prettyPrint/forUndefined/prettyPrintUndefined.ts new file mode 100644 index 0000000..17de489 --- /dev/null +++ b/lib/prettyPrint/forUndefined/prettyPrintUndefined.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const prettyPrintUndefined = function (value: undefined): string { + return 'undefined'; +}; + +export { + prettyPrintUndefined +}; diff --git a/lib/prettyPrint/prettyPrintEqualDiff.ts b/lib/prettyPrint/prettyPrintEqualDiff.ts new file mode 100644 index 0000000..0a6e46d --- /dev/null +++ b/lib/prettyPrint/prettyPrintEqualDiff.ts @@ -0,0 +1,10 @@ +import { EqualDiff } from '../diffs/EqualDiff'; +import { prettyPrint } from './typeAware/prettyPrint'; + +const prettyPrintEqualDiff = function (diff: EqualDiff): string { + return prettyPrint(diff.value); +}; + +export { + prettyPrintEqualDiff +}; diff --git a/lib/prettyPrint/typeAware/prettyPrint.ts b/lib/prettyPrint/typeAware/prettyPrint.ts new file mode 100644 index 0000000..fe9f03c --- /dev/null +++ b/lib/prettyPrint/typeAware/prettyPrint.ts @@ -0,0 +1,80 @@ +import { InvalidOperation } from '../../errors'; +import { isArray } from '../../types/isArray'; +import { isBoolean } from '../../types/isBoolean'; +import { isError } from '../../types/isError'; +import { isFunction } from '../../types/isFunction'; +import { isMap } from '../../types/isMap'; +import { isNull } from '../../types/isNull'; +import { isNumber } from '../../types/isNumber'; +import { isObject } from '../../types/isObject'; +import { isRecursion } from '../../types/Recursion'; +import { isResult } from 'defekt'; +import { isSet } from '../../types/isSet'; +import { isString } from '../../types/isString'; +import { isSymbol } from '../../types/isSymbol'; +import { isUndefined } from '../../types/isUndefined'; +import { prettyPrintArray } from '../forArrays/prettyPrintArray'; +import { prettyPrintBoolean } from '../forBooleans/prettyPrintBoolean'; +import { prettyPrintError } from '../forErrors/prettyPrintError'; +import { prettyPrintFunction } from '../forFunctions/prettyPrintFunction'; +import { prettyPrintMap } from '../forMaps/prettyPrintMap'; +import { prettyPrintNull } from '../forNull/prettyPrintNull'; +import { prettyPrintNumber } from '../forNumbers/prettyPrintNumber'; +import { prettyPrintObject } from '../forObjects/prettyPrintObject'; +import { prettyPrintRecursion } from '../forRecursions/prettyPrintRecursion'; +import { prettyPrintResult } from '../forResults/prettyPrintResult'; +import { prettyPrintSet } from '../forSets/prettyPrintSet'; +import { prettyPrintString } from '../forStrings/prettyPrintString'; +import { prettyPrintSymbol } from '../forSymbols/prettyPrintSymbol'; +import { prettyPrintUndefined } from '../forUndefined/prettyPrintUndefined'; + +const prettyPrint = function (value: any, depth = 0): string { + if (isRecursion(value)) { + return prettyPrintRecursion(value); + } + if (isError(value)) { + return prettyPrintError(value, depth); + } + if (isSet(value)) { + return prettyPrintSet(value, depth); + } + if (isMap(value)) { + return prettyPrintMap(value, depth); + } + if (isArray(value)) { + return prettyPrintArray(value, depth); + } + if (isResult(value)) { + return prettyPrintResult(value, depth); + } + if (isNumber(value)) { + return prettyPrintNumber(value); + } + if (isString(value)) { + return prettyPrintString(value); + } + if (isBoolean(value)) { + return prettyPrintBoolean(value); + } + if (isSymbol(value)) { + return prettyPrintSymbol(value); + } + if (isFunction(value)) { + return prettyPrintFunction(value); + } + if (isObject(value)) { + return prettyPrintObject(value, depth); + } + if (isUndefined(value)) { + return prettyPrintUndefined(value); + } + if (isNull(value)) { + return prettyPrintNull(value); + } + + throw new InvalidOperation('Could not pretty print a value with unknown type.'); +}; + +export { + prettyPrint +}; diff --git a/lib/prettyPrint/typeAware/prettyPrintDiff.ts b/lib/prettyPrint/typeAware/prettyPrintDiff.ts new file mode 100644 index 0000000..a7594e5 --- /dev/null +++ b/lib/prettyPrint/typeAware/prettyPrintDiff.ts @@ -0,0 +1,80 @@ +import { InvalidOperation } from '../../errors'; +import { isArrayDiff } from '../../diffs/forArrays/ArrayDiff'; +import { isBooleanDiff } from '../../diffs/forBooleans/BooleanDiff'; +import { isEqualDiff } from '../../diffs/EqualDiff'; +import { isErrorDiff } from '../../diffs/forErrors/ErrorDiff'; +import { isFunctionDiff } from '../../diffs/forFunctions/FunctionDiff'; +import { isIncompatibleTypeDiff } from '../../diffs/IncompatibleTypeDiff'; +import { isMapDiff } from '../../diffs/forMaps/MapDiff'; +import { isNumberDiff } from '../../diffs/forNumbers/NumberDiff'; +import { isObjectDiff } from '../../diffs/forObjects/ObjectDiff'; +import { isRecursionDiff } from '../../diffs/forRecursions/RecursionDiff'; +import { isResultDiff } from '../../diffs/forResults/ResultDiff'; +import { isSetDiff } from '../../diffs/forSets/SetDiff'; +import { isStringDiff } from '../../diffs/forStrings/StringDiff'; +import { isSymbolDiff } from '../../diffs/forSymbols/SymbolDiff'; +import { prettyPrintArrayDiff } from '../forArrays/prettyPrintArrayDiff'; +import { prettyPrintBooleanDiff } from '../forBooleans/prettyPrintBooleanDiff'; +import { prettyPrintEqualDiff } from '../prettyPrintEqualDiff'; +import { prettyPrintErrorDiff } from '../forErrors/prettyPrintErrorDiff'; +import { prettyPrintFunctionDiff } from '../forFunctions/prettyPrintFunctionDiff'; +import { prettyPrintIncompatibleTypeDiff } from './prettyPrintIncompatibleTypeDiff'; +import { prettyPrintMapDiff } from '../forMaps/prettyPrintMapDiff'; +import { prettyPrintNumberDiff } from '../forNumbers/prettyPrintNumberDiff'; +import { prettyPrintObjectDiff } from '../forObjects/prettyPrintObjectDiff'; +import { prettyPrintRecursionDiff } from '../forRecursions/prettyPrintRecursionDiff'; +import { prettyPrintResultDiff } from '../forResults/prettyPrintResultDiff'; +import { prettyPrintSetDiff } from '../forSets/prettyPrintSetDiff'; +import { prettyPrintStringDiff } from '../forStrings/prettyPrintStringDiff'; +import { prettyPrintSymbolDiff } from '../forSymbols/prettyPrintSymbolDiff'; + +const prettyPrintDiff = function (diff: any, depth = 0): string { + if (isEqualDiff(diff)) { + return prettyPrintEqualDiff(diff); + } + if (isRecursionDiff(diff)) { + return prettyPrintRecursionDiff(diff); + } + if (isErrorDiff(diff)) { + return prettyPrintErrorDiff(diff, depth); + } + if (isSetDiff(diff)) { + return prettyPrintSetDiff(diff, depth); + } + if (isMapDiff(diff)) { + return prettyPrintMapDiff(diff, depth); + } + if (isArrayDiff(diff)) { + return prettyPrintArrayDiff(diff, depth); + } + if (isResultDiff(diff)) { + return prettyPrintResultDiff(diff, depth); + } + if (isNumberDiff(diff)) { + return prettyPrintNumberDiff(diff); + } + if (isStringDiff(diff)) { + return prettyPrintStringDiff(diff); + } + if (isBooleanDiff(diff)) { + return prettyPrintBooleanDiff(diff); + } + if (isSymbolDiff(diff)) { + return prettyPrintSymbolDiff(diff); + } + if (isFunctionDiff(diff)) { + return prettyPrintFunctionDiff(diff); + } + if (isObjectDiff(diff)) { + return prettyPrintObjectDiff(diff, depth); + } + if (isIncompatibleTypeDiff(diff)) { + return prettyPrintIncompatibleTypeDiff(diff, depth); + } + + throw new InvalidOperation('Could not pretty print a diff with unknown type.'); +}; + +export { + prettyPrintDiff +}; diff --git a/lib/prettyPrint/typeAware/prettyPrintIncompatibleTypeDiff.ts b/lib/prettyPrint/typeAware/prettyPrintIncompatibleTypeDiff.ts new file mode 100644 index 0000000..758dd86 --- /dev/null +++ b/lib/prettyPrint/typeAware/prettyPrintIncompatibleTypeDiff.ts @@ -0,0 +1,18 @@ +import chalk from 'chalk'; +import { IncompatibleTypeDiff } from '../../diffs/IncompatibleTypeDiff'; +import { oneLine } from 'common-tags'; +import { prettyPrint } from './prettyPrint'; + +const prettyPrintIncompatibleTypeDiff = function ( + incompatibleTypeDiff: IncompatibleTypeDiff, + depth = 0 +): string { + return oneLine` + ${chalk.red(prettyPrint(incompatibleTypeDiff.actual, depth + 1))} + ${chalk.green(prettyPrint(incompatibleTypeDiff.expected, depth + 1))} + `; +}; + +export { + prettyPrintIncompatibleTypeDiff +}; diff --git a/lib/prettyPrint/utils/formatNestedArray.ts b/lib/prettyPrint/utils/formatNestedArray.ts new file mode 100644 index 0000000..fdc0f36 --- /dev/null +++ b/lib/prettyPrint/utils/formatNestedArray.ts @@ -0,0 +1,69 @@ +import { InvalidOperation } from '../../errors'; +import { stripIndentTransformer, TemplateTag, TemplateTransformer, trimResultTransformer } from 'common-tags'; + +const twoDimensionalArrayTransformer = function (): TemplateTransformer { + return { + onSubstitution (substitution, resultSoFar): string { + if (!Array.isArray(substitution)) { + throw new InvalidOperation('Values formatted using the nested inline array transformer must be nested arrays.'); + } + + const lastSeparatorIndex = substitution.length - 2; + const indentationMatch = /\n(?[^\S\n]+)$/u.exec(resultSoFar); + + let endResult = ''; + + for (const [ index, part ] of substitution.entries()) { + if (!Array.isArray(part)) { + throw new InvalidOperation('Values formatted using the nested inline array transformer must be nested arrays.'); + } + const isFirstPart = index === 0; + + // If there is no indentation, we want to display the array parts + // inline. Thus, no newline, just a space to separate the parts. + endResult += isFirstPart ? '' : `${indentationMatch ? '\n' : ' '}`; + + for (const [ subIndex, subPart ] of part.entries()) { + const isLastSubPart = subIndex === part.length - 1; + const firstStringOverall = isFirstPart && subIndex === 0; + + // The first part of the entire substitution does not need an + // indentation, since it is already indented in the enclosing template + // string. That is where the indentation match comes from. + if (indentationMatch && !firstStringOverall) { + endResult += indentationMatch.groups?.indentation ?? ''; + } + + endResult += subPart; + + if (!isLastSubPart) { + if (indentationMatch) { + endResult += '\n'; + } else { + // If there is no indentation, we want to display the array parts + // inline. Thus, no newline, just a space to separate the + // sub-parts. + endResult += ' '; + } + } + } + + if (index <= lastSeparatorIndex) { + endResult += ','; + } + } + + return endResult; + } + }; +}; + +const formatNestedArray = new TemplateTag([ + twoDimensionalArrayTransformer(), + stripIndentTransformer(), + trimResultTransformer() +]); + +export { + formatNestedArray +}; diff --git a/lib/prettyPrint/utils/prepareAddition.ts b/lib/prettyPrint/utils/prepareAddition.ts new file mode 100644 index 0000000..059f809 --- /dev/null +++ b/lib/prettyPrint/utils/prepareAddition.ts @@ -0,0 +1,23 @@ +import chalk from 'chalk'; +import { maximumDepth } from '../../constants/maximumDepth'; + +const prepareAddition = function (content: string, depth: number): string[] { + return `${content}`. + split('\n'). + map( + (line, index): string => { + if (depth >= maximumDepth) { + return chalk.red(line); + } + if (index !== 0) { + return chalk.red(` ${line}`); + } + + return chalk.red(`- ${line}`); + } + ); +}; + +export { + prepareAddition +}; diff --git a/lib/prettyPrint/utils/prepareChange.ts b/lib/prettyPrint/utils/prepareChange.ts new file mode 100644 index 0000000..915679f --- /dev/null +++ b/lib/prettyPrint/utils/prepareChange.ts @@ -0,0 +1,23 @@ +import chalk from 'chalk'; +import { maximumDepth } from '../../constants/maximumDepth'; + +const prepareChange = function (content: string, depth: number): string [] { + return `${content}`. + split('\n'). + map( + (line, index): string => { + if (depth >= maximumDepth) { + return line; + } + if (index !== 0) { + return ` ${line}`; + } + + return `${chalk.yellow('* ')}${line}`; + } + ); +}; + +export { + prepareChange +}; diff --git a/lib/prettyPrint/utils/prepareOmission.ts b/lib/prettyPrint/utils/prepareOmission.ts new file mode 100644 index 0000000..ca0920f --- /dev/null +++ b/lib/prettyPrint/utils/prepareOmission.ts @@ -0,0 +1,23 @@ +import chalk from 'chalk'; +import { maximumDepth } from '../../constants/maximumDepth'; + +const prepareOmission = function (content: any, depth: number): string[] { + return `${content}`. + split('\n'). + map( + (line, index): string => { + if (depth >= maximumDepth) { + return chalk.green(line); + } + if (index !== 0) { + return chalk.green(` ${line}`); + } + + return chalk.green(`+ ${line}`); + } + ); +}; + +export { + prepareOmission +}; diff --git a/lib/prettyPrint/utils/prepareSimple.ts b/lib/prettyPrint/utils/prepareSimple.ts new file mode 100644 index 0000000..7835605 --- /dev/null +++ b/lib/prettyPrint/utils/prepareSimple.ts @@ -0,0 +1,19 @@ +import { maximumDepth } from '../../constants/maximumDepth'; + +const prepareSimple = function (content: string, depth: number): string[] { + return `${content}`. + split('\n'). + map( + (line): string => { + if (depth >= maximumDepth) { + return line; + } + + return ` ${line}`; + } + ); +}; + +export { + prepareSimple +}; diff --git a/lib/prettyPrint/utils/propagateDiffSymbols.ts b/lib/prettyPrint/utils/propagateDiffSymbols.ts new file mode 100644 index 0000000..7904aad --- /dev/null +++ b/lib/prettyPrint/utils/propagateDiffSymbols.ts @@ -0,0 +1,22 @@ +const greenAnsi = '\u001B[32m'; +// eslint-disable-next-line no-control-regex +const additionRegExp = /^ {2}\u001B\[32m\+ /gmu; + +const redAnsi = '\u001B[31m'; +// eslint-disable-next-line no-control-regex +const omissionRegExp = /^ {2}\u001B\[31m- /gmu; + +const yellowAnsi = '\u001B[33m'; +// eslint-disable-next-line no-control-regex +const changeRegExp = /^ {2}\u001B\[33m\* /gmu; + +const propagateDiffSymbols = function (prettyDiff: string): string { + return prettyDiff. + replace(additionRegExp, `${greenAnsi}+ `). + replace(omissionRegExp, `${redAnsi}- `). + replace(changeRegExp, `${yellowAnsi}* `); +}; + +export { + propagateDiffSymbols +}; diff --git a/lib/report.ts b/lib/report.ts new file mode 100644 index 0000000..f5ba526 --- /dev/null +++ b/lib/report.ts @@ -0,0 +1,20 @@ +import { AssertionFailed } from './errors'; +import { formatErrorMessage } from './formatErrorMessage'; +import { Result } from 'defekt'; + +const report = function (result: Result): void { + if (result.hasValue()) { + return; + } + + throw new Error(formatErrorMessage({ + message: result.error.message, + expected: result.error.data.expected, + actual: result.error.data.actual, + diff: result.error.data.diff + })); +}; + +export { + report +}; diff --git a/lib/size/forArrays/arraySize.ts b/lib/size/forArrays/arraySize.ts new file mode 100644 index 0000000..43f104c --- /dev/null +++ b/lib/size/forArrays/arraySize.ts @@ -0,0 +1,15 @@ +import { size } from '../typeAware/size'; +import { sum } from '../../utils/sum'; + +const arraySize = function (value: TContent[]): number { + return sum( + value. + map( + (item): number => size(item) + ) + ); +}; + +export { + arraySize +}; diff --git a/lib/size/forErrors/errorSize.ts b/lib/size/forErrors/errorSize.ts new file mode 100644 index 0000000..5c86bb5 --- /dev/null +++ b/lib/size/forErrors/errorSize.ts @@ -0,0 +1,14 @@ +import { objectSize } from '../forObjects/objectSize'; + +const errorSize = function (value: TError): number { + const errorButWithoutTheShittiness = { + ...value, + message: value.message + }; + + return objectSize(errorButWithoutTheShittiness); +}; + +export { + errorSize +}; diff --git a/lib/size/forMaps/mapSize.ts b/lib/size/forMaps/mapSize.ts new file mode 100644 index 0000000..8965e2c --- /dev/null +++ b/lib/size/forMaps/mapSize.ts @@ -0,0 +1,14 @@ +import { size } from '../typeAware/size'; +import { sum } from '../../utils/sum'; + +const mapSize = function (value: Map): number { + return sum( + [ ...value.values() ].map( + (item): number => size(item) + ) + ); +}; + +export { + mapSize +}; diff --git a/lib/size/forObjects/objectSize.ts b/lib/size/forObjects/objectSize.ts new file mode 100644 index 0000000..15763a0 --- /dev/null +++ b/lib/size/forObjects/objectSize.ts @@ -0,0 +1,12 @@ +import { size } from '../typeAware/size'; +import { sum } from '../../utils/sum'; + +const objectSize = function (value: object): number { + return sum([ ...Object.values(value) ].map( + (item: any): number => size(item) + )); +}; + +export { + objectSize +}; diff --git a/lib/size/forResults/resultSize.ts b/lib/size/forResults/resultSize.ts new file mode 100644 index 0000000..145a503 --- /dev/null +++ b/lib/size/forResults/resultSize.ts @@ -0,0 +1,15 @@ +import { errorSize } from '../forErrors/errorSize'; +import { Result } from 'defekt'; +import { size } from '../typeAware/size'; + +const resultSize = function (value: Result): number { + if (value.hasValue()) { + return size(value.value); + } + + return errorSize(value.error); +}; + +export { + resultSize +}; diff --git a/lib/size/forSets/setSize.ts b/lib/size/forSets/setSize.ts new file mode 100644 index 0000000..3bd916f --- /dev/null +++ b/lib/size/forSets/setSize.ts @@ -0,0 +1,14 @@ +import { size } from '../typeAware/size'; +import { sum } from '../../utils/sum'; + +const setSize = function (value: Set): number { + return sum( + [ ...value.values() ].map( + (item): number => size(item) + ) + ); +}; + +export { + setSize +}; diff --git a/lib/size/typeAware/size.ts b/lib/size/typeAware/size.ts new file mode 100644 index 0000000..20dd8dd --- /dev/null +++ b/lib/size/typeAware/size.ts @@ -0,0 +1,38 @@ +import { arraySize } from '../forArrays/arraySize'; +import { errorSize } from '../forErrors/errorSize'; +import { isArray } from '../../types/isArray'; +import { isMap } from '../../types/isMap'; +import { isObject } from '../../types/isObject'; +import { isSet } from '../../types/isSet'; +import { mapSize } from '../forMaps/mapSize'; +import { objectSize } from '../forObjects/objectSize'; +import { resultSize } from '../forResults/resultSize'; +import { setSize } from '../forSets/setSize'; +import { isError, isResult } from 'defekt'; + +const size = function (value: any): number { + if (isArray(value)) { + return arraySize(value); + } + if (isSet(value)) { + return setSize(value); + } + if (isMap(value)) { + return mapSize(value); + } + if (isObject(value)) { + return objectSize(value); + } + if (isResult(value)) { + return resultSize(value); + } + if (isError(value)) { + return errorSize(value); + } + + return 1; +}; + +export { + size +}; diff --git a/lib/types/Recursion.ts b/lib/types/Recursion.ts new file mode 100644 index 0000000..def6bf9 --- /dev/null +++ b/lib/types/Recursion.ts @@ -0,0 +1,29 @@ +const recursionSymbol: unique symbol = Symbol('recursion'); + +const recursion = function (parameters: Omit): Recursion { + return { + type: recursionSymbol, + ...parameters + }; +}; + +interface Recursion { + type: typeof recursionSymbol; + recursionPath: string; +} + +const isRecursion = function (value: any): value is Recursion { + return typeof value === 'object' && + value !== null && + 'type' in value && + value.type === recursionSymbol; +}; + +export type { + Recursion +}; + +export { + isRecursion, + recursion +}; diff --git a/lib/types/Type.ts b/lib/types/Type.ts new file mode 100644 index 0000000..2f02e93 --- /dev/null +++ b/lib/types/Type.ts @@ -0,0 +1,18 @@ +type Type = + 'array' | + 'boolean' | + 'error' | + 'function' | + 'map' | + 'null' | + 'number' | + 'object' | + 'result' | + 'set' | + 'string' | + 'symbol' | + 'undefined'; + +export type { + Type +}; diff --git a/lib/types/isArray.ts b/lib/types/isArray.ts new file mode 100644 index 0000000..ed486b2 --- /dev/null +++ b/lib/types/isArray.ts @@ -0,0 +1,7 @@ +const isArray = function (value: any): value is any[] { + return Array.isArray(value); +}; + +export { + isArray +}; diff --git a/lib/types/isBoolean.ts b/lib/types/isBoolean.ts new file mode 100644 index 0000000..ceca7c5 --- /dev/null +++ b/lib/types/isBoolean.ts @@ -0,0 +1,7 @@ +const isBoolean = function (value: any): value is boolean { + return typeof value === 'boolean'; +}; + +export { + isBoolean +}; diff --git a/lib/types/isError.ts b/lib/types/isError.ts new file mode 100644 index 0000000..9b5ddd9 --- /dev/null +++ b/lib/types/isError.ts @@ -0,0 +1,7 @@ +const isError = function (value: any): value is Error { + return typeof value === 'object' && value instanceof Error; +}; + +export { + isError +}; diff --git a/lib/types/isFunction.ts b/lib/types/isFunction.ts new file mode 100644 index 0000000..33f7212 --- /dev/null +++ b/lib/types/isFunction.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line @typescript-eslint/ban-types +const isFunction = function (value: any): value is Function { + return typeof value === 'function'; +}; + +export { + isFunction +}; diff --git a/lib/types/isMap.ts b/lib/types/isMap.ts new file mode 100644 index 0000000..4e0bd69 --- /dev/null +++ b/lib/types/isMap.ts @@ -0,0 +1,7 @@ +const isMap = function (value: any): value is Map { + return value instanceof Map; +}; + +export { + isMap +}; diff --git a/lib/types/isNull.ts b/lib/types/isNull.ts new file mode 100644 index 0000000..bde6681 --- /dev/null +++ b/lib/types/isNull.ts @@ -0,0 +1,7 @@ +const isNull = function (value: any): value is null { + return value === null; +}; + +export { + isNull +}; diff --git a/lib/types/isNumber.ts b/lib/types/isNumber.ts new file mode 100644 index 0000000..a7b2acd --- /dev/null +++ b/lib/types/isNumber.ts @@ -0,0 +1,7 @@ +const isNumber = function (value: any): value is number { + return typeof value === 'number'; +}; + +export { + isNumber +}; diff --git a/lib/types/isObject.ts b/lib/types/isObject.ts new file mode 100644 index 0000000..b558e0c --- /dev/null +++ b/lib/types/isObject.ts @@ -0,0 +1,7 @@ +const isObject = function (value: any): value is object { + return typeof value === 'object' && value !== null; +}; + +export { + isObject +}; diff --git a/lib/types/isScalar.ts b/lib/types/isScalar.ts new file mode 100644 index 0000000..e317216 --- /dev/null +++ b/lib/types/isScalar.ts @@ -0,0 +1,19 @@ +import { isBoolean } from './isBoolean'; +import { isNull } from './isNull'; +import { isNumber } from './isNumber'; +import { isString } from './isString'; +import { isSymbol } from './isSymbol'; +import { isUndefined } from './isUndefined'; + +const isScalar = function (value: any): value is number | string | boolean | symbol { + return isNumber(value) || + isString(value) || + isBoolean(value) || + isSymbol(value) || + isUndefined(value) || + isNull(value); +}; + +export { + isScalar +}; diff --git a/lib/types/isSet.ts b/lib/types/isSet.ts new file mode 100644 index 0000000..a8544cd --- /dev/null +++ b/lib/types/isSet.ts @@ -0,0 +1,7 @@ +const isSet = function (value: any): value is Set { + return value instanceof Set; +}; + +export { + isSet +}; diff --git a/lib/types/isString.ts b/lib/types/isString.ts new file mode 100644 index 0000000..b575880 --- /dev/null +++ b/lib/types/isString.ts @@ -0,0 +1,7 @@ +const isString = function (value: any): value is string { + return typeof value === 'string'; +}; + +export { + isString +}; diff --git a/lib/types/isSymbol.ts b/lib/types/isSymbol.ts new file mode 100644 index 0000000..690b8e9 --- /dev/null +++ b/lib/types/isSymbol.ts @@ -0,0 +1,7 @@ +const isSymbol = function (value: any): value is symbol { + return typeof value === 'symbol'; +}; + +export { + isSymbol +}; diff --git a/lib/types/isUndefined.ts b/lib/types/isUndefined.ts new file mode 100644 index 0000000..ea06ce9 --- /dev/null +++ b/lib/types/isUndefined.ts @@ -0,0 +1,7 @@ +const isUndefined = function (value: any): value is undefined { + return typeof value === 'undefined'; +}; + +export { + isUndefined +}; diff --git a/lib/utils/sum.ts b/lib/utils/sum.ts new file mode 100644 index 0000000..c2b79eb --- /dev/null +++ b/lib/utils/sum.ts @@ -0,0 +1,7 @@ +const sum = function (numbers: number[]): number { + return numbers.reduce((acc, val): number => acc + val, 0); +}; + +export { + sum +}; diff --git a/notes.org b/notes.org new file mode 100644 index 0000000..5929455 --- /dev/null +++ b/notes.org @@ -0,0 +1,168 @@ +#+TITLE: Notes + +* todos +** Pretty printing diffs +*** TODO Pretty printing diffs: leave out long stretches without changes +*** TODO Fix type switches: if one is an array and one an object, the object/object case takes effect +*** TODO Fix type recognition: I forgot ~undefined~ + +* draft control flow + +#+BEGIN_SRC js +// type-aware assertthat +const assertthat = function () { + return { + that (actual) { + // based on type of actual + if (isArray(actual)) { + return assertthatForArray(actual); + } + } + }; +}; + +// specific assertthat for array +const assertthatForArray = function (actual) { + return { + is: { + equalTo (expected) { + assertArrayIsEqualToArray(actual, expected) + }, + + not: { + } + } + }; +}; + +// specific isEqual assertion for array +const assertArrayIsEqualToArray = function (actual, expected) { + if (isArrayEqualToArray(actual, expected)) { + return value(); + } + + return error(new AssertionFailed({ + // reason why the assertion failed. good message, diff etc. + })); +}; + +// specific isEqual comparison for array +const isArrayEqualToArray = function (actual, expected) { + // some sanity checks regarding array length... + + for (let i = 0; i < actual.length; i++) { + const actualValue = actual[i], + expectedValue = expected[i]; + + if (!isValueEqualToValue(actualValue, expectedValue)) { + return false; + } + } + + return true; +}; + +// type-aware isEqual comparison +const isValueEqualToValue = function (actual, expected) { + // based on type of values + if (isNumber(actual) && isNumber(expected)) { + return isNumberEqualToNumber(actual, expected); + } +}; + +// specific isEqual comparison for number +const isNumberEqualToNumber = function (actual, expected) { + // numbers are scalars and thus comparable by identity + return isSame(actual, expected); +}; + +const isEqual = function (actual, expected) { + return actual === expected; +} +#+END_SRC + +** Assertion + +An assertion asserts a property or relation of one or more values. It either does nothing or throws a descriptive error of why the assertion was not fulfilled. + +Thus an assertions interface looks like this: + +#+BEGIN_SRC typescript +type Assertion = (...values: any[]): void | Promise; +#+END_SRC + +This is obviously incomplete, since TypeScript's type system can not depict errors. The errors thrown by assertions look like this: + +#+BEGIN_SRC typescript +class AssertionFailed extends defekt({ code: 'AssertionFailed' }) {} + +const arrayIsMissingValuesItShouldHaveContained = new AssertionFailed({ + message: 'The array is not containing all of the expected value.', + data: { + expected: 'To contain all of these values:\n...', + actual: prettyPrintArray(...), + diff: 'These values are missing:\n...' + } +}); +#+END_SRC + +The properties ~expected~, ~actual~ and ~diff~ are all optional and should be filled as appropriate. + +** Comparisons + +A comparisons checks one or more values for a property or relation. + +- Should a comparison result in true/false? +- Should a comparison return a differentiated result containing information on what went wrong? + - This would make the assertion step basically obsolete, since the comparisons could return a ~Result~ directly. + - Does it make sense to let the comparison determine the result message? This seems intuitive, but if many comparisons are nested (e.g. comparing three-times-nested arrays) the error messages have to be assembled somehow. Making a diff on the top level seems easier. But making the diff is the same thing as comparing, isn't it? + - One of the use cases of comparisons is negation. If a comparisons decides its own error messages, negation outside of the comparison does not work, since the wording can't be negated programmatically. + - What /is/ possible is returning a diff, if possible. This diff can then presented either way. E.g. when checking the arrays ~[ 1, 2, 3 ]~ and ~[ 2, 3, 4 ]~ for equality, the diff ~+[ 1 ], -[ 4 ]~ can be propagated, but when checking them for non-equality the diff can be translated to 👍. + - How do different diffs look? How do diffs look for unary operations? + - ~isTruthy(something)~ returns either ~value()~ or ~error({})~ + - ~isEqual(anArray, anotherArray)~ returns either ~value()~ or ~error({ additional: ..., missing: ... })~ + +** Diffs + +What should a diff look like? Is there a universal form? Is there a type-dependent form? Maybe not even that? + +*** Unary Operations +In unary operations there is nothing to diff, so the diff's existence is enough without further information. +Examples: +- isArrayEmpty([ 5, 7 ]) => error(new Diff()) +- isArrayContentUnique([ 5, 5 ]) => error(new Diff([ 5 ])) doesn't really work. this one needs additional info +*** Numbers +- isNumberGreaterThanNumber => diff should contain how much less the number is +- isNumberNan => no information needed +- isNumberEqualToNumber => diff should contain the difference of the numbers +- isNumberFalsy => no information needed +*** Strings +- isStringEqualToString => diff between the strings +- isStringEmpty => no information needed +- isStringNotContainingAnyOf => diff should contain which value is wrongly contained +- isStringMatchingRegExp => no information needed, can't diff a regexp +*** Booleans +- isBooleanTrue => no information needed +- isBooleanSameJsonAsBoolean => string diff, but does not do a lot xD +*** Arrays +- isArrayContaining => no information needed +- isArrayEqualToArray => diff between the arrays +- isArrayContainingAllOf => diff should contain which values are wrongly missing +*** Objects +- isObjectEqualToObject => diff between the objects +*** Functions +- isFunctionThrowing + - without parameter => no information needed. diff when the function does not throw + - with message => diff should contain the diff between the messages + - with regexp => no information needed, can't diff a regexp + - with predicate => no information needed, can't diff a predicate application + + +** Diff Structure +*** Diff between Arrays +- Segment = Equal(T[]) | Omission(T[]) | Addition(T[]) +- actual: [ 1, 2, 5, 6, 8 ] vs expected: [ 1, 2, 3, 4, 5, 6] + => [ Equal([ 1, 2 ]), Missing([ 3, 4 ]), Equal([ 5, 6 ]), Additional([ 8 ]) ] +*** Diff between Strings +- convert to array of chars, use same algo as for arrays +- rewrite for presentation diff --git a/package-lock.json b/package-lock.json index 2b1b657..fb94cc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,39 +1,39 @@ { "name": "assertthat", - "version": "5.2.8", + "version": "6.0.0-internal.7", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", - "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "@babel/highlight": "^7.10.1" + "@babel/highlight": "^7.12.13" } }, "@babel/compat-data": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", - "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", "dev": true }, "@babel/core": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.16.tgz", - "integrity": "sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.0.tgz", + "integrity": "sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.16", + "@babel/generator": "^7.14.0", "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.13.14", - "@babel/helpers": "^7.13.16", - "@babel/parser": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.0", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.0", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.16", + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -42,87 +42,11 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -152,12 +76,12 @@ } }, "@babel/generator": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.16.tgz", - "integrity": "sha512-grBBR75UnKOcUWMp8WoDxNsWCFl//XCK6HWTrBQKTr5SV9f5g0pNOjdyzi/DTBv12S9GnYPInIXQBTky7OXEMg==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.1.tgz", + "integrity": "sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==", "dev": true, "requires": { - "@babel/types": "^7.13.16", + "@babel/types": "^7.14.1", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -221,27 +145,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", - "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz", + "integrity": "sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.13.12", "@babel/helper-replace-supers": "^7.13.12", "@babel/helper-simple-access": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.0", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.14" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" } }, "@babel/helper-optimise-call-expression": { @@ -284,9 +200,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", - "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", "dev": true }, "@babel/helper-validator-option": { @@ -296,23 +212,23 @@ "dev": true }, "@babel/helpers": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.17.tgz", - "integrity": "sha512-Eal4Gce4kGijo1/TGJdqp3WuhllaMLSrW6XcL0ulyUAQOuxHcCafZE8KHg9857gcTehsm/v7RcOx2+jp0Ryjsg==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", "dev": true, "requires": { "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.17", - "@babel/types": "^7.13.17" + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" } }, "@babel/highlight": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", - "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.1", + "@babel/helper-validator-identifier": "^7.14.0", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -370,9 +286,9 @@ } }, "@babel/parser": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.16.tgz", - "integrity": "sha512-6bAg36mCwuqLO0hbR+z7PHuqWiCeP7Dzg73OpQwsAB1Eb8HnGEz5xYBzCfbu+YjoaJsJs+qheDxVAuqbt3ILEw==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.1.tgz", + "integrity": "sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==", "dev": true }, "@babel/template": { @@ -384,196 +300,32 @@ "@babel/code-frame": "^7.12.13", "@babel/parser": "^7.12.13", "@babel/types": "^7.12.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@babel/traverse": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.17.tgz", - "integrity": "sha512-BMnZn0R+X6ayqm3C3To7o1j7Q020gWdqdyP50KEoVqaCO2c/Im7sYZSmVgvefp8TTMQ+9CtwuBp0Z1CZ8V3Pvg==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.0.tgz", + "integrity": "sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.16", + "@babel/generator": "^7.14.0", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.16", - "@babel/types": "^7.13.17", + "@babel/parser": "^7.14.0", + "@babel/types": "^7.14.0", "debug": "^4.1.0", "globals": "^11.1.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@babel/types": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.17.tgz", - "integrity": "sha512-RawydLgxbOPDlTLJNtoIypwdmAy//uQIzlKt2+iBiJaRlVuI6QLUxVAyWGNfOzp8Yu4L4lLIacoCyTNtpb4wiA==", + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.1.tgz", + "integrity": "sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - } } }, "@eslint/eslintrc": { @@ -617,28 +369,28 @@ } }, "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.3", + "@nodelib/fs.stat": "2.0.4", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.3", + "@nodelib/fs.scandir": "2.1.4", "fastq": "^1.6.0" } }, @@ -652,9 +404,9 @@ } }, "@octokit/core": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.3.1.tgz", - "integrity": "sha512-Dc5NNQOYjgZU5S1goN6A/E500yXOfDUFRGQB8/2Tl16AcfvS3H9PudyOe3ZNE/MaVyHPIfC0htReHMJb1tMrvw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz", + "integrity": "sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==", "dev": true, "requires": { "@octokit/auth-token": "^2.4.4", @@ -689,9 +441,9 @@ } }, "@octokit/openapi-types": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz", - "integrity": "sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz", + "integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw==", "dev": true }, "@octokit/plugin-paginate-rest": { @@ -710,28 +462,26 @@ "dev": true }, "@octokit/plugin-rest-endpoint-methods": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.14.0.tgz", - "integrity": "sha512-QoZ469GDvFALHuxhcRA4KFGTaPeu5Z0MILGPa7irTGfYE0WfL+LFqWmULm9tuFKaKNlTcEQ7c5uJ0p4k5uvmNQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz", + "integrity": "sha512-vvWbPtPqLyIzJ7A4IPdTl+8IeuKAwMJ4LjvmqWOOdfSuqWQYZXq2CEd0hsnkidff2YfKlguzujHs/reBdAx8Sg==", "dev": true, "requires": { - "@octokit/types": "^6.13.0", + "@octokit/types": "^6.13.1", "deprecation": "^2.3.1" } }, "@octokit/request": { - "version": "5.4.14", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.14.tgz", - "integrity": "sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==", + "version": "5.4.15", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz", + "integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==", "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.0.0", "@octokit/types": "^6.7.1", - "deprecation": "^2.0.0", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.1", - "once": "^1.4.0", "universal-user-agent": "^6.0.0" } }, @@ -747,24 +497,24 @@ } }, "@octokit/rest": { - "version": "18.4.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.4.0.tgz", - "integrity": "sha512-3bFg0vyD3O+6EukYzLTu4tUapMofSR4nYgMEOJc25sefippsatiWfNoOnx0QNj3PIXVJdW0riUjQnDwgS0JNWA==", + "version": "18.5.3", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.3.tgz", + "integrity": "sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA==", "dev": true, "requires": { "@octokit/core": "^3.2.3", "@octokit/plugin-paginate-rest": "^2.6.2", "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "4.14.0" + "@octokit/plugin-rest-endpoint-methods": "5.0.1" } }, "@octokit/types": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz", - "integrity": "sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz", + "integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==", "dev": true, "requires": { - "@octokit/openapi-types": "^6.0.0" + "@octokit/openapi-types": "^7.0.0" } }, "@semantic-release/changelog": { @@ -817,9 +567,9 @@ } }, "@semantic-release/github": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-7.2.0.tgz", - "integrity": "sha512-tMRnWiiWb43whRHvbDGXq4DGEbKRi56glDpXDJZit4PIiwDPX7Kx3QzmwRtDOcG+8lcpGjpdPabYZ9NBxoI2mw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-7.2.2.tgz", + "integrity": "sha512-nYKxp0sfoFrHn2uNbhkon8oAxIffWPfu5LEEVs8I5Eqv/0IHvU+Kr3u8l4BTD+NNHA9ATTdy2bhQqQrZJ7wTvQ==", "dev": true, "requires": { "@octokit/rest": "^18.0.0", @@ -828,7 +578,7 @@ "bottleneck": "^2.18.1", "debug": "^4.0.0", "dir-glob": "^3.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "globby": "^11.0.0", "http-proxy-agent": "^4.0.0", "https-proxy-agent": "^5.0.0", @@ -838,22 +588,35 @@ "p-filter": "^2.0.0", "p-retry": "^4.0.0", "url-join": "^4.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + } } }, "@semantic-release/npm": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.0.10.tgz", - "integrity": "sha512-DXFEhgSt5u22imTWbw8wfcVGB90nFJNcjUBtJI3zswJojzZ7yXpY4i2Va5RBRQRTtj00BfG0stbilAtKrKp35g==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-7.1.3.tgz", + "integrity": "sha512-x52kQ/jR09WjuWdaTEHgQCvZYMOTx68WnS+TZ4fya5ZAJw4oRtJETtrvUw10FdfM28d/keInQdc66R1Gw5+OEQ==", "dev": true, "requires": { "@semantic-release/error": "^2.2.0", "aggregate-error": "^3.0.0", "execa": "^5.0.0", - "fs-extra": "^9.0.0", + "fs-extra": "^10.0.0", "lodash": "^4.17.15", "nerf-dart": "^1.0.0", - "normalize-url": "^5.0.0", - "npm": "^6.14.9", + "normalize-url": "^6.0.0", + "npm": "^7.0.0", "rc": "^1.2.8", "read-pkg": "^5.0.0", "registry-auth-token": "^4.0.0", @@ -878,10 +641,21 @@ "strip-final-newline": "^2.0.0" } }, + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "get-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", - "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "human-signals": { @@ -889,15 +663,6 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } } } }, @@ -925,16 +690,10 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, - "@types/chai": { - "version": "4.2.17", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.17.tgz", - "integrity": "sha512-LaiwWNnYuL8xJlQcE91QB2JoswWZckq9A4b+nMPq8dt8AP96727Nb3X4e74u+E3tm4NLTILNI9MYFsyVc30wSA==", - "dev": true - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "@types/common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-htRqZr5qn8EzMelhX/Xmx142z218lLyGaeZ3YR8jlze4TATRU9huKKvuBmAJEW4LCC4pnY1N6JAm6p85fMHjhg==", "dev": true }, "@types/inquirer": { @@ -978,9 +737,9 @@ "dev": true }, "@types/node": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz", - "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", + "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==", "dev": true }, "@types/normalize-package-data": { @@ -1001,11 +760,6 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, - "@types/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@types/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-ryxTolaNg1l809rknW9q9T7wG8QHcjtZX6syJx7kpOLY2qev75VzC9HMVimUxlA1YzjpGsDI29yLjHBotqhUhA==" - }, "@types/through": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", @@ -1015,6 +769,11 @@ "@types/node": "*" } }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" + }, "@typescript-eslint/eslint-plugin": { "version": "4.22.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", @@ -1159,26 +918,11 @@ "source-map": "^0.6.1" }, "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true } } }, @@ -1258,12 +1002,12 @@ "dev": true }, "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "requires": { - "type-fest": "^0.11.0" + "type-fest": "^0.21.3" } }, "ansi-regex": { @@ -1273,12 +1017,10 @@ "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -1374,12 +1116,6 @@ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -1408,9 +1144,9 @@ } }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base64-js": { @@ -1420,9 +1156,9 @@ "dev": true }, "before-after-hook": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.0.tgz", - "integrity": "sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", + "integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==", "dev": true }, "big.js": { @@ -1486,14 +1222,14 @@ "dev": true }, "browserslist": { - "version": "4.16.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.5.tgz", - "integrity": "sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001214", + "caniuse-lite": "^1.0.30001219", "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.719", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", "node-releases": "^1.1.71" } @@ -1531,18 +1267,6 @@ "humanize-string": "2.1.0", "inquirer": "8.0.0", "ora": "5.4.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "call-bind": { @@ -1555,10 +1279,16 @@ "get-intrinsic": "^1.0.2" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, "camelcase-keys": { @@ -1570,12 +1300,20 @@ "camelcase": "^5.3.1", "map-obj": "^4.0.0", "quick-lru": "^4.0.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } } }, "caniuse-lite": { - "version": "1.0.30001219", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001219.tgz", - "integrity": "sha512-c0yixVG4v9KBc/tQ2rlbB3A/bgBFRvl8h8M4IeUbqCca4gsiCfvtaheUssbnux/Mb66Vjz7x8yYjDgYcNQOhyQ==", + "version": "1.0.30001222", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001222.tgz", + "integrity": "sha512-rPmwUK0YMjfMlZVmH6nVB5U3YJ5Wnx3vmT5lnRO3nIKO8bJ+TRWMbGuuiSugDJqESy/lz+1hSrlQEagCtoOAWQ==", "dev": true }, "cardinal": { @@ -1588,25 +1326,10 @@ "redeyed": "~2.1.0" } }, - "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1618,12 +1341,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, "chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", @@ -1723,7 +1440,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -1731,8 +1447,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "colorette": { "version": "1.2.2", @@ -1854,8 +1569,7 @@ "common-tags": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", - "dev": true + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" }, "compare-func": { "version": "2.0.0", @@ -1867,15 +1581,6 @@ "dot-prop": "^5.1.0" } }, - "comparejs": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/comparejs/-/comparejs-4.0.7.tgz", - "integrity": "sha512-dgaPNZ8dqupwahc7V0D4VsihHqzjnQ7RZrHTMHEhL5ugm/8iucbVn9BzNQmSRorRhnz7vAQlHmwPradl4I22Wg==", - "requires": { - "is-subset-of": "3.1.3", - "typedescriptor": "3.0.2" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1959,6 +1664,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "core-util-is": { @@ -2016,9 +1729,9 @@ "dev": true }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2057,15 +1770,6 @@ } } }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2090,8 +1794,7 @@ "defekt": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/defekt/-/defekt-7.1.0.tgz", - "integrity": "sha512-T8VyawKBexNQodFPju6VPF4+W2GDxvXEsOcVKPRf/hgZSrAJxNJcPR6pCM5+dE8ieC+RMNC3nCStu3CmyrgzwQ==", - "dev": true + "integrity": "sha512-T8VyawKBexNQodFPju6VPF4+W2GDxvXEsOcVKPRf/hgZSrAJxNJcPR6pCM5+dE8ieC+RMNC3nCStu3CmyrgzwQ==" }, "define-properties": { "version": "1.1.3", @@ -2158,30 +1861,6 @@ "scss-parser": "^1.0.4", "semver": "^7.3.2", "yargs": "^16.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - } } }, "deprecation": { @@ -2227,14 +1906,6 @@ "dev": true, "requires": { "is-obj": "^2.0.0" - }, - "dependencies": { - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - } } }, "dotenv": { @@ -2266,13 +1937,28 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } } } }, "electron-to-chromium": { - "version": "1.3.723", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.723.tgz", - "integrity": "sha512-L+WXyXI7c7+G1V8ANzRsPI5giiimLAUDC6Zs1ojHHPhYXb3k/iTABFmWjivEtsWrRQymjnO66/rO2ZTABGdmWg==", + "version": "1.3.727", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", + "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==", "dev": true }, "emoji-regex": { @@ -2425,60 +2111,6 @@ "@babel/highlight": "^7.10.4" } }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "globals": { "version": "13.8.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", @@ -2488,33 +2120,12 @@ "type-fest": "^0.20.2" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2617,23 +2228,6 @@ "reserved-words": "^0.1.2", "safe-regex": "^2.1.1", "semver": "^7.3.5" - }, - "dependencies": { - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "eslint-scope": { @@ -2677,9 +2271,9 @@ } }, "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true }, "espree": { @@ -2794,9 +2388,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", - "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -2820,9 +2414,9 @@ "dev": true }, "fastq": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", - "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -2944,6 +2538,21 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } } } }, @@ -3005,12 +2614,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -3022,11 +2625,6 @@ "has-symbols": "^1.0.1" } }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -3065,6 +2663,12 @@ "util-deprecate": "~1.0.1" } }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "split2": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", @@ -3074,6 +2678,15 @@ "through2": "~2.0.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -3101,9 +2714,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -3116,9 +2729,9 @@ "dev": true }, "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -3186,8 +2799,7 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.2", @@ -3214,9 +2826,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "http-proxy-agent": { @@ -3289,9 +2901,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -3374,14 +2986,6 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" - }, - "dependencies": { - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - } } }, "internal-slot": { @@ -3427,9 +3031,9 @@ "dev": true }, "is-bigint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", - "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", "dev": true }, "is-binary-path": { @@ -3466,9 +3070,9 @@ "dev": true }, "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", + "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", "dev": true, "requires": { "has": "^1.0.3" @@ -3526,9 +3130,10 @@ "dev": true }, "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true }, "is-path-cwd": { "version": "2.2.0", @@ -3543,9 +3148,9 @@ "dev": true }, "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-plain-object": { @@ -3564,11 +3169,6 @@ "has-symbols": "^1.0.1" } }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" - }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -3581,21 +3181,6 @@ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", "dev": true }, - "is-subset-of": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/is-subset-of/-/is-subset-of-3.1.3.tgz", - "integrity": "sha512-xpXFzI2hnsQGbl1cAEq+W52j3/V2zvOaD1sHL9qfYrSVpeYUKs7PipcWNbkGTTceFH6ZUIoUMw4V/YDWEiyG6w==", - "requires": { - "typedescriptor": "3.0.1" - }, - "dependencies": { - "typedescriptor": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/typedescriptor/-/typedescriptor-3.0.1.tgz", - "integrity": "sha512-/GOMDHbjUs2EviA6rRnbNmLTgfdpcSn+8uhWKP781jp3Q2EB88Zr8Y7mKfaU1hL/BbruTG9XPvkScquD+E7cyQ==" - } - } - }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -3828,9 +3413,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.camelcase": { @@ -3919,12 +3504,12 @@ } }, "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "^4.0.0" + "yallist": "^3.0.2" } }, "magic-string": { @@ -3943,15 +3528,15 @@ "dev": true }, "map-obj": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.0.tgz", - "integrity": "sha512-NAq0fCmZYGz9UFEQyndp7sisrow4GroyGeKluyKC/chuITZsPyOyC1UJZPJlVFImhXdROIP5xqouRLThT3BbpQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz", + "integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==", "dev": true }, "marked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.1.tgz", - "integrity": "sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", + "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", "dev": true }, "marked-terminal": { @@ -3996,6 +3581,15 @@ "lru-cache": "^6.0.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "normalize-package-data": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", @@ -4008,30 +3602,17 @@ "validate-npm-package-license": "^3.0.1" } }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -4065,13 +3646,13 @@ "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "mime": { @@ -4123,6 +3704,12 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true } } }, @@ -4174,6 +3761,12 @@ "color-convert": "^1.9.0" } }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "chokidar": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", @@ -4216,6 +3809,15 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -4338,15 +3940,6 @@ "ansi-regex": "^4.1.0" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -4358,7 +3951,13 @@ "strip-ansi": "^5.0.0" } }, - "yargs": { + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", @@ -4542,178 +4141,275 @@ "dev": true }, "normalize-url": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.3.0.tgz", - "integrity": "sha512-9/nOVLYYe/dO/eJeQUNaGUF4m4Z5E7cb9oNTKabH+bNf19mqj60txTcveQxL0GlcWLXCxkOu2/LwL8oW0idIDA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.0.0.tgz", + "integrity": "sha512-3nv3dKMucKPEXhx/FEtJQR26ksYdyVlLEP9/dYvYwCbLbP6H8ya94IRf+mB93ec+fndv/Ye8SylWfD7jmN6kSA==", "dev": true }, "npm": { - "version": "6.14.11", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.11.tgz", - "integrity": "sha512-1Zh7LjuIoEhIyjkBflSSGzfjuPQwDlghNloppjruOH5bmj9midT9qcNT0tRUZRR04shU9ekrxNy9+UTBrqeBpQ==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.11.2.tgz", + "integrity": "sha512-NxcaNqAfdLEubG5dlPFWbXrVAG2zb45lB5y6W3PK1PK2feHaffpCN4rnXPD/BPp1kCJespCAv8Kw/QUYR85BRw==", "dev": true, "requires": { - "JSONStream": "^1.3.5", + "@npmcli/arborist": "^2.4.1", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^2.2.0", + "@npmcli/run-script": "^1.8.5", "abbrev": "~1.1.1", "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", - "aproba": "^2.0.0", "archy": "~1.0.0", - "bin-links": "^1.1.8", - "bluebird": "^3.5.5", - "byte-size": "^5.0.1", - "cacache": "^12.0.3", - "call-limit": "^1.1.1", - "chownr": "^1.1.4", - "ci-info": "^2.0.0", + "byte-size": "^7.0.1", + "cacache": "^15.0.6", + "chalk": "^4.1.0", + "chownr": "^2.0.0", "cli-columns": "^3.1.2", - "cli-table3": "^0.5.1", - "cmd-shim": "^3.0.3", + "cli-table3": "^0.6.0", "columnify": "~1.5.4", - "config-chain": "^1.1.12", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.3.1", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.8", - "iferr": "^1.0.2", - "imurmurhash": "*", - "infer-owner": "^1.0.4", - "inflight": "~1.0.6", - "inherits": "^2.0.4", - "ini": "^1.3.8", - "init-package-json": "^1.10.3", - "is-cidr": "^3.0.0", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^4.0.8", - "libnpm": "^3.0.1", - "libnpmaccess": "^3.0.2", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "libnpx": "^10.2.4", - "lock-verify": "^2.1.0", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^5.1.1", - "meant": "^1.0.2", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.5", - "move-concurrently": "^1.0.1", - "node-gyp": "^5.1.0", - "nopt": "^4.0.3", - "normalize-package-data": "^2.5.0", - "npm-audit-report": "^1.3.3", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "^3.0.2", - "npm-lifecycle": "^3.1.5", - "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.8", - "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.7", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "hosted-git-info": "^4.0.2", + "ini": "^2.0.0", + "init-package-json": "^2.0.3", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "leven": "^3.1.0", + "libnpmaccess": "^4.0.2", + "libnpmdiff": "^2.0.4", + "libnpmexec": "^1.0.1", + "libnpmfund": "^1.0.2", + "libnpmhook": "^6.0.2", + "libnpmorg": "^2.0.2", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.1", + "libnpmsearch": "^3.1.1", + "libnpmteam": "^2.0.3", + "libnpmversion": "^1.2.0", + "make-fetch-happen": "^8.0.14", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.4", + "npm-package-arg": "^8.1.2", + "npm-pick-manifest": "^6.1.1", + "npm-profile": "^5.0.3", + "npm-registry-fetch": "^10.1.1", "npm-user-validate": "^1.0.1", "npmlog": "~4.1.2", - "once": "~1.4.0", "opener": "^1.5.2", - "osenv": "^0.1.5", - "pacote": "^9.5.12", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", + "pacote": "^11.3.3", + "parse-conflict-json": "^1.1.1", "qrcode-terminal": "^0.12.0", - "query-string": "^6.8.2", - "qw": "~1.0.1", "read": "~1.0.7", - "read-cmd-shim": "^1.0.5", - "read-installed": "~4.0.3", - "read-package-json": "^2.1.1", - "read-package-tree": "^5.3.1", - "readable-stream": "^3.6.0", + "read-package-json": "^3.0.1", + "read-package-json-fast": "^2.0.2", "readdir-scoped-modules": "^1.1.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "^2.7.1", - "safe-buffer": "^5.1.2", - "semver": "^5.7.1", - "sha": "^3.0.0", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.1", - "stringify-package": "^1.0.1", - "tar": "^4.4.13", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "tar": "^6.1.0", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", - "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "^1.1.1", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.3", - "validate-npm-package-license": "^3.0.4", + "treeverse": "^1.0.4", "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.7.0", - "write-file-atomic": "^2.4.3" + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" }, "dependencies": { - "JSONStream": { - "version": "1.3.5", + "@npmcli/arborist": { + "version": "2.4.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.2", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.0", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^10.0.0", + "pacote": "^11.2.6", + "parse-conflict-json": "^1.1.1", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "semver": "^7.3.5", + "tar": "^6.1.0", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/ci-detect": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "@npmcli/config": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/git": { + "version": "2.0.8", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + } + }, + "@npmcli/metavuln-calculator": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "@npmcli/node-gyp": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "bundled": true, + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "1.8.5", "bundled": true, "dev": true, "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "infer-owner": "^1.0.4", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" } }, + "@tootallnate/once": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, "abbrev": { "version": "1.1.1", "bundled": true, "dev": true }, "agent-base": { - "version": "4.3.0", + "version": "6.0.2", "bundled": true, "dev": true, "requires": { - "es6-promisify": "^5.0.0" + "debug": "4" } }, "agentkeepalive": { - "version": "3.5.2", + "version": "4.1.4", "bundled": true, "dev": true, "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", "humanize-ms": "^1.2.1" } }, - "ansi-align": { - "version": "2.0.0", + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", "bundled": true, "dev": true, "requires": { - "string-width": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ansi-regex": { @@ -4722,11 +4418,11 @@ "dev": true }, "ansi-styles": { - "version": "3.2.1", + "version": "4.3.0", "bundled": true, "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" } }, "ansicolors": { @@ -4750,36 +4446,12 @@ "dev": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "dev": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } } }, "asap": { @@ -4811,12 +4483,12 @@ "dev": true }, "aws4": { - "version": "1.8.0", + "version": "1.11.0", "bundled": true, "dev": true }, "balanced-match": { - "version": "1.0.0", + "version": "1.0.2", "bundled": true, "dev": true }, @@ -4824,43 +4496,28 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "tweetnacl": "^0.14.3" } }, "bin-links": { - "version": "1.1.8", + "version": "2.2.1", "bundled": true, "dev": true, "requires": { - "bluebird": "^3.5.3", - "cmd-shim": "^3.0.0", - "gentle-fs": "^2.3.0", - "graceful-fs": "^4.1.15", + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", "npm-normalize-package-bin": "^1.0.0", - "write-file-atomic": "^2.3.0" + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" } }, - "bluebird": { - "version": "3.5.5", + "binary-extensions": { + "version": "2.2.0", "bundled": true, "dev": true }, - "boxen": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - } - }, "brace-expansion": { "version": "1.1.11", "bundled": true, @@ -4870,98 +4527,69 @@ "concat-map": "0.0.1" } }, - "buffer-from": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "builtins": { "version": "1.0.3", "bundled": true, "dev": true }, - "byline": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, "byte-size": { - "version": "5.0.1", + "version": "7.0.1", "bundled": true, "dev": true }, "cacache": { - "version": "12.0.3", + "version": "15.0.6", "bundled": true, "dev": true, "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" } }, - "call-limit": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "caseless": { "version": "0.12.0", "bundled": true, "dev": true }, "chalk": { - "version": "2.4.1", + "version": "4.1.1", "bundled": true, "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "chownr": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "ci-info": { "version": "2.0.0", "bundled": true, "dev": true }, "cidr-regex": { - "version": "2.0.10", + "version": "3.1.1", "bundled": true, "dev": true, "requires": { - "ip-regex": "^2.1.0" + "ip-regex": "^4.1.0" } }, - "cli-boxes": { - "version": "1.0.0", + "clean-stack": { + "version": "2.2.0", "bundled": true, "dev": true }, @@ -4975,51 +4603,41 @@ } }, "cli-table3": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true, "requires": { "colors": "^1.1.2", "object-assign": "^4.1.0", - "string-width": "^2.1.1" - } - }, - "cliui": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", + "version": "5.0.0", "bundled": true, "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", + "version": "3.0.0", "bundled": true, "dev": true }, "string-width": { - "version": "3.1.0", + "version": "4.2.2", "bundled": true, "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", + "version": "6.0.0", "bundled": true, "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } } } @@ -5030,12 +4648,11 @@ "dev": true }, "cmd-shim": { - "version": "3.0.3", + "version": "4.1.0", "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" + "mkdirp-infer-owner": "^2.0.0" } }, "code-point-at": { @@ -5044,20 +4661,20 @@ "dev": true }, "color-convert": { - "version": "1.9.1", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { - "color-name": "^1.1.1" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", + "version": "1.1.4", "bundled": true, "dev": true }, "colors": { - "version": "1.3.3", + "version": "1.4.0", "bundled": true, "dev": true, "optional": true @@ -5072,211 +4689,67 @@ } }, "combined-stream": { - "version": "1.0.6", + "version": "1.0.8", "bundled": true, "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, - "concat-map": { - "version": "0.0.1", + "common-ancestor-path": { + "version": "1.0.1", "bundled": true, "dev": true }, - "concat-stream": { - "version": "1.6.2", - "bundled": true, - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "configstore": { - "version": "3.1.5", + "concat-map": { + "version": "0.0.1", "bundled": true, - "dev": true, - "requires": { - "dot-prop": "^4.2.1", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - } + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, "dev": true }, - "copy-concurrently": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, "core-util-is": { "version": "1.0.2", "bundled": true, "dev": true }, - "create-error-class": { - "version": "3.0.2", + "dashdash": { + "version": "1.14.1", "bundled": true, "dev": true, "requires": { - "capture-stack-trace": "^1.0.0" + "assert-plus": "^1.0.0" } }, - "cross-spawn": { - "version": "5.1.0", + "debug": { + "version": "4.3.1", "bundled": true, "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "ms": "2.1.2" }, "dependencies": { - "lru-cache": { - "version": "4.1.5", - "bundled": true, - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { + "ms": { "version": "2.1.2", "bundled": true, "dev": true } } }, - "crypto-random-string": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "cyclist": { - "version": "0.2.2", + "debuglog": { + "version": "1.0.1", "bundled": true, "dev": true }, - "dashdash": { - "version": "1.14.1", + "defaults": { + "version": "1.0.3", "bundled": true, "dev": true, "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true - } - } - }, - "debuglog": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true - }, - "defaults": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "object-keys": "^1.0.12" + "clone": "^1.0.2" } }, "delayed-stream": { @@ -5289,13 +4762,8 @@ "bundled": true, "dev": true }, - "detect-indent": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "detect-newline": { - "version": "2.1.0", + "depd": { + "version": "1.1.2", "bundled": true, "dev": true }, @@ -5308,174 +4776,44 @@ "wrappy": "1" } }, - "dot-prop": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "duplexer3": { - "version": "0.1.4", + "diff": { + "version": "5.0.0", "bundled": true, "dev": true }, - "duplexify": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "ecc-jsbn": { "version": "0.1.2", "bundled": true, "dev": true, - "optional": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, - "editor": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "emoji-regex": { - "version": "7.0.3", + "version": "8.0.0", "bundled": true, "dev": true }, "encoding": { - "version": "0.1.12", - "bundled": true, - "dev": true, - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", + "version": "0.1.13", "bundled": true, "dev": true, + "optional": true, "requires": { - "once": "^1.4.0" + "iconv-lite": "^0.6.2" } }, "env-paths": { - "version": "2.2.0", + "version": "2.2.1", "bundled": true, "dev": true }, "err-code": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "errno": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "bundled": true, - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "bundled": true, - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "1.0.5", + "version": "2.0.3", "bundled": true, "dev": true }, - "execa": { - "version": "0.7.0", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, "extend": { "version": "3.0.2", "bundled": true, @@ -5486,169 +4824,37 @@ "bundled": true, "dev": true }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "figgy-pudding": { - "version": "3.5.1", + "fast-deep-equal": { + "version": "3.1.3", "bundled": true, "dev": true }, - "find-npm-prefix": { - "version": "1.0.2", + "fast-json-stable-stringify": { + "version": "2.1.0", "bundled": true, "dev": true }, - "flush-write-stream": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "forever-agent": { "version": "0.6.1", "bundled": true, "dev": true }, "form-data": { - "version": "2.3.2", + "version": "2.3.3", "bundled": true, "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "1.0.6", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, - "from2": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "fs-minipass": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^2.6.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "fs-vacuum": { - "version": "1.2.10", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", + "version": "2.1.0", "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "minipass": "^3.0.0" } }, "fs.realpath": { @@ -5681,6 +4887,14 @@ "bundled": true, "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, "string-width": { "version": "1.0.2", "bundled": true, @@ -5693,54 +4907,6 @@ } } }, - "genfun": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "gentle-fs": { - "version": "2.3.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.2", - "chownr": "^1.1.2", - "cmd-shim": "^3.0.3", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "infer-owner": "^1.0.4", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "iferr": { - "version": "0.1.5", - "bundled": true, - "dev": true - } - } - }, - "get-caller-file": { - "version": "2.0.5", - "bundled": true, - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "getpass": { "version": "0.1.7", "bundled": true, @@ -5762,41 +4928,8 @@ "path-is-absolute": "^1.0.0" } }, - "global-dirs": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "got": { - "version": "6.7.1", - "bundled": true, - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, "graceful-fs": { - "version": "4.2.4", + "version": "4.2.6", "bundled": true, "dev": true }, @@ -5812,29 +4945,6 @@ "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "bundled": true, - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "bundled": true, - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "bundled": true, - "dev": true - } } }, "has": { @@ -5846,12 +4956,7 @@ } }, "has-flag": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "has-symbols": { - "version": "1.0.0", + "version": "4.0.0", "bundled": true, "dev": true }, @@ -5861,22 +4966,26 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", + "version": "4.0.2", "bundled": true, - "dev": true + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "http-cache-semantics": { - "version": "3.8.1", + "version": "4.1.0", "bundled": true, "dev": true }, "http-proxy-agent": { - "version": "2.1.0", + "version": "4.0.1", "bundled": true, "dev": true, "requires": { - "agent-base": "4", - "debug": "3.1.0" + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" } }, "http-signature": { @@ -5890,12 +4999,12 @@ } }, "https-proxy-agent": { - "version": "2.2.4", + "version": "5.0.0", "bundled": true, "dev": true, "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "agent-base": "6", + "debug": "4" } }, "humanize-ms": { @@ -5907,18 +5016,14 @@ } }, "iconv-lite": { - "version": "0.4.23", + "version": "0.6.2", "bundled": true, "dev": true, + "optional": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "iferr": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "ignore-walk": { "version": "3.0.3", "bundled": true, @@ -5927,13 +5032,13 @@ "minimatch": "^3.0.4" } }, - "import-lazy": { - "version": "2.1.0", + "imurmurhash": { + "version": "0.1.4", "bundled": true, "dev": true }, - "imurmurhash": { - "version": "0.1.4", + "indent-string": { + "version": "4.0.0", "bundled": true, "dev": true }, @@ -5957,22 +5062,22 @@ "dev": true }, "ini": { - "version": "1.3.8", + "version": "2.0.0", "bundled": true, "dev": true }, "init-package-json": { - "version": "1.10.3", + "version": "2.0.3", "bundled": true, "dev": true, "requires": { "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "npm-package-arg": "^8.1.2", "promzard": "^0.3.0", "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", + "read-package-json": "^3.0.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^3.0.0" } }, @@ -5982,108 +5087,35 @@ "dev": true }, "ip-regex": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "is-callable": { - "version": "1.1.4", + "version": "4.3.0", "bundled": true, "dev": true }, - "is-ci": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ci-info": "^1.5.0" - }, - "dependencies": { - "ci-info": { - "version": "1.6.0", - "bundled": true, - "dev": true - } - } - }, "is-cidr": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "cidr-regex": "^2.0.10" - } - }, - "is-date-object": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", + "version": "4.0.2", "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "cidr-regex": "^3.1.1" } }, - "is-installed-globally": { - "version": "0.1.0", + "is-core-module": { + "version": "2.2.0", "bundled": true, "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "has": "^1.0.3" } }, - "is-npm": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-obj": { - "version": "1.0.1", + "is-fullwidth-code-point": { + "version": "2.0.0", "bundled": true, "dev": true }, - "is-path-inside": { + "is-lambda": { "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-redirect": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } + "bundled": true, + "dev": true }, "is-typedarray": { "version": "1.0.0", @@ -6108,11 +5140,10 @@ "jsbn": { "version": "0.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", + "json-parse-even-better-errors": { + "version": "2.3.1", "bundled": true, "dev": true }, @@ -6121,6 +5152,16 @@ "bundled": true, "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true, + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "bundled": true, @@ -6142,624 +5183,403 @@ "verror": "1.10.0" } }, - "latest-version": { - "version": "3.1.0", + "just-diff": { + "version": "3.1.1", "bundled": true, - "dev": true, - "requires": { - "package-json": "^4.0.0" - } + "dev": true }, - "lazy-property": { - "version": "1.0.0", + "just-diff-apply": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "leven": { + "version": "3.1.0", "bundled": true, "dev": true }, - "libcipm": { - "version": "4.0.8", + "libnpmaccess": { + "version": "4.0.2", "bundled": true, "dev": true, "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "ini": "^1.3.5", - "lock-verify": "^2.1.0", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^9.1.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^10.0.0" } }, - "libnpm": { - "version": "3.0.1", + "libnpmdiff": { + "version": "2.0.4", "bundled": true, "dev": true, "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.3", - "find-npm-prefix": "^1.0.2", - "libnpmaccess": "^3.0.2", - "libnpmconfig": "^1.2.1", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmpublish": "^1.1.2", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "lock-verify": "^2.0.2", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.0", - "npmlog": "^4.1.2", - "pacote": "^9.5.3", - "read-package-json": "^2.0.13", - "stringify-package": "^1.0.0" + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", + "tar": "^6.1.0" } }, - "libnpmaccess": { - "version": "3.0.2", + "libnpmexec": { + "version": "1.0.1", "bundled": true, "dev": true, "requires": { - "aproba": "^2.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0" + "@npmcli/arborist": "^2.3.0", + "@npmcli/ci-detect": "^1.3.0", + "@npmcli/run-script": "^1.8.4", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^8.1.2", + "pacote": "^11.3.1", + "proc-log": "^1.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2" } }, - "libnpmconfig": { - "version": "1.2.1", + "libnpmfund": { + "version": "1.0.2", "bundled": true, "dev": true, "requires": { - "figgy-pudding": "^3.5.1", - "find-up": "^3.0.0", - "ini": "^1.3.5" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - } + "@npmcli/arborist": "^2.0.0" } }, "libnpmhook": { - "version": "5.0.3", + "version": "6.0.2", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^10.0.0" } }, "libnpmorg": { - "version": "1.0.1", + "version": "2.0.2", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^10.0.0" + } + }, + "libnpmpack": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" } }, "libnpmpublish": { - "version": "1.1.2", + "version": "4.0.1", "bundled": true, "dev": true, "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" + "normalize-package-data": "^3.0.2", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^10.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.1" } }, "libnpmsearch": { - "version": "2.0.2", + "version": "3.1.1", "bundled": true, "dev": true, "requires": { - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^10.0.0" } }, "libnpmteam": { - "version": "1.0.2", + "version": "2.0.3", "bundled": true, "dev": true, "requires": { "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" + "npm-registry-fetch": "^10.0.0" } }, - "libnpx": { - "version": "10.2.4", + "libnpmversion": { + "version": "1.2.0", "bundled": true, "dev": true, "requires": { - "dotenv": "^5.0.1", - "npm-package-arg": "^6.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.0", - "update-notifier": "^2.3.0", - "which": "^1.3.0", - "y18n": "^4.0.0", - "yargs": "^14.2.3" + "@npmcli/git": "^2.0.7", + "@npmcli/run-script": "^1.8.4", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.5", + "stringify-package": "^1.0.1" } }, - "lock-verify": { - "version": "2.1.0", + "lru-cache": { + "version": "6.0.0", "bundled": true, "dev": true, "requires": { - "npm-package-arg": "^6.1.0", - "semver": "^5.4.1" + "yallist": "^4.0.0" } }, - "lockfile": { - "version": "1.0.4", + "make-fetch-happen": { + "version": "8.0.14", "bundled": true, "dev": true, "requires": { - "signal-exit": "^3.0.2" + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" } }, - "lodash._baseindexof": { - "version": "3.1.0", + "mime-db": { + "version": "1.47.0", "bundled": true, "dev": true }, - "lodash._baseuniq": { - "version": "4.6.0", + "mime-types": { + "version": "2.1.30", "bundled": true, "dev": true, "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" + "mime-db": "1.47.0" } }, - "lodash._bindcallback": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash._cacheindexof": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "lodash._createcache": { - "version": "3.1.2", + "minimatch": { + "version": "3.0.4", "bundled": true, "dev": true, "requires": { - "lodash._getnative": "^3.0.0" + "brace-expansion": "^1.1.7" } }, - "lodash._createset": { - "version": "4.0.3", - "bundled": true, - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "bundled": true, - "dev": true - }, - "lodash._root": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.restparam": { - "version": "3.6.1", - "bundled": true, - "dev": true - }, - "lodash.union": { - "version": "4.6.0", - "bundled": true, - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "bundled": true, - "dev": true - }, - "lodash.without": { - "version": "4.4.0", - "bundled": true, - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "lru-cache": { - "version": "5.1.1", + "minipass": { + "version": "3.1.3", "bundled": true, "dev": true, "requires": { - "yallist": "^3.0.2" + "yallist": "^4.0.0" } }, - "make-dir": { - "version": "1.3.0", + "minipass-collect": { + "version": "1.0.2", "bundled": true, "dev": true, "requires": { - "pify": "^3.0.0" + "minipass": "^3.0.0" } }, - "make-fetch-happen": { - "version": "5.0.2", + "minipass-fetch": { + "version": "1.3.3", "bundled": true, "dev": true, "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" } }, - "meant": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "mime-db": { - "version": "1.35.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.19", + "minipass-flush": { + "version": "1.0.5", "bundled": true, "dev": true, "requires": { - "mime-db": "~1.35.0" + "minipass": "^3.0.0" } }, - "minimatch": { - "version": "3.0.4", + "minipass-json-stream": { + "version": "1.0.1", "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" } }, - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - }, - "minizlib": { - "version": "1.3.3", + "minipass-pipeline": { + "version": "1.2.4", "bundled": true, "dev": true, "requires": { - "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } + "minipass": "^3.0.0" } }, - "mississippi": { - "version": "3.0.0", + "minipass-sized": { + "version": "1.0.3", "bundled": true, "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "minipass": "^3.0.0" } }, - "mkdirp": { - "version": "0.5.5", + "minizlib": { + "version": "2.1.2", "bundled": true, "dev": true, "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - } + "minipass": "^3.0.0", + "yallist": "^4.0.0" } }, - "move-concurrently": { - "version": "1.0.1", + "mkdirp": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", "bundled": true, "dev": true, "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" } }, "ms": { - "version": "2.1.1", + "version": "2.1.3", "bundled": true, "dev": true }, "mute-stream": { - "version": "0.0.7", + "version": "0.0.8", "bundled": true, "dev": true }, - "node-fetch-npm": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, "node-gyp": { - "version": "5.1.0", + "version": "7.1.2", "bundled": true, "dev": true, "requires": { "env-paths": "^2.2.0", "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", "npmlog": "^4.1.2", - "request": "^2.88.0", - "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" } }, "nopt": { - "version": "4.0.3", + "version": "5.0.0", "bundled": true, "dev": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1" } }, "normalize-package-data": { - "version": "2.5.0", + "version": "3.0.2", "bundled": true, "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "resolve": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } } }, "npm-audit-report": { - "version": "1.3.3", + "version": "2.1.4", "bundled": true, "dev": true, "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" + "chalk": "^4.0.0" } }, "npm-bundled": { - "version": "1.1.1", + "version": "1.1.2", "bundled": true, "dev": true, "requires": { "npm-normalize-package-bin": "^1.0.1" } }, - "npm-cache-filename": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "npm-install-checks": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" - } - }, - "npm-lifecycle": { - "version": "3.1.5", + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.15", - "node-gyp": "^5.0.2", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", - "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" + "semver": "^7.1.1" } }, - "npm-logical-tree": { - "version": "1.2.1", - "bundled": true, - "dev": true - }, "npm-normalize-package-bin": { "version": "1.0.1", "bundled": true, "dev": true }, "npm-package-arg": { - "version": "6.1.1", + "version": "8.1.2", "bundled": true, "dev": true, "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", "validate-npm-package-name": "^3.0.0" } }, "npm-packlist": { - "version": "1.4.8", + "version": "2.1.5", "bundled": true, "dev": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", "npm-normalize-package-bin": "^1.0.1" } }, "npm-pick-manifest": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-profile": { - "version": "4.0.4", + "version": "6.1.1", "bundled": true, "dev": true, "requires": { - "aproba": "^1.1.2 || 2", - "figgy-pudding": "^3.4.1", - "npm-registry-fetch": "^4.0.0" + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" } }, - "npm-registry-fetch": { - "version": "4.0.7", + "npm-profile": { + "version": "5.0.3", "bundled": true, "dev": true, "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "bundled": true, - "dev": true - } + "npm-registry-fetch": "^10.0.0" } }, - "npm-run-path": { - "version": "2.0.2", + "npm-registry-fetch": { + "version": "10.1.1", "bundled": true, "dev": true, "requires": { - "path-key": "^2.0.0" + "lru-cache": "^6.0.0", + "make-fetch-happen": "^8.0.9", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" } }, "npm-user-validate": { @@ -6793,20 +5613,6 @@ "bundled": true, "dev": true }, - "object-keys": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, "once": { "version": "1.4.0", "bundled": true, @@ -6820,143 +5626,55 @@ "bundled": true, "dev": true }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "package-json": { - "version": "4.0.1", + "p-map": { + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "aggregate-error": "^3.0.0" } }, "pacote": { - "version": "9.5.12", + "version": "11.3.3", "bundled": true, "dev": true, "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", + "@npmcli/git": "^2.0.1", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-normalize-package-bin": "^1.0.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^3.0.0", - "npm-registry-fetch": "^4.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "parallel-transform": { - "version": "1.1.0", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^10.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "parse-conflict-json": { + "version": "1.1.1", "bundled": true, "dev": true, "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" } }, - "path-exists": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "bundled": true, "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "path-key": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, "path-parse": { "version": "1.0.6", "bundled": true, @@ -6967,18 +5685,23 @@ "bundled": true, "dev": true }, - "pify": { - "version": "3.0.0", + "proc-log": { + "version": "1.0.0", "bundled": true, "dev": true }, - "prepend-http": { - "version": "1.0.4", + "process-nextick-args": { + "version": "2.0.1", "bundled": true, "dev": true }, - "process-nextick-args": { - "version": "2.0.0", + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-call-limit": { + "version": "1.0.1", "bundled": true, "dev": true }, @@ -6988,19 +5711,12 @@ "dev": true }, "promise-retry": { - "version": "1.1.1", + "version": "2.0.1", "bundled": true, "dev": true, "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "bundled": true, - "dev": true - } + "err-code": "^2.0.2", + "retry": "^0.12.0" } }, "promzard": { @@ -7011,66 +5727,13 @@ "read": "1" } }, - "proto-list": { - "version": "1.2.4", - "bundled": true, - "dev": true - }, - "protoduck": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "prr": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "psl": { - "version": "1.1.29", + "version": "1.8.0", "bundled": true, "dev": true }, - "pump": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "bundled": true, - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, "punycode": { - "version": "1.4.1", + "version": "2.1.1", "bundled": true, "dev": true }, @@ -7084,32 +5747,6 @@ "bundled": true, "dev": true }, - "query-string": { - "version": "6.8.2", - "bundled": true, - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "qw": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "read": { "version": "1.0.7", "bundled": true, @@ -7119,57 +5756,42 @@ } }, "read-cmd-shim": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "read-installed": { - "version": "4.0.3", + "version": "2.0.0", "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" - } + "dev": true }, "read-package-json": { - "version": "2.1.1", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", "npm-normalize-package-bin": "^1.0.0" } }, - "read-package-tree": { - "version": "5.3.1", + "read-package-json-fast": { + "version": "2.0.2", "bundled": true, "dev": true, "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" } }, "readable-stream": { - "version": "3.6.0", + "version": "2.3.7", "bundled": true, "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdir-scoped-modules": { @@ -7183,25 +5805,8 @@ "once": "^1.3.0" } }, - "registry-auth-token": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, "request": { - "version": "2.88.0", + "version": "2.88.2", "bundled": true, "dev": true, "requires": { @@ -7212,7 +5817,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -7222,25 +5827,30 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, - "require-directory": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "resolve-from": { - "version": "4.0.0", + "resolve": { + "version": "1.20.0", "bundled": true, - "dev": true + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } }, "retry": { "version": "0.12.0", @@ -7248,28 +5858,13 @@ "dev": true }, "rimraf": { - "version": "2.7.1", + "version": "3.0.2", "bundled": true, "dev": true, "requires": { "glob": "^7.1.3" } }, - "run-queue": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.1.1" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - } - } - }, "safe-buffer": { "version": "5.1.2", "bundled": true, @@ -7281,51 +5876,20 @@ "dev": true }, "semver": { - "version": "5.7.1", - "bundled": true, - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "sha": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", + "version": "7.3.5", "bundled": true, "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "lru-cache": "^6.0.0" } }, - "shebang-regex": { - "version": "1.0.0", + "set-blocking": { + "version": "2.0.0", "bundled": true, "dev": true }, "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "slide": { - "version": "1.1.6", + "version": "3.0.3", "bundled": true, "dev": true }, @@ -7335,81 +5899,26 @@ "dev": true }, "socks": { - "version": "2.3.3", + "version": "2.6.1", "bundled": true, "dev": true, "requires": { - "ip": "1.1.5", + "ip": "^1.1.5", "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "sorted-object": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "sorted-union-stream": { - "version": "2.1.3", + "version": "5.0.0", "bundled": true, "dev": true, "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" - }, - "dependencies": { - "from2": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" - } - }, - "isarray": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "bundled": true, - "dev": true - } + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" } }, "spdx-correct": { - "version": "3.0.0", + "version": "3.1.1", "bundled": true, "dev": true, "requires": { @@ -7418,12 +5927,12 @@ } }, "spdx-exceptions": { - "version": "2.1.0", + "version": "2.3.0", "bundled": true, "dev": true }, "spdx-expression-parse": { - "version": "3.0.0", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { @@ -7432,17 +5941,12 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "bundled": true, - "dev": true - }, - "split-on-first": { - "version": "1.1.0", + "version": "3.0.7", "bundled": true, "dev": true }, "sshpk": { - "version": "1.14.2", + "version": "1.16.1", "bundled": true, "dev": true, "requires": { @@ -7458,65 +5962,13 @@ } }, "ssri": { - "version": "6.0.1", - "bundled": true, - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stream-each": { - "version": "1.2.2", - "bundled": true, - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-iterate": { - "version": "1.2.0", + "version": "8.0.1", "bundled": true, "dev": true, "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "minipass": "^3.1.1" } }, - "stream-shift": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strict-uri-encode": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "string-width": { "version": "2.1.1", "bundled": true, @@ -7531,11 +5983,6 @@ "bundled": true, "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "strip-ansi": { "version": "4.0.0", "bundled": true, @@ -7547,18 +5994,11 @@ } }, "string_decoder": { - "version": "1.3.0", + "version": "1.1.1", "bundled": true, "dev": true, "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.0", - "bundled": true, - "dev": true - } + "safe-buffer": "~5.1.0" } }, "stringify-package": { @@ -7574,55 +6014,25 @@ "ansi-regex": "^2.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, "supports-color": { - "version": "5.4.0", + "version": "7.2.0", "bundled": true, "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "tar": { - "version": "4.4.13", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "term-size": { - "version": "1.2.0", + "version": "6.1.0", "bundled": true, "dev": true, "requires": { - "execa": "^0.7.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" } }, "text-table": { @@ -7630,62 +6040,15 @@ "bundled": true, "dev": true }, - "through": { - "version": "2.3.8", - "bundled": true, - "dev": true - }, - "through2": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, "tiny-relative-date": { "version": "1.3.0", "bundled": true, "dev": true }, - "tough-cookie": { - "version": "2.4.3", + "treeverse": { + "version": "1.0.4", "bundled": true, - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } + "dev": true }, "tunnel-agent": { "version": "0.6.0", @@ -7698,23 +6061,15 @@ "tweetnacl": { "version": "0.14.5", "bundled": true, - "dev": true, - "optional": true - }, - "typedarray": { - "version": "0.0.6", - "bundled": true, - "dev": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, "dev": true }, - "umask": { - "version": "1.1.0", + "typedarray-to-buffer": { + "version": "3.1.5", "bundled": true, - "dev": true + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } }, "unique-filename": { "version": "1.1.1", @@ -7725,69 +6080,19 @@ } }, "unique-slug": { - "version": "2.0.0", + "version": "2.0.2", "bundled": true, "dev": true, "requires": { "imurmurhash": "^0.1.4" } }, - "unique-string": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "unzip-response": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, "uri-js": { - "version": "4.4.0", + "version": "4.4.1", "bundled": true, "dev": true, "requires": { "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "bundled": true, - "dev": true - } - } - }, - "url-parse-lax": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "prepend-http": "^1.0.1" } }, "util-deprecate": { @@ -7795,21 +6100,8 @@ "bundled": true, "dev": true }, - "util-extend": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "util-promisify": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, "uuid": { - "version": "3.3.3", + "version": "3.4.0", "bundled": true, "dev": true }, @@ -7834,107 +6126,39 @@ "version": "1.10.0", "bundled": true, "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "which": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" } }, - "widest-line": { - "version": "2.0.1", + "walk-up-path": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "wcwidth": { + "version": "1.0.1", "bundled": true, "dev": true, "requires": { - "string-width": "^2.1.1" + "defaults": "^1.0.3" } }, - "worker-farm": { - "version": "1.7.0", + "which": { + "version": "2.0.2", "bundled": true, "dev": true, "requires": { - "errno": "~0.1.7" + "isexe": "^2.0.0" } }, - "wrap-ansi": { - "version": "5.1.0", + "wide-align": { + "version": "1.1.3", "bundled": true, "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^1.0.2 || 2" } }, "wrappy": { @@ -7943,136 +6167,20 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.3", + "version": "3.0.3", "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "xdg-basedir": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "xtend": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "y18n": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, "yallist": { - "version": "3.0.3", + "version": "4.0.0", "bundled": true, "dev": true - }, - "yargs": { - "version": "14.2.3", - "bundled": true, - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "find-up": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "15.0.1", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "bundled": true, - "dev": true - } - } } } }, @@ -8161,9 +6269,9 @@ } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { "mimic-fn": "^2.1.0" @@ -8258,9 +6366,9 @@ "dev": true }, "p-retry": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.4.0.tgz", - "integrity": "sha512-gVB/tBsG+3AHI1SyDHRrX6n9ZL0Bcbifps9W9/Bgu3Oyu4/OrAh8SvDzDsvpP0oxfCt3oWNT+0fQ9LyUGwBTLg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.5.0.tgz", + "integrity": "sha512-5Hwh4aVQSu6BEP+w2zKlVXtFAaYQe1qWuVADSgoeVlLjwe/Q/AMSoRR4MDeaAfu8llT+YNbEijWu/YF3m6avkg==", "dev": true, "requires": { "@types/retry": "^0.12.0", @@ -8280,20 +6388,12 @@ "dev": true, "requires": { "callsites": "^3.0.0" - }, - "dependencies": { - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - } } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -8332,16 +6432,10 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "dev": true }, "pify": { @@ -8579,6 +6673,12 @@ "lodash": "^4.17.15" } }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -8638,18 +6738,6 @@ "type-fest": "^0.6.0" }, "dependencies": { - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - }, "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", @@ -8799,11 +6887,12 @@ "dev": true }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -8875,31 +6964,6 @@ "ts-node": "9.1.1", "tsconfig-paths": "3.9.0", "typescript": "4.2.4" - }, - "dependencies": { - "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "run-async": { @@ -8909,10 +6973,13 @@ "dev": true }, "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } }, "rxjs": { "version": "6.6.7", @@ -8924,9 +6991,9 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safe-regex": { @@ -8945,9 +7012,9 @@ "dev": true }, "sass": { - "version": "1.32.11", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.11.tgz", - "integrity": "sha512-O9tRcob/fegUVSIV1ihLLZcftIOh0AF1VpKgusUfLqnb2jQ0GLDwI5ivv1FYWivGv8eZ/AwntTyTzjcHu0c/qw==", + "version": "1.32.12", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", + "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0" @@ -9028,9 +7095,9 @@ } }, "get-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", - "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, "hosted-git-info": { @@ -9048,13 +7115,13 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "mimic-fn": "^2.1.0" + "yallist": "^4.0.0" } }, "resolve-from": { @@ -9062,6 +7129,12 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -9077,10 +7150,30 @@ } }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } }, "semver-compare": { "version": "1.0.0", @@ -9321,9 +7414,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", "dev": true }, "split": { @@ -9374,6 +7467,21 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } } } }, @@ -9384,9 +7492,9 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -9430,22 +7538,12 @@ } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" + "safe-buffer": "~5.2.0" } }, "strip-ansi": { @@ -9485,18 +7583,17 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "requires": { "has-flag": "^4.0.0" } }, "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", "dev": true, "requires": { "has-flag": "^4.0.0", @@ -9721,23 +7818,12 @@ "prelude-ls": "^1.2.1" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, - "typedescriptor": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/typedescriptor/-/typedescriptor-3.0.2.tgz", - "integrity": "sha512-hyVbaCUd18UiXk656g/imaBLMogpdijIEpnhWYrSda9rhvO4gOU16n2nh7xG5lv/rjumnZzGOdz0CEGTmFe0fQ==" - }, "typescript": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", @@ -9751,9 +7837,9 @@ "dev": true }, "uglify-js": { - "version": "3.13.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.2.tgz", - "integrity": "sha512-SbMu4D2Vo95LMC/MetNaso1194M1htEA+JrqE9Hk+G2DhI+itfS9TRu9ZKeCahLDNa/J3n4MqUJ/fOHMzQpRWw==", + "version": "3.13.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz", + "integrity": "sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==", "dev": true, "optional": true }, @@ -9811,6 +7897,11 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -9978,21 +8069,21 @@ "dev": true }, "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true }, "yargs": { @@ -10008,20 +8099,12 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" - }, - "dependencies": { - "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", - "dev": true - } } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.7", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", "dev": true }, "yargs-unparser": { @@ -10036,23 +8119,11 @@ "is-plain-obj": "^2.1.0" }, "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true } } }, diff --git a/package.json b/package.json index 06dbf8a..32d88f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "assertthat", - "version": "5.2.8", + "version": "6.0.0-internal.7", "description": "assertthat provides fluent TDD.", "contributors": [ { @@ -31,13 +31,14 @@ "main": "build/lib/assertthat.js", "types": "build/lib/assertthat.d.ts", "dependencies": { - "@types/stringify-object": "3.3.0", - "comparejs": "4.0.7", - "stringify-object": "3.3.0" + "@types/uuid": "8.3.0", + "chalk": "4.1.1", + "common-tags": "1.8.0", + "defekt": "7.1.0", + "uuid": "8.3.2" }, "devDependencies": { - "@types/chai": "4.2.17", - "chai": "4.3.4", + "@types/common-tags": "1.8.0", "roboter": "11.6.40", "semantic-release-configuration": "2.0.0" }, diff --git a/test/unit/assertions/forAny/assertActualIsEqualToExpectedTests.ts b/test/unit/assertions/forAny/assertActualIsEqualToExpectedTests.ts new file mode 100644 index 0000000..73fe6e9 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsEqualToExpectedTests.ts @@ -0,0 +1,64 @@ +import { assert } from '../../../../lib'; +import { assertActualIsEqualToExpected } from '../../../../lib/assertions/forAny/assertActualIsEqualToExpected'; +import { AssertionFailed } from '../../../../lib/errors'; +import { compare } from '../../../../lib/comparisons/typeAware/compare'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { error, value } from 'defekt'; + +suite('assertActualIsEqualToExpected', (): void => { + test('does not return an error if actual is equal to expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3, 7 ]), + map: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3, 7 ]), + map: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + + assert.that( + assertActualIsEqualToExpected(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is not equal to expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3, 7 ]), + map: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3 ]), + map: new Map([[ 'foo', 'not foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + + assert.that( + assertActualIsEqualToExpected(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The values are not equal.', + actual: prettyPrint(actual), + expected: prettyPrint(expected), + diff: prettyPrintDiff(compare(actual, expected)) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsFalseTests.ts b/test/unit/assertions/forAny/assertActualIsFalseTests.ts new file mode 100644 index 0000000..a105112 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsFalseTests.ts @@ -0,0 +1,20 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertActualIsFalse } from '../../../../lib/assertions/forAny/assertActualIsFalse'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsFalse', (): void => { + test('does not return an error if actual is false.', async (): Promise => { + const actual = false; + + assert.that(assertActualIsFalse(actual)).is.equalTo(value()); + }); + + test('returns an error if actual is not false.', async (): Promise => { + const actual = true; + + assert.that(assertActualIsFalse(actual)).is.equalTo(error(new AssertionFailed({ + message: 'The value is not false.' + }))); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsFalsyTests.ts b/test/unit/assertions/forAny/assertActualIsFalsyTests.ts new file mode 100644 index 0000000..64e4ba1 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsFalsyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { assertActualIsFalsy } from '../../../../lib/assertions/forAny/assertActualIsFalsy'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsFalsy', (): void => { + test('does not return an error if actual is falsy.', async (): Promise => { + const actual = 0; + + assert.that( + assertActualIsFalsy(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is not falsy.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsFalsy(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is not falsy.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsIdenticalToExpectedTests.ts b/test/unit/assertions/forAny/assertActualIsIdenticalToExpectedTests.ts new file mode 100644 index 0000000..57e024c --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsIdenticalToExpectedTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertActualIsIdenticalToExpected } from '../../../../lib/assertions/forAny/assertActualIsIdenticalToExpected'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsIdenticalToExpected', (): void => { + test('does not return an error if actual and expected have the same identity.', async (): Promise => { + const actual = {}; + const expected = actual; + + assert.that( + assertActualIsIdenticalToExpected(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual and expected do not have the same identity, even if they are equal.', async (): Promise => { + const actual = {}; + const expected = {}; + + assert.that( + assertActualIsIdenticalToExpected(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The values are not identical.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotEqualToExpectedTests.ts b/test/unit/assertions/forAny/assertActualIsNotEqualToExpectedTests.ts new file mode 100644 index 0000000..2573f72 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotEqualToExpectedTests.ts @@ -0,0 +1,60 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotEqualToExpected } from '../../../../lib/assertions/forAny/assertActualIsNotEqualToExpected'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotEqualToExpected', (): void => { + test('does not return an error if actual is not equal to expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3, 7 ]), + map: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3 ]), + map: new Map([[ 'foo', 'not foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + + assert.that( + assertActualIsNotEqualToExpected(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is equal to expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3, 7 ]), + map: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + set: new Set([ 1, 3, 7 ]), + map: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + bam: false, + bas: { bat: {}} + }; + + assert.that( + assertActualIsNotEqualToExpected(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The values are equal.', + expected: `Not to equal:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotFalseTests.ts b/test/unit/assertions/forAny/assertActualIsNotFalseTests.ts new file mode 100644 index 0000000..91ec8af --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotFalseTests.ts @@ -0,0 +1,20 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertActualIsNotFalse } from '../../../../lib/assertions/forAny/assertActualIsNotFalse'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotFalse', (): void => { + test('does not return an error if actual is not false.', async (): Promise => { + const actual = true; + + assert.that(assertActualIsNotFalse(actual)).is.equalTo(value()); + }); + + test('returns an error if actual is false.', async (): Promise => { + const actual = false; + + assert.that(assertActualIsNotFalse(actual)).is.equalTo(error(new AssertionFailed({ + message: 'The value is false.' + }))); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotFalsyTests.ts b/test/unit/assertions/forAny/assertActualIsNotFalsyTests.ts new file mode 100644 index 0000000..4386fca --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotFalsyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotFalsy } from '../../../../lib/assertions/forAny/assertActualIsNotFalsy'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotFalsy', (): void => { + test('does not return an error if actual is not falsy.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsNotFalsy(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is falsy.', async (): Promise => { + const actual = 0; + + assert.that( + assertActualIsNotFalsy(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is falsy.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotIdenticalToExpectedTests.ts b/test/unit/assertions/forAny/assertActualIsNotIdenticalToExpectedTests.ts new file mode 100644 index 0000000..13d13a2 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotIdenticalToExpectedTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertActualIsNotIdenticalToExpected } from '../../../../lib/assertions/forAny/assertActualIsNotIdenticalToExpected'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotIdenticalToExpected', (): void => { + test('does not return an error if actual and expected do not have the same identity, even if they are equal.', async (): Promise => { + const actual = {}; + const expected = {}; + + assert.that( + assertActualIsNotIdenticalToExpected(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual and expected have the same identity.', async (): Promise => { + const actual = {}; + const expected = actual; + + assert.that( + assertActualIsNotIdenticalToExpected(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The values are identical.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotNullTests.ts b/test/unit/assertions/forAny/assertActualIsNotNullTests.ts new file mode 100644 index 0000000..482f19c --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotNullTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotNull } from '../../../../lib/assertions/forAny/assertActualIsNotNull'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotNull', (): void => { + test('does not return an error if actual is not null.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsNotNull(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is null.', async (): Promise => { + const actual = null; + + assert.that( + assertActualIsNotNull(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is null.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotOfTypeTests.ts b/test/unit/assertions/forAny/assertActualIsNotOfTypeTests.ts new file mode 100644 index 0000000..b73de5f --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotOfTypeTests.ts @@ -0,0 +1,42 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotOfType } from '../../../../lib/assertions/forAny/assertActualIsNotOfType'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { Type } from '../../../../lib/types/Type'; + +suite('assertActualIsNotOfType', (): void => { + test('does not return an error when asserting for the wrong type.', async (): Promise => { + const actual: any[] = []; + const type = 'string'; + + assert.that( + assertActualIsNotOfType(actual, type as Type) + ).is.aValue(); + }); + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ] + ]) { + test('returns an error when asserting for a matching type.', async (): Promise => { + assert.that( + assertActualIsNotOfType(value, type as Type) + ).is.equalTo(error(new AssertionFailed({ + message: `The value is of type ${type}.`, + actual: prettyPrint(value) + }))); + }); + } +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotSameJsonAsExpectedTests.ts b/test/unit/assertions/forAny/assertActualIsNotSameJsonAsExpectedTests.ts new file mode 100644 index 0000000..647065a --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotSameJsonAsExpectedTests.ts @@ -0,0 +1,51 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotSameJsonAsExpected } from '../../../../lib/assertions/forAny/assertActualIsNotSameJsonAsExpected'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotSameJsonAsExpected', (): void => { + test('does not return an error if actual does not have the same JSON representation as expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2, 3, 4 ], + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + bam: true, + bas: { bat: {}} + }; + + assert.that( + assertActualIsNotSameJsonAsExpected(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual has the same JSON representation as expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2 ], + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + bam: false, + bas: { bat: {}} + }; + + assert.that( + assertActualIsNotSameJsonAsExpected(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The values have the same JSON representation.', + expected: `Not to equal:\n${JSON.stringify(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotTrue.ts b/test/unit/assertions/forAny/assertActualIsNotTrue.ts new file mode 100644 index 0000000..6580079 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotTrue.ts @@ -0,0 +1,20 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertActualIsNotTrue } from '../../../../lib/assertions/forAny/assertActualIsNotTrue'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotTrue', (): void => { + test('does not return an error if actual is not true.', async (): Promise => { + const actual = false; + + assert.that(assertActualIsNotTrue(actual)).is.equalTo(value()); + }); + + test('returns an error if actual is true.', async (): Promise => { + const actual = true; + + assert.that(assertActualIsNotTrue(actual)).is.equalTo(error(new AssertionFailed({ + message: 'The value is not false.' + }))); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotTruthyTests.ts b/test/unit/assertions/forAny/assertActualIsNotTruthyTests.ts new file mode 100644 index 0000000..2ea7f8a --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotTruthyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotTruthy } from '../../../../lib/assertions/forAny/assertActualIsNotTruthy'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotTruthy', (): void => { + test('does not return an error if actual is not truthy.', async (): Promise => { + const actual = 0; + + assert.that( + assertActualIsNotTruthy(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is truthy.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsNotTruthy(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is truthy.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNotUndefinedTests.ts b/test/unit/assertions/forAny/assertActualIsNotUndefinedTests.ts new file mode 100644 index 0000000..2a66ae4 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNotUndefinedTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNotUndefined } from '../../../../lib/assertions/forAny/assertActualIsNotUndefined'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsNotUndefined', (): void => { + test('does not return an error if actual is not undefined.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsNotUndefined(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is undefined.', async (): Promise => { + const actual = undefined; + + assert.that( + assertActualIsNotUndefined(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is undefined.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsNullTests.ts b/test/unit/assertions/forAny/assertActualIsNullTests.ts new file mode 100644 index 0000000..99f0333 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsNullTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { assertActualIsNull } from '../../../../lib/assertions/forAny/assertActualIsNull'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsNull', (): void => { + test('does not return an error if actual is null.', async (): Promise => { + const actual = null; + + assert.that( + assertActualIsNull(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is not null.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsNull(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is not null.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsOfTypeTests.ts b/test/unit/assertions/forAny/assertActualIsOfTypeTests.ts new file mode 100644 index 0000000..635f748 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsOfTypeTests.ts @@ -0,0 +1,42 @@ +import { assert } from '../../../../lib'; +import { assertActualIsOfType } from '../../../../lib/assertions/forAny/assertActualIsOfType'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { Type } from '../../../../lib/types/Type'; + +suite('assertActualIsOfType', (): void => { + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ] + ]) { + test('does not return an error when asserting for the correct type.', async (): Promise => { + assert.that( + assertActualIsOfType(value, type as Type) + ).is.aValue(); + }); + } + + test('returns an error when asserting for the wrong type.', async (): Promise => { + const actual: any[] = []; + const type = 'string'; + + assert.that( + assertActualIsOfType(actual, type as Type) + ).is.equalTo(error(new AssertionFailed({ + message: 'The value is not of type string.', + actual: prettyPrint(actual) + }))); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsSameJsonAsExpectedTests.ts b/test/unit/assertions/forAny/assertActualIsSameJsonAsExpectedTests.ts new file mode 100644 index 0000000..81faf75 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsSameJsonAsExpectedTests.ts @@ -0,0 +1,55 @@ +import { assert } from '../../../../lib'; +import { assertActualIsSameJsonAsExpected } from '../../../../lib/assertions/forAny/assertActualIsSameJsonAsExpected'; +import { AssertionFailed } from '../../../../lib/errors'; +import { compare } from '../../../../lib/comparisons/typeAware/compare'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { error, value } from 'defekt'; + +suite('assertActualIsSameJsonAsExpected', (): void => { + test('does not return an error if actual has the same JSON representation as expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2 ], + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + bam: false, + bas: { bat: {}} + }; + + assert.that( + assertActualIsSameJsonAsExpected(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual does not have the same JSON representation as expected.', async (): Promise => { + const actual = { + foo: 'foo', + bar: [ 1, 2, 3, 4 ], + bam: false, + bas: { bat: {}} + }; + const expected = { + foo: 'foo', + bar: [ 1, 2 ], + bam: true, + bas: { bat: {}} + }; + + assert.that( + assertActualIsSameJsonAsExpected(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The values do not have the same JSON representation.', + actual: JSON.stringify(actual), + expected: JSON.stringify(expected), + diff: prettyPrintDiff(compare(JSON.stringify(actual), JSON.stringify(expected))) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsTrueTests.ts b/test/unit/assertions/forAny/assertActualIsTrueTests.ts new file mode 100644 index 0000000..526d40a --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsTrueTests.ts @@ -0,0 +1,20 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertActualIsTrue } from '../../../../lib/assertions/forAny/assertActualIsTrue'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertActualIsTrue', (): void => { + test('does not return an error if actual is true.', async (): Promise => { + const actual = true; + + assert.that(assertActualIsTrue(actual)).is.equalTo(value()); + }); + + test('returns an error if actual is not true.', async (): Promise => { + const actual = false; + + assert.that(assertActualIsTrue(actual)).is.equalTo(error(new AssertionFailed({ + message: 'The value is not true.' + }))); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsTruthyTests.ts b/test/unit/assertions/forAny/assertActualIsTruthyTests.ts new file mode 100644 index 0000000..abb78f4 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsTruthyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { assertActualIsTruthy } from '../../../../lib/assertions/forAny/assertActualIsTruthy'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsTruthy', (): void => { + test('does not return an error if actual is truthy.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsTruthy(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is not truthy.', async (): Promise => { + const actual = 0; + + assert.that( + assertActualIsTruthy(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is not truthy.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forAny/assertActualIsUndefinedTests.ts b/test/unit/assertions/forAny/assertActualIsUndefinedTests.ts new file mode 100644 index 0000000..14209c9 --- /dev/null +++ b/test/unit/assertions/forAny/assertActualIsUndefinedTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { assertActualIsUndefined } from '../../../../lib/assertions/forAny/assertActualIsUndefined'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertActualIsUndefined', (): void => { + test('does not return an error if actual is undefined.', async (): Promise => { + const actual = undefined; + + assert.that( + assertActualIsUndefined(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is not undefined.', async (): Promise => { + const actual = 15; + + assert.that( + assertActualIsUndefined(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The value is not undefined.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsContainingAllOfIterableTests.ts b/test/unit/assertions/forArrays/assertArrayIsContainingAllOfIterableTests.ts new file mode 100644 index 0000000..759c772 --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsContainingAllOfIterableTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsContainingAllOfIterable } from '../../../../lib/assertions/forArrays/assertArrayIsContainingAllOfIterable'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsContainingAllOfIterable', (): void => { + test('does not return an error if the array contains all expected items.', async (): Promise => { + const actual = [ 1, 2, 4, { foo: 'bar' }]; + const iterable = [ 2, { foo: 'bar' }]; + + assert.that( + assertArrayIsContainingAllOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array does not contain all expected items.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + const iterable = [ 2, 4, { foo: 'bar' }]; + + assert.that( + assertArrayIsContainingAllOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array does not contain all expected items.', + expected: `To contain all of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual), + diff: `Missing these items:\n${prettyPrint(new Set([{ foo: 'bar' }]))}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsContainingAnyOfIterableTests.ts b/test/unit/assertions/forArrays/assertArrayIsContainingAnyOfIterableTests.ts new file mode 100644 index 0000000..a74d2c8 --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsContainingAnyOfIterableTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsContainingAnyOfIterable } from '../../../../lib/assertions/forArrays/assertArrayIsContainingAnyOfIterable'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsContainingAnyOfIterable', (): void => { + test('does not return an error if the array contains any of the expected items.', async (): Promise => { + const actual = [ 1, 2, 4, { foo: 'bar' }]; + const iterable: any[] = [ 2, new Set([ 5, 8 ]) ]; + + assert.that( + assertArrayIsContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array does not contain any of the expected items.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + const iterable = [ 17, 32, { foo: 'bar' }]; + + assert.that( + assertArrayIsContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array does not contain any of the expected items.', + expected: `To contain any of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsContainingItemTests.ts b/test/unit/assertions/forArrays/assertArrayIsContainingItemTests.ts new file mode 100644 index 0000000..040fd2e --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsContainingItemTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsContainingItem } from '../../../../lib/assertions/forArrays/assertArrayIsContainingItem'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsContainingItem', (): void => { + test('does not return an error if the array contains the item.', async (): Promise => { + const actual = [ 1, 2, 4, { foo: 'bar' }]; + const item = { foo: 'bar' }; + + assert.that( + assertArrayIsContainingItem(actual, item) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array does not contain the item.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + const item = 15; + + assert.that( + assertArrayIsContainingItem(actual, item) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array does not contain the expected item.', + expected: `To contain:\n${prettyPrint(item)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsEmptyTests.ts b/test/unit/assertions/forArrays/assertArrayIsEmptyTests.ts new file mode 100644 index 0000000..b7f0994 --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsEmptyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsEmpty } from '../../../../lib/assertions/forArrays/assertArrayIsEmpty'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsEmpty', (): void => { + test('does not return an error if the array is empty.', async (): Promise => { + const actual: any[] = []; + + assert.that( + assertArrayIsEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array is not empty.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + + assert.that( + assertArrayIsEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array is not empty.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsNotContainingAllOfIterableTests.ts b/test/unit/assertions/forArrays/assertArrayIsNotContainingAllOfIterableTests.ts new file mode 100644 index 0000000..b280680 --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsNotContainingAllOfIterableTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsNotContainingAllOfIterable } from '../../../../lib/assertions/forArrays/assertArrayIsNotContainingAllOfIterable'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsNotContainingAllOfIterable', (): void => { + test('does not return an error if the array does not contain all expected items.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + const iterable = [ 2, 4, { foo: 'bar' }]; + + assert.that( + assertArrayIsNotContainingAllOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array contains all expected items.', async (): Promise => { + const actual = [ 1, 2, 4, { foo: 'bar' }]; + const iterable = [ 2, { foo: 'bar' }]; + + assert.that( + assertArrayIsNotContainingAllOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array contains all items in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain all of:\n${prettyPrint(iterable)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsNotContainingAnyOfIterableTests.ts b/test/unit/assertions/forArrays/assertArrayIsNotContainingAnyOfIterableTests.ts new file mode 100644 index 0000000..658516c --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsNotContainingAnyOfIterableTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsNotContainingAnyOfIterable } from '../../../../lib/assertions/forArrays/assertArrayIsNotContainingAnyOfIterable'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsNotContainingAnyOfIterable', (): void => { + test('returns an error if the array contains any of the expected items.', async (): Promise => { + const actual = [ 1, 2, 4, { foo: 'bar' }]; + const iterable: any[] = [ 2, new Set([ 5, 8 ]) ]; + + assert.that( + assertArrayIsNotContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array contains one or more of the items in the iterable.', + expected: `To not contain any of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual), + diff: `These items are contained, but should not be:\n${prettyPrint(new Set([ 2 ]))}` + })) + ); + }); + + test('does not return an error if the array does not contain any of the expected items.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + const iterable = [ 17, 32, { foo: 'bar' }]; + + assert.that( + assertArrayIsNotContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsNotContainingItemTests.ts b/test/unit/assertions/forArrays/assertArrayIsNotContainingItemTests.ts new file mode 100644 index 0000000..4df0e7b --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsNotContainingItemTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsNotContainingItem } from '../../../../lib/assertions/forArrays/assertArrayIsNotContainingItem'; +import { AssertionFailed } from '../../../../lib/errors'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertArrayIsNotContainingItem', (): void => { + test('does not return an error if the array does not contain the item.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + const item = 15; + + assert.that( + assertArrayIsNotContainingItem(actual, item) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array contains the item.', async (): Promise => { + const actual = [ 1, 2, 4, { foo: 'bar' }]; + const item = { foo: 'bar' }; + + assert.that( + assertArrayIsNotContainingItem(actual, item) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array contains the item.', + expected: `To not contain:\n${prettyPrint(item)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forArrays/assertArrayIsNotEmptyTests.ts b/test/unit/assertions/forArrays/assertArrayIsNotEmptyTests.ts new file mode 100644 index 0000000..8a78d06 --- /dev/null +++ b/test/unit/assertions/forArrays/assertArrayIsNotEmptyTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertArrayIsNotEmpty } from '../../../../lib/assertions/forArrays/assertArrayIsNotEmpty'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertArrayIsNotEmpty', (): void => { + test('does not return an error if the array is not empty.', async (): Promise => { + const actual = [ 1, 2, 4 ]; + + assert.that( + assertArrayIsNotEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the array is empty.', async (): Promise => { + const actual: any[] = []; + + assert.that( + assertArrayIsNotEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The array is empty.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forFunctions/assertFunctionIsNotThrowingAsyncTests.ts b/test/unit/assertions/forFunctions/assertFunctionIsNotThrowingAsyncTests.ts new file mode 100644 index 0000000..8331edd --- /dev/null +++ b/test/unit/assertions/forFunctions/assertFunctionIsNotThrowingAsyncTests.ts @@ -0,0 +1,115 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertFunctionIsNotThrowingAsync } from '../../../../lib/assertions/forFunctions/assertFunctionIsNotThrowingAsync'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertFunctionIsNotThrowingAsync', (): void => { + test('returns an error if actual is not returning a promise.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + // Intentionally left empty. + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun)).is.equalTo( + error(new AssertionFailed({ + message: 'The function did not return a Promise.' + })) + ); + }); + + test('does not return an error if actual is not throwing an asynchronous error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + // Intentionally left empty. + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynochronous error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + actual: 'Error message:\nFoo' + })) + ); + }); + + test('does not return an error if actual is not throwing an asynchronous error that is equal to the given message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun, 'Not foo')).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynchronous error that is equal to the given message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun, 'Foo')).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: 'The message should not have been:\nFoo', + actual: 'Error message:\nFoo' + })) + ); + }); + + test('does not return an error if actual is throwing an asynchronous error that does not match the given RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Not foo'); + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun, /^Foo.*/u)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynchronous error whose message matches the given RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo bar'); + }; + const regExp = /^Foo.*/u; + + assert.that(await assertFunctionIsNotThrowingAsync(fun, regExp)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The message should not have matched:\n${regExp.toString()}`, + actual: 'Error message:\nFoo bar' + })) + ); + }); + + test('does not return an error if actual is not throwing an asynchronous error that fulfils the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Not foo'); + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun, (ex): boolean => ex.message === 'Foo')).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynchronous error that fulfils the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that(await assertFunctionIsNotThrowingAsync(fun, (ex): boolean => ex.message === 'Foo')).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: 'The exception should not have fulfilled a predicate.', + actual: 'Error message:\nFoo' + })) + ); + }); +}); diff --git a/test/unit/assertions/forFunctions/assertFunctionIsNotThrowingTests.ts b/test/unit/assertions/forFunctions/assertFunctionIsNotThrowingTests.ts new file mode 100644 index 0000000..5d3ffca --- /dev/null +++ b/test/unit/assertions/forFunctions/assertFunctionIsNotThrowingTests.ts @@ -0,0 +1,107 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertFunctionIsNotThrowing } from '../../../../lib/assertions/forFunctions/assertFunctionIsNotThrowing'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertFunctionIsNotThrowing', (): void => { + test('does not return an error if actual is not throwing an error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + // Intentionally left blank. + }; + + assert.that(assertFunctionIsNotThrowing(fun)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo'); + }; + + assert.that(assertFunctionIsNotThrowing(fun)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + actual: 'Error message:\nFoo' + })) + ); + }); + + test('does not return an error if actual is not throwing an error that is equal to the given message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo'); + }; + + assert.that(assertFunctionIsNotThrowing(fun, 'Not foo')).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error that is equal to the given message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo'); + }; + + assert.that(assertFunctionIsNotThrowing(fun, 'Foo')).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: 'The message should not have been:\nFoo', + actual: 'Error message:\nFoo' + })) + ); + }); + + test('does not return an error if actual is throwing an error that does not match the given RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Bar.'); + }; + const regExp = /^Foo.*/u; + + assert.that(assertFunctionIsNotThrowing(fun, regExp)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error whose message matches the given RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + const regExp = /^Foo.*/u; + + assert.that(assertFunctionIsNotThrowing(fun, regExp)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The message should not have matched:\n${regExp.toString()}`, + actual: 'Error message:\nFoo bar.' + })) + ); + }); + + test('does not return an error if actual is not throwing an error that fulfils the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + // eslint-disable-next-line unicorn/consistent-function-scoping + const predicate = (ex: Error): boolean => ex.message === 'Not foo.'; + + assert.that(assertFunctionIsNotThrowing(fun, predicate)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error that fulfils the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + // eslint-disable-next-line unicorn/consistent-function-scoping + const predicate = (ex: Error): boolean => ex.message === 'Foo bar.'; + + assert.that(assertFunctionIsNotThrowing(fun, predicate)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: 'The exception should not have fulfilled a predicate.', + actual: 'Error message:\nFoo bar.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forFunctions/assertFunctionIsThrowingAsyncTests.ts b/test/unit/assertions/forFunctions/assertFunctionIsThrowingAsyncTests.ts new file mode 100644 index 0000000..d348dc1 --- /dev/null +++ b/test/unit/assertions/forFunctions/assertFunctionIsThrowingAsyncTests.ts @@ -0,0 +1,122 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertFunctionIsThrowingAsync } from '../../../../lib/assertions/forFunctions/assertFunctionIsThrowingAsync'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertFunctionIsThrowingAsync', (): void => { + test('returns an error if actual is not returning a promise.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + // Intentionally left blank. + }; + + assert.that(await assertFunctionIsThrowingAsync(fun)).is.equalTo( + error(new AssertionFailed({ + message: 'The function did not return a Promise.' + })) + ); + }); + + test('does not return an error if actual is throwing an asynchronous error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that(await assertFunctionIsThrowingAsync(fun)).is.equalTo(value()); + }); + + test('returns an error if actual is not throwing an asynchronous error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + // Intentionally left blank. + }; + + assert.that(await assertFunctionIsThrowingAsync(fun)).is.equalTo( + error(new AssertionFailed({ + message: 'The function did not throw an asynchronous exception.' + })) + ); + }); + + test('does not return an error if actual is throwing an asynchronous error with the correct message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that( + await assertFunctionIsThrowingAsync(fun, 'Foo') + ).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynchronous error with an incorrect message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Not foo'); + }; + + assert.that(await assertFunctionIsThrowingAsync(fun, 'Foo')).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: 'The message should have been:\nFoo', + actual: 'Error message:\nNot foo' + })) + ); + }); + + test('does not return an error if actual is throwing an asynchronous error whose message matches the RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo bar'); + }; + + assert.that( + await assertFunctionIsThrowingAsync(fun, /^Foo.*/u) + ).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynchronous error whose message does not match the RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Not foo'); + }; + const regExp = /^Foo.*/u; + + assert.that(await assertFunctionIsThrowingAsync(fun, regExp)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: `The message should have matched:\n${regExp.toString()}`, + actual: 'Error message:\nNot foo' + })) + ); + }); + + test('does not return an error if actual is throwing an asynchronous error that fulfils the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Foo'); + }; + + assert.that( + await assertFunctionIsThrowingAsync(fun, (ex): boolean => ex.message === 'Foo') + ).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an asynchronous error that does not fulfil the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = async (): Promise => { + throw new Error('Not foo'); + }; + + assert.that( + await assertFunctionIsThrowingAsync(fun, (ex): boolean => ex.message === 'Foo') + ).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected asynchronous exception.', + expected: 'The exception should have fulfilled a predicate.', + actual: 'Error message:\nNot foo' + })) + ); + }); +}); diff --git a/test/unit/assertions/forFunctions/assertFunctionIsThrowingTests.ts b/test/unit/assertions/forFunctions/assertFunctionIsThrowingTests.ts new file mode 100644 index 0000000..17cf346 --- /dev/null +++ b/test/unit/assertions/forFunctions/assertFunctionIsThrowingTests.ts @@ -0,0 +1,106 @@ +import { assert } from '../../../../lib/assertthat'; +import { assertFunctionIsThrowing } from '../../../../lib/assertions/forFunctions/assertFunctionIsThrowing'; +import { AssertionFailed } from '../../../../lib/errors'; +import { error, value } from 'defekt'; + +suite('assertFunctionIsThrowing', (): void => { + test('does not return an error if actual is throwing an error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo'); + }; + + assert.that(assertFunctionIsThrowing(fun)).is.equalTo(value()); + }); + + test('returns an error if actual is not throwing an error.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + // Intentionally left blank. + }; + + assert.that(assertFunctionIsThrowing(fun)).is.equalTo( + error(new AssertionFailed({ + message: 'The function did not throw an exception.' + })) + ); + }); + + test('does not return an error if actual is throwing an error with the correct message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo'); + }; + + assert.that(assertFunctionIsThrowing(fun, 'Foo')).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error with an incorrect message.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo'); + }; + + assert.that(assertFunctionIsThrowing(fun, 'Bar')).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: 'The message should have been:\nBar', + actual: 'Error message:\nFoo' + })) + ); + }); + + test('does not return an error if actual is throwing an error whose message matches the RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + const regExp = /^Foo.*/u; + + assert.that(assertFunctionIsThrowing(fun, regExp)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error whose message does not match the RegExp.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + const regExp = /^Not foo.*/u; + + assert.that(assertFunctionIsThrowing(fun, regExp)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: `The message should have matched:\n${regExp.toString()}`, + actual: 'Error message:\nFoo bar.' + })) + ); + }); + + test('does not return an error if actual is throwing an error that fulfils the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + // eslint-disable-next-line unicorn/consistent-function-scoping + const predicate = (ex: Error): boolean => ex.message === 'Foo bar.'; + + assert.that(assertFunctionIsThrowing(fun, predicate)).is.equalTo(value()); + }); + + test('returns an error if actual is throwing an error that does not fulfil the predicate.', async (): Promise => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const fun = (): void => { + throw new Error('Foo bar.'); + }; + // eslint-disable-next-line unicorn/consistent-function-scoping + const predicate = (ex: Error): boolean => ex.message === 'Not foo.'; + + assert.that(assertFunctionIsThrowing(fun, predicate)).is.equalTo( + error(new AssertionFailed({ + message: 'The function threw an unexpected exception.', + expected: 'The exception should have fulfilled a predicate.', + actual: 'Error message:\nFoo bar.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forMaps/assertMapIsAtLeastMapTests.ts b/test/unit/assertions/forMaps/assertMapIsAtLeastMapTests.ts new file mode 100644 index 0000000..c3207cc --- /dev/null +++ b/test/unit/assertions/forMaps/assertMapIsAtLeastMapTests.ts @@ -0,0 +1,99 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertMapIsAtLeastMap } from '../../../../lib/assertions/forMaps/assertMapIsAtLeastMap'; +import { mapDiff } from '../../../../lib/diffs/forMaps/MapDiff'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { error, value } from 'defekt'; + +suite('assertMapIsAtLeastMap', (): void => { + test('does not return an error if the expected map is entirely contained in the actual map.', async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + const expected = new Map([ + [ 13, 37 ], + [ 'heck', 'meck' ] + ]); + + assert.that( + assertMapIsAtLeastMap(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the expected map is not entirely contained in the actual map.', async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + const expected = new Map([ + [ 'heck', 'meck' ], + [ 'uiae', 'nrtd' ] + ]); + + assert.that( + assertMapIsAtLeastMap(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The expected map is not entirely contained in the actual map.', + actual: prettyPrint(actual), + expected: `To entirely contain:\n${prettyPrint(expected)}`, + diff: `The following sub-map shows relevant changes between actual and expected:\n${prettyPrintDiff( + mapDiff({ + equal: new Map(), + additions: new Map(), + changes: new Map(), + omissions: new Map([[ 'uiae', 'nrtd' ]]), + cost: 0.5 + }) + )}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`returns an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ] + ]); + const expected = new Map([ + [ 'foo', 'bam' ] + ]); + + assert.that( + assertMapIsAtLeastMap(actual, expected) + ).is.not.equalTo( + value() + ); + }); + + test(`does not return an error if a nested map on the actual side contains additional properties.`, async (): Promise => { + const actual = new Map([ + [ 'foo', 'foo' ], + [ 'bar', { + foo: 'foo', + bar: 'bar' + }] + ]); + const expected = new Map([ + [ 'foo', 'foo' ], + [ 'bar', { + foo: 'foo' + }] + ]); + + assert.that( + assertMapIsAtLeastMap(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forMaps/assertMapIsAtMostMapTests.ts b/test/unit/assertions/forMaps/assertMapIsAtMostMapTests.ts new file mode 100644 index 0000000..f805b40 --- /dev/null +++ b/test/unit/assertions/forMaps/assertMapIsAtMostMapTests.ts @@ -0,0 +1,99 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertMapIsAtMostMap } from '../../../../lib/assertions/forMaps/assertMapIsAtMostMap'; +import { mapDiff } from '../../../../lib/diffs/forMaps/MapDiff'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { error, value } from 'defekt'; + +suite('assertMapIsAtMostMap', (): void => { + test('does not return an error if the actual map is entirely contained in the expected map.', async (): Promise => { + const actual = new Map([ + [ 13, 37 ], + [ 'heck', 'meck' ] + ]); + const expected = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + + assert.that( + assertMapIsAtMostMap(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the actual map is not entirely contained in the expected map.', async (): Promise => { + const actual = new Map([ + [ 'heck', 'meck' ], + [ 'uiae', 'nrtd' ] + ]); + const expected = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + + assert.that( + assertMapIsAtMostMap(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The actual map is not entirely contained in the expected map.', + actual: prettyPrint(actual), + expected: `To be entirely contained in:\n${prettyPrint(expected)}`, + diff: `The following sub-map shows relevant changes between actual and expected:\n${prettyPrintDiff( + mapDiff({ + equal: new Map(), + additions: new Map([[ 'uiae', 'nrtd' ]]), + changes: new Map(), + omissions: new Map(), + cost: 0.5 + }) + )}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`returns an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ] + ]); + const expected = new Map([ + [ 'foo', 'bam' ] + ]); + + assert.that( + assertMapIsAtMostMap(actual, expected) + ).is.not.equalTo( + value() + ); + }); + + test(`does not return an error if a nested map on the expected side contains additional properties.`, async (): Promise => { + const actual = new Map([ + [ 'foo', 'foo' ], + [ 'bar', { + foo: 'foo' + }] + ]); + const expected = new Map([ + [ 'foo', 'foo' ], + [ 'bar', { + foo: 'foo', + bar: 'bar' + }] + ]); + + assert.that( + assertMapIsAtMostMap(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forMaps/assertMapIsEmptyTests.ts b/test/unit/assertions/forMaps/assertMapIsEmptyTests.ts new file mode 100644 index 0000000..64e7265 --- /dev/null +++ b/test/unit/assertions/forMaps/assertMapIsEmptyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertMapIsEmpty } from '../../../../lib/assertions/forMaps/assertMapIsEmpty'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertMapIsEmpty', (): void => { + test('does not return an error if the map is empty.', async (): Promise => { + const actual = new Map(); + + assert.that( + assertMapIsEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the map is not empty.', async (): Promise => { + const actual = new Map([[ 'foo', 'bar' ]]); + + assert.that( + assertMapIsEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The map is not empty.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forMaps/assertMapIsNotAtLeastMapTests.ts b/test/unit/assertions/forMaps/assertMapIsNotAtLeastMapTests.ts new file mode 100644 index 0000000..19239d1 --- /dev/null +++ b/test/unit/assertions/forMaps/assertMapIsNotAtLeastMapTests.ts @@ -0,0 +1,66 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertMapIsNotAtLeastMap } from '../../../../lib/assertions/forMaps/assertMapIsNotAtLeastMap'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertMapIsNotAtLeastMap', (): void => { + test('does not return an error if the expected map is not entirely contained in the actual map.', async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + const expected = new Map([ + [ 'heck', 'meck' ], + [ 'uiae', 'nrtd' ] + ]); + + assert.that( + assertMapIsNotAtLeastMap(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the expected map is entirely contained in the actual map.', async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + const expected = new Map([ + [ 13, 37 ], + [ 'heck', 'meck' ] + ]); + + assert.that( + assertMapIsNotAtLeastMap(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The expected map is entirely contained in the actual map.', + actual: prettyPrint(actual), + expected: `To not entirely contain:\n${prettyPrint(expected)}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`does not return an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ] + ]); + const expected = new Map([ + [ 'foo', 'bam' ] + ]); + + assert.that( + assertMapIsNotAtLeastMap(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forMaps/assertMapIsNotAtMostMapTests.ts b/test/unit/assertions/forMaps/assertMapIsNotAtMostMapTests.ts new file mode 100644 index 0000000..ef0763a --- /dev/null +++ b/test/unit/assertions/forMaps/assertMapIsNotAtMostMapTests.ts @@ -0,0 +1,66 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertMapIsNotAtMostMap } from '../../../../lib/assertions/forMaps/assertMapIsNotAtMostMap'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertMapIsNotAtMostMap', (): void => { + test('does not return an error if the actual map is not entirely contained in the expected map.', async (): Promise => { + const actual = new Map([ + [ 'heck', 'meck' ], + [ 'uiae', 'nrtd' ] + ]); + const expected = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + + assert.that( + assertMapIsNotAtMostMap(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the actual map is entirely contained in the expected map.', async (): Promise => { + const actual = new Map([ + [ 13, 37 ], + [ 'heck', 'meck' ] + ]); + const expected = new Map([ + [ 'foo', 'bar' ], + [ 13, 37 ], + [ 12, 34 ], + [ 'heck', 'meck' ] + ]); + + assert.that( + assertMapIsNotAtMostMap(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The actual map is entirely contained in the expected map.', + actual: prettyPrint(actual), + expected: `To not be entirely contained in:\n${prettyPrint(expected)}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`does not return an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = new Map([ + [ 'foo', 'bar' ] + ]); + const expected = new Map([ + [ 'foo', 'bam' ] + ]); + + assert.that( + assertMapIsNotAtMostMap(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forMaps/assertMapIsNotEmptyTests.ts b/test/unit/assertions/forMaps/assertMapIsNotEmptyTests.ts new file mode 100644 index 0000000..52b4bfd --- /dev/null +++ b/test/unit/assertions/forMaps/assertMapIsNotEmptyTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertMapIsNotEmpty } from '../../../../lib/assertions/forMaps/assertMapIsNotEmpty'; +import { error, value } from 'defekt'; + +suite('assertMapIsNotEmpty', (): void => { + test('does not return an error if the map is not empty.', async (): Promise => { + const actual = new Map([[ 'foo', 'bar' ]]); + + assert.that( + assertMapIsNotEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the map is empty.', async (): Promise => { + const actual = new Map(); + + assert.that( + assertMapIsNotEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The map is empty.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsAtLeastNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsAtLeastNumberTests.ts new file mode 100644 index 0000000..6f1816d --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsAtLeastNumberTests.ts @@ -0,0 +1,44 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsAtLeastNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsAtLeastNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsAtLeastNumber', (): void => { + test('does not return an error if actual is greater than expected.', async (): Promise => { + const actual = 15; + const expected = 7; + + assert.that( + assertNumberIsAtLeastNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('does not return an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsAtLeastNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is less than expected.', async (): Promise => { + const actual = 8; + const expected = 15; + + assert.that( + assertNumberIsAtLeastNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is less than the expected number.', + actual: prettyPrint(actual), + expected: `To be at least:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsAtMostNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsAtMostNumberTests.ts new file mode 100644 index 0000000..e912fec --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsAtMostNumberTests.ts @@ -0,0 +1,44 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsAtMostNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsAtMostNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsAtMostNumber', (): void => { + test('does not return an error if actual is less than expected.', async (): Promise => { + const actual = 7; + const expected = 15; + + assert.that( + assertNumberIsAtMostNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('does not return an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsAtMostNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is greater than expected.', async (): Promise => { + const actual = 15; + const expected = 8; + + assert.that( + assertNumberIsAtMostNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than the expected number.', + actual: prettyPrint(actual), + expected: `To be at most:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsGreaterThanNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsGreaterThanNumberTests.ts new file mode 100644 index 0000000..824c0e9 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsGreaterThanNumberTests.ts @@ -0,0 +1,48 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsGreaterThanNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsGreaterThanNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsGreaterThanNumber', (): void => { + test('does not return an error if actual is greater than expected.', async (): Promise => { + const actual = 15; + const expected = 7; + + assert.that( + assertNumberIsGreaterThanNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsGreaterThanNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To be greater than:\n${prettyPrint(expected)}` + })) + ); + }); + + test('returns an error if actual is less than expected.', async (): Promise => { + const actual = 8; + const expected = 15; + + assert.that( + assertNumberIsGreaterThanNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To be greater than:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsLessThanNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsLessThanNumberTests.ts new file mode 100644 index 0000000..9abd0d6 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsLessThanNumberTests.ts @@ -0,0 +1,48 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsLessThanNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsLessThanNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsLessThanNumber', (): void => { + test('does not return an error if actual is less than expected.', async (): Promise => { + const actual = 7; + const expected = 15; + + assert.that( + assertNumberIsLessThanNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsLessThanNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To be less than:\n${prettyPrint(expected)}` + })) + ); + }); + + test('returns an error if actual is less than expected.', async (): Promise => { + const actual = 15; + const expected = 8; + + assert.that( + assertNumberIsLessThanNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To be less than:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsNaNTests.ts b/test/unit/assertions/forNumbers/assertNumberIsNaNTests.ts new file mode 100644 index 0000000..8452805 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsNaNTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsNaN } from '../../../../lib/assertions/forNumbers/assertNumberIsNaN'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsNaN', (): void => { + test('does not return an error if the number is nan.', async (): Promise => { + const actual = Number.NaN; + + assert.that( + assertNumberIsNaN(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the number is not nan.', async (): Promise => { + const actual = 17; + + assert.that( + assertNumberIsNaN(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is not nan.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsNotAtLeastNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsNotAtLeastNumberTests.ts new file mode 100644 index 0000000..5f9a6e2 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsNotAtLeastNumberTests.ts @@ -0,0 +1,48 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsNotAtLeastNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsNotAtLeastNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsNotAtLeastNumber', (): void => { + test('does not return an error if actual is less than expected.', async (): Promise => { + const actual = 8; + const expected = 15; + + assert.that( + assertNumberIsNotAtLeastNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsNotAtLeastNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To not be at least:\n${prettyPrint(expected)}` + })) + ); + }); + + test('returns an error if actual is greater than expected.', async (): Promise => { + const actual = 15; + const expected = 7; + + assert.that( + assertNumberIsNotAtLeastNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To not be at least:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsNotAtMostNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsNotAtMostNumberTests.ts new file mode 100644 index 0000000..96a1e25 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsNotAtMostNumberTests.ts @@ -0,0 +1,48 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsNotAtMostNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsNotAtMostNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsNotAtMostNumber', (): void => { + test('does not return an error if actual is greater than expected.', async (): Promise => { + const actual = 15; + const expected = 8; + + assert.that( + assertNumberIsNotAtMostNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsNotAtMostNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To not be at most:\n${prettyPrint(expected)}` + })) + ); + }); + + test('returns an error if actual is less than expected.', async (): Promise => { + const actual = 7; + const expected = 15; + + assert.that( + assertNumberIsNotAtMostNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is less than or equal to the expected number.', + actual: prettyPrint(actual), + expected: `To not be at most:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsNotGreaterThanNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsNotGreaterThanNumberTests.ts new file mode 100644 index 0000000..281a2d6 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsNotGreaterThanNumberTests.ts @@ -0,0 +1,44 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsNotGreaterThanNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsNotGreaterThanNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsNotGreaterThanNumber', (): void => { + test('does not return an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsNotGreaterThanNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('does not return an error if actual is less than expected.', async (): Promise => { + const actual = 8; + const expected = 15; + + assert.that( + assertNumberIsNotGreaterThanNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is greater than expected.', async (): Promise => { + const actual = 15; + const expected = 7; + + assert.that( + assertNumberIsNotGreaterThanNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than the expected number.', + actual: prettyPrint(actual), + expected: `To not be greater than:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsNotLessThanNumberTests.ts b/test/unit/assertions/forNumbers/assertNumberIsNotLessThanNumberTests.ts new file mode 100644 index 0000000..fbb35f3 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsNotLessThanNumberTests.ts @@ -0,0 +1,44 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsNotLessThanNumber } from '../../../../lib/assertions/forNumbers/assertNumberIsNotLessThanNumber'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertNumberIsNotLessThanNumber', (): void => { + test('does not return an error if actual is equal to expected.', async (): Promise => { + const actual = 15; + const expected = 15; + + assert.that( + assertNumberIsNotLessThanNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('does not return an error if actual is less than expected.', async (): Promise => { + const actual = 15; + const expected = 8; + + assert.that( + assertNumberIsNotLessThanNumber(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is less than expected.', async (): Promise => { + const actual = 7; + const expected = 15; + + assert.that( + assertNumberIsNotLessThanNumber(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is greater than the expected number.', + actual: prettyPrint(actual), + expected: `To not be less than:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forNumbers/assertNumberIsNotNaNTests.ts b/test/unit/assertions/forNumbers/assertNumberIsNotNaNTests.ts new file mode 100644 index 0000000..763c326 --- /dev/null +++ b/test/unit/assertions/forNumbers/assertNumberIsNotNaNTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertNumberIsNotNaN } from '../../../../lib/assertions/forNumbers/assertNumberIsNotNaN'; +import { error, value } from 'defekt'; + +suite('assertNumberIsNotNaN', (): void => { + test('does not return an error if the number is not nan.', async (): Promise => { + const actual = 17; + + assert.that( + assertNumberIsNotNaN(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the number is nan.', async (): Promise => { + const actual = Number.NaN; + + assert.that( + assertNumberIsNotNaN(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The number is nan.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsAtLeastObjectTests.ts b/test/unit/assertions/forObjects/assertObjectIsAtLeastObjectTests.ts new file mode 100644 index 0000000..f6ae017 --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsAtLeastObjectTests.ts @@ -0,0 +1,164 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsAtLeastObject } from '../../../../lib/assertions/forObjects/assertObjectIsAtLeastObject'; +import { objectDiff } from '../../../../lib/diffs/forObjects/ObjectDiff'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { error, value } from 'defekt'; + +suite('assertObjectIsAtLeastObject', (): void => { + test('does not return an error if the expected object is entirely contained in the actual object.', async (): Promise => { + const actual = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + const expected = { + 13: 37, + heck: 'meck' + }; + + assert.that( + assertObjectIsAtLeastObject(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the expected object is not entirely contained in the actual object.', async (): Promise => { + const actual = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + const expected = { + heck: 'meck', + uiae: 'nrtd' + }; + + assert.that( + assertObjectIsAtLeastObject(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The expected object is not entirely contained in the actual object.', + actual: prettyPrint(actual), + expected: `To entirely contain:\n${prettyPrint(expected)}`, + diff: `The following sub-object shows relevant changes between actual and expected:\n${prettyPrintDiff( + objectDiff({ + equal: {}, + additions: {}, + changes: {}, + omissions: { uiae: 'nrtd' }, + cost: 0.5 + }) + )}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`returns an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = { + foo: 'bar' + }; + const expected = { + foo: 'bam' + }; + + assert.that( + assertObjectIsAtLeastObject(actual, expected) + ).is.not.equalTo( + value() + ); + }); + + test(`does not return an error if a nested object on the actual side contains additional properties.`, async (): Promise => { + const actual = { + foo: 'foo', + bar: { + foo: 'foo', + bar: 'bar' + } + }; + const expected = { + foo: 'foo', + bar: { + foo: 'foo' + } + }; + + assert.that( + assertObjectIsAtLeastObject(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('does not return an error if a nested array on the actual side contain additional values.', async (): Promise => { + const actual = { + foo: { + bar: [ + { foo: 'foo', bar: 'bar' } + ] + } + }; + const expected = { + foo: { + bar: [ + { foo: 'foo' } + ] + } + }; + + assert.that( + assertObjectIsAtLeastObject(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('does not return an error if a nested array on the actual side contain additional values 2.', async (): Promise => { + /* eslint-disable @typescript-eslint/naming-convention */ + const actual = { + sampleView: { + all: [ + { + aggregateIdentifier: { context: { name: 'sampleContext', __typename: 'SampleView_all_resultItemT0AggregateIdentifierT0ContextT0' }, __typename: 'SampleView_all_resultItemT0AggregateIdentifierT0' }, + id: 'a2c1ff6c-31bc-48e0-ba65-004906f00735', + __typename: 'SampleView_all_resultItemT0' + }, + { + aggregateIdentifier: { context: { name: 'sampleContext', __typename: 'SampleView_all_resultItemT0AggregateIdentifierT0ContextT0' }, __typename: 'SampleView_all_resultItemT0AggregateIdentifierT0' }, + id: 'd4712787-51df-4505-ad3b-2bf0c14fe332', + __typename: 'SampleView_all_resultItemT0' + } + ], + __typename: 'sampleView' + } + }; + /* eslint-enable @typescript-eslint/naming-convention */ + const expected = { + sampleView: { + all: [ + { + aggregateIdentifier: { context: { name: 'sampleContext' }}, + id: 'a2c1ff6c-31bc-48e0-ba65-004906f00735' + }, + { + aggregateIdentifier: { context: { name: 'sampleContext' }}, + id: 'd4712787-51df-4505-ad3b-2bf0c14fe332' + } + ] + } + }; + + assert.that( + assertObjectIsAtLeastObject(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsAtMostObjectTests.ts b/test/unit/assertions/forObjects/assertObjectIsAtMostObjectTests.ts new file mode 100644 index 0000000..2cc48e8 --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsAtMostObjectTests.ts @@ -0,0 +1,99 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsAtMostObject } from '../../../../lib/assertions/forObjects/assertObjectIsAtMostObject'; +import { objectDiff } from '../../../../lib/diffs/forObjects/ObjectDiff'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { error, value } from 'defekt'; + +suite('assertObjectIsAtMostObject', (): void => { + test('does not return an error if the actual object is entirely contained in the expected object.', async (): Promise => { + const actual = { + 13: 37, + heck: 'meck' + }; + const expected = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + + assert.that( + assertObjectIsAtMostObject(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the actual object is not entirely contained in the expected object.', async (): Promise => { + const actual = { + heck: 'meck', + uiae: 'nrtd' + }; + const expected = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + + assert.that( + assertObjectIsAtMostObject(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The actual object is not entirely contained in the expected object.', + actual: prettyPrint(actual), + expected: `To be entirely contained in:\n${prettyPrint(expected)}`, + diff: `The following sub-object shows relevant changes between actual and expected:\n${prettyPrintDiff( + objectDiff({ + equal: {}, + additions: { uiae: 'nrtd' }, + changes: {}, + omissions: {}, + cost: 0.5 + }) + )}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`returns an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = { + foo: 'bar' + }; + const expected = { + foo: 'bam' + }; + + assert.that( + assertObjectIsAtMostObject(actual, expected) + ).is.not.equalTo( + value() + ); + }); + + test(`does not return an error if a nested object on the expected side contains additional properties.`, async (): Promise => { + const actual = { + foo: 'foo', + bar: { + foo: 'foo' + } + }; + const expected = { + foo: 'foo', + bar: { + foo: 'foo', + bar: 'bar' + } + }; + + assert.that( + assertObjectIsAtMostObject(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsEmptyTests.ts b/test/unit/assertions/forObjects/assertObjectIsEmptyTests.ts new file mode 100644 index 0000000..fa651b6 --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsEmptyTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsEmpty } from '../../../../lib/assertions/forObjects/assertObjectIsEmpty'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertObjectIsEmpty', (): void => { + test('does not return an error if actual is an empty object.', async (): Promise => { + const actual = {}; + + assert.that( + assertObjectIsEmpty(actual) + ).is.aValue(); + }); + + test('returns an error if actual is not an empty object.', async (): Promise => { + const actual = { foo: 'bar' }; + + assert.that( + assertObjectIsEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The object is empty.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsInstanceOfClassTests.ts b/test/unit/assertions/forObjects/assertObjectIsInstanceOfClassTests.ts new file mode 100644 index 0000000..c538239 --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsInstanceOfClassTests.ts @@ -0,0 +1,35 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsInstanceOfClass } from '../../../../lib/assertions/forObjects/assertObjectIsInstanceOfClass'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertObjectIsInstanceOfClass', (): void => { + test('does not return an error if the object is an instance of the class.', async (): Promise => { + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class TestClass {} + + const actual = new TestClass(); + + assert.that( + assertObjectIsInstanceOfClass(actual, TestClass) + ).is.aValue(); + }); + + test('returns an error if the object is not an instance of the class.', async (): Promise => { + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class TestClass {} + + const actual = {}; + + assert.that( + assertObjectIsInstanceOfClass(actual, TestClass) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The object is not an instance of the expected class.', + actual: prettyPrint(actual), + expected: 'To be an instance of:\nTestClass' + })) + ); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsNotAtLeastObjectTests.ts b/test/unit/assertions/forObjects/assertObjectIsNotAtLeastObjectTests.ts new file mode 100644 index 0000000..4cddfaf --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsNotAtLeastObjectTests.ts @@ -0,0 +1,66 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsNotAtLeastObject } from '../../../../lib/assertions/forObjects/assertObjectIsNotAtLeastObject'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertObjectIsNotAtLeastObject', (): void => { + test('does not return an error if the expected object is not entirely contained in the actual object.', async (): Promise => { + const actual = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + const expected = { + heck: 'meck', + uiae: 'nrtd' + }; + + assert.that( + assertObjectIsNotAtLeastObject(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the expected object is entirely contained in the actual object.', async (): Promise => { + const actual = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + const expected = { + 13: 37, + heck: 'meck' + }; + + assert.that( + assertObjectIsNotAtLeastObject(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The expected object is entirely contained in the actual object.', + actual: prettyPrint(actual), + expected: `To not entirely contain:\n${prettyPrint(expected)}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`does not return an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = { + foo: 'bar' + }; + const expected = { + foo: 'bam' + }; + + assert.that( + assertObjectIsNotAtLeastObject(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsNotAtMostObjectTests.ts b/test/unit/assertions/forObjects/assertObjectIsNotAtMostObjectTests.ts new file mode 100644 index 0000000..09b0971 --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsNotAtMostObjectTests.ts @@ -0,0 +1,66 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsNotAtMostObject } from '../../../../lib/assertions/forObjects/assertObjectIsNotAtMostObject'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertObjectIsNotAtMostObject', (): void => { + test('does not return an error if the actual object is not entirely contained in the expected object.', async (): Promise => { + const actual = { + heck: 'meck', + uiae: 'nrtd' + }; + const expected = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + + assert.that( + assertObjectIsNotAtMostObject(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the actual object is entirely contained in the expected object.', async (): Promise => { + const actual = { + 13: 37, + heck: 'meck' + }; + const expected = { + foo: 'bar', + 13: 37, + 12: 34, + heck: 'meck' + }; + + assert.that( + assertObjectIsNotAtMostObject(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The actual object is entirely contained in the expected object.', + actual: prettyPrint(actual), + expected: `To not be entirely contained in:\n${prettyPrint(expected)}` + })) + ); + }); + + suite('regression tests', (): void => { + test(`does not return an error if a key's value is present in actual and expected, but with different values.`, async (): Promise => { + const actual = { + foo: 'bar' + }; + const expected = { + foo: 'bam' + }; + + assert.that( + assertObjectIsNotAtMostObject(actual, expected) + ).is.equalTo( + value() + ); + }); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsNotEmptyTests.ts b/test/unit/assertions/forObjects/assertObjectIsNotEmptyTests.ts new file mode 100644 index 0000000..bccf7fe --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsNotEmptyTests.ts @@ -0,0 +1,26 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsNotEmpty } from '../../../../lib/assertions/forObjects/assertObjectIsNotEmpty'; +import { error } from 'defekt'; + +suite('assertObjectIsNotEmpty', (): void => { + test('does not return an error if actual is not an empty object.', async (): Promise => { + const actual = { foo: 'bar' }; + + assert.that( + assertObjectIsNotEmpty(actual) + ).is.aValue(); + }); + + test('returns an error if actual is an empty object.', async (): Promise => { + const actual = {}; + + assert.that( + assertObjectIsNotEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The object is empty.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forObjects/assertObjectIsNotInstanceOfClassTests.ts b/test/unit/assertions/forObjects/assertObjectIsNotInstanceOfClassTests.ts new file mode 100644 index 0000000..9eef323 --- /dev/null +++ b/test/unit/assertions/forObjects/assertObjectIsNotInstanceOfClassTests.ts @@ -0,0 +1,35 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertObjectIsNotInstanceOfClass } from '../../../../lib/assertions/forObjects/assertObjectIsNotInstanceOfClass'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertObjectIsNotInstanceOfClass', (): void => { + test('does not return an error if the object is not an instance of the class.', async (): Promise => { + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class TestClass {} + + const actual = {}; + + assert.that( + assertObjectIsNotInstanceOfClass(actual, TestClass) + ).is.aValue(); + }); + + test('returns an error if the object is an instance of the class.', async (): Promise => { + // eslint-disable-next-line @typescript-eslint/no-extraneous-class + class TestClass {} + + const actual = new TestClass(); + + assert.that( + assertObjectIsNotInstanceOfClass(actual, TestClass) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The object is an instance of the class.', + actual: prettyPrint(actual), + expected: 'To not be an instance of:\nTestClass' + })) + ); + }); +}); diff --git a/test/unit/assertions/forResults/assertResultIsAValueTests.ts b/test/unit/assertions/forResults/assertResultIsAValueTests.ts new file mode 100644 index 0000000..c70490e --- /dev/null +++ b/test/unit/assertions/forResults/assertResultIsAValueTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertResultIsAValue } from '../../../../lib/assertions/forResults/assertResultIsAValue'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertResultIsAValue', (): void => { + test('does not return an error if actual is a value result.', async (): Promise => { + const actual = value(); + + assert.that( + assertResultIsAValue(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is an error result.', async (): Promise => { + const actual = error(new Error('foo')); + + assert.that( + assertResultIsAValue(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The result is an error.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forResults/assertResultIsAnErrorTests.ts b/test/unit/assertions/forResults/assertResultIsAnErrorTests.ts new file mode 100644 index 0000000..d76f526 --- /dev/null +++ b/test/unit/assertions/forResults/assertResultIsAnErrorTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertResultIsAnError } from '../../../../lib/assertions/forResults/assertResultIsAnError'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertResultIsAnError', (): void => { + test('does not return an error if actual is an error result.', async (): Promise => { + const actual = error(new Error('foo')); + + assert.that( + assertResultIsAnError(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is a value result.', async (): Promise => { + const actual = value(); + + assert.that( + assertResultIsAnError(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The result is a value.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forResults/assertResultIsAnErrorWithMessageTests.ts b/test/unit/assertions/forResults/assertResultIsAnErrorWithMessageTests.ts new file mode 100644 index 0000000..1d34b8c --- /dev/null +++ b/test/unit/assertions/forResults/assertResultIsAnErrorWithMessageTests.ts @@ -0,0 +1,45 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertResultIsAnErrorWithMessage } from '../../../../lib/assertions/forResults/assertResultIsAnErrorWithMessage'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintString } from '../../../../lib/prettyPrint/forStrings/prettyPrintString'; +import { error, value } from 'defekt'; + +suite('assertResultIsAnErrorWithMessage', (): void => { + test('does not return an error if actual is an error result with the expected message.', async (): Promise => { + const actual = error(new Error('foo')); + + assert.that( + assertResultIsAnErrorWithMessage(actual, 'foo') + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is a value result.', async (): Promise => { + const actual = value(); + + assert.that( + assertResultIsAnErrorWithMessage(actual, 'foo') + ).is.equalTo( + error(new AssertionFailed({ + message: 'The result is a value.', + expected: `To be an error with message ${prettyPrintString('foo')}.` + })) + ); + }); + + test('returns an error if actual is an error result with the wrong message.', async (): Promise => { + const actual = error(new Error('bar')); + + assert.that( + assertResultIsAnErrorWithMessage(actual, 'foo') + ).is.equalTo( + error(new AssertionFailed({ + message: 'The error does not have the expected message.', + expected: `To have the message ${prettyPrintString('foo')}`, + actual: prettyPrint(actual.error.message) + })) + ); + }); +}); diff --git a/test/unit/assertions/forResults/assertResultIsNotAValueTests.ts b/test/unit/assertions/forResults/assertResultIsNotAValueTests.ts new file mode 100644 index 0000000..04ee796 --- /dev/null +++ b/test/unit/assertions/forResults/assertResultIsNotAValueTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertResultIsNotAValue } from '../../../../lib/assertions/forResults/assertResultIsNotAValue'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertResultIsNotAValue', (): void => { + test('does not return an error if actual is an error result.', async (): Promise => { + const actual = error(new Error('foo')); + + assert.that( + assertResultIsNotAValue(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is a value result.', async (): Promise => { + const actual = value(); + + assert.that( + assertResultIsNotAValue(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The result is an error.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forResults/assertResultIsNotAnErrorTests.ts b/test/unit/assertions/forResults/assertResultIsNotAnErrorTests.ts new file mode 100644 index 0000000..bdaf987 --- /dev/null +++ b/test/unit/assertions/forResults/assertResultIsNotAnErrorTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertResultIsNotAnError } from '../../../../lib/assertions/forResults/assertResultIsNotAnError'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertResultIsNotAnError', (): void => { + test('does not return an error if actual is a value result.', async (): Promise => { + const actual = value(); + + assert.that( + assertResultIsNotAnError(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is an error result.', async (): Promise => { + const actual = error(new Error('foo')); + + assert.that( + assertResultIsNotAnError(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The result is a value.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forResults/assertResultIsNotAnErrorWithMessageTests.ts b/test/unit/assertions/forResults/assertResultIsNotAnErrorWithMessageTests.ts new file mode 100644 index 0000000..1d046af --- /dev/null +++ b/test/unit/assertions/forResults/assertResultIsNotAnErrorWithMessageTests.ts @@ -0,0 +1,40 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertResultIsNotAnErrorWithMessage } from '../../../../lib/assertions/forResults/assertResultIsNotAnErrorWithMessage'; +import { prettyPrintString } from '../../../../lib/prettyPrint/forStrings/prettyPrintString'; +import { error, value } from 'defekt'; + +suite('assertResultIsNotAnErrorWithMessage', (): void => { + test('does not return an error if actual is a value result.', async (): Promise => { + const actual = value(); + + assert.that( + assertResultIsNotAnErrorWithMessage(actual, 'foo') + ).is.equalTo( + value() + ); + }); + + test('does not return an error if actual is an error result with the wrong message.', async (): Promise => { + const actual = error(new Error('bar')); + + assert.that( + assertResultIsNotAnErrorWithMessage(actual, 'foo') + ).is.equalTo( + value() + ); + }); + + test('returns an error if actual is an error result with the expected message.', async (): Promise => { + const actual = error(new Error('foo')); + + assert.that( + assertResultIsNotAnErrorWithMessage(actual, 'foo') + ).is.equalTo( + error(new AssertionFailed({ + message: 'The error has the expected message.', + expected: `To not have the message ${prettyPrintString('foo')}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsAtLeastSetTests.ts b/test/unit/assertions/forSets/assertSetIsAtLeastSetTests.ts new file mode 100644 index 0000000..2fd46f4 --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsAtLeastSetTests.ts @@ -0,0 +1,59 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsAtLeastSet } from '../../../../lib/assertions/forSets/assertSetIsAtLeastSet'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { setDiff } from '../../../../lib/diffs/forSets/SetDiff'; +import { error, value } from 'defekt'; + +suite('assertSetIsAtLeastSet', (): void => { + test('does not return an error if the expected set is entirely contained in the actual set.', async (): Promise => { + const actual = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + const expected = new Set([ + 13, + 'heck' + ]); + + assert.that( + assertSetIsAtLeastSet(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the expected set is not entirely contained in the actual set.', async (): Promise => { + const actual = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + const expected = new Set([ + 'heck', + 'uiae' + ]); + + assert.that( + assertSetIsAtLeastSet(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The expected set is not entirely contained in the actual set.', + actual: prettyPrint(actual), + expected: `To entirely contain:\n${prettyPrint(expected)}`, + diff: `The following sub-set shows relevant changes between actual and expected:\n${prettyPrintDiff( + setDiff({ + equal: new Set(), + additions: new Set(), + omissions: new Set([ 'uiae' ]), + cost: 0.5 + }) + )}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsAtMostSetTests.ts b/test/unit/assertions/forSets/assertSetIsAtMostSetTests.ts new file mode 100644 index 0000000..a4c6c2f --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsAtMostSetTests.ts @@ -0,0 +1,59 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsAtMostSet } from '../../../../lib/assertions/forSets/assertSetIsAtMostSet'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { prettyPrintDiff } from '../../../../lib/prettyPrint/typeAware/prettyPrintDiff'; +import { setDiff } from '../../../../lib/diffs/forSets/SetDiff'; +import { error, value } from 'defekt'; + +suite('assertSetIsAtMostSet', (): void => { + test('does not return an error if the actual set is entirely contained in the expected set.', async (): Promise => { + const actual = new Set([ + 13, + 'heck' + ]); + const expected = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + + assert.that( + assertSetIsAtMostSet(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the actual set is not entirely contained in the expected set.', async (): Promise => { + const actual = new Set([ + 'heck', + 'uiae' + ]); + const expected = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + + assert.that( + assertSetIsAtMostSet(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The actual set is not entirely contained in the expected set.', + actual: prettyPrint(actual), + expected: `To be entirely contained in:\n${prettyPrint(expected)}`, + diff: `The following sub-set shows relevant changes between actual and expected:\n${prettyPrintDiff( + setDiff({ + equal: new Set(), + additions: new Set([ 'uiae' ]), + omissions: new Set(), + cost: 0.5 + }) + )}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsContainingAllOfIterableTests.ts b/test/unit/assertions/forSets/assertSetIsContainingAllOfIterableTests.ts new file mode 100644 index 0000000..969850c --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsContainingAllOfIterableTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsContainingAllOfIterable } from '../../../../lib/assertions/forSets/assertSetIsContainingAllOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsContainingAllOfIterable', (): void => { + test('does not return an error if the set contains all expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4, { foo: 'bar' }]); + const iterable = new Set([ 2, { foo: 'bar' }]); + + assert.that( + assertSetIsContainingAllOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set does not contain all expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + const iterable = new Set([ 2, 4, { foo: 'bar' }]); + + assert.that( + assertSetIsContainingAllOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set does not contain all expected items.', + expected: `To contain all of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual), + diff: `Missing these items:\n${prettyPrint(new Set([{ foo: 'bar' }]))}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsContainingAnyOfIterableTests.ts b/test/unit/assertions/forSets/assertSetIsContainingAnyOfIterableTests.ts new file mode 100644 index 0000000..cde1c3c --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsContainingAnyOfIterableTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsContainingAnyOfIterable } from '../../../../lib/assertions/forSets/assertSetIsContainingAnyOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsContainingAnyOfIterable', (): void => { + test('does not return an error if the set contains any of the expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4, { foo: 'bar' }]); + const iterable: Set = new Set([ 2, new Set([ 5, 8 ]) ]); + + assert.that( + assertSetIsContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set does not contain any of the expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + const iterable = new Set([ 17, 32, { foo: 'bar' }]); + + assert.that( + assertSetIsContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set does not contain any of the expected items.', + expected: `To contain any of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsContainingItemTests.ts b/test/unit/assertions/forSets/assertSetIsContainingItemTests.ts new file mode 100644 index 0000000..d90a16d --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsContainingItemTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsContainingItem } from '../../../../lib/assertions/forSets/assertSetIsContainingItem'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsContainingItem', (): void => { + test('does not return an error if the set contains the item.', async (): Promise => { + const actual = new Set([ 1, 2, 4, { foo: 'bar' }]); + const item = { foo: 'bar' }; + + assert.that( + assertSetIsContainingItem(actual, item) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set does not contain the item.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + const item = 15; + + assert.that( + assertSetIsContainingItem(actual, item) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set does not contain the expected item.', + expected: `To contain:\n${prettyPrint(item)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsEmptyTests.ts b/test/unit/assertions/forSets/assertSetIsEmptyTests.ts new file mode 100644 index 0000000..0ff4a69 --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsEmptyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsEmpty } from '../../../../lib/assertions/forSets/assertSetIsEmpty'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsEmpty', (): void => { + test('does not return an error if the set is empty.', async (): Promise => { + const actual: Set = new Set([]); + + assert.that( + assertSetIsEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set is not empty.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + + assert.that( + assertSetIsEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set is not empty.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsNotAtLeastSetTests.ts b/test/unit/assertions/forSets/assertSetIsNotAtLeastSetTests.ts new file mode 100644 index 0000000..2b2bc7d --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsNotAtLeastSetTests.ts @@ -0,0 +1,49 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsNotAtLeastSet } from '../../../../lib/assertions/forSets/assertSetIsNotAtLeastSet'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsNotAtLeastSet', (): void => { + test('does not return an error if the expected set is not entirely contained in the actual set.', async (): Promise => { + const actual = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + const expected = new Set([ + 'heck', + 'uiae' + ]); + + assert.that( + assertSetIsNotAtLeastSet(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the expected set is entirely contained in the actual set.', async (): Promise => { + const actual = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + const expected = new Set([ + 13, + 'heck' + ]); + + assert.that( + assertSetIsNotAtLeastSet(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The expected set is entirely contained in the actual set.', + actual: prettyPrint(actual), + expected: `To not entirely contain:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsNotAtMostSetTests.ts b/test/unit/assertions/forSets/assertSetIsNotAtMostSetTests.ts new file mode 100644 index 0000000..b6f6c90 --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsNotAtMostSetTests.ts @@ -0,0 +1,49 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsNotAtMostSet } from '../../../../lib/assertions/forSets/assertSetIsNotAtMostSet'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsNotAtMostSet', (): void => { + test('does not return an error if the actual set is not entirely contained in the expected set.', async (): Promise => { + const actual = new Set([ + 'heck', + 'uiae' + ]); + const expected = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + + assert.that( + assertSetIsNotAtMostSet(actual, expected) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the actual set is entirely contained in the expected set.', async (): Promise => { + const actual = new Set([ + 13, + 'heck' + ]); + const expected = new Set([ + 'foo', + 13, + 12, + 'heck' + ]); + + assert.that( + assertSetIsNotAtMostSet(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The actual set is entirely contained in the expected set.', + actual: prettyPrint(actual), + expected: `To not be entirely contained in:\n${prettyPrint(expected)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsNotContainingAllOfIterableTests.ts b/test/unit/assertions/forSets/assertSetIsNotContainingAllOfIterableTests.ts new file mode 100644 index 0000000..2873d04 --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsNotContainingAllOfIterableTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsNotContainingAllOfIterable } from '../../../../lib/assertions/forSets/assertSetIsNotContainingAllOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsNotContainingAllOfIterable', (): void => { + test('does not return an error if the set does not contain all expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + const iterable = new Set([ 2, 4, { foo: 'bar' }]); + + assert.that( + assertSetIsNotContainingAllOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set contains all expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4, { foo: 'bar' }]); + const iterable = new Set([ 2, { foo: 'bar' }]); + + assert.that( + assertSetIsNotContainingAllOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set contains all items in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain all of:\n${prettyPrint(iterable)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsNotContainingAnyOfIterableTests.ts b/test/unit/assertions/forSets/assertSetIsNotContainingAnyOfIterableTests.ts new file mode 100644 index 0000000..f05873c --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsNotContainingAnyOfIterableTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsNotContainingAnyOfIterable } from '../../../../lib/assertions/forSets/assertSetIsNotContainingAnyOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsNotContainingAnyOfIterable', (): void => { + test('returns an error if the set contains any of the expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4, { foo: 'bar' }]); + const iterable: Set = new Set([ 2, new Set([ 5, 8 ]) ]); + + assert.that( + assertSetIsNotContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set contains one or more of the items in the iterable.', + expected: `To not contain any of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual), + diff: `These items are contained, but should not be:\n${prettyPrint(new Set([ 2 ]))}` + })) + ); + }); + + test('does not return an error if the set does not contain any of the expected items.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + const iterable = new Set([ 17, 32, { foo: 'bar' }]); + + assert.that( + assertSetIsNotContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsNotContainingItemTests.ts b/test/unit/assertions/forSets/assertSetIsNotContainingItemTests.ts new file mode 100644 index 0000000..14bd765 --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsNotContainingItemTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsNotContainingItem } from '../../../../lib/assertions/forSets/assertSetIsNotContainingItem'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertSetIsNotContainingItem', (): void => { + test('does not return an error if the set does not contain the item.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + const item = 15; + + assert.that( + assertSetIsNotContainingItem(actual, item) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set contains the item.', async (): Promise => { + const actual = new Set([ 1, 2, 4, { foo: 'bar' }]); + const item = { foo: 'bar' }; + + assert.that( + assertSetIsNotContainingItem(actual, item) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set contains the item.', + expected: `To not contain:\n${prettyPrint(item)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forSets/assertSetIsNotEmptyTests.ts b/test/unit/assertions/forSets/assertSetIsNotEmptyTests.ts new file mode 100644 index 0000000..8ea7dcc --- /dev/null +++ b/test/unit/assertions/forSets/assertSetIsNotEmptyTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertSetIsNotEmpty } from '../../../../lib/assertions/forSets/assertSetIsNotEmpty'; +import { error, value } from 'defekt'; + +suite('assertSetIsNotEmpty', (): void => { + test('does not return an error if the set is not empty.', async (): Promise => { + const actual = new Set([ 1, 2, 4 ]); + + assert.that( + assertSetIsNotEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the set is empty.', async (): Promise => { + const actual: Set = new Set([]); + + assert.that( + assertSetIsNotEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The set is empty.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsContainingAllOfIterableTests.ts b/test/unit/assertions/forStrings/assertStringIsContainingAllOfIterableTests.ts new file mode 100644 index 0000000..f14fe07 --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsContainingAllOfIterableTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsContainingAllOfIterable } from '../../../../lib/assertions/forStrings/assertStringIsContainingAllOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertStringIsContainingAllOfIterable', (): void => { + test('does not return an error if the string contains all expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'oo', ' bar', 'o b' ]; + + assert.that( + assertStringIsContainingAllOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the string does not contain all expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'foo', 'heck', 'bar' ]; + + assert.that( + assertStringIsContainingAllOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string does not contain all expected sub-strings.', + expected: `To contain all of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual), + diff: `Missing these sub-strings:\n${prettyPrint(new Set([ 'heck' ]))}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsContainingAnyOfIterableTests.ts b/test/unit/assertions/forStrings/assertStringIsContainingAnyOfIterableTests.ts new file mode 100644 index 0000000..28468e7 --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsContainingAnyOfIterableTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsContainingAnyOfIterable } from '../../../../lib/assertions/forStrings/assertStringIsContainingAnyOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertStringIsContainingAnyOfIterable', (): void => { + test('does not return an error if the string contains any of the expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'foo', 'heck' ]; + + assert.that( + assertStringIsContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the string does not contain any of the expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'heck', 'uiae', 'nrtd' ]; + + assert.that( + assertStringIsContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string does not contain any of the expected sub-strings.', + expected: `To contain any of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsContainingStringTests.ts b/test/unit/assertions/forStrings/assertStringIsContainingStringTests.ts new file mode 100644 index 0000000..60eed5a --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsContainingStringTests.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsContainingString } from '../../../../lib/assertions/forStrings/assertStringIsContainingString'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsContainingString', (): void => { + test('does not return an error if actual contains expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'bar'; + + assert.that( + assertStringIsContainingString(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual does not contain expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'heck'; + + assert.that( + assertStringIsContainingString(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not containing the expected sub-string.', + actual: prettyPrint(actual), + expected: `To contain:\n"heck"` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsEmptyTests.ts b/test/unit/assertions/forStrings/assertStringIsEmptyTests.ts new file mode 100644 index 0000000..76d972a --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsEmptyTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsEmpty } from '../../../../lib/assertions/forStrings/assertStringIsEmpty'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertStringIsEmpty', (): void => { + test('does not return an error if the string is empty.', async (): Promise => { + const actual = ''; + + assert.that( + assertStringIsEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the string is not empty.', async (): Promise => { + const actual = 'foo'; + + assert.that( + assertStringIsEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not empty.', + actual: prettyPrint(actual) + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsEndingWithTests.ts b/test/unit/assertions/forStrings/assertStringIsEndingWithTests.ts new file mode 100644 index 0000000..8cb996c --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsEndingWithTests.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsEndingWithString } from '../../../../lib/assertions/forStrings/assertStringIsEndingWithString'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsEndingWith', (): void => { + test('does not return an error if actual ends with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'bam'; + + assert.that( + assertStringIsEndingWithString(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual does not end with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'heck'; + + assert.that( + assertStringIsEndingWithString(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not end withing the expected sub-string.', + actual: prettyPrint(actual), + expected: `To end with:\n"heck"` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsMatchingRegExp.ts b/test/unit/assertions/forStrings/assertStringIsMatchingRegExp.ts new file mode 100644 index 0000000..5c443e5 --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsMatchingRegExp.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsMatchingRegExp } from '../../../../lib/assertions/forStrings/assertStringIsMatchingRegExp'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsMatchingRegExp', (): void => { + test('does not return an error if actual contains expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = /bar/u; + + assert.that( + assertStringIsMatchingRegExp(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual does not contain expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = /bar/u; + + assert.that( + assertStringIsMatchingRegExp(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not matching the expected regex.', + actual: prettyPrint(actual), + expected: `To match:\n${expected.toString()}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotContainingAllOfIterableTests.ts b/test/unit/assertions/forStrings/assertStringIsNotContainingAllOfIterableTests.ts new file mode 100644 index 0000000..e45589d --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotContainingAllOfIterableTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotContainingAllOfIterable } from '../../../../lib/assertions/forStrings/assertStringIsNotContainingAllOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertStringIsNotContainingAllOfIterable', (): void => { + test('does not return an error if the string does not contain all expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'o b', 'ar', 'heck' ]; + + assert.that( + assertStringIsNotContainingAllOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the string contains all expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'oo', 'ar' ]; + + assert.that( + assertStringIsNotContainingAllOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string contains all sub-strings in the iterable.', + actual: prettyPrint(actual), + expected: `To not contain all of:\n${prettyPrint(iterable)}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotContainingAnyOfIterableTests.ts b/test/unit/assertions/forStrings/assertStringIsNotContainingAnyOfIterableTests.ts new file mode 100644 index 0000000..68a1d65 --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotContainingAnyOfIterableTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotContainingAnyOfIterable } from '../../../../lib/assertions/forStrings/assertStringIsNotContainingAnyOfIterable'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; +import { error, value } from 'defekt'; + +suite('assertStringIsNotContainingAnyOfIterable', (): void => { + test('returns an error if the string contains any of the expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'heck', 'foo' ]; + + assert.that( + assertStringIsNotContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string contains one or more of the sub-strings in the iterable.', + expected: `To not contain any of:\n${prettyPrint(iterable)}`, + actual: prettyPrint(actual), + diff: `These sub-strings are contained, but should not be:\n${prettyPrint(new Set([ 'foo' ]))}` + })) + ); + }); + + test('does not return an error if the string does not contain any of the expected items.', async (): Promise => { + const actual = 'foo bar'; + const iterable = [ 'heck', 'uiae' ]; + + assert.that( + assertStringIsNotContainingAnyOfIterable(actual, iterable) + ).is.equalTo( + value() + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotContainingStringTests.ts b/test/unit/assertions/forStrings/assertStringIsNotContainingStringTests.ts new file mode 100644 index 0000000..3a5b3ac --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotContainingStringTests.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotContainingString } from '../../../../lib/assertions/forStrings/assertStringIsNotContainingString'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsNotContainingString', (): void => { + test('does not return an error if actual does not contain expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'heck'; + + assert.that( + assertStringIsNotContainingString(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual contains expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'bar'; + + assert.that( + assertStringIsNotContainingString(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is containing the sub-string.', + actual: prettyPrint(actual), + expected: `To not contain:\n"bar"` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotEmptyTests.ts b/test/unit/assertions/forStrings/assertStringIsNotEmptyTests.ts new file mode 100644 index 0000000..923b97d --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotEmptyTests.ts @@ -0,0 +1,28 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotEmpty } from '../../../../lib/assertions/forStrings/assertStringIsNotEmpty'; +import { error, value } from 'defekt'; + +suite('assertStringIsNotEmpty', (): void => { + test('does not return an error if the string is not empty.', async (): Promise => { + const actual = 'foo'; + + assert.that( + assertStringIsNotEmpty(actual) + ).is.equalTo( + value() + ); + }); + + test('returns an error if the string is empty.', async (): Promise => { + const actual = ''; + + assert.that( + assertStringIsNotEmpty(actual) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is empty.' + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotEndingWithTests.ts b/test/unit/assertions/forStrings/assertStringIsNotEndingWithTests.ts new file mode 100644 index 0000000..4e0442d --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotEndingWithTests.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotEndingWithString } from '../../../../lib/assertions/forStrings/assertStringIsNotEndingWithString'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsNotEndingWith', (): void => { + test('does not return an error if actual does not end with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'heck'; + + assert.that( + assertStringIsNotEndingWithString(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual ends with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'bam'; + + assert.that( + assertStringIsNotEndingWithString(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not end withing the expected sub-string.', + actual: prettyPrint(actual), + expected: `To not end with:\n"bam"` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotMatchingRegExp.ts b/test/unit/assertions/forStrings/assertStringIsNotMatchingRegExp.ts new file mode 100644 index 0000000..118ee4f --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotMatchingRegExp.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotMatchingRegExp } from '../../../../lib/assertions/forStrings/assertStringIsNotMatchingRegExp'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsNotMatchingRegExp', (): void => { + test('does not return an error if actual does not contain expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = /bar/u; + + assert.that( + assertStringIsNotMatchingRegExp(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual contains expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = /bar/u; + + assert.that( + assertStringIsNotMatchingRegExp(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not matching the expected regex.', + actual: prettyPrint(actual), + expected: `To match:\n${expected.toString()}` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsNotStartingWithTests.ts b/test/unit/assertions/forStrings/assertStringIsNotStartingWithTests.ts new file mode 100644 index 0000000..763f594 --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsNotStartingWithTests.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsNotStartingWithString } from '../../../../lib/assertions/forStrings/assertStringIsNotStartingWithString'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsNotStartingWith', (): void => { + test('does not return an error if actual does not start with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'heck'; + + assert.that( + assertStringIsNotStartingWithString(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual starts with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'foo'; + + assert.that( + assertStringIsNotStartingWithString(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not start withing the expected sub-string.', + actual: prettyPrint(actual), + expected: `To not start with:\n"foo"` + })) + ); + }); +}); diff --git a/test/unit/assertions/forStrings/assertStringIsStartingWithTests.ts b/test/unit/assertions/forStrings/assertStringIsStartingWithTests.ts new file mode 100644 index 0000000..39ebe49 --- /dev/null +++ b/test/unit/assertions/forStrings/assertStringIsStartingWithTests.ts @@ -0,0 +1,31 @@ +import { assert } from '../../../../lib/assertthat'; +import { AssertionFailed } from '../../../../lib/errors'; +import { assertStringIsStartingWithString } from '../../../../lib/assertions/forStrings/assertStringIsStartingWithString'; +import { error } from 'defekt'; +import { prettyPrint } from '../../../../lib/prettyPrint/typeAware/prettyPrint'; + +suite('assertStringIsStartingWith', (): void => { + test('does not return an error if actual starts with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'foo'; + + assert.that( + assertStringIsStartingWithString(actual, expected) + ).is.aValue(); + }); + + test('returns an error if actual does not start with expected.', async (): Promise => { + const actual = 'foo bar bam'; + const expected = 'heck'; + + assert.that( + assertStringIsStartingWithString(actual, expected) + ).is.equalTo( + error(new AssertionFailed({ + message: 'The string is not start withing the expected sub-string.', + actual: prettyPrint(actual), + expected: `To start with:\n"heck"` + })) + ); + }); +}); diff --git a/test/unit/assertthatTests.ts b/test/unit/assertthatTests.ts deleted file mode 100644 index d82f84e..0000000 --- a/test/unit/assertthatTests.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { assert } from '../../lib/assertthat'; -import chaiStatic from 'chai'; - -const chai = chaiStatic.assert; - -suite('assert', (): void => { - test('is an object.', async (): Promise => { - chai.typeOf(assert, 'object'); - }); - - suite('that', (): void => { - test('does not throw an error if actual is undefined.', async (): Promise => { - chai.doesNotThrow((): void => { - // eslint-disable-next-line unicorn/no-useless-undefined - assert.that(undefined); - }); - }); - - test('returns an object.', async (): Promise => { - chai.typeOf(assert.that(23), 'object'); - }); - - suite('is', (): void => { - test('is an object.', async (): Promise => { - chai.typeOf(assert.that(23).is, 'object'); - }); - - suite('has constraints', (): void => { - test('atLeast.', async (): Promise => { - chai.typeOf(assert.that(23).is.atLeast, 'function'); - }); - - test('atMost.', async (): Promise => { - chai.typeOf(assert.that(23).is.atMost, 'function'); - }); - - test('between.', async (): Promise => { - chai.typeOf(assert.that(23).is.between, 'function'); - }); - - test('containing.', async (): Promise => { - chai.typeOf(assert.that(23).is.containing, 'function'); - }); - - test('containingAnyOf.', async (): Promise => { - chai.typeOf(assert.that(23).is.containingAnyOf, 'function'); - }); - - test('containingAll.', async (): Promise => { - chai.typeOf(assert.that(23).is.containingAllOf, 'function'); - }); - - test('endingWith.', async (): Promise => { - chai.typeOf(assert.that(23).is.endingWith, 'function'); - }); - - test('equalTo.', async (): Promise => { - chai.typeOf(assert.that(23).is.equalTo, 'function'); - }); - - test('false.', async (): Promise => { - chai.typeOf(assert.that(23).is.false, 'function'); - }); - - test('falsy.', async (): Promise => { - chai.typeOf(assert.that(23).is.falsy, 'function'); - }); - - test('greaterThan.', async (): Promise => { - chai.typeOf(assert.that(23).is.greaterThan, 'function'); - }); - - test('instanceOf.', async (): Promise => { - chai.typeOf(assert.that(23).is.instanceOf, 'function'); - }); - - test('lessThan.', async (): Promise => { - chai.typeOf(assert.that(23).is.lessThan, 'function'); - }); - - test('matching.', async (): Promise => { - chai.typeOf(assert.that(23).is.matching, 'function'); - }); - - test('NaN.', async (): Promise => { - chai.typeOf(assert.that(23).is.NaN, 'function'); - }); - - test('null.', async (): Promise => { - chai.typeOf(assert.that(23).is.null, 'function'); - }); - - test('ofType.', async (): Promise => { - chai.typeOf(assert.that(23).is.ofType, 'function'); - }); - - test('sameAs.', async (): Promise => { - chai.typeOf(assert.that(23).is.sameAs, 'function'); - }); - - test('sameJsonAs.', async (): Promise => { - chai.typeOf(assert.that(23).is.sameJsonAs, 'function'); - }); - - test('startingWith.', async (): Promise => { - chai.typeOf(assert.that(23).is.startingWith, 'function'); - }); - - test('throwing.', async (): Promise => { - chai.typeOf(assert.that(23).is.throwing, 'function'); - }); - - test('true.', async (): Promise => { - chai.typeOf(assert.that(23).is.true, 'function'); - }); - - test('undefined.', async (): Promise => { - chai.typeOf(assert.that(23).is.undefined, 'function'); - }); - }); - - suite('not', (): void => { - test('is an object.', async (): Promise => { - chai.typeOf(assert.that(23).is.not, 'object'); - }); - - suite('has constraints', (): void => { - test('atLeast.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.atLeast, 'function'); - }); - - test('atMost.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.atMost, 'function'); - }); - - test('between.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.between, 'function'); - }); - - test('containing.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.containing, 'function'); - }); - - test('containingAnyOf.', async (): Promise => { - chai.typeOf(assert.that([ 1, 2, 3 ]).is.not.containingAnyOf, 'function'); - }); - - test('containingAll.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.containingAllOf, 'function'); - }); - - test('endingWith.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.endingWith, 'function'); - }); - - test('equalTo.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.equalTo, 'function'); - }); - - test('false.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.false, 'function'); - }); - - test('falsy.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.falsy, 'function'); - }); - - test('greaterThan.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.greaterThan, 'function'); - }); - - test('instanceOf.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.instanceOf, 'function'); - }); - - test('lessThan.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.lessThan, 'function'); - }); - - test('matching.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.matching, 'function'); - }); - - test('NaN.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.NaN, 'function'); - }); - - test('null.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.null, 'function'); - }); - - test('ofType.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.ofType, 'function'); - }); - - test('sameAs.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.sameAs, 'function'); - }); - - test('sameJsonAs.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.sameJsonAs, 'function'); - }); - - test('startingWith.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.startingWith, 'function'); - }); - - test('throwing.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.throwing, 'function'); - }); - - test('true.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.true, 'function'); - }); - - test('undefined.', async (): Promise => { - chai.typeOf(assert.that(23).is.not.undefined, 'function'); - }); - }); - }); - }); - }); -}); diff --git a/test/unit/comparisons/forArrays/compareArraysTests.ts b/test/unit/comparisons/forArrays/compareArraysTests.ts new file mode 100644 index 0000000..cf11ab7 --- /dev/null +++ b/test/unit/comparisons/forArrays/compareArraysTests.ts @@ -0,0 +1,73 @@ +import { arrayDiff } from '../../../../lib/diffs/forArrays/ArrayDiff'; +import { assert } from '../../../../lib'; +import { compareArrays } from '../../../../lib/comparisons/forArrays/compareArrays'; +import { compareStrings } from 'lib/comparisons/forStrings/compareStrings'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; + +suite('compareArrays', (): void => { + test('returns an equal diff if the arrays are equal.', async (): Promise => { + const actual = [ 'foo', 'bar' ]; + const expected = [ 'foo', 'bar' ]; + + const diff = compareArrays(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a array diff containing addition segments if the arrays are not equal.', async (): Promise => { + const actual = [ 'foo', 'bar' ]; + const expected: any[] = []; + + const diff = compareArrays(actual, expected); + + assert.that(diff).is.equalTo( + arrayDiff({ + cost: 2, + segments: [ + { + addition: [ 'foo', 'bar' ], + cost: 2 + } + ] + }) + ); + }); + + test('returns a array diff containing omission segments if the arrays are not equal.', async (): Promise => { + const actual: any[] = []; + const expected = [ 'foo', 'bar' ]; + + const diff = compareArrays(actual, expected); + + assert.that(diff).is.equalTo( + arrayDiff({ + cost: 2, + segments: [ + { + omission: [ 'foo', 'bar' ], + cost: 2 + } + ] + }) + ); + }); + + test('returns a array diff containing change segments if the arrays are not equal.', async (): Promise => { + const actual = [ 'abc' ]; + const expected = [ 'abd' ]; + + const diff = compareArrays(actual, expected); + + assert.that(diff).is.equalTo( + arrayDiff({ + cost: 1, + segments: [ + { + change: [ compareStrings('abc', 'abd') ], + cost: 1 + } + ] + }) + ); + }); +}); diff --git a/test/unit/comparisons/forBooleans/compareBooleansTests.ts b/test/unit/comparisons/forBooleans/compareBooleansTests.ts new file mode 100644 index 0000000..b69b6a2 --- /dev/null +++ b/test/unit/comparisons/forBooleans/compareBooleansTests.ts @@ -0,0 +1,51 @@ +import { assert } from '../../../../lib'; +import { booleanDiff } from '../../../../lib/diffs/forBooleans/BooleanDiff'; +import { compareBooleans } from '../../../../lib/comparisons/forBooleans/compareBooleans'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { unequalBooleanCost } from '../../../../lib/constants/costs'; + +suite('compareBooleans', (): void => { + test('returns an equal diff if both booleans are true.', async (): Promise => { + const actual = true; + const expected = true; + + const diff = compareBooleans(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns an equal diff if both booleans are false.', async (): Promise => { + const actual = false; + const expected = false; + + const diff = compareBooleans(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a boolean diff if actual is true and expected is false.', async (): Promise => { + const actual = true; + const expected = false; + + const diff = compareBooleans(actual, expected); + + assert.that(diff).is.equalTo(booleanDiff({ + actual, + expected, + cost: unequalBooleanCost + })); + }); + + test('returns a boolean diff if actual is false and expected is true.', async (): Promise => { + const actual = false; + const expected = true; + + const diff = compareBooleans(actual, expected); + + assert.that(diff).is.equalTo(booleanDiff({ + actual, + expected, + cost: unequalBooleanCost + })); + }); +}); diff --git a/test/unit/comparisons/forErrors/compareErrorsTests.ts b/test/unit/comparisons/forErrors/compareErrorsTests.ts new file mode 100644 index 0000000..aba01f8 --- /dev/null +++ b/test/unit/comparisons/forErrors/compareErrorsTests.ts @@ -0,0 +1,32 @@ +import { assert } from '../../../../lib/assertthat'; +import { compareErrors } from '../../../../lib/comparisons/forErrors/compareErrors'; +import { compareObjects } from '../../../../lib/comparisons/forObjects/compareObjects'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { errorDiff } from '../../../../lib/diffs/forErrors/ErrorDiff'; +import { ObjectDiff } from '../../../../lib/diffs/forObjects/ObjectDiff'; + +suite('compareErrors', (): void => { + test('returns an equal diff if the errors are equal.', async (): Promise => { + const actual = new Error('foo'); + const expected = new Error('foo'); + + const diff = compareErrors(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns an error diff containing an object diff if the errors are not equal.', async (): Promise => { + const actual = new Error('foo'); + const expected = new Error('bar'); + + const diff = compareErrors(actual, expected); + + assert.that(diff).is.equalTo(errorDiff({ + cost: 3, + objectDiff: compareObjects( + { ...actual, message: actual.message }, + { ...expected, message: expected.message } + ) as ObjectDiff + })); + }); +}); diff --git a/test/unit/comparisons/forFunctions/compareFunctionsTests.ts b/test/unit/comparisons/forFunctions/compareFunctionsTests.ts new file mode 100644 index 0000000..df12e15 --- /dev/null +++ b/test/unit/comparisons/forFunctions/compareFunctionsTests.ts @@ -0,0 +1,36 @@ +/* eslint-disable unicorn/consistent-function-scoping */ +import { assert } from '../../../../lib'; +import { compareFunctions } from '../../../../lib/comparisons/forFunctions/compareFunctions'; +import { compareStrings } from '../../../../lib/comparisons/forStrings/compareStrings'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { functionDiff } from '../../../../lib/diffs/forFunctions/FunctionDiff'; +import { StringDiff } from '../../../../lib/diffs/forStrings/StringDiff'; +import { unequalFunctionCost } from '../../../../lib/constants/costs'; + +suite('compareFunctions', (): void => { + test(`returns an equal diff if both functions' string representations are equal.`, async (): Promise => { + const actual = (): string => 'foo'; + const expected = (): string => 'foo'; + + const diff = compareFunctions(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test(`returns a function diff containing a string diff if the functions' string representations are not equal.`, async (): Promise => { + const actual = (): string => 'foo'; + const expected = (): string => 'bar'; + + const diff = compareFunctions(actual, expected); + + assert.that(diff).is.equalTo( + functionDiff({ + cost: unequalFunctionCost, + stringRepresentationDiff: compareStrings( + actual.toString(), + expected.toString() + ) as StringDiff + }) + ); + }); +}); diff --git a/test/unit/comparisons/forMaps/compareMapsTests.ts b/test/unit/comparisons/forMaps/compareMapsTests.ts new file mode 100644 index 0000000..33c5efe --- /dev/null +++ b/test/unit/comparisons/forMaps/compareMapsTests.ts @@ -0,0 +1,61 @@ +import { assert } from '../../../../lib/assertthat'; +import { compareMaps } from '../../../../lib/comparisons/forMaps/compareMaps'; +import { compareStrings } from '../../../../lib/comparisons/forStrings/compareStrings'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { mapDiff } from '../../../../lib/diffs/forMaps/MapDiff'; + +suite('compareMaps', (): void => { + test('returns an equal diff if the maps have equal content.', async (): Promise => { + const actual = new Map([[ 'foo', 5 ], [ 'bar', 8 ]]); + const expected = new Map([[ 'foo', 5 ], [ 'bar', 8 ]]); + + const diff = compareMaps(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a map diff with additions if the maps are not equal.', async (): Promise => { + const actual = new Map([[ 'foo', 5 ], [ 'bar', 8 ], [ 'bam', 13 ]]); + const expected = new Map([[ 'bam', 13 ]]); + + const diff = compareMaps(actual, expected); + + assert.that(diff).is.equalTo(mapDiff({ + cost: 2, + additions: new Map([[ 'foo', 5 ], [ 'bar', 8 ]]), + omissions: new Map(), + changes: new Map(), + equal: new Map([[ 'bam', 13 ]]) + })); + }); + + test('returns a map diff with omissions if the maps are not equal.', async (): Promise => { + const actual = new Map([[ 'bam', 13 ]]); + const expected = new Map([[ 'foo', 5 ], [ 'bar', 8 ], [ 'bam', 13 ]]); + + const diff = compareMaps(actual, expected); + + assert.that(diff).is.equalTo(mapDiff({ + cost: 2, + additions: new Map(), + omissions: new Map([[ 'foo', 5 ], [ 'bar', 8 ]]), + changes: new Map(), + equal: new Map([[ 'bam', 13 ]]) + })); + }); + + test('returns a map diff with changes if the maps are not equal.', async (): Promise => { + const actual = new Map([[ 'foo', 'foo' ], [ 'bam', 13 ]]); + const expected = new Map([[ 'foo', 'bar' ], [ 'bam', 13 ]]); + + const diff = compareMaps(actual, expected); + + assert.that(diff).is.equalTo(mapDiff({ + cost: 3, + additions: new Map(), + omissions: new Map(), + changes: new Map([[ 'foo', compareStrings('foo', 'bar') ]]), + equal: new Map([[ 'bam', 13 ]]) + })); + }); +}); diff --git a/test/unit/comparisons/forNumbers/compareNumbersTests.ts b/test/unit/comparisons/forNumbers/compareNumbersTests.ts new file mode 100644 index 0000000..95a5d07 --- /dev/null +++ b/test/unit/comparisons/forNumbers/compareNumbersTests.ts @@ -0,0 +1,30 @@ +import { assert } from '../../../../lib'; +import { compareNumbers } from '../../../../lib/comparisons/forNumbers/compareNumbers'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { numberDiff } from '../../../../lib/diffs/forNumbers/NumberDiff'; +import { unequalNumberCost } from '../../../../lib/constants/costs'; + +suite('compareNumbers', (): void => { + test('returns an equal diff if the numbers are equal.', async (): Promise => { + const actual = 5; + const expected = 5; + + const diff = compareNumbers(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a number diff if the numbers are not equal.', async (): Promise => { + const actual = 5; + const expected = 19; + + const diff = compareNumbers(actual, expected); + + assert.that(diff).is.equalTo(numberDiff({ + actual, + expected, + difference: actual - expected, + cost: unequalNumberCost + })); + }); +}); diff --git a/test/unit/comparisons/forObjects/compareObjectsTests.ts b/test/unit/comparisons/forObjects/compareObjectsTests.ts new file mode 100644 index 0000000..a12caa0 --- /dev/null +++ b/test/unit/comparisons/forObjects/compareObjectsTests.ts @@ -0,0 +1,61 @@ +import { assert } from '../../../../lib/assertthat'; +import { compareObjects } from '../../../../lib/comparisons/forObjects/compareObjects'; +import { compareStrings } from '../../../../lib/comparisons/forStrings/compareStrings'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { objectDiff } from '../../../../lib/diffs/forObjects/ObjectDiff'; + +suite('compareObjects', (): void => { + test('returns an equal diff if the objects have equal content.', async (): Promise => { + const actual = { foo: 5, bar: 8 }; + const expected = { foo: 5, bar: 8 }; + + const diff = compareObjects(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a object diff with additions if the objects are not equal.', async (): Promise => { + const actual = { foo: 5, bar: 8, bam: 13 }; + const expected = { bam: 13 }; + + const diff = compareObjects(actual, expected); + + assert.that(diff).is.equalTo(objectDiff({ + cost: 2, + additions: { foo: 5, bar: 8 }, + omissions: {}, + changes: {}, + equal: { bam: 13 } + })); + }); + + test('returns a object diff with omissions if the objects are not equal.', async (): Promise => { + const actual = { bam: 13 }; + const expected = { foo: 5, bar: 8, bam: 13 }; + + const diff = compareObjects(actual, expected); + + assert.that(diff).is.equalTo(objectDiff({ + cost: 2, + additions: {}, + omissions: { foo: 5, bar: 8 }, + changes: {}, + equal: { bam: 13 } + })); + }); + + test('returns a object diff with changes if the objects are not equal.', async (): Promise => { + const actual = { foo: 'foo', bam: 13 }; + const expected = { foo: 'bar', bam: 13 }; + + const diff = compareObjects(actual, expected); + + assert.that(diff).is.equalTo(objectDiff({ + cost: 3, + additions: {}, + omissions: {}, + changes: { foo: compareStrings('foo', 'bar') }, + equal: { bam: 13 } + })); + }); +}); diff --git a/test/unit/comparisons/forRecursions/compareRecursionsTests.ts b/test/unit/comparisons/forRecursions/compareRecursionsTests.ts new file mode 100644 index 0000000..c423942 --- /dev/null +++ b/test/unit/comparisons/forRecursions/compareRecursionsTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../../lib'; +import { compareRecursions } from '../../../../lib/comparisons/forRecursions/compareRecursions'; +import { compareStrings } from '../../../../lib/comparisons/forStrings/compareStrings'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { recursion } from '../../../../lib/types/Recursion'; +import { recursionDiff } from '../../../../lib/diffs/forRecursions/RecursionDiff'; +import { StringDiff } from '../../../../lib/diffs/forStrings/StringDiff'; +import { unequalRecursionCost } from '../../../../lib/constants/costs'; + +suite('compareRecursions', (): void => { + test('returns an equal diff if the recursions have the same recursion path.', async (): Promise => { + const actual = recursion({ recursionPath: 'foo.bar' }); + const expected = recursion({ recursionPath: 'foo.bar' }); + + const diff = compareRecursions(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a recursion diff containing a string diff if the recursions are not equal.', async (): Promise => { + const actual = recursion({ recursionPath: 'foo.bar' }); + const expected = recursion({ recursionPath: 'bam.bas' }); + + const diff = compareRecursions(actual, expected); + + assert.that(diff).is.equalTo(recursionDiff({ + cost: unequalRecursionCost, + recursionPathDiff: compareStrings( + actual.recursionPath, + expected.recursionPath + ) as StringDiff + })); + }); +}); diff --git a/test/unit/comparisons/forResults/compareResultsTests.ts b/test/unit/comparisons/forResults/compareResultsTests.ts new file mode 100644 index 0000000..35a137a --- /dev/null +++ b/test/unit/comparisons/forResults/compareResultsTests.ts @@ -0,0 +1,85 @@ +import { assert } from '../../../../lib/assertthat'; +import { compare } from '../../../../lib/comparisons/typeAware/compare'; +import { compareResults } from '../../../../lib/comparisons/forResults/compareResults'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { unequalResultCost } from '../../../../lib/constants/costs'; +import { error, value } from 'defekt'; +import { expectedErrorGotValueResultDiff, expectedValueGotErrorResultDiff, unequalErrorResultDiff, unequalValueResultDiff } from '../../../../lib/diffs/forResults/ResultDiff'; + +suite('compareResults', (): void => { + test('returns an equal diff if the results are equal values.', async (): Promise => { + const actual = value('foo'); + const expected = value('foo'); + + const diff = compareResults(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns an equal diff if the results are equal errors.', async (): Promise => { + const actual = error(new Error('foo')); + const expected = error(new Error('foo')); + + const diff = compareResults(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test(`returns an 'expected value, got error' diff if actual is an error but expected is a value.`, async (): Promise => { + const actual = error(new Error('foo')); + const expected = value('foo'); + + const diff = compareResults(actual, expected); + + assert.that(diff).is.equalTo( + expectedValueGotErrorResultDiff({ + actual, + expected, + cost: unequalResultCost + }) + ); + }); + + test(`returns an 'expected error, got value' diff if actual is a value but expected is an error.`, async (): Promise => { + const actual = value('foo'); + const expected = error(new Error('foo')); + + const diff = compareResults(actual, expected); + + assert.that(diff).is.equalTo( + expectedErrorGotValueResultDiff({ + actual, + expected, + cost: unequalResultCost + }) + ); + }); + + test('returns an error diff if the results are differing errors.', async (): Promise => { + const actual = error(new Error('foo')); + const expected = error(new Error('bar')); + + const diff = compareResults(actual, expected); + + assert.that(diff).is.equalTo( + unequalErrorResultDiff({ + diff: compare(actual.error, expected.error), + cost: 3 + }) + ); + }); + + test('returns a value diff if the results are differing values.', async (): Promise => { + const actual = value('foo'); + const expected = value('bar'); + + const diff = compareResults(actual, expected); + + assert.that(diff).is.equalTo( + unequalValueResultDiff({ + diff: compare(actual.value, expected.value), + cost: 3 + }) + ); + }); +}); diff --git a/test/unit/comparisons/forSets/compareSetsTests.ts b/test/unit/comparisons/forSets/compareSetsTests.ts new file mode 100644 index 0000000..90ca576 --- /dev/null +++ b/test/unit/comparisons/forSets/compareSetsTests.ts @@ -0,0 +1,43 @@ +import { assert } from '../../../../lib/assertthat'; +import { compareSets } from '../../../../lib/comparisons/forSets/compareSets'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { setDiff } from '../../../../lib/diffs/forSets/SetDiff'; + +suite('compareSets', (): void => { + test('returns an equal diff if the sets have equal content.', async (): Promise => { + const actual = new Set([ 5, 8 ]); + const expected = new Set([ 5, 8 ]); + + const diff = compareSets(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a set diff with additions if the sets are not equal.', async (): Promise => { + const actual = new Set([ 3, 5, 8 ]); + const expected = new Set([ 3 ]); + + const diff = compareSets(actual, expected); + + assert.that(diff).is.equalTo(setDiff({ + cost: 2, + additions: new Set([ 5, 8 ]), + omissions: new Set(), + equal: new Set([ 3 ]) + })); + }); + + test('returns a set diff with omissions if the sets are not equal.', async (): Promise => { + const actual = new Set([ 3 ]); + const expected = new Set([ 3, 5, 8 ]); + + const diff = compareSets(actual, expected); + + assert.that(diff).is.equalTo(setDiff({ + cost: 2, + additions: new Set(), + omissions: new Set([ 5, 8 ]), + equal: new Set([ 3 ]) + })); + }); +}); diff --git a/test/unit/comparisons/forStrings/compareStringsTests.ts b/test/unit/comparisons/forStrings/compareStringsTests.ts new file mode 100644 index 0000000..1d8eadb --- /dev/null +++ b/test/unit/comparisons/forStrings/compareStringsTests.ts @@ -0,0 +1,112 @@ +import { assert } from '../../../../lib'; +import { compareStrings } from '../../../../lib/comparisons/forStrings/compareStrings'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { stringDiff } from '../../../../lib/diffs/forStrings/StringDiff'; + +suite('compareStrings', (): void => { + test('returns an equal diff if the strings are equal.', async (): Promise => { + const actual = 'foo bar'; + const expected = 'foo bar'; + + const diff = compareStrings(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a string diff containing addition segments if the strings are not equal.', async (): Promise => { + const actual = 'foo bar'; + const expected = ''; + + const diff = compareStrings(actual, expected); + + assert.that(diff).is.equalTo( + stringDiff({ + cost: 7, + segments: [ + { + addition: 'foo bar', + cost: 7 + } + ] + }) + ); + }); + + test('returns a string diff containing omission segments if the strings are not equal.', async (): Promise => { + const actual = ''; + const expected = 'foo bar'; + + const diff = compareStrings(actual, expected); + + assert.that(diff).is.equalTo( + stringDiff({ + cost: 7, + segments: [ + { + omission: 'foo bar', + cost: 7 + } + ] + }) + ); + }); + + test('returns a string diff containing replace segments if the strings are not equal.', async (): Promise => { + const actual = 'abc'; + const expected = 'xyz'; + + const diff = compareStrings(actual, expected); + + assert.that(diff).is.equalTo( + stringDiff({ + cost: 3, + segments: [ + { + replace: 'abc', + replaceWith: 'xyz', + cost: 3 + } + ] + }) + ); + }); + + test('returns a mixed string diff.', async (): Promise => { + const actual = 'GCTGATATAGCT'; + const expected = 'GGGTGATTAGCT'; + + const diff = compareStrings(actual, expected); + + assert.that(diff).is.equalTo( + stringDiff({ + cost: 3, + segments: [ + { omission: 'G', cost: 1 }, + { equal: 'G', cost: 0 }, + { replace: 'C', replaceWith: 'G', cost: 1 }, + { equal: 'TGAT', cost: 0 }, + { addition: 'A', cost: 1 }, + { equal: 'TAGCT', cost: 0 } + ] + }) + ); + }); + + test('uses the chunk delimiter to determine word border for diffing.', async (): Promise => { + const actual = 'foo\nbar'; + const expected = 'foo\nfoo'; + + const diff = compareStrings(actual, expected, '\n'); + + assert.that(diff).is.equalTo( + stringDiff({ + cost: 2, + segments: [ + { equal: 'foo', cost: 0 }, + { addition: 'bar', cost: 1 }, + { omission: 'foo', cost: 1 } + ] + }) + ); + }); +}); diff --git a/test/unit/comparisons/forSymbols/compareSymbolsTests.ts b/test/unit/comparisons/forSymbols/compareSymbolsTests.ts new file mode 100644 index 0000000..d28743c --- /dev/null +++ b/test/unit/comparisons/forSymbols/compareSymbolsTests.ts @@ -0,0 +1,33 @@ +import { assert } from '../../../../lib'; +import { compareStrings } from '../../../../lib/comparisons/forStrings/compareStrings'; +import { compareSymbols } from '../../../../lib/comparisons/forSymbols/compareSymbols'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { StringDiff } from '../../../../lib/diffs/forStrings/StringDiff'; +import { symbolDiff } from '../../../../lib/diffs/forSymbols/SymbolDiff'; +import { unequalSymbolCost } from '../../../../lib/constants/costs'; + +suite('compareSymbols', (): void => { + test(`returns an equal diff if the symbols' string representations are equal.`, async (): Promise => { + const actual = Symbol('foo'); + const expected = Symbol('foo'); + + const diff = compareSymbols(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test(`returns a symbol diff containing a string diff if the symbols' string representations are not equal.`, async (): Promise => { + const actual = Symbol('foo'); + const expected = Symbol('bar'); + + const diff = compareSymbols(actual, expected); + + assert.that(diff).is.equalTo(symbolDiff({ + cost: unequalSymbolCost, + descriptionDiff: compareStrings( + actual.description!, + expected.description! + ) as StringDiff + })); + }); +}); diff --git a/test/unit/comparisons/typeAware/compareTests.ts b/test/unit/comparisons/typeAware/compareTests.ts new file mode 100644 index 0000000..ca91df2 --- /dev/null +++ b/test/unit/comparisons/typeAware/compareTests.ts @@ -0,0 +1,180 @@ +import { arrayDiff } from 'lib/diffs/forArrays/ArrayDiff'; +import { assert } from '../../../../lib/assertthat'; +import { compare } from '../../../../lib/comparisons/typeAware/compare'; +import { equalDiff } from '../../../../lib/diffs/EqualDiff'; +import { incompatibleTypeDiff } from '../../../../lib/diffs/IncompatibleTypeDiff'; +import { incompatibleTypesCost } from '../../../../lib/constants/costs'; +import { mapDiff } from 'lib/diffs/forMaps/MapDiff'; +import { objectDiff } from '../../../../lib/diffs/forObjects/ObjectDiff'; +import { setDiff } from 'lib/diffs/forSets/SetDiff'; +import { stringDiff } from 'lib/diffs/forStrings/StringDiff'; + +suite('compare', (): void => { + test('returns equal diff for complex data.', async (): Promise => { + const actual = { + one: 'one', + two: [ 1, 2, 3 ], + three: 13, + four: new Set([ 1, 2, 3 ]), + five: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + six: { + seven: { + eight: 'foo.bar' + } + } + }; + const expected = { + one: 'one', + two: [ 1, 2, 3 ], + three: 13, + four: new Set([ 1, 2, 3 ]), + five: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + six: { + seven: { + eight: 'foo.bar' + } + } + }; + + const diff = compare(actual, expected); + + assert.that(diff).is.equalTo(equalDiff({ value: actual })); + }); + + test('returns a deep diff for complex data.', async (): Promise => { + const actual = { + one: 'one', + two: [ 1, 2, 3 ], + three: 13, + four: new Set([ 1, 2, 3 ]), + five: new Map([[ 'foo', 'foo' ], [ 'bar', 'bar' ]]), + six: { + seven: { + eight: 'foo.bar' + } + } + }; + const expected = { + two: [ 2, 3 ], + three: 13, + four: new Set([ 1 ]), + five: new Map([[ 'foo', 'bar' ], [ 'bar', 'foo' ]]), + six: { + seven: { + eight: 'heck' + } + }, + nine: 'nine' + }; + + const diff = compare(actual, expected); + + assert.that(diff).is.equalTo( + objectDiff({ + cost: 18, + additions: { + one: 'one' + }, + omissions: { + nine: 'nine' + }, + changes: { + two: arrayDiff({ + cost: 1, + segments: [ + { addition: [ 1 ], cost: 1 }, + { equal: [ 2, 3 ], cost: 0 } + ] + }), + four: setDiff({ + cost: 2, + additions: new Set([ 2, 3 ]), + omissions: new Set(), + equal: new Set([ 1 ]) + }), + five: mapDiff({ + cost: 6, + additions: new Map(), + omissions: new Map(), + changes: new Map([ + [ 'foo', stringDiff({ + cost: 3, + segments: [{ replace: 'foo', replaceWith: 'bar', cost: 3 }] + }) ], + [ 'bar', stringDiff({ + cost: 3, + segments: [{ replace: 'bar', replaceWith: 'foo', cost: 3 }] + }) ] + ]), + equal: new Map() + }), + six: objectDiff({ + cost: 7, + additions: {}, + omissions: {}, + changes: { + seven: objectDiff({ + cost: 7, + additions: {}, + omissions: {}, + changes: { + eight: stringDiff({ + cost: 7, + segments: [ + { addition: 'foo', cost: 3 }, + { replace: '.bar', replaceWith: 'heck', cost: 4 } + ] + }) + }, + equal: {} + }) + }, + equal: {} + }) + }, + equal: { + three: 13 + } + }) + ); + }); + + suite('simple cases', (): void => { + test('returns an equal diff for two undefineds.', async (): Promise => { + const actual = undefined; + const expected = undefined; + + const diff = compare(actual, expected); + + assert.that(diff).is.equalTo( + equalDiff({ value: undefined }) + ); + }); + + test('returns an equal diff for two nulls.', async (): Promise => { + const actual = null; + const expected = null; + + const diff = compare(actual, expected); + + assert.that(diff).is.equalTo( + equalDiff({ value: null }) + ); + }); + + test('returns a type mismatch diff for two different types.', async (): Promise => { + const actual = 5; + const expected = 'foo'; + + const diff = compare(actual, expected); + + assert.that(diff).is.equalTo( + incompatibleTypeDiff({ + actual, + expected, + cost: incompatibleTypesCost + }) + ); + }); + }); +}); diff --git a/test/unit/constraints/NaNTests.ts b/test/unit/constraints/NaNTests.ts deleted file mode 100644 index e66cc36..0000000 --- a/test/unit/constraints/NaNTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { isNan } from '../../../lib/constraints/nan'; - -const chai = chaiStatic.assert; - -suite('NaN', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isNan, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isNan(Number.NaN), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is NaN.', async (): Promise => { - chai.doesNotThrow((): void => { - isNan(Number.NaN)(); - }); - }); - - test('throws an error if actual is not NaN.', async (): Promise => { - chai.throw((): void => { - isNan(23)(); - }, 'Expected 23 to be NaN.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isNan.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isNan.negated(Number.NaN), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not NaN.', async (): Promise => { - chai.doesNotThrow((): void => { - isNan.negated(23)(); - }); - }); - - test('throws an error if actual is NaN.', async (): Promise => { - chai.throw((): void => { - isNan.negated(Number.NaN)(); - }, 'Expected NaN not to be NaN.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/atLeastTests.ts b/test/unit/constraints/atLeastTests.ts deleted file mode 100644 index 85e9943..0000000 --- a/test/unit/constraints/atLeastTests.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { atLeast } from '../../../lib/constraints/atLeast'; -import chaiStatic from 'chai'; - -const chai = chaiStatic.assert; - -suite('atLeast', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(atLeast, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(atLeast(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is at least expected.', async (): Promise => { - chai.doesNotThrow((): void => { - atLeast(23)(23); - }); - }); - - test('throws an error if actual is not at least expected.', async (): Promise => { - chai.throw((): void => { - atLeast(23)(42); - }, 'Expected 23 to be at least 42.'); - }); - }); - - suite('negated', (): void => { - suite('constraint', (): void => { - test('does not throw an error if actual is not at least expected.', async (): Promise => { - chai.doesNotThrow((): void => { - atLeast.negated(23)(42); - }); - }); - - test('throws an error if actual is at least expected.', async (): Promise => { - chai.throw((): void => { - atLeast.negated(23)(23); - }, 'Expected 23 not to be at least 23.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/atMostTests.ts b/test/unit/constraints/atMostTests.ts deleted file mode 100644 index 21863ba..0000000 --- a/test/unit/constraints/atMostTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { atMost } from '../../../lib/constraints/atMost'; -import chaiStatic from 'chai'; - -const chai = chaiStatic.assert; - -suite('atMost', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(atMost, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(atMost(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is at most expected.', async (): Promise => { - chai.doesNotThrow((): void => { - atMost(23)(23); - }); - }); - - test('throws an error if actual is not at most expected.', async (): Promise => { - chai.throw((): void => { - atMost(42)(23); - }, 'Expected 42 to be at most 23.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(atMost.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(atMost.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not at most expected.', async (): Promise => { - chai.doesNotThrow((): void => { - atMost.negated(42)(23); - }); - }); - - test('throws an error if actual is at most expected.', async (): Promise => { - chai.throw((): void => { - atMost.negated(23)(23); - }, 'Expected 23 to be at most 23.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/betweenTests.ts b/test/unit/constraints/betweenTests.ts deleted file mode 100644 index e66b904..0000000 --- a/test/unit/constraints/betweenTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { between } from '../../../lib/constraints/between'; -import chaiStatic from 'chai'; - -const chai = chaiStatic.assert; - -suite('between', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(between, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(between(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is between the expected lower and upper bounds.', async (): Promise => { - chai.doesNotThrow((): void => { - between(23)(7, 42); - }); - }); - - test('throws an error if actual is not between the expected lower and upper bounds.', async (): Promise => { - chai.throw((): void => { - between(7)(23, 42); - }, 'Expected 7 to be between 23 and 42.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(between.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(between.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not between the expected lower and upper bounds.', async (): Promise => { - chai.doesNotThrow((): void => { - between.negated(42)(7, 23); - }); - }); - - test('throws an error if actual is between the expected lower and upper bounds.', async (): Promise => { - chai.throw((): void => { - between.negated(23)(7, 42); - }, 'Expected 23 not to be between 7 and 42.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/containingAllOfTests.ts b/test/unit/constraints/containingAllOfTests.ts deleted file mode 100644 index 1b5b2e0..0000000 --- a/test/unit/constraints/containingAllOfTests.ts +++ /dev/null @@ -1,57 +0,0 @@ -import chaiStatic from 'chai'; -import { containingAllOf } from '../../../lib/constraints/containingAllOf'; - -const chai = chaiStatic.assert; - -suite('containingAll', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(containingAllOf, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(containingAllOf([ 'foo', 'bar' ]), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual contains all of expected.', async (): Promise => { - chai.doesNotThrow((): void => { - containingAllOf([ 1, 2, 3 ])([ 1, 2, 3 ]); - containingAllOf([ 1, 2, 3 ])([ 1 ]); - containingAllOf([ 1, 2, 3 ])([ 3, 2 ]); - containingAllOf([ 1, 2, 3 ])([ ]); - }); - }); - - test('throws an error if actual does not contain all of expected.', async (): Promise => { - chai.throw((): void => { - containingAllOf([ 1, 2, 3 ])([ 2, 5 ]); - }, 'Expected [\n 1,\n 2,\n 3\n] to contain all of [\n 2,\n 5\n].'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(containingAllOf.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(containingAllOf.negated([ 'foo', 'bar' ]), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual does not contain all of expected.', async (): Promise => { - chai.doesNotThrow((): void => { - containingAllOf.negated([ 1, 2, 3 ])([ 5 ]); - containingAllOf.negated([ 1, 2, 3 ])([ 1, 5 ]); - containingAllOf.negated([ 1, 2, 3 ])([ 1, 2, 3, 5 ]); - }); - }); - - test('throws an error if actual contains all of expected.', async (): Promise => { - chai.throw((): void => { - containingAllOf.negated([ 1, 2, 3 ])([ 1, 3 ]); - }, 'Expected [\n 1,\n 2,\n 3\n] not to contain all of [\n 1,\n 3\n].'); - }); - }); - }); -}); diff --git a/test/unit/constraints/containingAnyOfTests.ts b/test/unit/constraints/containingAnyOfTests.ts deleted file mode 100644 index cc53f6a..0000000 --- a/test/unit/constraints/containingAnyOfTests.ts +++ /dev/null @@ -1,55 +0,0 @@ -import chaiStatic from 'chai'; -import { containingAnyOf } from '../../../lib/constraints/containingAnyOf'; - -const chai = chaiStatic.assert; - -suite('containingAnyOf', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(containingAnyOf, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(containingAnyOf([ 'foo', 'bar' ]), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual contains any of expected.', async (): Promise => { - chai.doesNotThrow((): void => { - containingAnyOf([ 1, 2, 3 ])([ 1 ]); - containingAnyOf([ 1, 2, 3 ])([ 1, 3 ]); - containingAnyOf([ 1, 2, 3 ])([ 1, 5 ]); - containingAnyOf([ 1, 2, 3 ])([ 1, 3, 5 ]); - }); - }); - - test('throws an error if actual does not contain any of expected.', async (): Promise => { - chai.throw((): void => { - containingAnyOf([ 1, 2, 3 ])([ 4 ]); - }, 'Expected [\n 1,\n 2,\n 3\n] to contain any of [\n 4\n].'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(containingAnyOf.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(containingAnyOf.negated([ 'foo', 'bar' ]), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual does not contain any of expected.', async (): Promise => { - chai.doesNotThrow((): void => { - containingAnyOf.negated([ 1, 2, 3 ])([ 4 ]); - }); - }); - - test('throws an error if actual contains any of expected.', async (): Promise => { - chai.throw((): void => { - containingAnyOf.negated([ 1, 2, 3 ])([ 5, 1 ]); - }, 'Expected [\n 1,\n 2,\n 3\n] not to contain any of [\n 5,\n 1\n].'); - }); - }); - }); -}); diff --git a/test/unit/constraints/containingTests.ts b/test/unit/constraints/containingTests.ts deleted file mode 100644 index 522f62e..0000000 --- a/test/unit/constraints/containingTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { containing } from '../../../lib/constraints/containing'; - -const chai = chaiStatic.assert; - -suite('containing', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(containing, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(containing('foobar'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual contains expected.', async (): Promise => { - chai.doesNotThrow((): void => { - containing('foobar')('ooba'); - }); - }); - - test('throws an error if actual does not contain expected.', async (): Promise => { - chai.throw((): void => { - containing('foobar')('nufta'); - }, 'Expected \'foobar\' to contain \'nufta\'.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(containing.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(containing.negated('foobar'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual does not contain expected.', async (): Promise => { - chai.doesNotThrow((): void => { - containing.negated('foobar')('nufta'); - }); - }); - - test('throws an error if actual contains expected.', async (): Promise => { - chai.throw((): void => { - containing.negated('foobar')('ooba'); - }, 'Expected \'foobar\' not to contain \'ooba\'.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/endingWithTests.ts b/test/unit/constraints/endingWithTests.ts deleted file mode 100644 index 1d64d69..0000000 --- a/test/unit/constraints/endingWithTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { endingWith } from '../../../lib/constraints/endingWith'; - -const chai = chaiStatic.assert; - -suite('endingWith', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(endingWith, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(endingWith('foobar'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual ends with expected.', async (): Promise => { - chai.doesNotThrow((): void => { - endingWith('foobar')('bar'); - }); - }); - - test('throws an error if actual does not end with expected.', async (): Promise => { - chai.throw((): void => { - endingWith('foobar')('foo'); - }, 'Expected \'foobar\' to end with \'foo\'.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(endingWith.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(endingWith.negated('foobar'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual does not end with expected.', async (): Promise => { - chai.doesNotThrow((): void => { - endingWith.negated('foobar')('foo'); - }); - }); - - test('throws an error if actual ends with expected.', async (): Promise => { - chai.throw((): void => { - endingWith.negated('foobar')('bar'); - }, 'Expected \'foobar\' not to end with \'bar\'.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/equalToTests.ts b/test/unit/constraints/equalToTests.ts deleted file mode 100644 index 538ef43..0000000 --- a/test/unit/constraints/equalToTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { equalTo } from '../../../lib/constraints/equalTo'; - -const chai = chaiStatic.assert; - -suite('equalTo', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(equalTo, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(equalTo(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is equal to expected.', async (): Promise => { - chai.doesNotThrow((): void => { - equalTo(23)(23); - }); - }); - - test('throws an error if actual is not equal to expected.', async (): Promise => { - chai.throw((): void => { - equalTo(23)(42); - }, 'Expected 23 to equal 42.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(equalTo.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(equalTo.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not equal to expected.', async (): Promise => { - chai.doesNotThrow((): void => { - equalTo.negated(23)(42); - }); - }); - - test('throws an error if actual is equal to expected.', async (): Promise => { - chai.throw((): void => { - equalTo.negated(23)(23); - }, 'Expected 23 not to equal 23.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/falseTests.ts b/test/unit/constraints/falseTests.ts deleted file mode 100644 index 503a87e..0000000 --- a/test/unit/constraints/falseTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { isFalse } from '../../../lib/constraints/false'; - -const chai = chaiStatic.assert; - -suite('false', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isFalse, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isFalse(false), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is false.', async (): Promise => { - chai.doesNotThrow((): void => { - isFalse(false)(); - }); - }); - - test('throws an error if actual is not false.', async (): Promise => { - chai.throw((): void => { - isFalse(true)(); - }, 'Expected true to be false.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isFalse.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isFalse.negated(false), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not false.', async (): Promise => { - chai.doesNotThrow((): void => { - isFalse.negated(true)(); - }); - }); - - test('throws an error if actual is false.', async (): Promise => { - chai.throw((): void => { - isFalse.negated(false)(); - }, 'Expected false not to be false.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/falsyTests.ts b/test/unit/constraints/falsyTests.ts deleted file mode 100644 index 8f8af62..0000000 --- a/test/unit/constraints/falsyTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { falsy } from '../../../lib/constraints/falsy'; - -const chai = chaiStatic.assert; - -suite('falsy', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(falsy, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(falsy(0), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is falsy.', async (): Promise => { - chai.doesNotThrow((): void => { - falsy(0)(); - }); - }); - - test('throws an error if actual is not falsy.', async (): Promise => { - chai.throw((): void => { - falsy(23)(); - }, 'Expected 23 to be falsy.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(falsy.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(falsy.negated(0), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not falsy.', async (): Promise => { - chai.doesNotThrow((): void => { - falsy.negated(23)(); - }); - }); - - test('throws an error if actual is falsy.', async (): Promise => { - chai.throw((): void => { - falsy.negated(0)(); - }, 'Expected 0 not to be falsy.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/greaterThanTests.ts b/test/unit/constraints/greaterThanTests.ts deleted file mode 100644 index 41bfb83..0000000 --- a/test/unit/constraints/greaterThanTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { greaterThan } from '../../../lib/constraints/greaterThan'; - -const chai = chaiStatic.assert; - -suite('greaterThan', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(greaterThan, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(greaterThan(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is greater than expected.', async (): Promise => { - chai.doesNotThrow((): void => { - greaterThan(42)(23); - }); - }); - - test('throws an error if actual is not greater than expected.', async (): Promise => { - chai.throw((): void => { - greaterThan(23)(42); - }, 'Expected 23 to be greater than 42.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(greaterThan.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(greaterThan.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not greater than expected.', async (): Promise => { - chai.doesNotThrow((): void => { - greaterThan.negated(23)(42); - }); - }); - - test('throws an error if actual is greater than expected.', async (): Promise => { - chai.throw((): void => { - greaterThan.negated(42)(23); - }, 'Expected 42 not to be greater than 23.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/instanceOfTests.ts b/test/unit/constraints/instanceOfTests.ts deleted file mode 100644 index d9dedbc..0000000 --- a/test/unit/constraints/instanceOfTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { instanceOf } from '../../../lib/constraints/instanceOf'; - -const chai = chaiStatic.assert; - -suite('instanceOf', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(instanceOf, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(instanceOf(new Error('Something went wrong.')), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is an instance of expected.', async (): Promise => { - chai.doesNotThrow((): void => { - instanceOf(new Error('Something went wrong.'))(Error); - }); - }); - - test('throws an error if actual is not an instance of expected.', async (): Promise => { - chai.throw((): void => { - instanceOf({})(Error); - }, 'Expected {} to be an instance of Error.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(instanceOf.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(instanceOf.negated(new Error('Something went wrong.')), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not an instance of expected.', async (): Promise => { - chai.doesNotThrow((): void => { - instanceOf.negated({})(Error); - }); - }); - - test('throws an error if actual is an instance of expected.', async (): Promise => { - chai.throw((): void => { - instanceOf.negated(new Error('Something went wrong.'))(Error); - }, `Expected 'Error' not to be an instance of Error.`); - }); - }); - }); -}); diff --git a/test/unit/constraints/lessThanTests.ts b/test/unit/constraints/lessThanTests.ts deleted file mode 100644 index ac97991..0000000 --- a/test/unit/constraints/lessThanTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { lessThan } from '../../../lib/constraints/lessThan'; - -const chai = chaiStatic.assert; - -suite('lessThan', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(lessThan, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(lessThan(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is less than expected.', async (): Promise => { - chai.doesNotThrow((): void => { - lessThan(23)(42); - }); - }); - - test('throws an error if actual is not less than expected.', async (): Promise => { - chai.throw((): void => { - lessThan(42)(23); - }, 'Expected 42 to be less than 23.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(lessThan.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(lessThan.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not less than expected.', async (): Promise => { - chai.doesNotThrow((): void => { - lessThan.negated(42)(23); - }); - }); - - test('throws an error if actual is less than expected.', async (): Promise => { - chai.throw((): void => { - lessThan.negated(23)(42); - }, 'Expected 23 not to be less than 42.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/matchingTests.ts b/test/unit/constraints/matchingTests.ts deleted file mode 100644 index 291e8a5..0000000 --- a/test/unit/constraints/matchingTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { matching } from '../../../lib/constraints/matching'; - -const chai = chaiStatic.assert; - -suite('matching', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(matching, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(matching('foo'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is matching expected.', async (): Promise => { - chai.doesNotThrow((): void => { - matching('foo')(/foo/u); - }); - }); - - test('throws an error if actual is not matching expected.', async (): Promise => { - chai.throw((): void => { - matching('foo')(/bar/u); - }, 'Expected \'foo\' to match /bar/u.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(matching.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(matching.negated('foo'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not matching expected.', async (): Promise => { - chai.doesNotThrow((): void => { - matching.negated('foo')(/bar/u); - }); - }); - - test('throws an error if actual is matching expected.', async (): Promise => { - chai.throw((): void => { - matching.negated('foo')(/foo/u); - }, 'Expected \'foo\' not to match /foo/u.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/nullTests.ts b/test/unit/constraints/nullTests.ts deleted file mode 100644 index 0c47c5d..0000000 --- a/test/unit/constraints/nullTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { isNull } from '../../../lib/constraints/null'; - -const chai = chaiStatic.assert; - -suite('null', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isNull, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isNull(null), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is null.', async (): Promise => { - chai.doesNotThrow((): void => { - isNull(null)(); - }); - }); - - test('throws an error if actual is not null.', async (): Promise => { - chai.throw((): void => { - isNull(23)(); - }, 'Expected 23 to be null.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isNull.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isNull.negated(null), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not null.', async (): Promise => { - chai.doesNotThrow((): void => { - isNull.negated(23)(); - }); - }); - - test('throws an error if actual is null.', async (): Promise => { - chai.throw((): void => { - isNull.negated(null)(); - }, 'Expected null not to be null.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/ofTypeTests.ts b/test/unit/constraints/ofTypeTests.ts deleted file mode 100644 index 57b9962..0000000 --- a/test/unit/constraints/ofTypeTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { ofType } from '../../../lib/constraints/ofType'; - -const chai = chaiStatic.assert; - -suite('ofType', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(ofType, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(ofType(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is equal to expected.', async (): Promise => { - chai.doesNotThrow((): void => { - ofType(23)('number'); - }); - }); - - test('throws an error if actual is not equal to expected.', async (): Promise => { - chai.throw((): void => { - ofType(23)('string'); - }, 'Expected 23 to be of type \'string\'.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(ofType.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(ofType.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not equal to expected.', async (): Promise => { - chai.doesNotThrow((): void => { - ofType.negated(23)('string'); - }); - }); - - test('throws an error if actual is equal to expected.', async (): Promise => { - chai.throw((): void => { - ofType.negated(23)('number'); - }, 'Expected 23 not to be of type \'number\'.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/sameAsTests.ts b/test/unit/constraints/sameAsTests.ts deleted file mode 100644 index d28c75d..0000000 --- a/test/unit/constraints/sameAsTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { sameAs } from '../../../lib/constraints/sameAs'; - -const chai = chaiStatic.assert; - -suite('sameAs', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(sameAs, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(sameAs(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is same as expected.', async (): Promise => { - chai.doesNotThrow((): void => { - sameAs(23)(23); - }); - }); - - test('throws an error if actual is not same as expected.', async (): Promise => { - chai.throw((): void => { - sameAs(23)(42); - }, 'Expected 23 to be same as 42.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(sameAs.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(sameAs.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not same as expected.', async (): Promise => { - chai.doesNotThrow((): void => { - sameAs.negated(23)(42); - }); - }); - - test('throws an error if actual is same as expected.', async (): Promise => { - chai.throw((): void => { - sameAs.negated(23)(23); - }, 'Expected 23 not to be same as 23.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/sameJsonAsTests.ts b/test/unit/constraints/sameJsonAsTests.ts deleted file mode 100644 index 1d7ac49..0000000 --- a/test/unit/constraints/sameJsonAsTests.ts +++ /dev/null @@ -1,78 +0,0 @@ -import chaiStatic from 'chai'; -import { sameJsonAs } from '../../../lib/constraints/sameJsonAs'; - -const chai = chaiStatic.assert; - -suite('sameJsonAs', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(sameJsonAs, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(sameJsonAs(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is the same JSON as expected.', async (): Promise => { - chai.doesNotThrow((): void => { - sameJsonAs({ foo: 'bar' })({ foo: 'bar' }); - }); - }); - - test('does not throw an error if actual is the same JSON as expected even when functions are used.', async (): Promise => { - chai.doesNotThrow((): void => { - sameJsonAs({ - foo: 'bar', - bar (): void { - // Intentionally left blank. - } - })({ - foo: 'bar' - }); - }); - }); - - test('throws an error if actual is not the same JSON as expected.', async (): Promise => { - chai.throw((): void => { - sameJsonAs({ foo: 'bar' })({ foo: 'baz' }); - }, 'Expected {\n foo: \'bar\'\n} to equal {\n foo: \'baz\'\n}.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(sameJsonAs.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(sameJsonAs.negated(23), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not the same JSON as expected.', async (): Promise => { - chai.doesNotThrow((): void => { - sameJsonAs.negated({ foo: 'bar' })({ foo: 'baz' }); - }); - }); - - test('throws an error if actual is the same JSON as expected.', async (): Promise => { - chai.throw((): void => { - sameJsonAs.negated({ foo: 'bar' })({ foo: 'bar' }); - }, 'Expected {\n foo: \'bar\'\n} not to equal {\n foo: \'bar\'\n}.'); - }); - - test('throws an error if actual is the same JSON as expected even when functions are used.', async (): Promise => { - chai.throw((): void => { - /* eslint-disable no-inline-comments */ - sameJsonAs.negated({ - foo: 'bar', - bar (): void { /* Intentionally left blank. */ } - })({ - foo: 'bar' - }); - /* eslint-enable no-inline-comments */ - }, 'Expected {\n foo: \'bar\',\n bar: bar() { }\n} not to equal {\n foo: \'bar\'\n}.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/startingWithTests.ts b/test/unit/constraints/startingWithTests.ts deleted file mode 100644 index b128fc2..0000000 --- a/test/unit/constraints/startingWithTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { startingWith } from '../../../lib/constraints/startingWith'; - -const chai = chaiStatic.assert; - -suite('startingWith', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(startingWith, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(startingWith('foobar'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual starts with expected.', async (): Promise => { - chai.doesNotThrow((): void => { - startingWith('foobar')('foo'); - }); - }); - - test('throws an error if actual does not start with expected.', async (): Promise => { - chai.throw((): void => { - startingWith('foobar')('bar'); - }, 'Expected \'foobar\' to start with \'bar\'.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(startingWith.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(startingWith.negated('foobar'), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual does not start with expected.', async (): Promise => { - chai.doesNotThrow((): void => { - startingWith.negated('foobar')('bar'); - }); - }); - - test('throws an error if actual starts with expected.', async (): Promise => { - chai.throw((): void => { - startingWith.negated('foobar')('foo'); - }, 'Expected \'foobar\' not to start with \'foo\'.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/throwingAsyncTests.ts b/test/unit/constraints/throwingAsyncTests.ts deleted file mode 100644 index 8c07a5c..0000000 --- a/test/unit/constraints/throwingAsyncTests.ts +++ /dev/null @@ -1,271 +0,0 @@ -import chaiStatic from 'chai'; -import { throwingAsync } from '../../../lib/constraints/throwingAsync'; - -const chai = chaiStatic.assert; - -suite('throwingAsync', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(throwingAsync, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(throwingAsync((): void => { - // Intentionally left blank. - }), 'function'); - }); - - suite('constraint', (): void => { - test('throws an error if actual is not throwing an exception, although one is expected.', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - // Intentionally left blank. - })(); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, 'Expected an exception.'); - } - }); - - test('throws an error if actual is not throwing an exception, although one is expected with message.', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - // Intentionally left blank. - })('Foo failed.'); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, 'Expected an exception with message \'Foo failed.\'.'); - } - }); - - test('does not throw an error if actual is throwing as expected.', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })(); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('does not throw an error if actual is throwing as expected with message.', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })('Foo failed.'); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('does not throw an error if actual is throwing as expected with message (regex).', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })(/Foo/u); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('does not throw an error if actual is throwing as expected with message (function).', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Foo')); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('throws an error if actual is throwing an exception as expected, but with wrong message.', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })('Bar failed.'); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, 'Expected \'Foo failed.\' to equal \'Bar failed.\'.'); - } - }); - - test('throws an error if actual is throwing an exception as expected, but with wrong message (regex).', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })(/Bar/u); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, 'Expected \'Foo failed.\' to equal /Bar/u.'); - } - }); - - test('throws an error if actual is throwing an exception as expected, but with wrong message (function).', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Bar')); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, 'Expected \'Foo failed.\' to fulfill predicate.'); - } - }); - - test('throws an error if actual is throwing an exception as expected, but not with empty message as expected.', async (): Promise => { - try { - await throwingAsync(async (): Promise => { - throw new Error('Foo failed.'); - })(''); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, 'Expected \'Foo failed.\' to equal \'\'.'); - } - }); - - test('correctly types the exception.', async (): Promise => { - class CustomError extends Error { - public code = 'ECODE'; - } - - try { - await throwingAsync(async (): Promise => { - throw new CustomError(); - })((ex): boolean => ex.code === 'ECODE'); - } catch { - throw new Error('Should not have thrown.'); - } - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(throwingAsync.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(throwingAsync.negated((): void => { - // Intentionally left blank. - }), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not throwing at all as expected.', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - // Intentionally left blank. - })(); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('throws an error if actual is throwing although no throwing at all is expected.', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })(); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, `Expected not to throw an exception (received: 'Foo failed.').`); - } - }); - - test('does not throw an error if actual is not throwing and errX is not expected.', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - // Intentionally left blank. - })('Foo failed.'); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('throws an error if actual is throwing errX and errY is not expected.', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })('Foo failed.'); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, `Expected not to throw an exception with message 'Foo failed.'.`); - } - }); - - test('throws an error if actual is throwing errX and errY is not expected (regex).', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })(/Foo/u); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, `Expected not to throw an exception with message /Foo/u.`); - } - }); - - test('throws an error if actual is throwing errX and errY is not expected (function).', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Foo')); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, `Expected 'Foo failed.' not to fulfill predicate.`); - } - }); - - test('does not throw an error if actual is throwing errX and errY is not expected.', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })('Bar failed.'); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('does not throw an error if actual is throwing errX and errY is not expected (regex).', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })(/Bar/u); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('does not throw an error if actual is throwing errX and errY is not expected (function).', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Bar')); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('does not throw an error if actual is throwing errX and there is no errY expected.', async (): Promise => { - try { - await throwingAsync.negated(async (): Promise => { - throw new Error('Foo failed.'); - })(''); - } catch { - throw new Error('Should not have thrown.'); - } - }); - - test('correctly types the exception.', async (): Promise => { - class CustomError extends Error { - public code = 'ECODE'; - } - - try { - await throwingAsync.negated(async (): Promise => { - throw new CustomError('Foo failed.'); - })((ex): boolean => ex.code === 'ECODE'); - throw new Error('Should have thrown.'); - } catch (ex: unknown) { - chai.equal((ex as NodeJS.ErrnoException).message, `Expected 'Foo failed.' not to fulfill predicate.`); - } - }); - }); - }); -}); diff --git a/test/unit/constraints/throwingTests.ts b/test/unit/constraints/throwingTests.ts deleted file mode 100644 index 65644b0..0000000 --- a/test/unit/constraints/throwingTests.ts +++ /dev/null @@ -1,216 +0,0 @@ -import chaiStatic from 'chai'; -import { throwing } from '../../../lib/constraints/throwing'; - -const chai = chaiStatic.assert; - -suite('throwing', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(throwing, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(throwing((): void => { - // Intentionally left blank. - }), 'function'); - }); - - suite('constraint', (): void => { - test('throws an error if actual is not throwing an exception, although one is expected.', async (): Promise => { - chai.throw((): void => { - throwing((): void => { - // Intentionally left blank. - })(); - }, 'Expected an exception.'); - }); - - test('throws an error if actual is not throwing an exception, although one is expected with message.', async (): Promise => { - chai.throw((): void => { - throwing((): void => { - // Intentionally left blank. - })('Foo failed.'); - }, 'Expected an exception with message \'Foo failed.\'.'); - }); - - test('does not throw an error if actual is throwing as expected.', async (): Promise => { - chai.doesNotThrow((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })(); - }); - }); - - test('does not throw an error if actual is throwing as expected with message.', async (): Promise => { - chai.doesNotThrow((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })('Foo failed.'); - }); - }); - - test('does not throw an error if actual is throwing as expected with message (regex).', async (): Promise => { - chai.doesNotThrow((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })(/Foo/u); - }); - }); - - test('does not throw an error if actual is throwing as expected with message (function).', async (): Promise => { - chai.doesNotThrow((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Foo')); - }); - }); - - test('throws an error if actual is throwing an exception as expected, but with wrong message.', async (): Promise => { - chai.throw((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })('Bar failed.'); - }, 'Expected \'Foo failed.\' to equal \'Bar failed.\'.'); - }); - - test('throws an error if actual is throwing an exception as expected, but with wrong message (regex).', async (): Promise => { - chai.throw((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })(/Bar/u); - }, 'Expected \'Foo failed.\' to equal /Bar/u.'); - }); - - test('throws an error if actual is throwing an exception as expected, but with wrong message (function).', async (): Promise => { - chai.throw((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Bar')); - }, 'Expected \'Foo failed.\' to fulfill predicate.'); - }); - - test('throws an error if actual is throwing an exception as expected, but not with empty message as expected.', async (): Promise => { - chai.throw((): void => { - throwing((): void => { - throw new Error('Foo failed.'); - })(''); - }, 'Expected \'Foo failed.\' to equal \'\'.'); - }); - - test('correctly types the exception.', async (): Promise => { - class CustomError extends Error { - public code = 'ECODE'; - } - - chai.doesNotThrow((): void => { - throwing((): void => { - throw new CustomError(); - })((ex): boolean => ex.code === 'ECODE'); - }); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(throwing.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(throwing.negated((): void => { - // Intentionally left blank. - }), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not throwing at all as expected.', async (): Promise => { - chai.doesNotThrow((): void => { - throwing.negated((): void => { - // Intentionally left blank. - })(); - }); - }); - - test('throws an error if actual is throwing although no throwing at all is expected.', async (): Promise => { - chai.throw((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })(); - }); - }); - - test('does not throw an error if actual is not throwing and errX is not expected.', async (): Promise => { - chai.doesNotThrow((): void => { - throwing.negated((): void => { - // Intentionally left blank. - })('Foo failed.'); - }); - }); - - test('throws an error if actual is throwing errX and errY is not expected.', async (): Promise => { - chai.throw((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })('Foo failed.'); - }); - }); - - test('throws an error if actual is throwing errX and errY is not expected (regex).', async (): Promise => { - chai.throw((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })(/Foo/u); - }); - }); - - test('throws an error if actual is throwing errX and errY is not expected (function).', async (): Promise => { - chai.throw((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Foo')); - }); - }); - - test('does not throw an error if actual is throwing errX and errY is not expected.', async (): Promise => { - chai.doesNotThrow((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })('Bar failed.'); - }); - }); - - test('does not throw an error if actual is throwing errX and errY is not expected (regex).', async (): Promise => { - chai.doesNotThrow((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })(/Bar/u); - }); - }); - - test('does not throw an error if actual is throwing errX and errY is not expected (function).', async (): Promise => { - chai.doesNotThrow((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })((ex: Error): boolean => ex.message.includes('Bar')); - }); - }); - - test('does not throw an error if actual is throwing errX and there is no errY expected.', async (): Promise => { - chai.doesNotThrow((): void => { - throwing.negated((): void => { - throw new Error('Foo failed.'); - })(''); - }); - }); - - test('correctly types the exception.', async (): Promise => { - class CustomError extends Error { - public code = 'ECODE'; - } - - chai.throw((): void => { - throwing.negated((): void => { - throw new CustomError(); - })((ex): boolean => ex.code === 'ECODE'); - }); - }); - }); - }); -}); diff --git a/test/unit/constraints/trueTests.ts b/test/unit/constraints/trueTests.ts deleted file mode 100644 index e0e52cc..0000000 --- a/test/unit/constraints/trueTests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chaiStatic from 'chai'; -import { isTrue } from '../../../lib/constraints/true'; - -const chai = chaiStatic.assert; - -suite('true', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isTrue, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isTrue(true), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is true.', async (): Promise => { - chai.doesNotThrow((): void => { - isTrue(true)(); - }); - }); - - test('throws an error if actual is not true.', async (): Promise => { - chai.throw((): void => { - isTrue(false)(); - }, 'Expected false to be true.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isTrue.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - chai.typeOf(isTrue.negated(true), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not true.', async (): Promise => { - chai.doesNotThrow((): void => { - isTrue.negated(false)(); - }); - }); - - test('throws an error if actual is true.', async (): Promise => { - chai.throw((): void => { - isTrue.negated(true)(); - }, 'Expected true not to be true.'); - }); - }); - }); -}); diff --git a/test/unit/constraints/undefinedTests.ts b/test/unit/constraints/undefinedTests.ts deleted file mode 100644 index 84eca6d..0000000 --- a/test/unit/constraints/undefinedTests.ts +++ /dev/null @@ -1,56 +0,0 @@ -import chaiStatic from 'chai'; -import { isUndefined } from '../../../lib/constraints/undefined'; - -const chai = chaiStatic.assert; - -suite('undefined', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isUndefined, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - // eslint-disable-next-line unicorn/no-useless-undefined - chai.typeOf(isUndefined(undefined), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is undefined.', async (): Promise => { - chai.doesNotThrow((): void => { - // eslint-disable-next-line unicorn/no-useless-undefined - isUndefined(undefined)(); - }); - }); - - test('throws an error if actual is not undefined.', async (): Promise => { - chai.throw((): void => { - isUndefined(23)(); - }, 'Expected 23 to be undefined.'); - }); - }); - - suite('negated', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(isUndefined.negated, 'function'); - }); - - test('returns a constraint.', async (): Promise => { - // eslint-disable-next-line unicorn/no-useless-undefined - chai.typeOf(isUndefined.negated(undefined), 'function'); - }); - - suite('constraint', (): void => { - test('does not throw an error if actual is not undefined.', async (): Promise => { - chai.doesNotThrow((): void => { - isUndefined.negated(23)(); - }); - }); - - test('throws an error if actual is undefined.', async (): Promise => { - chai.throw((): void => { - // eslint-disable-next-line unicorn/no-useless-undefined - isUndefined.negated(undefined)(); - }, 'Expected undefined not to be undefined.'); - }); - }); - }); -}); diff --git a/test/unit/dispel/dispelArrayTests.ts b/test/unit/dispel/dispelArrayTests.ts new file mode 100644 index 0000000..450117a --- /dev/null +++ b/test/unit/dispel/dispelArrayTests.ts @@ -0,0 +1,53 @@ +import { assert } from '../../../lib'; +import { dispelArray } from '../../../lib/dispel/dispelArray'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('dispelArray', (): void => { + test('returns the array unchanged if no ancestors are given and no recursions are contained.', async (): Promise => { + const array = [ 1, 2, 3, [ 4, 5 ]]; + + const dispelledArray = dispelArray(array); + + assert.that(dispelledArray).is.equalTo(array); + }); + + test('returns a recursion object if the array is contained in the ancestors.', async (): Promise => { + const array = [ 1, 2, 3, [ 4, 5 ]]; + const path = '/0/'; + + const dispelledArray = dispelArray(array, path, [{ reference: array, path: '/' }]); + + assert.that(dispelledArray).is.equalTo( + recursion({ + recursionPath: '/' + }) + ); + }); + + test('replaces a contained value with a recursion object if it is contained in the ancestors.', async (): Promise => { + const array: any[] = [[ 1, 2, 3 ]]; + const path = '/0/'; + + array[0].push(array); + + const dispelledArray = dispelArray(array[0], path, [{ reference: array, path: '/' }]); + + assert.that(dispelledArray).is.equalTo( + [ 1, 2, 3, recursion({ recursionPath: '/' }) ] + ); + }); + + test('builds paths by using the array index as path segment.', async (): Promise => { + const array: any[] = [ 1, 2, [ 3, [[]]]]; + + array[2][1][0].push(array[2][1]); + + const dispelledArray = dispelArray(array) as any[]; + + assert.that(dispelledArray[2][1][0][0]).is.equalTo( + recursion({ + recursionPath: '/2/1/' + }) + ); + }); +}); diff --git a/test/unit/dispel/dispelMapTests.ts b/test/unit/dispel/dispelMapTests.ts new file mode 100644 index 0000000..2150948 --- /dev/null +++ b/test/unit/dispel/dispelMapTests.ts @@ -0,0 +1,39 @@ +import { assert } from '../../../lib'; +import { dispelMap } from '../../../lib/dispel/dispelMap'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('dispelMap', (): void => { + test('returns the map unchanged if no ancestors are given and no recursions are contained.', async (): Promise => { + const map = new Map([[ 'foo', 5 ], [ 'bar', 6 ]]); + + const dispelledMap = dispelMap(map); + + assert.that(dispelledMap).is.equalTo(map); + }); + + test('returns a recursion object if the map is contained in the ancestors.', async (): Promise => { + const map = new Map([[ 'foo', 5 ], [ 'bar', 6 ]]); + const path = '/0/'; + + const dispelledMap = dispelMap(map, path, [{ reference: map, path: '/' }]); + + assert.that(dispelledMap).is.equalTo( + recursion({ + recursionPath: '/' + }) + ); + }); + + test('replaces a contained value with a recursion object if it is contained in the ancestors.', async (): Promise => { + const outer: any[] = [ new Map([[ 'foo', 5 ]]) ]; + const path = '/0/'; + + outer[0].set('bar', outer); + + const dispelledMap = dispelMap(outer[0], path, [{ reference: outer, path: '/' }]); + + assert.that(dispelledMap).is.equalTo( + new Map([[ 'foo', 5 ], [ 'bar', recursion({ recursionPath: '/' }) ]]) + ); + }); +}); diff --git a/test/unit/dispel/dispelObjectTests.ts b/test/unit/dispel/dispelObjectTests.ts new file mode 100644 index 0000000..cda7c79 --- /dev/null +++ b/test/unit/dispel/dispelObjectTests.ts @@ -0,0 +1,53 @@ +import { assert } from '../../../lib'; +import { dispelObject } from '../../../lib/dispel/dispelObject'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('dispelObject', (): void => { + test('returns the object unchanged if no ancestors are given and no recursions are contained.', async (): Promise => { + const object = { foo: 5, bar: 7 }; + + const dispelledObject = dispelObject(object); + + assert.that(dispelledObject).is.equalTo(object); + }); + + test('returns a recursion object if the object is contained in the ancestors.', async (): Promise => { + const object = { foo: 5, bar: 7 }; + const path = '/0/'; + + const dispelledObject = dispelObject(object, path, [{ reference: object, path: '/' }]); + + assert.that(dispelledObject).is.equalTo( + recursion({ + recursionPath: '/' + }) + ); + }); + + test('replaces a contained value with a recursion object if it is contained in the ancestors.', async (): Promise => { + const object: any[] = [{ foo: 5, bar: 7 }]; + const path = '/0/'; + + object[0].heck = object; + + const dispelledObject = dispelObject(object[0], path, [{ reference: object, path: '/' }]); + + assert.that(dispelledObject).is.equalTo( + { foo: 5, bar: 7, heck: recursion({ recursionPath: '/' }) } + ); + }); + + test('builds paths by using the object index as path segment.', async (): Promise => { + const object: any = { foo: { bar: { heck: {}}}}; + + object.foo.bar.heck.what = object.foo.bar.heck; + + const dispelledObject = dispelObject(object) as any; + + assert.that(dispelledObject.foo.bar.heck.what).is.equalTo( + recursion({ + recursionPath: '/foo/bar/heck/' + }) + ); + }); +}); diff --git a/test/unit/dispel/dispelResultTests.ts b/test/unit/dispel/dispelResultTests.ts new file mode 100644 index 0000000..ee4979c --- /dev/null +++ b/test/unit/dispel/dispelResultTests.ts @@ -0,0 +1,68 @@ +import { assert } from '../../../lib'; +import { dispelResult } from '../../../lib/dispel/dispelResult'; +import { recursion } from '../../../lib/types/Recursion'; +import { error, Result, value } from 'defekt'; + +suite('dispelResult', (): void => { + test('returns the result unchanged if no ancestors are given and no recursions are contained.', async (): Promise => { + const result = value([]); + + const dispelledResult = dispelResult(result); + + assert.that(dispelledResult).is.equalTo(result); + }); + + test('returns a recursion object if the result is contained in the ancestors.', async (): Promise => { + const result = value([]); + const path = '/0/'; + + const dispelledResult = dispelResult(result, path, [{ reference: result, path: '/' }]); + + assert.that(dispelledResult).is.equalTo( + recursion({ + recursionPath: '/' + }) + ); + }); + + test('replaces a contained value with a recursion object if it is contained in the ancestors.', async (): Promise => { + const result: any[] = [ value([]) ]; + const path = '/0/'; + + result[0].value.push(result); + + const dispelledResult = dispelResult(result[0], path, [{ reference: result, path: '/' }]); + + assert.that(dispelledResult).is.equalTo( + value([ recursion({ recursionPath: '/' }) ]) + ); + }); + + test(`builds paths through value results with the path segment 'value'.`, async (): Promise => { + const result: Result = value([[[]]]); + + result.value[0][0].push(result.value[0]); + + const dispelledResult = dispelResult(result) as any; + + assert.that(dispelledResult.value[0][0][0]).is.equalTo( + recursion({ + recursionPath: '/value/0/' + }) + ); + }); + + test(`builds paths through error results with the path segment 'error'.`, async (): Promise => { + const result: Result = error([[[]]] as any); + + result.error[0][0].push(result.error[0]); + + const dispelledResult = dispelResult(result) as any; + + assert.that(dispelledResult.error[0][0][0]).is.equalTo( + recursion({ + recursionPath: '/error/0/' + }) + ); + }); +}); diff --git a/test/unit/dispel/dispelSetTests.ts b/test/unit/dispel/dispelSetTests.ts new file mode 100644 index 0000000..38c546c --- /dev/null +++ b/test/unit/dispel/dispelSetTests.ts @@ -0,0 +1,39 @@ +import { assert } from '../../../lib'; +import { dispelSet } from '../../../lib/dispel/dispelSet'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('dispelSet', (): void => { + test('returns the set unchanged if no ancestors are given and no recursions are contained.', async (): Promise => { + const set = new Set([ 'foo', 'bar' ]); + + const dispelledSet = dispelSet(set); + + assert.that(dispelledSet).is.equalTo(set); + }); + + test('returns a recursion object if the set is contained in the ancestors.', async (): Promise => { + const set = new Set([ 'foo', 'bar' ]); + const path = '/0/'; + + const dispelledSet = dispelSet(set, path, [{ reference: set, path: '/' }]); + + assert.that(dispelledSet).is.equalTo( + recursion({ + recursionPath: '/' + }) + ); + }); + + test('replaces a contained value with a recursion object if it is contained in the ancestors.', async (): Promise => { + const outer: any[] = [ new Set([ 'foo' ]) ]; + const path = '/0/'; + + outer[0].add(outer); + + const dispelledSet = dispelSet(outer[0], path, [{ reference: outer, path: '/' }]); + + assert.that(dispelledSet).is.equalTo( + new Set([ 'foo', recursion({ recursionPath: '/' }) ]) + ); + }); +}); diff --git a/test/unit/failTests.ts b/test/unit/failTests.ts deleted file mode 100644 index 5f5b378..0000000 --- a/test/unit/failTests.ts +++ /dev/null @@ -1,16 +0,0 @@ -import chaiStatic from 'chai'; -import { fail } from '../../lib/fail'; - -const chai = chaiStatic.assert; - -suite('fail', (): void => { - test('is a function.', async (): Promise => { - chai.typeOf(fail, 'function'); - }); - - test('throws an error with the given values.', async (): Promise => { - chai.throw((): void => { - fail('Expected %s to equal %s.', [ 23, 42 ]); - }, 'Expected 23 to equal 42.'); - }); -}); diff --git a/test/unit/humanReadableTests.ts b/test/unit/humanReadableTests.ts deleted file mode 100644 index 0d9b76a..0000000 --- a/test/unit/humanReadableTests.ts +++ /dev/null @@ -1,51 +0,0 @@ -import chaiStatic from 'chai'; -import { humanReadable } from '../../lib/humanReadable'; - -const chai = chaiStatic.assert; - -suite('humanReadable', (): void => { - test('returns the stringified value when given a number.', async (): Promise => { - chai.equal(humanReadable(23), '23'); - }); - - test('returns the stringified value when given a boolean.', async (): Promise => { - chai.equal(humanReadable(false), 'false'); - }); - - test('returns the stringified value when given undefined.', async (): Promise => { - // eslint-disable-next-line unicorn/no-useless-undefined - chai.equal(humanReadable(undefined), 'undefined'); - }); - - test('returns the stringified value when given null.', async (): Promise => { - chai.equal(humanReadable(null), 'null'); - }); - - test('returns a quoted value when given a string.', async (): Promise => { - chai.equal(humanReadable('foo'), '\'foo\''); - }); - - test('returns a formatted value when given an object.', async (): Promise => { - chai.equal(humanReadable({ - foo: 'bar' - }), '{\n foo: \'bar\'\n}'); - }); - - test('returns a formatted value when given a regular expression.', async (): Promise => { - chai.equal(humanReadable(/foo/u), '/foo/u'); - }); - - test('returns a formatted value when given a function.', async (): Promise => { - /* eslint-disable prefer-arrow-callback */ - chai.equal(humanReadable(function Foo (): void { - // Intentionally left empty. - }), 'Foo'); - /* eslint-enable prefer-arrow-callback */ - }); - - test('returns a formatted value when given an anonymous function.', async (): Promise => { - chai.equal(humanReadable((): void => { - // Intentionally left empty. - }), '(anonymous)'); - }); -}); diff --git a/test/unit/prettyPrint/utils/formatNestedArrayTests.ts b/test/unit/prettyPrint/utils/formatNestedArrayTests.ts new file mode 100644 index 0000000..caf5937 --- /dev/null +++ b/test/unit/prettyPrint/utils/formatNestedArrayTests.ts @@ -0,0 +1,29 @@ +import { assert } from '../../../../lib'; +import { formatNestedArray } from '../../../../lib/prettyPrint/utils/formatNestedArray'; + +suite('formatNestedArray', (): void => { + test(`formats a nested array by putting each leaf on a line, separating the outer array's items with commas.`, async (): Promise => { + const parts = [ + [ 'foo', 'bar' ], + [ 'bam', 'baz' ], + [ 'bar' ] + ]; + const result = formatNestedArray` + Even with indentation: + ${parts} + `; + + assert.that(result).is.equalTo('Even with indentation:\n foo\n bar,\n bam\n baz,\n bar'); + }); + + test(`formats a nested array by appending the leafs, if the substitution is not indented.`, async (): Promise => { + const parts = [ + [ 'foo', 'bar' ], + [ 'bam', 'baz' ], + [ 'bar' ] + ]; + const result = formatNestedArray`No indentation: ${parts}`; + + assert.that(result).is.equalTo('No indentation: foo bar, bam baz, bar'); + }); +}); diff --git a/test/unit/types/RecursionTests.ts b/test/unit/types/RecursionTests.ts new file mode 100644 index 0000000..759942b --- /dev/null +++ b/test/unit/types/RecursionTests.ts @@ -0,0 +1,35 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isRecursion, recursion } from '../../../lib/types/Recursion'; + +suite('Recursion', (): void => { + suite('isRecursion', (): void => { + for (const [ value, type ] of [ + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isRecursion(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isRecursion(value)).is.false(); + }); + } + }); +}); diff --git a/test/unit/types/isArrayTests.ts b/test/unit/types/isArrayTests.ts new file mode 100644 index 0000000..efbe05a --- /dev/null +++ b/test/unit/types/isArrayTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isArray } from '../../../lib/types/isArray'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isArray', (): void => { + for (const [ value, type ] of [ + [[], 'array' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isArray(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isArray(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isBooleanTests.ts b/test/unit/types/isBooleanTests.ts new file mode 100644 index 0000000..2ce22d8 --- /dev/null +++ b/test/unit/types/isBooleanTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isBoolean } from '../../../lib/types/isBoolean'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isBoolean', (): void => { + for (const [ value, type ] of [ + [ false, 'boolean' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isBoolean(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isBoolean(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isErrorTests.ts b/test/unit/types/isErrorTests.ts new file mode 100644 index 0000000..3e21491 --- /dev/null +++ b/test/unit/types/isErrorTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isError } from '../../../lib/types/isError'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isError', (): void => { + for (const [ value, type ] of [ + [ new Error('foo'), 'error' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isError(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isError(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isFunctionTests.ts b/test/unit/types/isFunctionTests.ts new file mode 100644 index 0000000..5034e3a --- /dev/null +++ b/test/unit/types/isFunctionTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isFunction } from '../../../lib/types/isFunction'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isFunction', (): void => { + for (const [ value, type ] of [ + [ (): string => 'foo', 'function' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isFunction(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ new Error('foo'), 'error' ], + [ false, 'boolean' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isFunction(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isMapTests.ts b/test/unit/types/isMapTests.ts new file mode 100644 index 0000000..ced233e --- /dev/null +++ b/test/unit/types/isMapTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isMap } from '../../../lib/types/isMap'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isMap', (): void => { + for (const [ value, type ] of [ + [ new Map(), 'map' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isMap(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isMap(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isNullTests.ts b/test/unit/types/isNullTests.ts new file mode 100644 index 0000000..ef8931b --- /dev/null +++ b/test/unit/types/isNullTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isNull } from '../../../lib/types/isNull'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isNull', (): void => { + for (const [ value, type ] of [ + [ null, 'null' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isNull(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isNull(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isNumberTests.ts b/test/unit/types/isNumberTests.ts new file mode 100644 index 0000000..325b698 --- /dev/null +++ b/test/unit/types/isNumberTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isNumber } from '../../../lib/types/isNumber'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isNumber', (): void => { + for (const [ value, type ] of [ + [ 5, 'number' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isNumber(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isNumber(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isObjectTests.ts b/test/unit/types/isObjectTests.ts new file mode 100644 index 0000000..20899f1 --- /dev/null +++ b/test/unit/types/isObjectTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isObject } from '../../../lib/types/isObject'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isObject', (): void => { + for (const [ value, type ] of [ + [[], 'array' ], + [ new Error('foo'), 'error' ], + [ new Map(), 'map' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isObject(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [ false, 'boolean' ], + [ (): string => 'foo', 'function' ], + [ null, 'null' ], + [ 5, 'number' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isObject(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isScalarTests.ts b/test/unit/types/isScalarTests.ts new file mode 100644 index 0000000..a316be7 --- /dev/null +++ b/test/unit/types/isScalarTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isScalar } from '../../../lib/types/isScalar'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isScalar', (): void => { + for (const [ value, type ] of [ + [ false, 'boolean' ], + [ null, 'null' ], + [ 5, 'number' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isScalar(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isScalar(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isSetTests.ts b/test/unit/types/isSetTests.ts new file mode 100644 index 0000000..35cb5bd --- /dev/null +++ b/test/unit/types/isSetTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isSet } from '../../../lib/types/isSet'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isSet', (): void => { + for (const [ value, type ] of [ + [ new Set(), 'set' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isSet(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isSet(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isStringTests.ts b/test/unit/types/isStringTests.ts new file mode 100644 index 0000000..1be25b9 --- /dev/null +++ b/test/unit/types/isStringTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isString } from '../../../lib/types/isString'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isString', (): void => { + for (const [ value, type ] of [ + [ 'foo', 'string' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isString(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ Symbol('foo'), 'symbol' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isString(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isSymbolTests.ts b/test/unit/types/isSymbolTests.ts new file mode 100644 index 0000000..1c5b39e --- /dev/null +++ b/test/unit/types/isSymbolTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isSymbol } from '../../../lib/types/isSymbol'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isSymbol', (): void => { + for (const [ value, type ] of [ + [ Symbol('foo'), 'symbol' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isSymbol(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ undefined, 'undefined' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isSymbol(value)).is.false(); + }); + } +}); diff --git a/test/unit/types/isUndefinedTests.ts b/test/unit/types/isUndefinedTests.ts new file mode 100644 index 0000000..4f4f90c --- /dev/null +++ b/test/unit/types/isUndefinedTests.ts @@ -0,0 +1,34 @@ +import { assert } from '../../../lib/assertthat'; +import { error } from 'defekt'; +import { isUndefined } from '../../../lib/types/isUndefined'; +import { recursion } from '../../../lib/types/Recursion'; + +suite('isUndefined', (): void => { + for (const [ value, type ] of [ + [ undefined, 'undefined' ] + ] as const) { + test(`returns true if given a(n) ${type}.`, async (): Promise => { + assert.that(isUndefined(value)).is.true(); + }); + } + + for (const [ value, type ] of [ + [[], 'array' ], + [ false, 'boolean' ], + [ new Error('foo'), 'error' ], + [ (): string => 'foo', 'function' ], + [ new Map(), 'map' ], + [ null, 'null' ], + [ 5, 'number' ], + [{}, 'object' ], + [ error(new Error('foo')), 'Result' ], + [ new Set(), 'set' ], + [ 'foo', 'string' ], + [ Symbol('foo'), 'symbol' ], + [ recursion({ recursionPath: 'foo.bar' }), 'recursion' ] + ] as const) { + test(`returns false if given a(n) ${type}.`, async (): Promise => { + assert.that(isUndefined(value)).is.false(); + }); + } +});