Skip to content

Commit

Permalink
feat: Rewrite - type-appropriate assertions and better diffs. (#341)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

Many assertions are now only usable with types they make sense for. This should probably not break much code, but also probably will. Not entirely sure about the repercussions these changes will have.

Some rarely used assertions have been removed (e.g. between).
Some have been added: Support for Results, Maps, Sets and Errors.
Recursions in asserted values are now recognized and in most cases even correctly compared (excluding maps and sets).

Error messages are now much more informational and dare I say beautiful, since the new implementation is based on an in-depth diffing algorithm based on value equality.
  • Loading branch information
Hannes Leutloff authored May 6, 2021
1 parent 5557f62 commit f6c430f
Show file tree
Hide file tree
Showing 407 changed files with 13,693 additions and 6,074 deletions.
305 changes: 170 additions & 135 deletions README.md

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions lib/assertions/forAny/AssertThatForAny.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CommonAssertions } from './CommonAssertions';

type AssertThatForAny = <TAny>(actual: TAny) => {
is: CommonAssertions<TAny> & {
not: CommonAssertions<TAny>;
};
};

export type {
AssertThatForAny
};
21 changes: 21 additions & 0 deletions lib/assertions/forAny/CommonAssertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Type } from '../../types/Type';

interface CommonAssertions<TAny> {
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
};
29 changes: 29 additions & 0 deletions lib/assertions/forAny/assertActualIsEqualToExpected.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
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
};
21 changes: 21 additions & 0 deletions lib/assertions/forAny/assertActualIsFalse.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
const diff = compare(actual, false);

if (diff.cost === 0) {
return value();
}

return error(new AssertionFailed({
message: 'The value is not false.'
}));
};

export {
assertActualIsFalse
};
20 changes: 20 additions & 0 deletions lib/assertions/forAny/assertActualIsFalsy.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
if (!actual) {
return value();
}

return error(new AssertionFailed({
message: 'The value is not falsy.',
actual: prettyPrint(actual)
}));
};

export {
assertActualIsFalsy
};
19 changes: 19 additions & 0 deletions lib/assertions/forAny/assertActualIsIdenticalToExpected.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AssertionFailed } from '../../errors';
import { error, Result, value } from 'defekt';

const assertActualIsIdenticalToExpected = function (
actual: any,
expected: any
): Result<undefined, AssertionFailed> {
if (actual === expected) {
return value();
}

return error(new AssertionFailed({
message: 'The values are not identical.'
}));
};

export {
assertActualIsIdenticalToExpected
};
26 changes: 26 additions & 0 deletions lib/assertions/forAny/assertActualIsNotEqualToExpected.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
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
};
21 changes: 21 additions & 0 deletions lib/assertions/forAny/assertActualIsNotFalse.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
const diff = compare(actual, true);

if (diff.cost === 0) {
return value();
}

return error(new AssertionFailed({
message: 'The value is false.'
}));
};

export {
assertActualIsNotFalse
};
20 changes: 20 additions & 0 deletions lib/assertions/forAny/assertActualIsNotFalsy.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
if (actual) {
return value();
}

return error(new AssertionFailed({
message: 'The value is falsy.',
actual: prettyPrint(actual)
}));
};

export {
assertActualIsNotFalsy
};
19 changes: 19 additions & 0 deletions lib/assertions/forAny/assertActualIsNotIdenticalToExpected.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AssertionFailed } from '../../errors';
import { error, Result, value } from 'defekt';

const assertActualIsNotIdenticalToExpected = function (
actual: any,
expected: any
): Result<undefined, AssertionFailed> {
if (actual !== expected) {
return value();
}

return error(new AssertionFailed({
message: 'The values are identical.'
}));
};

export {
assertActualIsNotIdenticalToExpected
};
18 changes: 18 additions & 0 deletions lib/assertions/forAny/assertActualIsNotNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AssertionFailed } from '../../errors';
import { error, Result, value } from 'defekt';

const assertActualIsNotNull = function (
actual: any
): Result<undefined, AssertionFailed> {
if (actual !== null) {
return value();
}

return error(new AssertionFailed({
message: 'The value is null.'
}));
};

export {
assertActualIsNotNull
};
149 changes: 149 additions & 0 deletions lib/assertions/forAny/assertActualIsNotOfType.ts
Original file line number Diff line number Diff line change
@@ -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<undefined, AssertionFailed> {
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
};
Loading

0 comments on commit f6c430f

Please sign in to comment.