Skip to content

Commit

Permalink
feat: Expose default deepEquals matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
NiGhTTraX committed Sep 26, 2021
1 parent 2029128 commit 7d1d015
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 61 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ console.log(instance(fn)(
```

Available matchers:
- `deepEquals` - the default, uses deep equality,
- `is` - uses `Object.is` for comparison,
- `isAny` - matches anything,
- `isNumber` - matches any number,
Expand Down
80 changes: 44 additions & 36 deletions src/expectation/matcher.spec.ts
Original file line number Diff line number Diff line change
@@ -1,101 +1,109 @@
import { expect } from 'tdd-buffet/expect/jest';
import { describe, it } from 'tdd-buffet/suite/node';
import { deepEquals, It } from './matcher';
import { It } from './matcher';
import { expectAnsilessEqual } from '../../tests/ansiless';

describe('It', () => {
describe('deepEquals', () => {
it('should match primitives', () => {
expect(deepEquals(1).matches(1)).toBeTruthy();
expect(deepEquals(1).matches(2)).toBeFalsy();
expect(It.deepEquals(1).matches(1)).toBeTruthy();
expect(It.deepEquals(1).matches(2)).toBeFalsy();

expect(deepEquals(1.0).matches(1.0)).toBeTruthy();
expect(deepEquals(1.0).matches(1.1)).toBeFalsy();
expect(It.deepEquals(1.0).matches(1.0)).toBeTruthy();
expect(It.deepEquals(1.0).matches(1.1)).toBeFalsy();

expect(deepEquals(true).matches(true)).toBeTruthy();
expect(deepEquals(true).matches(false)).toBeFalsy();
expect(It.deepEquals(true).matches(true)).toBeTruthy();
expect(It.deepEquals(true).matches(false)).toBeFalsy();

expect(deepEquals('a').matches('a')).toBeTruthy();
expect(deepEquals('a').matches('b')).toBeFalsy();
expect(It.deepEquals('a').matches('a')).toBeTruthy();
expect(It.deepEquals('a').matches('b')).toBeFalsy();
});

it('should match arrays', () => {
expect(deepEquals([1, 2, 3]).matches([1, 2, 3])).toBeTruthy();
expect(deepEquals([1, 2, 3]).matches([1, 2, 4])).toBeFalsy();
expect(deepEquals([1, 2, 3]).matches([2, 3])).toBeFalsy();
expect(It.deepEquals([1, 2, 3]).matches([1, 2, 3])).toBeTruthy();
expect(It.deepEquals([1, 2, 3]).matches([1, 2, 4])).toBeFalsy();
expect(It.deepEquals([1, 2, 3]).matches([2, 3])).toBeFalsy();
});

it('should match objects', () => {
expect(deepEquals({ foo: 'bar' }).matches({ foo: 'bar' })).toBeTruthy();
expect(deepEquals({ foo: 'bar' }).matches({ foo: 'baz' })).toBeFalsy();
expect(deepEquals({ foo: 'bar' }).matches({})).toBeFalsy();
expect(deepEquals({}).matches({ foo: 'bar' })).toBeFalsy();
expect(
It.deepEquals({ foo: 'bar' }).matches({ foo: 'bar' })
).toBeTruthy();
expect(It.deepEquals({ foo: 'bar' }).matches({ foo: 'baz' })).toBeFalsy();
expect(It.deepEquals({ foo: 'bar' }).matches({})).toBeFalsy();
expect(It.deepEquals({}).matches({ foo: 'bar' })).toBeFalsy();
});

it('should match nested objects', () => {
expect(
deepEquals({ foo: { bar: 'baz' } }).matches({ foo: { bar: 'baz' } })
It.deepEquals({ foo: { bar: 'baz' } }).matches({ foo: { bar: 'baz' } })
).toBeTruthy();
expect(
deepEquals({ foo: { bar: 'baz' } }).matches({ foo: { bar: 'boo' } })
It.deepEquals({ foo: { bar: 'baz' } }).matches({ foo: { bar: 'boo' } })
).toBeFalsy();
});

it('should not match objects with missing optional keys', () => {
expect(deepEquals({}).matches({ key: undefined })).toBeFalsy();
expect(deepEquals({ key: undefined }).matches({})).toBeFalsy();
expect(It.deepEquals({}).matches({ key: undefined })).toBeFalsy();
expect(It.deepEquals({ key: undefined }).matches({})).toBeFalsy();
});

it('should match objects with symbol keys', () => {
const foo = Symbol('foo');

expect(deepEquals({ [foo]: true }).matches({ [foo]: true })).toBeTruthy();
expect(deepEquals({ [foo]: true }).matches({ [foo]: false })).toBeFalsy();
expect(
It.deepEquals({ [foo]: true }).matches({ [foo]: true })
).toBeTruthy();
expect(
It.deepEquals({ [foo]: true }).matches({ [foo]: false })
).toBeFalsy();

expect(deepEquals({ [foo]: true }).matches({})).toBeFalsy();
expect(deepEquals({}).matches({ [foo]: false })).toBeFalsy();
expect(It.deepEquals({ [foo]: true }).matches({})).toBeFalsy();
expect(It.deepEquals({}).matches({ [foo]: false })).toBeFalsy();
});

it('should match sets', () => {
expect(
deepEquals(new Set([1, 2, 3])).matches(new Set([1, 2, 3]))
It.deepEquals(new Set([1, 2, 3])).matches(new Set([1, 2, 3]))
).toBeTruthy();
expect(
deepEquals(new Set([1, 2, 3])).matches(new Set([2, 3]))
It.deepEquals(new Set([1, 2, 3])).matches(new Set([2, 3]))
).toBeFalsy();
expect(
deepEquals(new Set([1, 2, 3])).matches(new Set([1, 2, 4]))
It.deepEquals(new Set([1, 2, 3])).matches(new Set([1, 2, 4]))
).toBeFalsy();
});

it('should match maps', () => {
expect(
deepEquals(new Map([[1, 2]])).matches(new Map([[1, 2]]))
It.deepEquals(new Map([[1, 2]])).matches(new Map([[1, 2]]))
).toBeTruthy();
expect(
deepEquals(new Map([[1, 2]])).matches(new Map([[1, 3]]))
It.deepEquals(new Map([[1, 2]])).matches(new Map([[1, 3]]))
).toBeFalsy();
expect(deepEquals(new Map([[1, 2]])).matches(new Map([]))).toBeFalsy();
expect(It.deepEquals(new Map([[1, 2]])).matches(new Map([]))).toBeFalsy();
});

it('should match dates', () => {
expect(deepEquals(new Date(1000)).matches(new Date(1000))).toBeTruthy();
expect(deepEquals(new Date(1000)).matches(new Date(1001))).toBeFalsy();
expect(
It.deepEquals(new Date(1000)).matches(new Date(1000))
).toBeTruthy();
expect(It.deepEquals(new Date(1000)).matches(new Date(1001))).toBeFalsy();
});

it('should match buffers', () => {
expect(
deepEquals(Buffer.from('abc')).matches(Buffer.from('abc'))
It.deepEquals(Buffer.from('abc')).matches(Buffer.from('abc'))
).toBeTruthy();
expect(
deepEquals(Buffer.from('abc')).matches(Buffer.from('abd'))
It.deepEquals(Buffer.from('abc')).matches(Buffer.from('abd'))
).toBeFalsy();
});

it('should pretty print', () => {
expectAnsilessEqual(deepEquals(23).toJSON(), '23');
expectAnsilessEqual(It.deepEquals(23).toJSON(), '23');
expectAnsilessEqual(
deepEquals({ foo: { bar: [1, 2, 3] } }).toJSON(),
It.deepEquals({ foo: { bar: [1, 2, 3] } }).toJSON(),
'{"foo": {"bar": [1, 2, 3]}}'
);
});
Expand Down
33 changes: 19 additions & 14 deletions src/expectation/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,29 @@ const matches = <T>(
};

/**
* The default matcher that checks for deep equality.
* Compare values using deep equality.
*
* @see It.is A matcher that uses strict equality.
*/
export const deepEquals = <T>(expected: T): TypeMatcher<T> =>
const deepEquals = <T>(expected: T): TypeMatcher<T> =>
matches(
(actual) => isEqual(actual, expected),
() => printArg(expected)
);

/**
* Compare values using `Object.is`.
*
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*
* @see It.deepEquals A matcher that uses deep equality.
*/
const is = <T = unknown>(expected: T): TypeMatcher<T> =>
matches(
(actual) => Object.is(actual, expected),
() => `${printExpected(expected)}`
);

/**
* Match any value, including `undefined` and `null`.
*
Expand Down Expand Up @@ -261,25 +276,15 @@ const willCapture = <T = unknown>(
return matcher as any;
};

/**
* Compare values using `Object.is`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
const is = <T = unknown>(expected: T): TypeMatcher<T> =>
matches(
(actual) => Object.is(actual, expected),
() => `${printExpected(expected)}`
);

/**
* Contains argument matchers that can be used to ignore arguments in an
* expectation or to match complex arguments.
*/
export const It = {
matches,
deepEquals,
is,
isAny,
matches,
isObject,
isNumber,
isString,
Expand Down
26 changes: 17 additions & 9 deletions src/expectation/strong-expectation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from 'tdd-buffet/expect/jest';
import { describe, it } from 'tdd-buffet/suite/node';
import { expectAnsilessEqual } from '../../tests/ansiless';
import { deepEquals } from './matcher';
import { It } from './matcher';
import { StrongExpectation } from './strong-expectation';

describe('StrongExpectation', () => {
Expand All @@ -21,9 +21,13 @@ describe('StrongExpectation', () => {
});

it('should match optional args against undefined', () => {
const expectation = new StrongExpectation('bar', [deepEquals(undefined)], {
value: 23,
});
const expectation = new StrongExpectation(
'bar',
[It.deepEquals(undefined)],
{
value: 23,
}
);

expect(expectation.matches([])).toBeTruthy();
});
Expand All @@ -35,25 +39,29 @@ describe('StrongExpectation', () => {
});

it('should not match missing expected optional arg', () => {
const expectation = new StrongExpectation('bar', [deepEquals(23)], {
const expectation = new StrongExpectation('bar', [It.deepEquals(23)], {
value: 23,
});

expect(expectation.matches([])).toBeFalsy();
});

it('should not match defined expected undefined optional arg', () => {
const expectation = new StrongExpectation('bar', [deepEquals(undefined)], {
value: 23,
});
const expectation = new StrongExpectation(
'bar',
[It.deepEquals(undefined)],
{
value: 23,
}
);

expect(expectation.matches([42])).toBeFalsy();
});

it('should print when, returns and invocation count', () => {
const expectation = new StrongExpectation(
'baz',
[deepEquals(4), deepEquals(5), deepEquals(6)],
[It.deepEquals(4), It.deepEquals(5), It.deepEquals(6)],
{
value: 42,
}
Expand Down
4 changes: 2 additions & 2 deletions src/mock/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { deepEquals, Matcher } from '../expectation/matcher';
import { It, Matcher } from '../expectation/matcher';

export type StrongMockDefaults = {
/**
Expand All @@ -19,7 +19,7 @@ export type StrongMockDefaults = {
};

const defaults: StrongMockDefaults = {
matcher: deepEquals,
matcher: It.deepEquals,
};

export let currentDefaults: StrongMockDefaults = defaults;
Expand Down

0 comments on commit 7d1d015

Please sign in to comment.