├── .gitignore ├── .npmignore ├── .eslintrc ├── .babelrc ├── renovate.json ├── nyc.config.js ├── .releaserc ├── .travis.yml ├── src ├── tests │ ├── chai-with-plugins.js │ └── deepEqualInAnyOrder.js └── index.js ├── LICENSE.txt ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.logs 3 | build 4 | .nyc_output 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /* 2 | !/build 3 | 4 | *.log 5 | *.swo 6 | *.swp 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-airbnb-improved", 3 | "rules": { 4 | "sort-keys": "off" 5 | }, 6 | "settings": { 7 | "import/resolver": { 8 | "babel-module": {} 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-es2015-modules-commonjs", 4 | [ 5 | "module-resolver", 6 | { 7 | "alias": { 8 | "deep-equal-in-any-order": "./src", 9 | } 10 | } 11 | ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "commitMessagePrefix": "[renovate] ", 3 | "groupName": "all dependencies", 4 | "labels": [ 5 | "renovate" 6 | ], 7 | "schedule": ["after 7am on saturday"], 8 | "updateNotScheduled": false, 9 | "rangeStrategy": "bump" 10 | } 11 | -------------------------------------------------------------------------------- /nyc.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lines: 100, 3 | functions: 100, 4 | branches: 100, 5 | statements: 100, 6 | watermarks: { 7 | lines: [90, 100], 8 | functions: [90, 100], 9 | branches: [90, 100], 10 | statements: [90, 100], 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | branches: ['master'], 3 | plugins: [ 4 | '@semantic-release/commit-analyzer', 5 | '@semantic-release/npm', 6 | [ 7 | '@semantic-release/git', 8 | { 9 | assets: ['package.json'], 10 | message: 'chore(release): ${nextRelease.version} [skip ci]', 11 | }, 12 | ], 13 | ], 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | env: 4 | - APP_DIR=src 5 | node_js: 6 | - 14 7 | - 16 8 | script: 9 | - yarn install --non-interactive 10 | - yarn lint 11 | - yarn coverage 12 | jobs: 13 | include: 14 | - stage: deploy 15 | on: 16 | branch: master 17 | node_js: 14 18 | script: 19 | - yarn install --non-interactive 20 | - npx semantic-release 21 | notifications: 22 | email: 23 | on_success: never 24 | on_failure: always 25 | -------------------------------------------------------------------------------- /src/tests/chai-with-plugins.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const chaiAsPromised = require('chai-as-promised'); 3 | const chaiSubset = require('chai-subset'); 4 | const chaiRoughly = require('chai-roughly-v2'); 5 | const dirtyChai = require('dirty-chai'); 6 | const sinonChai = require('sinon-chai'); 7 | const deepEqualInAnyOrder = require('deep-equal-in-any-order/index'); 8 | 9 | chai.use(chaiSubset); 10 | chai.use(chaiAsPromised); 11 | chai.use(sinonChai); 12 | chai.use(deepEqualInAnyOrder); 13 | chai.use(chaiRoughly); 14 | chai.use(dirtyChai); 15 | 16 | const { assert, expect } = chai; 17 | 18 | module.exports = { assert, expect }; 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 oprogramador 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import mapValues from 'lodash.mapvalues'; 2 | import sortAny from 'sort-any'; 3 | 4 | const sortDeep = (object) => { 5 | if (object instanceof Set) { 6 | return sortAny([...object]); 7 | } 8 | if (object instanceof Map) { 9 | return sortAny([...object]); 10 | } 11 | if (!Array.isArray(object)) { 12 | if (typeof object !== 'object' || object === null || object instanceof Date) { 13 | return object; 14 | } 15 | 16 | return mapValues(object, sortDeep); 17 | } 18 | 19 | return sortAny(object.map(sortDeep)); 20 | }; 21 | 22 | module.exports = (chai, utils) => { 23 | const { Assertion } = chai; 24 | utils.addMethod(Assertion.prototype, 'equalInAnyOrder', function equalInAnyOrder(b, m) { 25 | const a = utils.flag(this, 'object'); 26 | utils.flag(this, 'object', sortDeep(a)); 27 | this.equal(sortDeep(b), m); 28 | }); 29 | 30 | chai.assert.deepEqualInAnyOrder = (actual, expected, message) => chai.expect(actual) 31 | .to.deep.equalInAnyOrder(expected, message); 32 | chai.assert.notDeepEqualInAnyOrder = (actual, expected, message) => chai.expect(actual) 33 | .to.not.deep.equalInAnyOrder(expected, message); 34 | }; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deep-equal-in-any-order", 3 | "version": "2.1.0", 4 | "description": "chai plugin to match objects and arrays deep equality with arrays (including nested ones) being in any order", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "build": "rm -rf build && babel src --out-dir build --ignore tests/", 8 | "coverage": "nyc --check-coverage=true npm run test", 9 | "lint": "eslint src", 10 | "prepublishOnly": "npm run build", 11 | "test": "mocha -r babel-register --recursive src/tests --timeout 5000" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/oprogramador/deep-equal-in-any-order.git" 16 | }, 17 | "keywords": [ 18 | "chai", 19 | "plugin", 20 | "chai-plugin", 21 | "arrays", 22 | "objects", 23 | "any", 24 | "order", 25 | "deep", 26 | "equal" 27 | ], 28 | "author": "oprogramador", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/oprogramador/deep-equal-in-any-order/issues" 32 | }, 33 | "homepage": "https://github.com/oprogramador/deep-equal-in-any-order#readme", 34 | "devDependencies": { 35 | "@babel/core": "^7.13.14", 36 | "@semantic-release/commit-analyzer": "^8.0.1", 37 | "@semantic-release/git": "^9.0.0", 38 | "@semantic-release/npm": "^7.1.0", 39 | "babel-cli": "^6.26.0", 40 | "babel-core": "^6.26.3", 41 | "babel-plugin-module-resolver": "^4.1.0", 42 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", 43 | "babel-register": "^6.26.0", 44 | "chai": "^4.3.4", 45 | "chai-as-promised": "^7.1.1", 46 | "chai-roughly-v2": "^2.0.12", 47 | "chai-subset": "^1.6.0", 48 | "dirty-chai": "^2.0.1", 49 | "eslint": "^7.23.0", 50 | "eslint-config-airbnb-improved": "^5.0.0", 51 | "eslint-import-resolver-babel-module": "^5.2.0", 52 | "mocha": "^8.3.2", 53 | "nyc": "^15.1.0", 54 | "semantic-release": "19", 55 | "sinon": "^10.0.0", 56 | "sinon-as-promised": "^4.0.3", 57 | "sinon-chai": "^3.6.0" 58 | }, 59 | "dependencies": { 60 | "lodash.mapvalues": "^4.6.0", 61 | "sort-any": "^2.0.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deep-equal-in-any-order 2 | 3 | [![MIT License](https://img.shields.io/badge/license-mit-green.svg?style=flat-square)](https://opensource.org/licenses/MIT) 4 | [![Build Status](https://travis-ci.com/oprogramador/deep-equal-in-any-order.svg?branch=master)](https://travis-ci.com/oprogramador/deep-equal-in-any-order 5 | ) 6 | 7 | [![NPM status](https://nodei.co/npm/deep-equal-in-any-order.png?downloads=true&stars=true)](https://npmjs.org/package/deep-equal-in-any-order 8 | ) 9 | 10 | [Chai](https://www.chaijs.com/) plugin to match objects and arrays deep equality with arrays (including nested ones) being in any order. 11 | 12 | It works in similar way as `deep.equal` but it doesn't checks the arrays order (at any level of nested objects and arrays). The array elements can be any JS entity (boolean, null, number, string, object, array...). 13 | 14 | ## install 15 | ``` 16 | npm i --save deep-equal-in-any-order 17 | ``` 18 | or 19 | ``` 20 | yarn add deep-equal-in-any-order 21 | ``` 22 | 23 | ## usage 24 | 25 | ### `expect` 26 | ```js 27 | const deepEqualInAnyOrder = require('deep-equal-in-any-order'); 28 | const chai = require('chai'); 29 | 30 | chai.use(deepEqualInAnyOrder); 31 | 32 | const { expect } = chai; 33 | 34 | expect([1, 2]).to.deep.equalInAnyOrder([2, 1]); 35 | expect([1, 2]).to.not.deep.equalInAnyOrder([2, 1, 3]); 36 | expect({ foo: [1, 2], bar: [4, 89, 22] }).to.deep.equalInAnyOrder({ foo: [2, 1], bar: [4, 22, 89] }); 37 | expect({ foo: ['foo-1', 'foo-2', [1, 2], null ] }).to.deep.equalInAnyOrder({ foo: [null, [1, 2], 'foo-1', 'foo-2'] }); 38 | expect({ foo: [1, 2], bar: { baz: ['a', 'b', { lorem: [5, 6] }] } }).to.deep.equalInAnyOrder({ foo: [2, 1], bar: { baz: ['b', 'a', { lorem: [6, 5] }] } }); 39 | ``` 40 | 41 | ### `assert` 42 | ```js 43 | const deepEqualInAnyOrder = require('deep-equal-in-any-order'); 44 | const chai = require('chai'); 45 | 46 | chai.use(deepEqualInAnyOrder); 47 | 48 | const { assert } = chai; 49 | 50 | assert.deepEqualInAnyOrder([1, 2], [2, 1]); 51 | assert.notDeepEqualInAnyOrder(1, 2], [2, 1, 3]); 52 | assert.deepEqualInAnyOrder({ foo: [1, 2], bar: [4, 89, 22] }, { foo: [2, 1], bar: [4, 22, 89] }); 53 | assert.deepEqualInAnyOrder({ foo: ['foo-1', 'foo-2', [1, 2], null ] }, { foo: [null, [1, 2], 'foo-1', 'foo-2'] }); 54 | assert.deepEqualInAnyOrder({ foo: [1, 2], bar: { baz: ['a', 'b', { lorem: [5, 6] }] } }, { foo: [2, 1], bar: { baz: ['b', 'a', { lorem: [6, 5] }] } }); 55 | ``` 56 | -------------------------------------------------------------------------------- /src/tests/deepEqualInAnyOrder.js: -------------------------------------------------------------------------------- 1 | const { assert, expect } = require('deep-equal-in-any-order/tests/chai-with-plugins'); 2 | 3 | const expectToDeepEqualInAnyOrder = (a, b) => { 4 | expect(a).to.deep.equalInAnyOrder(b); 5 | expect(() => expect(a).to.not.deep.equalInAnyOrder(b, 'TEST')).to.throw(/^TEST: /); 6 | 7 | assert.deepEqualInAnyOrder(a, b); 8 | assert.throws(() => assert.notDeepEqualInAnyOrder(a, b, 'TEST'), /^TEST: /); 9 | }; 10 | 11 | const expectToNotDeepEqualInAnyOrder = (a, b) => { 12 | expect(a).to.not.deep.equalInAnyOrder(b); 13 | expect(() => expect(a).to.deep.equalInAnyOrder(b, 'TEST')).to.throw(/^TEST: /); 14 | 15 | assert.notDeepEqualInAnyOrder(a, b); 16 | assert.throws(() => assert.deepEqualInAnyOrder(a, b, 'TEST'), /^TEST: /); 17 | }; 18 | 19 | describe('equalInAnyOrder', () => { 20 | it('matches true with true', () => { 21 | expectToDeepEqualInAnyOrder(true, true); 22 | }); 23 | 24 | it('matches false with false', () => { 25 | expectToDeepEqualInAnyOrder(false, false); 26 | }); 27 | 28 | it('does not match false with true', () => { 29 | expectToNotDeepEqualInAnyOrder(false, true); 30 | }); 31 | 32 | it('matches null with null', () => { 33 | expectToDeepEqualInAnyOrder(null, null); 34 | }); 35 | 36 | it('does not match null with empty object', () => { 37 | expectToNotDeepEqualInAnyOrder(null, {}); 38 | }); 39 | 40 | it('does not match a date with empty object', () => { 41 | expectToNotDeepEqualInAnyOrder(new Date('2021-01-01'), {}); 42 | }); 43 | 44 | it('matches zero with zero', () => { 45 | expectToDeepEqualInAnyOrder(0, 0); 46 | }); 47 | 48 | it('matches positive number with the same number', () => { 49 | expectToDeepEqualInAnyOrder(7, 7); 50 | }); 51 | 52 | it('does not match different numbers', () => { 53 | expectToNotDeepEqualInAnyOrder(7, 2); 54 | }); 55 | 56 | it('matches empty string with empty string', () => { 57 | expectToDeepEqualInAnyOrder('', ''); 58 | }); 59 | 60 | it('matches non-empty string with the same string', () => { 61 | expectToDeepEqualInAnyOrder('foo', 'foo'); 62 | }); 63 | 64 | it('does not match different strings', () => { 65 | expectToNotDeepEqualInAnyOrder('foo', 'bar'); 66 | }); 67 | 68 | it('matches empty object with empty object', () => { 69 | expectToDeepEqualInAnyOrder({}, {}); 70 | }); 71 | 72 | it('matches empty array with empty array', () => { 73 | expectToDeepEqualInAnyOrder([], []); 74 | }); 75 | 76 | it('does not match empty object with empty array', () => { 77 | expectToNotDeepEqualInAnyOrder({}, []); 78 | }); 79 | 80 | it('does not match empty array with empty object', () => { 81 | expectToNotDeepEqualInAnyOrder([], {}); 82 | }); 83 | 84 | it('matches same empty sets', () => { 85 | const a = new Set([]); 86 | const b = new Set([]); 87 | expectToDeepEqualInAnyOrder(a, b); 88 | }); 89 | 90 | it('matches same sets', () => { 91 | const a = new Set([1, 20]); 92 | const b = new Set([20, 1]); 93 | expectToDeepEqualInAnyOrder(a, b); 94 | }); 95 | 96 | it('does not match different sets', () => { 97 | const a = new Set([1, 5]); 98 | const b = new Set([1, 3]); 99 | expectToNotDeepEqualInAnyOrder(a, b); 100 | }); 101 | 102 | it('matches same set nested inside another set', () => { 103 | const a = new Set([1, new Set([3, 4, 100, 20])]); 104 | const b = new Set([new Set([100, 3, 4, 20]), 1]); 105 | expectToDeepEqualInAnyOrder(a, b); 106 | }); 107 | 108 | it('does not match a different set nested inside another set', () => { 109 | const a = new Set([1, new Set([3, 4, 21])]); 110 | const b = new Set([new Set([3, 4, 20]), 1]); 111 | expectToNotDeepEqualInAnyOrder(a, b); 112 | }); 113 | 114 | it('matches same empty maps', () => { 115 | const a = new Map([]); 116 | const b = new Map([]); 117 | expectToDeepEqualInAnyOrder(a, b); 118 | }); 119 | 120 | it('matches same maps', () => { 121 | const a = new Map([['a', 1], ['b', 3]]); 122 | const b = new Map([['a', 1], ['b', 3]]); 123 | expectToDeepEqualInAnyOrder(a, b); 124 | }); 125 | 126 | it('matches same maps nested', () => { 127 | const a = { x: new Map([['a', 1], ['b', 3]]), y: new Map([['c', 5]]) }; 128 | const b = { x: new Map([['a', 1], ['b', 3]]), y: new Map([['c', 5]]) }; 129 | expectToDeepEqualInAnyOrder(a, b); 130 | }); 131 | 132 | it('matches same maps nested inside another map', () => { 133 | const a = { x: new Map([['a', 1], [new Map([['c', 25]]), new Map([['c', 5]])]]), y: new Map([['c', 5]]) }; 134 | const b = { x: new Map([['a', 1], [new Map([['c', 25]]), new Map([['c', 5]])]]), y: new Map([['c', 5]]) }; 135 | expectToDeepEqualInAnyOrder(a, b); 136 | }); 137 | 138 | it('does not match different maps nested inside another map - as a key', () => { 139 | const a = { x: new Map([['a', 1], [new Map([['c', 20]]), new Map([['c', 5]])]]), y: new Map([['c', 5]]) }; 140 | const b = { x: new Map([['a', 1], [new Map([['c', 25]]), new Map([['c', 5]])]]), y: new Map([['c', 5]]) }; 141 | expectToNotDeepEqualInAnyOrder(a, b); 142 | }); 143 | 144 | it('does not match different maps nested inside another map - as a value', () => { 145 | const a = { x: new Map([['a', 1], [new Map([['c', 25]]), new Map([['c', 5]])]]), y: new Map([['c', 5]]) }; 146 | const b = { x: new Map([['a', 1], [new Map([['c', 25]]), new Map([['c', 50]])]]), y: new Map([['c', 5]]) }; 147 | expectToNotDeepEqualInAnyOrder(a, b); 148 | }); 149 | 150 | it('does not match different maps', () => { 151 | const a = new Map([['a', 1], ['b', 3]]); 152 | const b = new Map([['a', 1], ['b', 2]]); 153 | expectToNotDeepEqualInAnyOrder(a, b); 154 | }); 155 | 156 | it('does not match different maps nested', () => { 157 | const a = { x: new Map([['a', 1], ['b', 3]]), y: new Map([['c', 5]]) }; 158 | const b = { x: new Map([['a', 1], ['b', 0]]), y: new Map([['c', 5]]) }; 159 | expectToNotDeepEqualInAnyOrder(a, b); 160 | }); 161 | 162 | it('matches same set nested inside a map', () => { 163 | const a = { x: new Map([[new Set('a0'), 1], ['b', 3]]), y: new Map([['c', 5]]) }; 164 | const b = { x: new Map([[new Set('a0'), 1], ['b', 3]]), y: new Map([['c', 5]]) }; 165 | expectToDeepEqualInAnyOrder(a, b); 166 | }); 167 | 168 | it('does not match a different set nested inside a map', () => { 169 | const a = { x: new Map([[new Set('a'), 1], ['b', 3]]), y: new Map([['c', 5]]) }; 170 | const b = { x: new Map([[new Set('a0'), 1], ['b', 3]]), y: new Map([['c', 5]]) }; 171 | expectToNotDeepEqualInAnyOrder(a, b); 172 | }); 173 | 174 | it('matches same map nested inside a set ', () => { 175 | const a = { x: new Set([123, new Map([[new Set('a0'), 1], ['b', 3]])]), y: new Map([['c', 5]]) }; 176 | const b = { x: new Set([new Map([[new Set('a0'), 1], ['b', 3]]), 123]), y: new Map([['c', 5]]) }; 177 | expectToDeepEqualInAnyOrder(a, b); 178 | }); 179 | 180 | it('does not match different map (different value) nested inside a set', () => { 181 | const a = { x: new Set([123, new Map([[new Set('a0'), 1], ['b', 3]])]), y: new Map([['c', 5]]) }; 182 | const b = { x: new Set([new Map([[new Set('a0'), 1], ['b', 30]]), 123]), y: new Map([['c', 5]]) }; 183 | expectToNotDeepEqualInAnyOrder(a, b); 184 | }); 185 | 186 | it('does not match different map (different key) nested inside a set', () => { 187 | const a = { x: new Set([123, new Map([[new Set('a0'), 1], ['b', 3]])]), y: new Map([['c', 5]]) }; 188 | const b = { x: new Set([new Map([[new Set('a0'), 1], ['b0', 3]]), 123]), y: new Map([['c', 5]]) }; 189 | expectToNotDeepEqualInAnyOrder(a, b); 190 | }); 191 | 192 | it('matches exact dates', () => { 193 | const a = new Date('2021-01-01'); 194 | const b = new Date('2021-01-01'); 195 | expectToDeepEqualInAnyOrder(a, b); 196 | }); 197 | 198 | it('does not match distinct dates', () => { 199 | const a = new Date('2021-01-01'); 200 | const b = new Date('2021-01-02'); 201 | expectToNotDeepEqualInAnyOrder(a, b); 202 | }); 203 | 204 | it('matches same flat objects', () => { 205 | const a = { 206 | foo: 'bar', 207 | foo2: 'bar2', 208 | }; 209 | const b = { 210 | foo: 'bar', 211 | foo2: 'bar2', 212 | }; 213 | expectToDeepEqualInAnyOrder(a, b); 214 | }); 215 | 216 | it('matches same flat objects with different keys order', () => { 217 | const a = { 218 | foo: 'bar', 219 | foo2: 'bar2', 220 | }; 221 | const b = { 222 | foo2: 'bar2', 223 | foo: 'bar', 224 | }; 225 | expectToDeepEqualInAnyOrder(a, b); 226 | }); 227 | 228 | it('matches same flat objects with different keys order, with a date', () => { 229 | const a = { 230 | foo: 'bar', 231 | foo2: 'bar2', 232 | foo3: new Date('2021-01-01'), 233 | }; 234 | const b = { 235 | foo2: 'bar2', 236 | foo3: new Date('2021-01-01'), 237 | foo: 'bar', 238 | }; 239 | expectToDeepEqualInAnyOrder(a, b); 240 | }); 241 | 242 | it('does not match different flat objects', () => { 243 | const a = { 244 | foo: 'bar', 245 | foo2: 'bar', 246 | }; 247 | const b = { 248 | foo: 'bar', 249 | foo2: 'bar2', 250 | }; 251 | expectToNotDeepEqualInAnyOrder(a, b); 252 | }); 253 | 254 | it('does not match flat objects with different dates', () => { 255 | const a = { 256 | foo: 'bar', 257 | foo2: new Date('2021-01-01'), 258 | }; 259 | const b = { 260 | foo: 'bar', 261 | foo2: new Date('2021-01-02'), 262 | }; 263 | expectToNotDeepEqualInAnyOrder(a, b); 264 | }); 265 | 266 | it('matches same flat arrays', () => { 267 | const a = [ 268 | 'foo', 269 | 'bar', 270 | ]; 271 | const b = [ 272 | 'foo', 273 | 'bar', 274 | ]; 275 | expectToDeepEqualInAnyOrder(a, b); 276 | }); 277 | 278 | it('matches same flat arrays in different order', () => { 279 | const a = [ 280 | 'foo', 281 | 'bar', 282 | ]; 283 | const b = [ 284 | 'bar', 285 | 'foo', 286 | ]; 287 | expectToDeepEqualInAnyOrder(a, b); 288 | }); 289 | 290 | it('matches same flat arrays of dates in different order', () => { 291 | const a = [ 292 | new Date('2021-01-01'), 293 | new Date('2021-01-02'), 294 | ]; 295 | const b = [ 296 | new Date('2021-01-02'), 297 | new Date('2021-01-01'), 298 | ]; 299 | expectToDeepEqualInAnyOrder(a, b); 300 | }); 301 | 302 | it('does not match flat arrays of different dates', () => { 303 | const a = [ 304 | new Date('2021-01-01'), 305 | new Date('2021-01-02'), 306 | ]; 307 | const b = [ 308 | new Date('2021-01-01'), 309 | new Date('2021-01-01'), 310 | ]; 311 | expectToNotDeepEqualInAnyOrder(a, b); 312 | }); 313 | 314 | it('does not match different flat arrays', () => { 315 | const a = [ 316 | 'foo', 317 | 'bar', 318 | ]; 319 | const b = [ 320 | 'foo', 321 | 'bar', 322 | 'baz', 323 | ]; 324 | expectToNotDeepEqualInAnyOrder(a, b); 325 | }); 326 | 327 | it('matches same nested objects', () => { 328 | const a = { 329 | foo: 'bar', 330 | foo2: { 331 | foo3: [ 332 | 'foo4', 333 | { 334 | foo5: 'bar5', 335 | foo6: 'bar6', 336 | foo7: { 337 | foo8: 'bar8', 338 | foo9: [ 339 | 'foo10', 340 | 'foo11', 341 | 'foo12', 342 | ], 343 | }, 344 | }, 345 | ], 346 | }, 347 | }; 348 | const b = { 349 | foo: 'bar', 350 | foo2: { 351 | foo3: [ 352 | 'foo4', 353 | { 354 | foo5: 'bar5', 355 | foo6: 'bar6', 356 | foo7: { 357 | foo8: 'bar8', 358 | foo9: [ 359 | 'foo10', 360 | 'foo11', 361 | 'foo12', 362 | ], 363 | }, 364 | }, 365 | ], 366 | }, 367 | }; 368 | expectToDeepEqualInAnyOrder(a, b); 369 | }); 370 | 371 | it('matches same nested objects with different items order', () => { 372 | const a = { 373 | foo: 'bar', 374 | foo2: { 375 | foo3: [ 376 | { 377 | foo5: 'bar5', 378 | foo6: 'bar6', 379 | foo7: { 380 | foo8: 'bar8', 381 | foo9: [ 382 | 'foo11', 383 | new Map([['a', 1], ['b', 3]]), 384 | 'foo10', 385 | 'foo12', 386 | new Date('2021-01-01'), 387 | ], 388 | }, 389 | }, 390 | 'foo4', 391 | ], 392 | }, 393 | }; 394 | const b = { 395 | foo: 'bar', 396 | foo2: { 397 | foo3: [ 398 | 'foo4', 399 | { 400 | foo5: 'bar5', 401 | foo6: 'bar6', 402 | foo7: { 403 | foo8: 'bar8', 404 | foo9: [ 405 | 'foo10', 406 | new Date('2021-01-01'), 407 | 'foo11', 408 | 'foo12', 409 | new Map([['a', 1], ['b', 3]]), 410 | ], 411 | }, 412 | }, 413 | ], 414 | }, 415 | }; 416 | expectToDeepEqualInAnyOrder(a, b); 417 | }); 418 | 419 | it('does not match same nested objects with different items order and different maps', () => { 420 | const a = { 421 | foo: 'bar', 422 | foo2: { 423 | foo3: [ 424 | { 425 | foo5: 'bar5', 426 | foo6: 'bar6', 427 | foo7: { 428 | foo8: 'bar8', 429 | foo9: [ 430 | 'foo11', 431 | new Map([['a', 10], ['b', 3]]), 432 | 'foo10', 433 | 'foo12', 434 | new Date('2021-01-01'), 435 | ], 436 | }, 437 | }, 438 | 'foo4', 439 | ], 440 | }, 441 | }; 442 | const b = { 443 | foo: 'bar', 444 | foo2: { 445 | foo3: [ 446 | 'foo4', 447 | { 448 | foo5: 'bar5', 449 | foo6: 'bar6', 450 | foo7: { 451 | foo8: 'bar8', 452 | foo9: [ 453 | 'foo10', 454 | new Date('2021-01-01'), 455 | 'foo11', 456 | 'foo12', 457 | new Map([['a', 1], ['b', 3]]), 458 | ], 459 | }, 460 | }, 461 | ], 462 | }, 463 | }; 464 | expectToNotDeepEqualInAnyOrder(a, b); 465 | }); 466 | 467 | it('does not match same nested objects with different items order and different dates', () => { 468 | const a = { 469 | foo: 'bar', 470 | foo2: { 471 | foo3: [ 472 | { 473 | foo5: 'bar5', 474 | foo6: 'bar6', 475 | foo7: { 476 | foo8: 'bar8', 477 | foo9: [ 478 | 'foo11', 479 | 'foo10', 480 | 'foo12', 481 | new Date('2021-01-01'), 482 | ], 483 | }, 484 | }, 485 | 'foo4', 486 | ], 487 | }, 488 | }; 489 | const b = { 490 | foo: 'bar', 491 | foo2: { 492 | foo3: [ 493 | 'foo4', 494 | { 495 | foo5: 'bar5', 496 | foo6: 'bar6', 497 | foo7: { 498 | foo8: 'bar8', 499 | foo9: [ 500 | 'foo10', 501 | new Date('2021-01-02'), 502 | 'foo11', 503 | 'foo12', 504 | ], 505 | }, 506 | }, 507 | ], 508 | }, 509 | }; 510 | expectToNotDeepEqualInAnyOrder(a, b); 511 | }); 512 | 513 | it('does not match different nested objects', () => { 514 | const a = { 515 | foo: 'bar', 516 | foo2: { 517 | foo3: [ 518 | 'foo4', 519 | { 520 | foo5: 'bar5', 521 | foo6: 'bar6', 522 | foo7: { 523 | foo8: 'bar8', 524 | foo9: [ 525 | 'foo10', 526 | 'foo11', 527 | 'foo12', 528 | ], 529 | }, 530 | }, 531 | ], 532 | }, 533 | }; 534 | const b = { 535 | foo: 'bar', 536 | foo2: { 537 | foo3: [ 538 | 'foo4-different', 539 | { 540 | foo5: 'bar5', 541 | foo6: 'bar6', 542 | foo7: { 543 | foo8: 'bar8', 544 | foo9: [ 545 | 'foo10', 546 | 'foo11', 547 | 'foo12', 548 | ], 549 | }, 550 | }, 551 | ], 552 | }, 553 | }; 554 | expectToNotDeepEqualInAnyOrder(a, b); 555 | }); 556 | 557 | it('prepends message from expect', () => { 558 | expect( 559 | () => expect(true, 'message1').to.deep.equalInAnyOrder(false), 560 | ).to.throw().and.satisfy(e => /^message1:/.test(e.message)); 561 | }); 562 | 563 | it('prepends message from equalInAnyOrder', () => { 564 | expect( 565 | () => expect(true).to.deep.equalInAnyOrder(false, 'message1'), 566 | ).to.throw().and.satisfy(e => /^message1:/.test(e.message)); 567 | }); 568 | 569 | it('prefers message from chain over expect', () => { 570 | expect( 571 | () => expect(true, 'message1').to.deep.equalInAnyOrder(false, 'message2'), 572 | ).to.throw().and.satisfy(e => /^message2:/.test(e.message)); 573 | }); 574 | 575 | it('matches arrays of objects in different ordering', () => { 576 | const a = [{ foo: 'foo' }, { bar: 'bar' }]; 577 | const b = [{ bar: 'bar' }, { foo: 'foo' }]; 578 | expectToDeepEqualInAnyOrder(a, b); 579 | }); 580 | 581 | it('works in combination with chai-roughly', () => { 582 | const a = { 583 | values: [122.9, 0], 584 | }; 585 | const b = { 586 | values: [0, 123], 587 | }; 588 | expect(a).to.roughly(0.1).to.deep.equalInAnyOrder(b); 589 | }); 590 | 591 | it('works in combination with chai-roughly - negate', () => { 592 | const a = { 593 | values: [122.8, 0], 594 | }; 595 | const b = { 596 | values: [0, 123], 597 | }; 598 | expect(a).to.roughly(0.1).to.not.deep.equalInAnyOrder(b); 599 | }); 600 | 601 | it('works in combination with chai-roughly - nested', () => { 602 | const a = { 603 | lorem: [ 604 | { 605 | foo: { 606 | foo1: [122.9, 0], 607 | }, 608 | bar: { 609 | bar1: [299.9, 0, 499.9, 199.9], 610 | }, 611 | }, 612 | 9.9, 613 | 5.1, 614 | ], 615 | }; 616 | const b = { 617 | lorem: [ 618 | 5, 619 | 10, 620 | { 621 | foo: { 622 | foo1: [0, 123], 623 | }, 624 | bar: { 625 | bar1: [0, 500, 200, 300], 626 | }, 627 | }, 628 | ], 629 | }; 630 | expect(a).to.roughly(0.2).to.deep.equalInAnyOrder(b); 631 | }); 632 | }); 633 | --------------------------------------------------------------------------------