package/package.json000644 000766 000024 0000004340 13046022043013011 0ustar00000000 000000 { "name": "is-equal", "version": "1.5.5", "description": "Are these two values conceptually equal?", "author": "Jordan Harband", "license": "MIT", "main": "index.js", "scripts": { "pretest": "npm run --silent lint", "test": "npm run --silent tests-only", "posttest": "npm run --silent security", "tests-only": "npm run --silent test:native && npm run --silent test:why && npm run --silent test:shimmed && npm run --silent test:corejs", "test:native": "node test/native", "test:why": "node test/why", "test:shimmed": "node test/shimmed", "test:corejs": "node test/corejs", "coverage": "covert test/native test/why", "coverage-quiet": "covert test/native.js --quiet", "lint": "npm run --silent jscs && npm run --silent eslint", "jscs": "jscs *.js test/*.js", "eslint": "eslint *.js test/*.js", "security": "nsp check" }, "repository": { "type": "git", "url": "git://github.com/ljharb/is-equal.git" }, "bugs": { "url": "https://github.com/ljharb/is-equal/issues" }, "homepage": "https://github.com/ljharb/is-equal", "keywords": [ "equal", "is", "compare", "comparison", "equality" ], "dependencies": { "has": "^1.0.1", "is-arrow-function": "^2.0.3", "is-boolean-object": "^1.0.0", "is-callable": "^1.1.3", "is-date-object": "^1.0.1", "is-generator-function": "^1.0.6", "is-number-object": "^1.0.3", "is-regex": "^1.0.3", "is-string": "^1.0.4", "is-symbol": "^1.0.1", "object.entries": "^1.0.4" }, "devDependencies": { "tape": "^4.6.3", "covert": "^1.1.0", "jscs": "^3.0.7", "foreach": "^2.0.5", "make-arrow-function": "^1.1.0", "make-generator-function": "^1.1.0", "semver": "^5.3.0", "eslint": "^3.15.0", "@ljharb/eslint-config": "^11.0.0", "nsp": "^2.6.2", "replace": "^0.3.0", "es6-shim": "^0.35.3", "core-js": "^2.4.1" }, "testling": { "files": "test/native.js", "browsers": [ "iexplore/6.0..latest", "firefox/3.0..6.0", "firefox/15.0..latest", "firefox/nightly", "chrome/4.0..10.0", "chrome/20.0..latest", "chrome/canary", "opera/10.0..12.0", "opera/15.0..latest", "opera/next", "safari/4.0..latest", "ipad/6.0..latest", "iphone/6.0..latest", "android-browser/4.2" ] }, "engines": { "node": ">= 0.4" } } package/.npmignore000644 000766 000024 0000000175 13042223760012531 0ustar00000000 000000 lib-cov *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results npm-debug.log node_modules html/ .travis.yml Makefile package/README.md000644 000766 000024 0000004027 12717106533012017 0ustar00000000 000000 #is-equal [![Version Badge][2]][1] [![Build Status][3]][4] [![dependency status][5]][6] [![dev dependency status][7]][8] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] [![npm badge][11]][1] [![browser support][9]][10] Are these two values conceptually equal? ## Example ```js var isEqual = require('is-equal'); var assert = require('assert'); var primitives = [true, false, undefined, 42, 'foo']; primitives.forEach(function (primitive) { assert.equal(isEqual(primitive, primitive), true); }); assert.equal(isEqual(/a/g, /a/g)); assert.equal(isEqual(/a/g, new RegExp('a', 'g'))); assert.equal(isEqual({ a: 2 }, { a: 2 }), true); assert.equal(isEqual([1, [2, 3], 4], [1, [2, 3], 4]), true); var timestamp = Date.now(); assert.equal(isEqual(new Date(timestamp), new Date(timestamp)), true); ``` ## Want to know *why* two values are not equal? Will return an empty string if `isEqual` would return `true` - otherwise will return a non-empty string that hopefully explains the reasoning. ``` var whyNotEqual = require('is-equal/why'); assert.equal(whyNotEqual(1, 1), ''); assert.equal( whyNotEqual({ a: 1 }, { a: 2 }), 'value at key "a" differs: numbers are different: 1 !== 2' ); ``` ## Tests Simply clone the repo, `npm install`, and run `npm test` [1]: https://npmjs.org/package/is-equal [2]: http://versionbadg.es/ljharb/is-equal.svg [3]: https://travis-ci.org/ljharb/is-equal.svg [4]: https://travis-ci.org/ljharb/is-equal [5]: https://david-dm.org/ljharb/is-equal.svg [6]: https://david-dm.org/ljharb/is-equal [7]: https://david-dm.org/ljharb/is-equal/dev-status.svg [8]: https://david-dm.org/ljharb/is-equal#info=devDependencies [9]: https://ci.testling.com/ljharb/is-equal.png [10]: https://ci.testling.com/ljharb/is-equal [11]: https://nodei.co/npm/is-equal.png?downloads=true&stars=true [license-image]: http://img.shields.io/npm/l/is-equal.svg [license-url]: LICENSE [downloads-image]: http://img.shields.io/npm/dm/is-equal.svg [downloads-url]: http://npm-stat.com/charts.html?package=is-equal package/LICENSE000644 000766 000024 0000002071 12717106533011542 0ustar00000000 000000 The MIT License (MIT) Copyright (c) 2014 Jordan Harband 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. package/index.js000644 000766 000024 0000000223 12717106533012177 0ustar00000000 000000 'use strict'; var whyNotEqual = require('./why'); module.exports = function isEqual(value, other) { return whyNotEqual(value, other) === ''; }; package/getSymbolIterator.js000644 000766 000024 0000001101 12717106533014543 0ustar00000000 000000 'use strict'; var isSymbol = require('is-symbol'); module.exports = function getSymbolIterator() { var symbolIterator = typeof Symbol === 'function' && isSymbol(Symbol.iterator) ? Symbol.iterator : null; if (typeof Object.getOwnPropertyNames === 'function' && typeof Map === 'function' && typeof Map.prototype.entries === 'function') { Object.getOwnPropertyNames(Map.prototype).forEach(function (name) { if (name !== 'entries' && name !== 'size' && Map.prototype[name] === Map.prototype.entries) { symbolIterator = name; } }); } return symbolIterator; }; package/getCollectionsForEach.js000644 000766 000024 0000001023 12717106533015275 0ustar00000000 000000 'use strict'; module.exports = function () { var mapForEach = (function () { if (typeof Map !== 'function') { return null; } try { Map.prototype.forEach.call({}, function () {}); } catch (e) { return Map.prototype.forEach; } return null; }()); var setForEach = (function () { if (typeof Set !== 'function') { return null; } try { Set.prototype.forEach.call({}, function () {}); } catch (e) { return Set.prototype.forEach; } return null; }()); return { Map: mapForEach, Set: setForEach }; }; package/why.js000644 000766 000024 0000026172 13046022043011677 0ustar00000000 000000 'use strict'; var ObjectPrototype = Object.prototype; var toStr = ObjectPrototype.toString; var booleanValue = Boolean.prototype.valueOf; var has = require('has'); var isArrowFunction = require('is-arrow-function'); var isBoolean = require('is-boolean-object'); var isDate = require('is-date-object'); var isGenerator = require('is-generator-function'); var isNumber = require('is-number-object'); var isRegex = require('is-regex'); var isString = require('is-string'); var isSymbol = require('is-symbol'); var isCallable = require('is-callable'); var isProto = Object.prototype.isPrototypeOf; var namedFoo = function foo() {}; var functionsHaveNames = namedFoo.name === 'foo'; var symbolValue = typeof Symbol === 'function' ? Symbol.prototype.valueOf : null; var symbolIterator = require('./getSymbolIterator')(); var collectionsForEach = require('./getCollectionsForEach')(); var getPrototypeOf = Object.getPrototypeOf; if (!getPrototypeOf) { /* eslint-disable no-proto */ if (typeof 'test'.__proto__ === 'object') { getPrototypeOf = function (obj) { return obj.__proto__; }; } else { getPrototypeOf = function (obj) { var constructor = obj.constructor, oldConstructor; if (has(obj, 'constructor')) { oldConstructor = constructor; if (!(delete obj.constructor)) { // reset constructor return null; // can't delete obj.constructor, return null } constructor = obj.constructor; // get real constructor obj.constructor = oldConstructor; // restore constructor } return constructor ? constructor.prototype : ObjectPrototype; // needed for IE }; } /* eslint-enable no-proto */ } var isArray = Array.isArray || function (value) { return toStr.call(value) === '[object Array]'; }; var normalizeFnWhitespace = function normalizeWhitespace(fnStr) { // this is needed in IE 9, at least, which has inconsistencies here. return fnStr.replace(/^function ?\(/, 'function (').replace('){', ') {'); }; var tryMapSetEntries = function tryCollectionEntries(collection) { var foundEntries = []; try { collectionsForEach.Map.call(collection, function (key, value) { foundEntries.push([key, value]); }); } catch (notMap) { try { collectionsForEach.Set.call(collection, function (value) { foundEntries.push([value]); }); } catch (notSet) { return false; } } return foundEntries; }; module.exports = function whyNotEqual(value, other) { if (value === other) { return ''; } if (value == null || other == null) { return value === other ? '' : String(value) + ' !== ' + String(other); } var valToStr = toStr.call(value); var otherToStr = toStr.call(other); if (valToStr !== otherToStr) { return 'toStringTag is not the same: ' + valToStr + ' !== ' + otherToStr; } var valIsBool = isBoolean(value); var otherIsBool = isBoolean(other); if (valIsBool || otherIsBool) { if (!valIsBool) { return 'first argument is not a boolean; second argument is'; } if (!otherIsBool) { return 'second argument is not a boolean; first argument is'; } var valBoolVal = booleanValue.call(value); var otherBoolVal = booleanValue.call(other); if (valBoolVal === otherBoolVal) { return ''; } return 'primitive value of boolean arguments do not match: ' + valBoolVal + ' !== ' + otherBoolVal; } var valIsNumber = isNumber(value); var otherIsNumber = isNumber(value); if (valIsNumber || otherIsNumber) { if (!valIsNumber) { return 'first argument is not a number; second argument is'; } if (!otherIsNumber) { return 'second argument is not a number; first argument is'; } var valNum = Number(value); var otherNum = Number(other); if (valNum === otherNum) { return ''; } var valIsNaN = isNaN(value); var otherIsNaN = isNaN(other); if (valIsNaN && !otherIsNaN) { return 'first argument is NaN; second is not'; } else if (!valIsNaN && otherIsNaN) { return 'second argument is NaN; first is not'; } else if (valIsNaN && otherIsNaN) { return ''; } return 'numbers are different: ' + value + ' !== ' + other; } var valIsString = isString(value); var otherIsString = isString(other); if (valIsString || otherIsString) { if (!valIsString) { return 'second argument is string; first is not'; } if (!otherIsString) { return 'first argument is string; second is not'; } var stringVal = String(value); var otherVal = String(other); if (stringVal === otherVal) { return ''; } return 'string values are different: "' + stringVal + '" !== "' + otherVal + '"'; } var valIsDate = isDate(value); var otherIsDate = isDate(other); if (valIsDate || otherIsDate) { if (!valIsDate) { return 'second argument is Date, first is not'; } if (!otherIsDate) { return 'first argument is Date, second is not'; } var valTime = +value; var otherTime = +other; if (valTime === otherTime) { return ''; } return 'Dates have different time values: ' + valTime + ' !== ' + otherTime; } var valIsRegex = isRegex(value); var otherIsRegex = isRegex(other); if (valIsRegex || otherIsRegex) { if (!valIsRegex) { return 'second argument is RegExp, first is not'; } if (!otherIsRegex) { return 'first argument is RegExp, second is not'; } var regexStringVal = String(value); var regexStringOther = String(other); if (regexStringVal === regexStringOther) { return ''; } return 'regular expressions differ: ' + regexStringVal + ' !== ' + regexStringOther; } var valIsArray = isArray(value); var otherIsArray = isArray(other); if (valIsArray || otherIsArray) { if (!valIsArray) { return 'second argument is an Array, first is not'; } if (!otherIsArray) { return 'first argument is an Array, second is not'; } if (value.length !== other.length) { return 'arrays have different length: ' + value.length + ' !== ' + other.length; } var index = value.length - 1; var equal = ''; var valHasIndex, otherHasIndex; while (equal === '' && index >= 0) { valHasIndex = has(value, index); otherHasIndex = has(other, index); if (!valHasIndex && otherHasIndex) { return 'second argument has index ' + index + '; first does not'; } if (valHasIndex && !otherHasIndex) { return 'first argument has index ' + index + '; second does not'; } equal = whyNotEqual(value[index], other[index]); index -= 1; } return equal; } var valueIsSym = isSymbol(value); var otherIsSym = isSymbol(other); if (valueIsSym !== otherIsSym) { if (valueIsSym) { return 'first argument is Symbol; second is not'; } return 'second argument is Symbol; first is not'; } if (valueIsSym && otherIsSym) { return symbolValue.call(value) === symbolValue.call(other) ? '' : 'first Symbol value !== second Symbol value'; } var valueIsGen = isGenerator(value); var otherIsGen = isGenerator(other); if (valueIsGen !== otherIsGen) { if (valueIsGen) { return 'first argument is a Generator; second is not'; } return 'second argument is a Generator; first is not'; } var valueIsArrow = isArrowFunction(value); var otherIsArrow = isArrowFunction(other); if (valueIsArrow !== otherIsArrow) { if (valueIsArrow) { return 'first argument is an Arrow function; second is not'; } return 'second argument is an Arrow function; first is not'; } if (isCallable(value) || isCallable(other)) { if (functionsHaveNames && whyNotEqual(value.name, other.name) !== '') { return 'Function names differ: "' + value.name + '" !== "' + other.name + '"'; } if (whyNotEqual(value.length, other.length) !== '') { return 'Function lengths differ: ' + value.length + ' !== ' + other.length; } var valueStr = normalizeFnWhitespace(String(value)); var otherStr = normalizeFnWhitespace(String(other)); if (whyNotEqual(valueStr, otherStr) === '') { return ''; } if (!valueIsGen && !valueIsArrow) { return whyNotEqual(valueStr.replace(/\)\s*\{/, '){'), otherStr.replace(/\)\s*\{/, '){')) === '' ? '' : 'Function string representations differ'; } return whyNotEqual(valueStr, otherStr) === '' ? '' : 'Function string representations differ'; } if (typeof value === 'object' || typeof other === 'object') { if (typeof value !== typeof other) { return 'arguments have a different typeof: ' + typeof value + ' !== ' + typeof other; } if (isProto.call(value, other)) { return 'first argument is the [[Prototype]] of the second'; } if (isProto.call(other, value)) { return 'second argument is the [[Prototype]] of the first'; } if (getPrototypeOf(value) !== getPrototypeOf(other)) { return 'arguments have a different [[Prototype]]'; } if (symbolIterator) { var valueIteratorFn = value[symbolIterator]; var valueIsIterable = isCallable(valueIteratorFn); var otherIteratorFn = other[symbolIterator]; var otherIsIterable = isCallable(otherIteratorFn); if (valueIsIterable !== otherIsIterable) { if (valueIsIterable) { return 'first argument is iterable; second is not'; } return 'second argument is iterable; first is not'; } if (valueIsIterable && otherIsIterable) { var valueIterator = valueIteratorFn.call(value); var otherIterator = otherIteratorFn.call(other); var valueNext, otherNext, nextWhy; do { valueNext = valueIterator.next(); otherNext = otherIterator.next(); if (!valueNext.done && !otherNext.done) { nextWhy = whyNotEqual(valueNext, otherNext); if (nextWhy !== '') { return 'iteration results are not equal: ' + nextWhy; } } } while (!valueNext.done && !otherNext.done); if (valueNext.done && !otherNext.done) { return 'first argument finished iterating before second'; } if (!valueNext.done && otherNext.done) { return 'second argument finished iterating before first'; } return ''; } } else if (collectionsForEach.Map || collectionsForEach.Set) { var valueEntries = tryMapSetEntries(value); var otherEntries = tryMapSetEntries(other); var valueEntriesIsArray = isArray(valueEntries); var otherEntriesIsArray = isArray(otherEntries); if (valueEntriesIsArray && !otherEntriesIsArray) { return 'first argument has Collection entries, second does not'; } if (!valueEntriesIsArray && otherEntriesIsArray) { return 'second argument has Collection entries, first does not'; } if (valueEntriesIsArray && otherEntriesIsArray) { var entriesWhy = whyNotEqual(valueEntries, otherEntries); return entriesWhy === '' ? '' : 'Collection entries differ: ' + entriesWhy; } } var key, valueKeyIsRecursive, otherKeyIsRecursive, keyWhy; for (key in value) { if (has(value, key)) { if (!has(other, key)) { return 'first argument has key "' + key + '"; second does not'; } valueKeyIsRecursive = !!value[key] && value[key][key] === value; otherKeyIsRecursive = !!other[key] && other[key][key] === other; if (valueKeyIsRecursive !== otherKeyIsRecursive) { if (valueKeyIsRecursive) { return 'first argument has a circular reference at key "' + key + '"; second does not'; } return 'second argument has a circular reference at key "' + key + '"; first does not'; } if (!valueKeyIsRecursive && !otherKeyIsRecursive) { keyWhy = whyNotEqual(value[key], other[key]); if (keyWhy !== '') { return 'value at key "' + key + '" differs: ' + keyWhy; } } } } for (key in other) { if (has(other, key) && !has(value, key)) { return 'second argument has key "' + key + '"; first does not'; } } return ''; } return false; }; package/CHANGELOG.md000644 000766 000024 0000012236 13046022043012337 0ustar00000000 000000 1.5.5 / 2017-02-05 ================= * [Fix] remove early failure for stringified arrays, to handle Symbols in arrays (https://github.com/mjackson/expect/issues/194) * [Dev Deps] update `eslint`, `@ljharb/eslint-config` 1.5.4 / 2017-01-25 ================= * Exclude `html` files, which were never intentionally added to the package (#4) * [Deps] update `object-entries`, `is-generator-function` * [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `jscs`, `semver`, `nsp`, `es6-shim`; remove extra `object.entries` * [Tests] up to `node` `v7.4`, `v6.9`, `v4.7`; improve test matrix 1.5.3 / 2016-05-24 ================= * [Fix] avoid false circular reference positives with falsy values in nested objects (#24) 1.5.2 / 2016-05-18 ================= * [Deps] update `is-callable` * [Dev Deps] update `tape`, `jscs`, `eslint`, `@ljharb/eslint-config`, `nsp`, `es6-shim`, `core-js` * [Tests] up to `node` `v6.1`, `v5.11`, `v4.4` * [Tests] use pretest/posttest for linting/security * [Fix] fix `Object.prototype.toString()` comparison 1.5.1 / 2016-02-22 ================= * [Fix] fix "why" message for circular reference inequality (#12) * [Refactor] Delete unreachable code (#13) * [Tests] fix assertion messages (#11) * [Docs] fix example (#10) 1.5.0 / 2016-02-15 ================= * [New] add “whyNotEqual” at `require(‘is-equal/why’)` to provide an inequality reason * [Refactor] use `whyNotEqual` internally in `isEqual` * [Deps] update `is-callable` * [Dev Deps] update `es6-shim`, `tape`, `nsp`, `core-js`, `jscs`, `eslint`, `@ljharb/eslint-config` * [Tests] add some more tests * [Tests] use `getSymbolIterator` internal module * [Tests] up to `node` `v5.6`, `v4.3` 1.4.2 / 2015-12-16 ================= * [Fix] avoid false positives when the first items in two arrays are not equal (#5) 1.4.1 / 2015-12-15 ================= * [Fix] ensure that https://github.com/zloirock/core-js/issues/144 doesn't cause false positives (#4) * [Refactor] move `Map`/`Set` `forEach` method detection to a separate module * [Refactor] Factor out "get Symbol.iterator" logic * [Robustness] cache `Object#isPrototypeOf` * [Tests] add more tests (#3, #4) 1.4.0 / 2015-12-10 ================= * [New] Restore basic circular reference support * [Deps] use `has` instead of relying on `Function#call` 1.3.1 / 2015-12-10 ================= * [Fix] Partially revert 2517c2151d57451f7f7009df065bf1601316ee46, since object key ordering shouldn't matter. Reopens #2. * [Fix] handle `Map`/`Set` in Safari 8, which lacks `Symbol.iterator` (#3) * [Fix] normalize function signature whitespace, for IE 9. * [Fix] ignore function name when comparing in engines that lack the "name" property (IE) * [Tests] Only skip generic iterable tests when there's no Symbol.iterator (#3) * [Refactor] don't attempt to run iterable comparisons when there's no Symbol.iterator * [Tests] Separate shimmed from native tests * [Tests] relocate native tests * [Tests] add tests for circular references (#2) 1.3.0 / 2015-12-09 ================= * [New] add support for comparing iterables, including native or `es6-shim`med `Map` and `Set` (#1) * [Refactor] Use `object.entries` to compare objects instead of for loops * [Deps] update `is-callable` * [Dev Deps] update `tape`, `jscs`, `semver`, `eslint`, `@ljharb/eslint-config`, `nsp` * [Tests] fix npm upgrades for older nodes * [Tests] up to `node` `v5.1` * [Tests] ensure node 0.8 doesn't fail 1.2.4 / 2015-09-27 ================= * [Fix] Boxed Symbols should be coerced to primitives before testing for equality * [Refactor] Use `is-boolean-object` to reliably detect Booleans * [Deps] update `is-arrow-function`, `is-date-object` * [Docs] Switch from vb.teelaun.ch to versionbadg.es for the npm version badge SVG * [Tests] up to `io.js` `v3.3`, `node` `v4.1` * [Tests] add `npm run security` and `npm run eslint` * [Dev Deps] update `tape`, `jscs`, `make-arrow-function`, `make-generator-function`, `semver`, `eslint`, `@ljharb/eslint-config`, `nsp`, `covert` 1.2.3 / 2015-02-06 ================= * Update `is-callable`, `is-number-object`, `is-string`, `is-generator-function`, `tape`, `jscs` * Run `travis-ci` tests on `iojs` and `node` v0.12; speed up builds; allow 0.8 failures. 1.2.2 / 2015-01-29 ================= * Update `is-arrow-function`, `is-callable`, `is-number-object`, `is-string` 1.2.1 / 2015-01-29 ================= * Use `is-string` and `is-callable` modules. 1.2.0 / 2015-01-28 ================= * Remove most `Object#toString` checks, to prepare for an ES6 @@toStringTag world where they aren’t reliable. 1.1.1 / 2015-01-20 ================= * Fix generator function detection in newer v8 / io.js * Update `is-arrow-function`, `is-generator-function`, `jscs`, `tape` * toString is a reserved word in older browsers 1.1.0 / 2014-12-15 ================= * Add tests and support for ES6 Symbols, generators, and arrow functions * Consider standard functions equal if name/body/arity are all equal. * Update `covert`, `tape`, `jscs` * Add a bunch of npm scripts 1.0.0 / 2014-08-08 ================== * Updating `tape`, `covert` * Make sure old and unstable nodes don't break Travis package/.jscs.json000644 000766 000024 0000010031 13042326256012441 0ustar00000000 000000 { "es3": true, "additionalRules": [], "requireSemicolons": true, "disallowMultipleSpaces": true, "disallowIdentifierNames": [], "requireCurlyBraces": { "allExcept": [], "keywords": ["if", "else", "for", "while", "do", "try", "catch"] }, "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch", "function"], "disallowSpaceAfterKeywords": [], "disallowSpaceBeforeComma": true, "disallowSpaceAfterComma": false, "disallowSpaceBeforeSemicolon": true, "disallowNodeTypes": [ "DebuggerStatement", "LabeledStatement", "SwitchCase", "SwitchStatement", "WithStatement" ], "requireObjectKeysOnNewLine": { "allExcept": ["sameLine"] }, "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, "beforeOpeningCurlyBrace": true }, "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true }, "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true }, "disallowSpacesInFunctionDeclaration": { "beforeOpeningRoundBrace": true }, "requireSpaceBetweenArguments": true, "disallowSpacesInsideParentheses": true, "disallowSpacesInsideArrayBrackets": true, "disallowQuotedKeysInObjects": { "allExcept": ["reserved"] }, "disallowSpaceAfterObjectKeys": true, "requireCommaBeforeLineBreak": true, "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], "requireSpaceAfterPrefixUnaryOperators": [], "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], "requireSpaceBeforePostfixUnaryOperators": [], "disallowSpaceBeforeBinaryOperators": [], "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="], "disallowSpaceAfterBinaryOperators": [], "disallowImplicitTypeConversion": ["binary", "string"], "disallowKeywords": ["with", "eval"], "requireKeywordsOnNewLine": [], "disallowKeywordsOnNewLine": ["else"], "requireLineFeedAtFileEnd": true, "disallowTrailingWhitespace": true, "disallowTrailingComma": true, "excludeFiles": ["node_modules/**", "vendor/**"], "disallowMultipleLineStrings": true, "requireDotNotation": { "allExcept": ["keywords"] }, "requireParenthesesAroundIIFE": true, "validateLineBreaks": "LF", "validateQuoteMarks": { "escape": true, "mark": "'" }, "disallowOperatorBeforeLineBreak": [], "requireSpaceBeforeKeywords": [ "do", "for", "if", "else", "switch", "case", "try", "catch", "finally", "while", "with", "return" ], "validateAlignedFunctionParameters": { "lineBreakAfterOpeningBraces": true, "lineBreakBeforeClosingBraces": true }, "requirePaddingNewLinesBeforeExport": true, "validateNewlineAfterArrayElements": { "maximum": 3 }, "requirePaddingNewLinesAfterUseStrict": true, "disallowArrowFunctions": true, "disallowMultiLineTernary": false, "validateOrderInObjectKeys": "asc-insensitive", "disallowIdenticalDestructuringNames": true, "disallowNestedTernaries": { "maxLevel": 1 }, "requireSpaceAfterComma": { "allExcept": ["trailing"] }, "requireAlignedMultilineParams": false, "requireSpacesInGenerator": { "afterStar": true }, "disallowSpacesInGenerator": { "beforeStar": true }, "disallowVar": false, "requireArrayDestructuring": false, "requireEnhancedObjectLiterals": false, "requireObjectDestructuring": false, "requireEarlyReturn": false, "requireCapitalizedConstructorsNew": { "allExcept": ["Function", "String", "Object", "Symbol", "Number", "Date", "RegExp", "Error", "Boolean", "Array"] }, "requireImportAlphabetized": false, "requireSpaceBeforeObjectValues": true, "requireSpaceBeforeDestructuredValues": true, "disallowSpacesInsideTemplateStringPlaceholders": true, "disallowArrayDestructuringReturn": false, "requireNewlineBeforeSingleStatementsInIf": false, "disallowUnusedVariables": true, "requireSpacesInsideImportedObjectBraces": true, "requireUseStrict": true } package/test/corejs.js000644 000766 000024 0000000114 12717106533013333 0ustar00000000 000000 'use strict'; require('core-js'); require('./why'); require('./native'); package/test/native.js000644 000766 000024 0000035714 13046022043013337 0ustar00000000 000000 'use strict'; var test = require('tape'); var isEqual = require('../'); var isSymbol = require('is-symbol'); var genFn = require('make-generator-function'); var hasGeneratorSupport = typeof genFn === 'function'; var arrowFunctions = require('make-arrow-function').list(); var hasArrowFunctionSupport = arrowFunctions.length > 0; var objectEntries = require('object.entries'); var forEach = require('foreach'); var collectionsForEach = require('../getCollectionsForEach')(); var fooFn = function fooFn() {}; var functionsHaveNames = fooFn.name === 'fooFn'; var symbolIterator = require('../getSymbolIterator')(); var copyFunction = function (fn) { /* eslint-disable no-new-func */ return Function('return ' + String(fn))(); }; test('primitives', function (t) { t.ok(isEqual(), 'undefineds are equal'); t.ok(isEqual(null, null), 'nulls are equal'); t.ok(isEqual(true, true), 'trues are equal'); t.ok(isEqual(false, false), 'falses are equal'); t.notOk(isEqual(true, false), 'true:false is not equal'); t.notOk(isEqual(false, true), 'false:true is not equal'); t.ok(isEqual('foo', 'foo'), 'strings are equal'); t.ok(isEqual(42, 42), 'numbers are equal'); t.ok(isEqual(0 / Infinity, -0 / Infinity), 'opposite sign zeroes are equal'); t.ok(isEqual(Infinity, Infinity), 'infinities are equal'); t.end(); }); test('NaN', function (t) { t.ok(isEqual(NaN, NaN), 'NaNs are equal'); t.end(); }); test('boxed primitives', function (t) { t.ok(isEqual(Object(''), ''), 'Empty String and empty string are equal'); t.ok(isEqual(Object('foo'), 'foo'), 'String and string are equal'); t.ok(isEqual(Object(true), true), 'Boolean true and boolean true are equal'); t.ok(isEqual(Object(false), false), 'Boolean false and boolean false are equal'); t.ok(isEqual(true, Object(true)), 'boolean true and Boolean true are equal'); t.ok(isEqual(false, Object(false)), 'boolean false and Boolean false are equal'); t.notOk(isEqual(Object(true), false), 'Boolean true and boolean false are not equal'); t.notOk(isEqual(Object(false), true), 'Boolean false and boolean true are not equal'); t.notOk(isEqual(false, Object(true)), 'boolean false and Boolean true are not equal'); t.notOk(isEqual(true, Object(false)), 'boolean true and Boolean false are not equal'); t.ok(isEqual(Object(42), 42), 'Number and number literal are equal'); t.end(); }); test('dates', function (t) { t.ok(isEqual(new Date(123), new Date(123)), 'two dates with the same timestamp are equal'); t.notOk(isEqual(new Date(123), new Date(456)), 'two dates with different timestamp are not equal'); t.end(); }); test('regexes', function (t) { t.ok(isEqual(/a/g, /a/g), 'two regex literals are equal'); t.notOk(isEqual(/a/g, /b/g), 'two different regex literals (same flags, diff source) are not equal'); t.notOk(isEqual(/a/i, /a/g), 'two different regex literals (same source, diff flags) are not equal'); t.ok(isEqual(new RegExp('a', 'g'), new RegExp('a', 'g')), 'two regex objects are equal'); t.notOk(isEqual(new RegExp('a', 'g'), new RegExp('b', 'g')), 'two different regex objects are equal'); t.ok(isEqual(new RegExp('a', 'g'), /a/g), 'regex object and literal, same content, are equal'); t.notOk(isEqual(new RegExp('a', 'g'), /b/g), 'regex object and literal, different content, are not equal'); t.end(); }); test('arrays', function (t) { t.ok(isEqual([], []), 'empty arrays are equal'); t.ok(isEqual([1, 2, 3], [1, 2, 3]), 'same arrays are equal'); t.notOk(isEqual([1, 2, 3], [3, 2, 1]), 'arrays in different order with same values are not equal'); t.notOk(isEqual([1, 2], [1, 2, 3]), 'arrays with different lengths are not equal'); t.notOk(isEqual([1, 2, 3], [1, 2]), 'arrays with different lengths are not equal'); t.test('nested values', function (st) { st.ok(isEqual([[1, 2], [2, 3], [3, 4]], [[1, 2], [2, 3], [3, 4]]), 'arrays with same array values are equal'); st.end(); }); t.test('nested objects', function (st) { var arr1 = [ { a: 0, b: '1', c: false }, { a: 1, b: '2', c: false } ]; var arr2 = [ { a: 0, b: '1', c: true }, { a: 1, b: '2', c: false } ]; st.notOk(isEqual(arr1[0], arr2[0]), 'array items 0 are not equal'); st.ok(isEqual(arr1[1], arr2[1]), 'array items 1 are equal'); st.notOk(isEqual(arr1, arr2), 'two arrays with nested inequal objects are not equal'); st.end(); }); t.end(); }); test('objects', function (t) { t.test('prototypes', function (st) { var F = function F() { this.foo = 42; }; var G = function G() {}; G.prototype = new F(); G.prototype.constructor = G; var H = function H() {}; H.prototype = G.prototype; var I = function I() {}; var f1 = new F(); var f2 = new F(); var g1 = new G(); var h1 = new H(); var i1 = new I(); st.ok(isEqual(f1, f2), 'two instances of the same thing are equal'); st.ok(isEqual(g1, h1), 'two instances of different things with the same prototype are equal'); st.notOk(isEqual(f1, i1), 'two instances of different things with a different prototype are not equal'); var isParentEqualToChild = isEqual(f1, g1); st.notOk(isParentEqualToChild, 'two instances of a parent and child are not equal'); var isChildEqualToParent = isEqual(g1, f1); st.notOk(isChildEqualToParent, 'two instances of a child and parent are not equal'); g1.foo = 'bar'; var g2 = new G(); st.notOk(isEqual(g1, g2), 'two instances of the same thing with different properties are not equal'); st.notOk(isEqual(g2, g1), 'two instances of the same thing with different properties are not equal'); st.end(); }); t.test('literals', function (st) { var a = { foo: 42 }; var b = { foo: 42 }; st.ok(isEqual(a, a), 'same hash is equal to itself'); st.ok(isEqual(a, b), 'two similar hashes are equal'); st.ok(isEqual({ nested: a }, { nested: a }), 'similar hashes with same nested hash are equal'); st.ok(isEqual({ nested: a }, { nested: b }), 'similar hashes with similar nested hash are equal'); st.notOk(isEqual({ a: 42, b: 0 }, { a: 42 }), 'second hash missing a key is not equal'); st.notOk(isEqual({ a: 42 }, { a: 42, b: 0 }), 'first hash missing a key is not equal'); st.notOk(isEqual({ a: 1 }, { a: 2 }), 'two objects with equal keys but inequal values are not equal'); st.notOk(isEqual({ c: 1 }, { a: 1 }), 'two objects with inequal keys but same values are not equal'); var obj1 = { a: 0, b: '1', c: false }; var obj2 = { a: 0, b: '1', c: true }; st.notOk(isEqual(obj1, obj2), 'two objects with inequal boolean keys are not equal'); st.end(); }); t.test('key ordering', function (st) { var a = { a: 1, b: 2 }; var b = { b: 2 }; b.a = 1; st.ok(isEqual(a, b), 'objects with different key orderings but same contents are equal'); st.end(); }); t.end(); }); test('functions', function (t) { var f1 = Object(function f() { /* SOME STUFF */ return 1; }); var f2 = Object(function f() { /* SOME STUFF */ return 1; }); var f3 = Object(function f() { /* SOME DIFFERENT STUFF */ return 2; }); var g = Object(function g() { /* SOME STUFF */ return 1; }); var anon1 = Object(function () { /* ANONYMOUS! */ return 'anon'; }); var anon2 = Object(function () { /* ANONYMOUS! */ return 'anon'; }); /* jscs: disable */ /* eslint-disable space-before-function-paren */ /* eslint-disable space-before-blocks */ var fnNoSpace = Object(function(){}); /* eslint-enable space-before-blocks */ /* eslint-enable space-before-function-paren */ /* jscs: enable */ var fnWithSpaceBeforeBody = Object(function () {}); var emptyFnWithName = Object(function a() {}); /* eslint-disable no-unused-vars */ var emptyFnOneArg = Object(function (a) {}); var anon1withArg = Object(function (a) { /* ANONYMOUS! */ return 'anon'; }); /* eslint-enable no-unused-vars */ /* for code coverage */ f1(); f2(); f3(); g(); anon1(); anon2(); /* end for code coverage */ t.ok(isEqual(f1, f1), 'same function is equal to itself'); t.ok(isEqual(anon1, anon1), 'same anon function is equal to itself'); t.notOk(isEqual(anon1, anon1withArg), 'similar anon function with different lengths are not equal'); if (functionsHaveNames) { t.notOk(isEqual(f1, g), 'functions with different names but same implementations are not equal'); } else { t.comment('* function names not supported *'); t.ok(isEqual(f1, g), 'functions with different names but same implementations should not be equal, but are'); } t.ok(isEqual(f1, f2), 'functions with same names but same implementations are equal'); t.notOk(isEqual(f1, f3), 'functions with same names but different implementations are not equal'); t.ok(isEqual(anon1, anon2), 'anon functions with same implementations are equal'); t.ok(isEqual(fnNoSpace, fnWithSpaceBeforeBody), 'functions with same arity/name/body are equal despite whitespace between signature and body'); if (functionsHaveNames) { t.notOk(isEqual(emptyFnWithName, fnNoSpace), 'functions with same arity/body, diff name, are not equal'); } else { t.comment('* function names not supported *'); t.notOk(isEqual(emptyFnWithName, fnNoSpace), 'functions with same arity/body, diff name, should not be equal, but are'); } t.notOk(isEqual(emptyFnOneArg, fnNoSpace), 'functions with same name/body, diff arity, are not equal'); t.test('generators', { skip: !hasGeneratorSupport }, function (st) { /* eslint-disable no-new-func */ var genFnStar = Function('return function* () {};')(); var genFnSpaceStar = Function('return function *() {};')(); var genNoSpaces = Function('return function*(){};')(); st.notOk(isEqual(fnNoSpace, genNoSpaces), 'generator and fn that are otherwise identical are not equal'); var generators = [genFnStar, genFnSpaceStar, genNoSpaces]; forEach(generators, function (generator) { st.ok(isEqual(generator, generator), generator + ' is equal to itself'); st.ok(isEqual(generator, copyFunction(generator)), generator + ' is equal to copyFunction(generator)'); }); st.end(); }); t.test('arrow functions', { skip: !hasArrowFunctionSupport }, function (st) { forEach(arrowFunctions, function (fn) { st.notOk(isEqual(fnNoSpace, fn), fn + ' not equal to ' + fnNoSpace); st.ok(isEqual(fn, fn), fn + ' equal to itself'); st.ok(isEqual(fn, copyFunction(fn)), fn + ' equal to copyFunction(fn)'); }); st.end(); }); t.end(); }); var hasSymbols = typeof Symbol === 'function' && isSymbol(Symbol('foo')); test('symbols', { skip: !hasSymbols }, function (t) { var foo = 'foo'; var fooSym = Symbol(foo); var objectFooSym = Object(fooSym); t.ok(isEqual(fooSym, fooSym), 'Symbol("foo") is equal to itself'); t.ok(isEqual(fooSym, objectFooSym), 'Symbol("foo") is equal to the object form of itself'); t.notOk(isEqual(Symbol(foo), Symbol(foo)), 'Symbol("foo") is not equal to Symbol("foo"), even when the string is the same instance'); t.notOk(isEqual(Symbol(foo), Object(Symbol(foo))), 'Symbol("foo") is not equal to Object(Symbol("foo")), even when the string is the same instance'); t.test('arrays containing symbols', function (st) { st.ok( isEqual([fooSym], [fooSym]), 'Arrays each containing the same instance of Symbol("foo") are equal' ); st.notOk( isEqual([Symbol(foo)], [Object(Symbol(foo))]), 'An array containing Symbol("foo") is not equal to Object(Symbol("foo")), even when the string is the same instance' ); st.end(); }); t.end(); }); var genericIterator = function (obj) { var entries = objectEntries(obj); return function iterator() { return { next: function () { return { done: entries.length === 0, value: entries.shift() }; } }; }; }; test('iterables', function (t) { t.test('Maps', { skip: !collectionsForEach.Map }, function (mt) { var a = new Map(); a.set('a', 'b'); a.set('c', 'd'); var b = new Map(); b.set('a', 'b'); b.set('c', 'd'); var c = new Map(); c.set('a', 'b'); mt.equal(isEqual(a, b), true, 'equal Maps (a, b) are equal'); mt.equal(isEqual(b, a), true, 'equal Maps (b, a) are equal'); mt.equal(isEqual(a, c), false, 'unequal Maps (a, c) are not equal'); mt.equal(isEqual(b, c), false, 'unequal Maps (b, c) are not equal'); mt.equal(isEqual(c, a), false, 'unequal Maps (c, a) are not equal'); mt.equal(isEqual(c, b), false, 'unequal Maps (c, b) are not equal'); mt.end(); }); t.test('Sets', { skip: !collectionsForEach.Set }, function (st) { var a = new Set(); a.add('a'); a.add('b'); var b = new Set(); b.add('a'); b.add('b'); var c = new Set(); c.add('a'); st.ok(isEqual(a, b), 'equal Set (a, b) are equal'); st.ok(isEqual(b, a), 'equal Set (b, a) are equal'); st.notOk(isEqual(a, c), 'unequal Set (a, c) are not equal'); st.notOk(isEqual(b, c), 'unequal Set (b, c) are not equal'); st.notOk(isEqual(c, a), 'unequal Set (c, a) are not equal'); st.notOk(isEqual(c, b), 'unequal Set (c, b) are not equal'); st.test('Sets with strings as iterables', function (sst) { var ab; // eslint-disable-next-line max-statements-per-line try { ab = new Set('ab'); } catch (e) { ab = new Set(); } // node 0.12 throws when given a string if (ab.size !== 2) { // work around IE 11 (and others) bug accepting iterables ab.add('a'); ab.add('b'); } var ac; // eslint-disable-next-line max-statements-per-line try { ac = new Set('ac'); } catch (e) { ac = new Set(); } // node 0.12 throws when given a string if (ab.size !== 2) { // work around IE 11 (and others) bug accepting iterables ab.add('a'); ab.add('c'); } st.notOk(isEqual(ab, ac), 'Sets initially populated with different strings are not equal'); sst.end(); }); st.end(); }); var obj = { a: { aa: true }, b: [2] }; t.test('generic iterables', { skip: !symbolIterator }, function (it) { var a = { foo: 'bar' }; var b = { bar: 'baz' }; it.equal(isEqual(a, b), false, 'normal a and normal b are not equal'); a[symbolIterator] = genericIterator(obj); it.equal(isEqual(a, b), false, 'iterable a / normal b are not equal'); it.equal(isEqual(b, a), false, 'iterable b / normal a are not equal'); it.equal(isEqual(a, obj), false, 'iterable a / normal obj are not equal'); it.equal(isEqual(obj, a), false, 'normal obj / iterable a are not equal'); b[symbolIterator] = genericIterator(obj); it.equal(isEqual(a, b), true, 'iterable a / iterable b are equal'); it.equal(isEqual(b, a), true, 'iterable b / iterable a are equal'); it.equal(isEqual(b, obj), false, 'iterable b and normal obj are not equal'); it.equal(isEqual(obj, b), false, 'normal obj / iterable b are not equal'); it.end(); }); t.test('unequal iterables', { skip: !symbolIterator }, function (it) { var c = {}; c[symbolIterator] = genericIterator({}); var d = {}; d[symbolIterator] = genericIterator(obj); it.equal(isEqual(c, d), false, 'iterable c / iterable d are not equal'); it.equal(isEqual(d, c), false, 'iterable d / iterable c are not equal'); it.end(); }); t.end(); }); var Circular = function Circular() { this.circularRef = this; }; test('circular references', function (t) { var a = new Circular(); var b = new Circular(); t.equal(isEqual(a, b), true, 'two circular referencing instances are equal'); var c = {}; var d = {}; c.c = c; d.d = d; t.equal(isEqual(c, d), false, 'two objects with different circular references are not equal'); t.end(); }); package/test/shimmed.js000644 000766 000024 0000000115 12717106533013475 0ustar00000000 000000 'use strict'; require('es6-shim'); require('./why'); require('./native'); package/test/why.js000644 000766 000024 0000052414 13046022043012654 0ustar00000000 000000 'use strict'; /* eslint-disable operator-linebreak */ var test = require('tape'); var isEqualWhy = require('../why'); var isSymbol = require('is-symbol'); var genFn = require('make-generator-function'); var hasGeneratorSupport = typeof genFn === 'function'; var arrowFunctions = require('make-arrow-function').list(); var hasArrowFunctionSupport = arrowFunctions.length > 0; var objectEntries = require('object.entries'); var forEach = require('foreach'); var collectionsForEach = require('../getCollectionsForEach')(); var fooFn = function fooFn() {}; var functionsHaveNames = fooFn.name === 'fooFn'; var symbolIterator = require('../getSymbolIterator')(); var copyFunction = function (fn) { /* eslint-disable no-new-func */ return Function('return ' + String(fn))(); }; test('primitives', function (t) { t.equal('', isEqualWhy(), 'undefineds are equal'); t.equal('', isEqualWhy(null, null), 'nulls are equal'); t.equal('', isEqualWhy(true, true), 'trues are equal'); t.equal('', isEqualWhy(false, false), 'falses are equal'); t.equal('primitive value of boolean arguments do not match: true !== false', isEqualWhy(true, false), 'true:false is not equal'); t.equal('primitive value of boolean arguments do not match: false !== true', isEqualWhy(false, true), 'false:true is not equal'); t.equal('', isEqualWhy('foo', 'foo'), 'strings are equal'); t.equal('', isEqualWhy(42, 42), 'numbers are equal'); t.equal('', isEqualWhy(0 / Infinity, -0 / Infinity), 'opposite sign zeroes are equal'); t.equal('', isEqualWhy(Infinity, Infinity), 'infinities are equal'); t.end(); }); test('NaN', function (t) { t.equal('', isEqualWhy(NaN, NaN), 'NaNs are equal'); t.end(); }); test('boxed primitives', function (t) { t.equal('', isEqualWhy(Object(''), ''), 'Empty String and empty string are equal'); t.equal('', isEqualWhy(Object('foo'), 'foo'), 'String and string are equal'); t.equal('', isEqualWhy(Object(true), true), 'Boolean true and boolean true are equal'); t.equal('', isEqualWhy(Object(false), false), 'Boolean false and boolean false are equal'); t.equal('', isEqualWhy(true, Object(true)), 'boolean true and Boolean true are equal'); t.equal('', isEqualWhy(false, Object(false)), 'boolean false and Boolean false are equal'); t.equal( 'primitive value of boolean arguments do not match: true !== false', isEqualWhy(Object(true), false), 'Boolean true and boolean false are not equal' ); t.equal( 'primitive value of boolean arguments do not match: false !== true', isEqualWhy(Object(false), true), 'Boolean false and boolean true are not equal' ); t.equal( 'primitive value of boolean arguments do not match: false !== true', isEqualWhy(false, Object(true)), 'boolean false and Boolean true are not equal' ); t.equal( 'primitive value of boolean arguments do not match: true !== false', isEqualWhy(true, Object(false)), 'boolean true and Boolean false are not equal' ); t.equal('', isEqualWhy(Object(42), 42), 'Number and number literal are equal'); t.end(); }); test('dates', function (t) { t.equal('', isEqualWhy(new Date(123), new Date(123)), 'two dates with the same timestamp are equal'); t.equal( 'Dates have different time values: 123 !== 456', isEqualWhy(new Date(123), new Date(456)), 'two dates with different timestamp are not equal' ); t.end(); }); test('regexes', function (t) { t.equal('', isEqualWhy(/a/g, /a/g), 'two regex literals are equal'); t.equal( 'regular expressions differ: /a/g !== /b/g', isEqualWhy(/a/g, /b/g), 'two different regex literals are not equal' ); t.equal( 'regular expressions differ: /a/i !== /a/g', isEqualWhy(/a/i, /a/g), 'two different regex literals (same source, diff flags) are not equal' ); t.equal('', isEqualWhy(new RegExp('a', 'g'), new RegExp('a', 'g')), 'two regex objects are equal'); t.equal( 'regular expressions differ: /a/g !== /b/g', isEqualWhy(new RegExp('a', 'g'), new RegExp('b', 'g')), 'two different regex objects are equal' ); t.equal('', isEqualWhy(new RegExp('a', 'g'), /a/g), 'regex object and literal, same content, are equal'); t.equal( 'regular expressions differ: /a/g !== /b/g', isEqualWhy(new RegExp('a', 'g'), /b/g), 'regex object and literal, different content, are not equal' ); t.end(); }); test('arrays', function (t) { t.equal('', isEqualWhy([], []), 'empty arrays are equal'); t.equal('', isEqualWhy([1, 2, 3], [1, 2, 3]), 'same arrays are equal'); t.equal( 'numbers are different: 3 !== 1', isEqualWhy([1, 2, 3], [3, 2, 1]), 'arrays in different order with same values are not equal' ); t.equal( 'arrays have different length: 2 !== 3', isEqualWhy([1, 2], [1, 2, 3]), 'arrays with different lengths are not equal' ); t.equal( 'arrays have different length: 3 !== 2', isEqualWhy([1, 2, 3], [1, 2]), 'arrays with different lengths are not equal' ); t.test('nested values', function (st) { st.equal('', isEqualWhy([[1, 2], [2, 3], [3, 4]], [[1, 2], [2, 3], [3, 4]]), 'arrays with same array values are equal'); st.end(); }); t.test('nested objects', function (st) { var arr1 = [ { a: 0, b: '1', c: false }, { a: 1, b: '2', c: false } ]; var arr2 = [ { a: 0, b: '1', c: true }, { a: 1, b: '2', c: false } ]; st.equal( isEqualWhy(arr1[0], arr2[0]), 'value at key "c" differs: primitive value of boolean arguments do not match: false !== true', 'array items 0 are not equal' ); st.equal( isEqualWhy(arr1[1], arr2[1]), '', 'array items 1 are equal' ); st.equal( isEqualWhy(arr1, arr2), 'value at key "c" differs: primitive value of boolean arguments do not match: false !== true', 'two arrays with nested inequal objects are not equal' ); st.end(); }); t.end(); }); test('objects', function (t) { t.test('prototypes', function (st) { var F = function F() { this.foo = 42; }; var G = function G() {}; G.prototype = new F(); G.prototype.constructor = G; var H = function H() {}; H.prototype = G.prototype; var I = function I() {}; var f1 = new F(); var f2 = new F(); var g1 = new G(); var h1 = new H(); var i1 = new I(); st.equal(isEqualWhy(f1, f2), '', 'two instances of the same thing are equal'); st.equal(isEqualWhy(g1, h1), '', 'two instances of different things with the same prototype are equal'); st.equal( isEqualWhy(f1, i1), 'arguments have a different [[Prototype]]', 'two instances of different things with a different prototype are not equal' ); var isParentEqualToChild = isEqualWhy(f1, g1); st.equal( isParentEqualToChild, 'arguments have a different [[Prototype]]', 'two instances of a parent and child are not equal' ); var isChildEqualToParent = isEqualWhy(g1, f1); st.equal( isChildEqualToParent, 'arguments have a different [[Prototype]]', 'two instances of a child and parent are not equal' ); g1.foo = 'bar'; var g2 = new G(); st.equal( isEqualWhy(g1, g2), 'first argument has key "foo"; second does not', 'two instances of the same thing with different properties are not equal' ); st.equal( isEqualWhy(g2, g1), 'second argument has key "foo"; first does not', 'two instances of the same thing with different properties are not equal' ); st.end(); }); t.test('literals', function (st) { var a = { foo: 42 }; var b = { foo: 42 }; st.equal('', isEqualWhy(a, a), 'same hash is equal to itself'); st.equal('', isEqualWhy(a, b), 'two similar hashes are equal'); st.equal('', isEqualWhy({ nested: a }, { nested: a }), 'similar hashes with same nested hash are equal'); st.equal('', isEqualWhy({ nested: a }, { nested: b }), 'similar hashes with similar nested hash are equal'); st.equal( isEqualWhy({ a: 42, b: 0 }, { a: 42 }), 'first argument has key "b"; second does not', 'second hash missing a key is not equal' ); st.equal( isEqualWhy({ a: 42 }, { a: 42, b: 0 }), 'second argument has key "b"; first does not', 'first hash missing a key is not equal' ); st.equal( isEqualWhy({ a: 1 }, { a: 2 }), 'value at key "a" differs: numbers are different: 1 !== 2', 'two objects with equal keys but inequal values are not equal' ); st.equal( isEqualWhy({ c: 1 }, { a: 1 }), 'first argument has key "c"; second does not', 'two objects with inequal keys but same values are not equal' ); var obj1 = { a: 0, b: '1', c: false }; var obj2 = { a: 0, b: '1', c: true }; st.equal( isEqualWhy(obj1, obj2), 'value at key "c" differs: primitive value of boolean arguments do not match: false !== true', 'two objects with inequal boolean keys are not equal' ); st.end(); }); t.test('key ordering', function (st) { var a = { a: 1, b: 2 }; var b = { b: 2 }; b.a = 1; st.equal('', isEqualWhy(a, b), 'objects with different key orderings but same contents are equal'); st.end(); }); t.end(); }); test('functions', function (t) { var f1 = Object(function f() { /* SOME STUFF */ return 1; }); var f2 = Object(function f() { /* SOME STUFF */ return 1; }); var f3 = Object(function f() { /* SOME DIFFERENT STUFF */ return 2; }); var g = Object(function g() { /* SOME STUFF */ return 1; }); var anon1 = Object(function () { /* ANONYMOUS! */ return 'anon'; }); var anon2 = Object(function () { /* ANONYMOUS! */ return 'anon'; }); /* jscs: disable */ /* eslint-disable space-before-function-paren */ /* eslint-disable space-before-blocks */ var fnNoSpace = Object(function(){}); /* eslint-enable space-before-blocks */ /* eslint-enable space-before-function-paren */ /* jscs: enable */ var fnWithSpaceBeforeBody = Object(function () {}); var emptyFnWithName = Object(function a() {}); /* eslint-disable no-unused-vars */ var emptyFnOneArg = Object(function (a) {}); var anon1withArg = Object(function (a) { /* ANONYMOUS! */ return 'anon'; }); /* eslint-enable no-unused-vars */ /* for code coverage */ f1(); f2(); f3(); g(); anon1(); anon2(); /* end for code coverage */ t.equal('', isEqualWhy(f1, f1), 'same function is equal to itself'); t.equal('', isEqualWhy(anon1, anon1), 'same anon function is equal to itself'); t.equal( isEqualWhy(anon1, anon1withArg), 'Function lengths differ: 0 !== 1', 'similar anon function with different lengths are not equal' ); if (functionsHaveNames) { t.equal( 'Function names differ: "f" !== "g"', isEqualWhy(f1, g), 'functions with different names but same implementations are not equal' ); } else { t.comment('** function names not supported **'); t.equal( 'Function string representations differ', isEqualWhy(f1, g), 'functions with different names but same implementations are not equal' ); } t.equal('', isEqualWhy(f1, f2), 'functions with same names but same implementations are equal'); t.equal( isEqualWhy(f1, f3), 'Function string representations differ', 'functions with same names but different implementations are not equal' ); t.equal('', isEqualWhy(anon1, anon2), 'anon functions with same implementations are equal'); t.equal('', isEqualWhy(fnNoSpace, fnWithSpaceBeforeBody), 'functions with same arity/name/body are equal despite whitespace between signature and body'); if (functionsHaveNames) { t.equal( isEqualWhy(emptyFnWithName, fnNoSpace), 'Function names differ: "a" !== ""', 'functions with same arity/body, diff name, are not equal' ); } else { t.comment('** function names not supported **'); t.equal( isEqualWhy(emptyFnWithName, fnNoSpace), 'Function string representations differ', 'functions with same arity/body, diff name, are not equal' ); } t.equal( isEqualWhy(emptyFnOneArg, fnNoSpace), 'Function lengths differ: ' + emptyFnOneArg.length + ' !== ' + fnNoSpace.length, 'functions with same name/body, diff arity, are not equal' ); t.test('generators', { skip: !hasGeneratorSupport }, function (st) { /* eslint-disable no-new-func */ var genFnStar = Function('return function* () {};')(); var genFnSpaceStar = Function('return function *() {};')(); var genNoSpaces = Function('return function*(){};')(); var reasonsMap = { 'second argument is a Generator; first is not': true, 'toStringTag is not the same: [object Function] !== [object GeneratorFunction]': true }; var reasons = objectEntries(reasonsMap); var actual = isEqualWhy(fnNoSpace, genNoSpaces); reasonsMap[actual] = true; st.deepEqual(objectEntries(reasonsMap), reasons, 'generator and fn that are otherwise identical are not equal'); var generators = [genFnStar, genFnSpaceStar, genNoSpaces]; forEach(generators, function (generator) { st.equal('', isEqualWhy(generator, generator), generator + ' is equal to itself'); st.equal('', isEqualWhy(generator, copyFunction(generator)), generator + ' is equal to copyFunction(generator)'); }); st.end(); }); t.test('arrow functions', { skip: !hasArrowFunctionSupport }, function (st) { forEach(arrowFunctions, function (fn) { st.equal( 'second argument is an Arrow function; first is not', isEqualWhy(fnNoSpace, fn), fn + ' not equal to ' + fnNoSpace ); st.equal('', isEqualWhy(fn, fn), fn + ' equal to itself'); st.equal('', isEqualWhy(fn, copyFunction(fn)), fn + ' equal to copyFunction(fn)'); }); st.end(); }); t.end(); }); var hasSymbols = typeof Symbol === 'function' && isSymbol(Symbol('foo')); test('symbols', { skip: !hasSymbols }, function (t) { var foo = 'foo'; var fooSym = Symbol(foo); var objectFooSym = Object(fooSym); t.equal('', isEqualWhy(fooSym, fooSym), 'Symbol("foo") is equal to itself'); t.equal('', isEqualWhy(fooSym, objectFooSym), 'Symbol("foo") is equal to the object form of itself'); t.equal( 'first Symbol value !== second Symbol value', isEqualWhy(Symbol(foo), Symbol(foo)), 'Symbol("foo") is not equal to Symbol("foo"), even when the string is the same instance' ); t.equal( 'first Symbol value !== second Symbol value', isEqualWhy(Symbol(foo), Object(Symbol(foo))), 'Symbol("foo") is not equal to Object(Symbol("foo")), even when the string is the same instance' ); t.test('arrays containing symbols', function (st) { st.equal( '', isEqualWhy([fooSym], [fooSym]), 'Arrays each containing the same instance of Symbol("foo") are equal' ); st.equal( 'first Symbol value !== second Symbol value', isEqualWhy([Symbol(foo)], [Object(Symbol(foo))]), 'An array containing Symbol("foo") is not equal to Object(Symbol("foo")), even when the string is the same instance' ); st.end(); }); t.end(); }); var genericIterator = function (obj) { var entries = objectEntries(obj); return function iterator() { return { next: function () { return { done: entries.length === 0, value: entries.shift() }; } }; }; }; test('iterables', function (t) { t.test('Maps', { skip: !collectionsForEach.Map }, function (mt) { var a = new Map(); a.set('a', 'b'); a.set('c', 'd'); var b = new Map(); b.set('a', 'b'); b.set('c', 'd'); var c = new Map(); c.set('a', 'b'); mt.equal( isEqualWhy(a, b), '', 'equal Maps (a, b) are equal' ); mt.equal( isEqualWhy(b, a), '', 'equal Maps (b, a) are equal' ); mt.equal( isEqualWhy(a, c), symbolIterator ? 'second argument finished iterating before first' : 'Collection entries differ: arrays have different length: 2 !== 1', 'unequal Maps (a, c) are not equal' ); mt.equal( isEqualWhy(b, c), symbolIterator ? 'second argument finished iterating before first' : 'Collection entries differ: arrays have different length: 2 !== 1', 'unequal Maps (b, c) are not equal' ); mt.equal( isEqualWhy(c, a), symbolIterator ? 'first argument finished iterating before second' : 'Collection entries differ: arrays have different length: 1 !== 2', 'unequal Maps (c, a) are not equal' ); mt.equal( isEqualWhy(c, b), symbolIterator ? 'first argument finished iterating before second' : 'Collection entries differ: arrays have different length: 1 !== 2', 'unequal Maps (c, b) are not equal' ); mt.end(); }); t.test('Sets', { skip: !collectionsForEach.Set }, function (st) { var a = new Set(); a.add('a'); a.add('b'); var b = new Set(); b.add('a'); b.add('b'); var c = new Set(); c.add('a'); st.equal('', isEqualWhy(a, b), 'equal Set (a, b) are equal'); st.equal('', isEqualWhy(b, a), 'equal Set (b, a) are equal'); st.equal( isEqualWhy(a, c), symbolIterator ? 'second argument finished iterating before first' : 'Collection entries differ: arrays have different length: 2 !== 1', 'unequal Set (a, c) are not equal' ); st.equal( isEqualWhy(b, c), symbolIterator ? 'second argument finished iterating before first' : 'Collection entries differ: arrays have different length: 2 !== 1', 'unequal Set (b, c) are not equal' ); st.equal( isEqualWhy(c, a), symbolIterator ? 'first argument finished iterating before second' : 'Collection entries differ: arrays have different length: 1 !== 2', 'unequal Set (c, a) are not equal' ); st.equal( isEqualWhy(c, b), symbolIterator ? 'first argument finished iterating before second' : 'Collection entries differ: arrays have different length: 1 !== 2', 'unequal Set (b, c) are not equal' ); st.test('Sets with strings as iterables', function (sst) { var ab; // eslint-disable-next-line max-statements-per-line try { ab = new Set('ab'); } catch (e) { ab = new Set(); } // node 0.12 throws when given a string if (ab.size !== 2) { // work around IE 11 (and others) bug accepting iterables ab.add('a'); ab.add('b'); } var ac; // eslint-disable-next-line max-statements-per-line try { ac = new Set('ac'); } catch (e) { ac = new Set(); } // node 0.12 throws when given a string if (ac.size !== 2) { // work around IE 11 (and others) bug accepting iterables ac.add('a'); ac.add('c'); } st.equal( isEqualWhy(ab, ac), symbolIterator ? 'iteration results are not equal: value at key "value" differs: string values are different: "b" !== "c"' : 'Collection entries differ: string values are different: "b" !== "c"', 'Sets initially populated with different strings are not equal' ); sst.end(); }); st.end(); }); var obj = { a: { aa: true }, b: [2] }; t.test('generic iterables', { skip: !symbolIterator }, function (it) { var a = { foo: 'bar' }; var b = { bar: 'baz' }; it.equal(isEqualWhy(a, b), 'first argument has key "foo"; second does not', 'normal a and normal b are not equal'); a[symbolIterator] = genericIterator(obj); it.equal(isEqualWhy(a, b), 'first argument is iterable; second is not', 'iterable a / normal b are not equal'); it.equal(isEqualWhy(b, a), 'second argument is iterable; first is not', 'iterable b / normal a are not equal'); it.equal( isEqualWhy(a, obj), 'first argument is iterable; second is not', 'iterable a / normal obj are not equal' ); it.equal( isEqualWhy(obj, a), 'second argument is iterable; first is not', 'normal obj / iterable a are not equal' ); b[symbolIterator] = genericIterator(obj); it.equal(isEqualWhy(a, b), '', 'iterable a / iterable b are equal'); it.equal(isEqualWhy(b, a), '', 'iterable b / iterable a are equal'); it.equal( isEqualWhy(b, obj), 'first argument is iterable; second is not', 'iterable b and normal obj are not equal' ); it.equal( isEqualWhy(obj, b), 'second argument is iterable; first is not', 'normal obj / iterable b are not equal' ); it.end(); }); t.test('unequal iterables', { skip: !symbolIterator }, function (it) { var c = {}; c[symbolIterator] = genericIterator({}); var d = {}; d[symbolIterator] = genericIterator(obj); it.equal( isEqualWhy(c, d), 'first argument finished iterating before second', 'iterable c / iterable d are not equal' ); it.equal( isEqualWhy(d, c), 'second argument finished iterating before first', 'iterable d / iterable c are not equal' ); it.end(); }); t.end(); }); var Circular = function Circular() { this.circularRef = this; }; test('circular references', function (t) { var a = new Circular(); var b = new Circular(); t.equal( isEqualWhy(a, b), '', 'two circular referencing instances are equal' ); var c = {}; var d = {}; c.c = c; d.d = d; t.equal( isEqualWhy(c, d), 'first argument has key "c"; second does not', 'two objects with different circular references are not equal' ); var e = {}; var f = {}; e.e = e; f.e = null; t.equal( isEqualWhy(e, f), 'first argument has a circular reference at key "e"; second does not', 'two objects without corresponding circular references are not equal' ); t.equal( isEqualWhy(f, e), 'second argument has a circular reference at key "e"; first does not', 'two objects without corresponding circular references are not equal' ); t.test('false positives', function (st) { st.equal( isEqualWhy({ bar: { baz: 'abc' } }, { bar: { baz: null } }), 'value at key "bar" differs: value at key "baz" differs: abc !== null', 'two nested structures with a string vs null key are not equal' ); st.equal( isEqualWhy({ bar: { baz: 'abc' } }, { bar: { baz: undefined } }), 'value at key "bar" differs: value at key "baz" differs: abc !== undefined', 'two nested structures with a string vs null key are not equal' ); st.equal( isEqualWhy({ bar: { baz: 'abc' } }, { bar: { baz: '' } }), 'value at key "bar" differs: value at key "baz" differs: string values are different: "abc" !== ""', 'two nested structures with different string keys are not equal' ); st.end(); }); t.end(); }); package/test/.eslintrc000644 000766 000024 0000000307 13017217117013333 0ustar00000000 000000 { "rules": { "func-name-matching": 0, "max-lines": 0, "max-nested-callbacks": [2, 3], "max-statements-per-line": [2, { "max": 2 }], "max-statements": [1, 25], "no-magic-numbers": 0 } } package/.eslintrc000644 000766 000024 0000000776 13042326260012364 0ustar00000000 000000 { "root": true, "extends": "@ljharb", "rules": { "complexity": [1, 10], "eqeqeq": [2, "allow-null"], "id-length": [2, { "min": 1, "max": 23 }], "max-depth": [2, 5], "max-statements": [1, 10], "max-statements-per-line": [2, { "max": 2 }], "no-extra-parens": [1], "no-implicit-coercion": [2, { "boolean": false, "number": false, "string": true }], "no-restricted-syntax": [2, "BreakStatement", "ContinueStatement", "DebuggerStatement", "LabeledStatement", "WithStatement"] } }