From 4fc75cab45ce2e12c96025550c253c8dd168158d Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 15 Aug 2023 17:55:33 -0500 Subject: [PATCH 1/5] start implementing `t.unorderedEqual()` --- docs/03-assertions.md | 4 ++ lib/assert.js | 96 +++++++++++++++++++++++++ lib/unordered-equal.js | 22 ++++++ test-tap/assert.js | 95 ++++++++++++++++++++++++ test-tap/test.js | 11 +-- test/assertions/fixtures/happy-path.js | 1 + test/assertions/snapshots/test.js.md | 1 + test/assertions/snapshots/test.js.snap | Bin 627 -> 661 bytes types/assertions.d.cts | 15 ++++ 9 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 lib/unordered-equal.js diff --git a/docs/03-assertions.md b/docs/03-assertions.md index cb04dcfdb..693775698 100644 --- a/docs/03-assertions.md +++ b/docs/03-assertions.md @@ -137,6 +137,10 @@ Assert that `actual` is deeply equal to `expected`. See [Concordance](https://gi Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqual()`. Returns a boolean indicating whether the assertion passed. +### `.unorderedEqual(actual, expected, message?)` + +Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. + ### `.like(actual, selector, message?)` Assert that `actual` is like `selector`. This is a variant of `.deepEqual()`, however `selector` does not need to have the same enumerable properties as `actual` does. diff --git a/lib/assert.js b/lib/assert.js index a1cd1c661..3890f9dde 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -6,6 +6,7 @@ import isPromise from 'is-promise'; import concordanceOptions from './concordance-options.js'; import {CIRCULAR_SELECTOR, isLikeSelector, selectComparable} from './like-selector.js'; import {SnapshotError, VersionMismatchError} from './snapshot-manager.js'; +import {checkValueForUnorderedEqual} from './unordered-equal.js'; function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) { options = {...options, ...concordanceOptions}; @@ -958,5 +959,100 @@ export class Assertions { pass(); return true; }); + + this.unorderedEqual = withSkip((actual, expected, message) => { + if (!checkMessage('unorderedEqual', message)) { + return false; + } + + const actualInfo = checkValueForUnorderedEqual(actual); + + if (!actualInfo.isValid) { + fail(new AssertionError({ + assertion: 'unorderedEqual', + improperUsage: true, + message: '`t.unorderedEqual` only compares Maps, Sets, and arrays', + values: [formatWithLabel('Called with:', actual)], + })); + return false; + } + + const expectedInfo = checkValueForUnorderedEqual(expected); + + if (!expectedInfo.isValid) { + fail(new AssertionError({ + assertion: 'unorderedEqual', + improperUsage: true, + message: '`t.unorderedEqual` only compares Maps, Sets, and arrays', + values: [formatWithLabel('Called with:', expected)], + })); + return false; + } + + if (actualInfo.size !== expectedInfo.size) { + fail(new AssertionError({ + assertion: 'unorderedEqual', + message: 'size must be equal', + })); + return false; + } + + if (actualInfo.type === 'map') { + if (expectedInfo.type !== 'map') { + fail(new AssertionError({ + assertion: 'unorderedEqual', + message: 'both must be maps', + })); + return false; + } + + const comparedKeysResult = concordance.compare(actual.keys, expected.keys, concordanceOptions); + if (!comparedKeysResult.pass) { + fail(new AssertionError({ + assertion: 'unorderedEqual', + message: 'keys must be equal', + })); + return false; + } + + for (const [key, value] of actual.entries()) { + const result = concordance.compare(value, expected.get(key), concordanceOptions); + if (!result.pass) { + fail(new AssertionError({ + assertion: 'unorderedEqual', + message: 'all values must be equal - map', + })); + return false; + } + } + + pass(); + return true; + } + + if (expectedInfo.type === 'map') { + fail(new AssertionError({ + assertion: 'unorderedEqual', + message: 'both must be set-likes', + })); + return false; + } + + const setActual = actualInfo.type === 'set' ? actual : new Set(actual); + const setExpected = expectedInfo.type === 'set' ? expected : new Set(expected); + + for (const value of setActual) { + if (!setExpected.has(value)) { + fail(new AssertionError({ + assertion: 'unorderedEqual', + message: 'all values must be equal - set', + })); + return false; + } + } + + pass(); + return true; + }); } } diff --git a/lib/unordered-equal.js b/lib/unordered-equal.js new file mode 100644 index 000000000..4f9edcb33 --- /dev/null +++ b/lib/unordered-equal.js @@ -0,0 +1,22 @@ +export const checkValueForUnorderedEqual = value => { + /* eslint-disable indent, operator-linebreak, unicorn/no-nested-ternary */ + const type = ( + value instanceof Map ? 'map' : + value instanceof Set ? 'set' : + Array.isArray(value) ? 'array' : + 'invalid' + ); + /* eslint-enable indent, operator-linebreak, unicorn/no-nested-ternary */ + + if (type === 'invalid') { + return {isValid: false}; + } + + return { + isValid: true, + type, + size: type === 'array' + ? value.length + : value.size, + }; +}; diff --git a/test-tap/assert.js b/test-tap/assert.js index 2cec483ad..32945973f 100644 --- a/test-tap/assert.js +++ b/test-tap/assert.js @@ -1809,3 +1809,98 @@ test('.assert()', t => { t.end(); }); + +test('.unorderedEqual()', t => { + passes(t, () => assertions.unorderedEqual([1, 2, 3], [2, 3, 1])); + + passes(t, () => assertions.unorderedEqual(new Set([1, 2, 3]), new Set([2, 3, 1]))); + + passes(t, () => assertions.unorderedEqual([1, 2, 3], new Set([2, 3, 1]))); + + passes(t, () => assertions.unorderedEqual(new Set([1, 2, 3]), [2, 3, 1])); + + passes(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['b', 2], ['c', 3]]), + new Map([['b', 2], ['c', 3], ['a', 1]]), + )); + + // Types must match + + fails(t, () => assertions.unorderedEqual('foo', [1, 2, 3])); + + fails(t, () => assertions.unorderedEqual([1, 2, 3], 'foo')); + + // Sizes must match + + fails(t, () => assertions.unorderedEqual([1, 2, 3], [1, 2, 3, 4])); + + fails(t, () => assertions.unorderedEqual([1, 2, 3, 4], [1, 2, 3])); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 3]), new Set([1, 2, 3, 4]))); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 3, 4]), new Set([1, 2, 3]))); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 3]), [1, 2, 3, 4])); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 3, 4]), [1, 2, 3])); + + fails(t, () => assertions.unorderedEqual([1, 2, 3], new Set([1, 2, 3, 4]))); + + fails(t, () => assertions.unorderedEqual([1, 2, 3, 4], new Set([1, 2, 3]))); + + fails(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['b', 2], ['c', 3]]), + new Map([['a', 1], ['b', 2]])), + ); + + fails(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['b', 2]]), + new Map([['a', 1], ['b', 2], ['c', 3]])), + ); + + // Keys must match - maps + + fails(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['b', 2], ['c', 3]]), + new Map([['a', 1], ['d', 2], ['c', 3]])), + ); + + fails(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['d', 2], ['c', 3]]), + new Map([['a', 1], ['b', 2], ['c', 3]])), + ); + + // Values must match - maps + + fails(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['b', 2], ['c', 3]]), + new Map([['a', 1], ['b', 4], ['c', 3]])), + ); + + fails(t, () => assertions.unorderedEqual( + new Map([['a', 1], ['b', 4], ['c', 3]]), + new Map([['a', 1], ['b', 2], ['c', 3]])), + ); + + // Values must match - sets + + fails(t, () => assertions.unorderedEqual([1, 2, 3], [1, 2, 4])); + + fails(t, () => assertions.unorderedEqual([1, 2, 4], [1, 2, 3])); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 3]), new Set([1, 2, 4]))); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 4]), new Set([1, 2, 3]))); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 3]), [1, 2, 4])); + + fails(t, () => assertions.unorderedEqual(new Set([1, 2, 4]), [1, 2, 3])); + + fails(t, () => assertions.unorderedEqual([1, 2, 3], new Set([1, 2, 4]))); + + fails(t, () => assertions.unorderedEqual([1, 2, 4], new Set([1, 2, 3]))); + + // TODO: check error messages + + t.end(); +}); diff --git a/test-tap/test.js b/test-tap/test.js index 261e108e2..bfa8f43e7 100644 --- a/test-tap/test.js +++ b/test-tap/test.js @@ -270,11 +270,12 @@ test('skipped assertions count towards the plan', t => { a.false.skip(false); a.regex.skip('foo', /foo/); a.notRegex.skip('bar', /foo/); + a.unorderedEqual.skip([1, 2, 3], [2, 3, 1]); }); return instance.run().then(result => { t.equal(result.passed, true); - t.equal(instance.planCount, 16); - t.equal(instance.assertCount, 16); + t.equal(instance.planCount, 17); + t.equal(instance.assertCount, 17); }); }); @@ -299,11 +300,12 @@ test('assertion.skip() is bound', t => { (a.false.skip)(false); (a.regex.skip)('foo', /foo/); (a.notRegex.skip)('bar', /foo/); + (a.unorderedEqual.skip)([1, 2, 3], [2, 3, 1]); }); return instance.run().then(result => { t.equal(result.passed, true); - t.equal(instance.planCount, 16); - t.equal(instance.assertCount, 16); + t.equal(instance.planCount, 17); + t.equal(instance.assertCount, 17); }); }); @@ -488,6 +490,7 @@ test('assertions are bound', t => (a.false)(false); (a.regex)('foo', /foo/); (a.notRegex)('bar', /foo/); + (a.unorderedEquals)([1, 2, 3], [2, 3, 1]); }).run().then(result => { t.ok(result.passed); }), diff --git a/test/assertions/fixtures/happy-path.js b/test/assertions/fixtures/happy-path.js index 8b2c6020a..08901d705 100644 --- a/test/assertions/fixtures/happy-path.js +++ b/test/assertions/fixtures/happy-path.js @@ -43,3 +43,4 @@ test(passes, 'false', false); test(passes, 'regex', 'foo', /foo/); test(passes, 'notRegex', 'bar', /foo/); test(passes, 'assert', 1); +test(passes, 'unorderedEqual', [1, 2, 3], [2, 3, 1]); diff --git a/test/assertions/snapshots/test.js.md b/test/assertions/snapshots/test.js.md index 610eee926..151ae2ba0 100644 --- a/test/assertions/snapshots/test.js.md +++ b/test/assertions/snapshots/test.js.md @@ -27,6 +27,7 @@ Generated by [AVA](https://avajs.dev). 't.throwsAsync() passes', 't.true(true) passes', 't.truthy(1) passes', + 't.unorderedEqual([1,2,3], [2,3,1]) passes', ] ## throws requires native errors diff --git a/test/assertions/snapshots/test.js.snap b/test/assertions/snapshots/test.js.snap index a1a251bd92051c4b1185b5cd06005b73e12a62e1..bc4064cf2e0a60c6a51d8c72ef20f37bf66a14dd 100644 GIT binary patch literal 661 zcmV;G0&4w1RzVk;*CQ3+V@`&KY|| zdQPfT7<))MC4DA+BmE?8RT+Ci@=2daUrFCdzep`!Z}QrKQgXt%`D!#8jBhgDVtkv3 z3TyK!bPkBQ8~oGgQnB zchDWkP=mdA7qM#2ObSeF%PKN!hH0W3XuIQxlHXc}ROSrbFgS%vUL7lq!P=i$z0s7~ zhNCgH!5EdKTA&n!a3&+9pjNdmhaWCGW-G@Pc4O6o=cO&G=VFgyOato{nRUbLEtqH5 z=VDsU#fF*W{$i3B@D|4Q(x{k~=o^M^FX)%Pir;CwNNpPGUXD7@lc--pw+z}qH|;3_ z+qt>a(p(zNDswjtD$q5D%f9kQyp{WVaCIyVgWNU9>OgKAu0@<0ft|jJP&1cR>PLT4 zK3+S9*+q90#mWN(-ZHO89jEK;9XsNP3a4{yhGv(`_`%YkP%st|;ms2W8g;hd@ZT5Ak|5&V3G`8* vFHz8`gUHiV$sCA8#Terxfy~Q52H_ts*=||6bpGG6E4S=7fjZu&#|8iZa3eV5 literal 627 zcmV-(0*w7ZRzVA zH_5gbJ0g81eIxxOby|!KNgnAF=_~0wsn0ihynUdQOgT4i-ENohEyi~k-zLIhyS&AC zyT^CXm0E*BXYUDFrB_`boEzpTx*kAsIGxGRS=y60w)gFkRQB0GTxwQlu*?ki(4ELo zgOlH@SgT|v15(GftIQ3<^w15ovo{bmzwH9CRt()RIDt!E2X>Z&y}z_avs3Du&KA@M z3sjO?fl?5{Tt-MqHTAAPKU@zi8^5bmqbcB7twbB8+@lz?#D-O-W0+?-^Zfd?IIXY6 zrkUhnKFLcsf`zj-DwZ|+rlEIp`jxNZciO2^TZVd2q7HN#jce#FgLcu)Mry!zX)d)k zmu1sr?zTY%x@KcJSN@21N`DVF=h8LEeS{f+(rB$VI^wW#&Rh$ls zN9!m;!E_cV&?1r;%t3%svCIxwsOJ%{X?&CaWvyRjm5Q&O0#x#M#%R~m@UoS(actual: Actual, expected: Expected, message?: string): actual is Expected; + + /** Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. */ + (actual: Actual, expected: Expected, message?: string): expected is Actual; + + /** Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. */ + (actual: Actual, expected: Expected, message?: string): boolean; + + /** Skip this assertion. */ + skip(actual: any, expected: any, message?: string): void; +}; From a39dab098154c8d4fbfd39a85a72e13e71004c7e Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 15 Aug 2023 17:58:52 -0500 Subject: [PATCH 2/5] fix(`types`): add `unorderedEqual` to `Assertions` --- types/assertions.d.cts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/assertions.d.cts b/types/assertions.d.cts index 5823ce4f9..316379f7c 100644 --- a/types/assertions.d.cts +++ b/types/assertions.d.cts @@ -123,6 +123,9 @@ export type Assertions = { * indicating whether the assertion passed. */ truthy: TruthyAssertion; + + /** Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. */ + unorderedEqual: UnorderedEqualAssertion; }; export type AssertAssertion = { From af206f6768f81dcf3fa338bc2be155f3d97e4900 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 21 Aug 2023 13:06:14 -0500 Subject: [PATCH 3/5] update assertion description --- docs/03-assertions.md | 6 +++++- types/assertions.d.cts | 30 +++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/03-assertions.md b/docs/03-assertions.md index 693775698..015656c0f 100644 --- a/docs/03-assertions.md +++ b/docs/03-assertions.md @@ -139,7 +139,11 @@ Assert that `actual` is not deeply equal to `expected`. The inverse of `.deepEqu ### `.unorderedEqual(actual, expected, message?)` -Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. +Assert that all values in `actual` are in `expected`. This is a variant of `.deepEqual()` that does not depend on the order of `actual`/`expected` and only compares instances of `Map`s or `Set`s/arrays. + +The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. + +Returns a boolean indicating whether the assertion passed. ### `.like(actual, selector, message?)` diff --git a/types/assertions.d.cts b/types/assertions.d.cts index 316379f7c..7be1760d6 100644 --- a/types/assertions.d.cts +++ b/types/assertions.d.cts @@ -348,13 +348,37 @@ export type TruthyAssertion = { // TODO: limit to Map | Set | Array export type UnorderedEqualAssertion = { - /** Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. */ + /** + * Assert that all values in `actual` are in `expected`. This is a variant of `.deepEqual()` that does not depend + * on the order of `actual`/`expected` and only compares instances of `Map`s or `Set`s/arrays. + * + * The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in + * `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. + * + * Returns a boolean indicating whether the assertion passed. + */ (actual: Actual, expected: Expected, message?: string): actual is Expected; - /** Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. */ + /** + * Assert that all values in `actual` are in `expected`. This is a variant of `.deepEqual()` that does not depend + * on the order of `actual`/`expected` and only compares instances of `Map`s or `Set`s/arrays. + * + * The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in + * `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. + * + * Returns a boolean indicating whether the assertion passed. + */ (actual: Actual, expected: Expected, message?: string): expected is Actual; - /** Assert that all values in `actual` are in `expected`, returning a boolean indicating whether the assertion passed. */ + /** + * Assert that all values in `actual` are in `expected`. This is a variant of `.deepEqual()` that does not depend + * on the order of `actual`/`expected` and only compares instances of `Map`s or `Set`s/arrays. + * + * The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in + * `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. + * + * Returns a boolean indicating whether the assertion passed. + */ (actual: Actual, expected: Expected, message?: string): boolean; /** Skip this assertion. */ From f0b4b80837663089581670934481d3957c9ca896 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 21 Aug 2023 13:06:18 -0500 Subject: [PATCH 4/5] tweaks --- lib/assert.js | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/lib/assert.js b/lib/assert.js index 3890f9dde..149a255d6 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -966,25 +966,29 @@ export class Assertions { } const actualInfo = checkValueForUnorderedEqual(actual); + const expectedInfo = checkValueForUnorderedEqual(expected); - if (!actualInfo.isValid) { + if (!actualInfo.isValid || !expectedInfo.isValid) { fail(new AssertionError({ assertion: 'unorderedEqual', improperUsage: true, message: '`t.unorderedEqual` only compares Maps, Sets, and arrays', - values: [formatWithLabel('Called with:', actual)], + values: [ + !actualInfo.isValid && formatWithLabel('Called with:', actual), + !expectedInfo.isValid && formatWithLabel('Called with:', expected), + ].filter(Boolean), })); return false; } - const expectedInfo = checkValueForUnorderedEqual(expected); - - if (!expectedInfo.isValid) { + if ( + actualInfo.type !== expectedInfo.type + && (actualInfo.type === 'map' || expectedInfo.type === 'map') + ) { fail(new AssertionError({ assertion: 'unorderedEqual', improperUsage: true, - message: '`t.unorderedEqual` only compares Maps, Sets, and arrays', - values: [formatWithLabel('Called with:', expected)], + message: 'types of actual and expected must be comparable', })); return false; } @@ -998,14 +1002,6 @@ export class Assertions { } if (actualInfo.type === 'map') { - if (expectedInfo.type !== 'map') { - fail(new AssertionError({ - assertion: 'unorderedEqual', - message: 'both must be maps', - })); - return false; - } - const comparedKeysResult = concordance.compare(actual.keys, expected.keys, concordanceOptions); if (!comparedKeysResult.pass) { fail(new AssertionError({ @@ -1030,14 +1026,6 @@ export class Assertions { return true; } - if (expectedInfo.type === 'map') { - fail(new AssertionError({ - assertion: 'unorderedEqual', - message: 'both must be set-likes', - })); - return false; - } - const setActual = actualInfo.type === 'set' ? actual : new Set(actual); const setExpected = expectedInfo.type === 'set' ? expected : new Set(expected); From 0f51da40b496d615bf6807d507c18b936ab0d949 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 26 Feb 2024 20:34:36 -0600 Subject: [PATCH 5/5] updates, simplify --- lib/assert.js | 51 +++++++++++++----------------------------- lib/unordered-equal.js | 17 +++++++------- types/assertions.d.cts | 8 +++---- 3 files changed, 29 insertions(+), 47 deletions(-) diff --git a/lib/assert.js b/lib/assert.js index 8d7bf6582..60d79ec9d 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -799,69 +799,53 @@ export class Assertions { }); this.unorderedEqual = withSkip((actual, expected, message) => { - if (!checkMessage('unorderedEqual', message)) { - return false; - } + assertMessage(message, 't.unorderedEqual()'); const actualInfo = checkValueForUnorderedEqual(actual); const expectedInfo = checkValueForUnorderedEqual(expected); if (!actualInfo.isValid || !expectedInfo.isValid) { - fail(new AssertionError({ - assertion: 'unorderedEqual', + throw fail(new AssertionError('`t.unorderedEqual()` only compares arrays, maps, and sets', { + assertion: 't.unorderedEqual()', improperUsage: true, - message: '`t.unorderedEqual` only compares Maps, Sets, and arrays', values: [ !actualInfo.isValid && formatWithLabel('Called with:', actual), !expectedInfo.isValid && formatWithLabel('Called with:', expected), ].filter(Boolean), })); - return false; } if ( actualInfo.type !== expectedInfo.type && (actualInfo.type === 'map' || expectedInfo.type === 'map') ) { - fail(new AssertionError({ - assertion: 'unorderedEqual', + throw fail(new AssertionError('types of actual and expected must be comparable', { + assertion: 't.unorderedEqual()', improperUsage: true, - message: 'types of actual and expected must be comparable', })); - return false; } if (actualInfo.size !== expectedInfo.size) { - fail(new AssertionError({ - assertion: 'unorderedEqual', - message: 'size must be equal', + throw fail(new AssertionError('size must be equal', { + assertion: 't.unorderedEqual()', })); - return false; } if (actualInfo.type === 'map') { - const comparedKeysResult = concordance.compare(actual.keys, expected.keys, concordanceOptions); - if (!comparedKeysResult.pass) { - fail(new AssertionError({ - assertion: 'unorderedEqual', - message: 'keys must be equal', - })); - return false; - } + // Keys are unique - if actual and expected are the same size, + // and expected has a value for every key in actual, then the two are equal. for (const [key, value] of actual.entries()) { const result = concordance.compare(value, expected.get(key), concordanceOptions); if (!result.pass) { - fail(new AssertionError({ - assertion: 'unorderedEqual', - message: 'all values must be equal - map', + // TODO: allow passing custom messages + throw fail(new AssertionError('all values must be equal - map', { + assertion: 't.unorderedEqual()', })); - return false; } } - pass(); - return true; + return pass(); } const setActual = actualInfo.type === 'set' ? actual : new Set(actual); @@ -869,16 +853,13 @@ export class Assertions { for (const value of setActual) { if (!setExpected.has(value)) { - fail(new AssertionError({ - assertion: 'unorderedEqual', - message: 'all values must be equal - set', + throw fail(new AssertionError('all values must be equal - array/set', { + assertion: 't.unorderedEqual()', })); - return false; } } - pass(); - return true; + return pass(); }); } } diff --git a/lib/unordered-equal.js b/lib/unordered-equal.js index 4f9edcb33..796371209 100644 --- a/lib/unordered-equal.js +++ b/lib/unordered-equal.js @@ -1,12 +1,13 @@ export const checkValueForUnorderedEqual = value => { - /* eslint-disable indent, operator-linebreak, unicorn/no-nested-ternary */ - const type = ( - value instanceof Map ? 'map' : - value instanceof Set ? 'set' : - Array.isArray(value) ? 'array' : - 'invalid' - ); - /* eslint-enable indent, operator-linebreak, unicorn/no-nested-ternary */ + let type = 'invalid'; + + if (value instanceof Map) { + type = 'map'; + } else if (value instanceof Set) { + type = 'set'; + } else if (Array.isArray(value)) { + type = 'array'; + } if (type === 'invalid') { return {isValid: false}; diff --git a/types/assertions.d.cts b/types/assertions.d.cts index 939c1e076..2df1b2281 100644 --- a/types/assertions.d.cts +++ b/types/assertions.d.cts @@ -413,7 +413,7 @@ export type UnorderedEqualAssertion = { * The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in * `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. * - * Returns a boolean indicating whether the assertion passed. + * Returns `true` if the assertion passed and throws otherwise. */ (actual: Actual, expected: Expected, message?: string): actual is Expected; @@ -424,7 +424,7 @@ export type UnorderedEqualAssertion = { * The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in * `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. * - * Returns a boolean indicating whether the assertion passed. + * Returns `true` if the assertion passed and throws otherwise. */ (actual: Actual, expected: Expected, message?: string): expected is Actual; @@ -435,9 +435,9 @@ export type UnorderedEqualAssertion = { * The size of `actual` and `expected` must be equal. For `Map`s, each key-value pair in `actual` must be in * `expected`, and vice-versa. For `Set`s/arrays, each value in `actual` must be in `expected`. * - * Returns a boolean indicating whether the assertion passed. + * Returns `true` if the assertion passed and throws otherwise. */ - (actual: Actual, expected: Expected, message?: string): boolean; + (actual: Actual, expected: Expected, message?: string): true; /** Skip this assertion. */ skip(actual: any, expected: any, message?: string): void;