equal-2.0.0/000077500000000000000000000000001315075330300126205ustar00rootroot00000000000000equal-2.0.0/.travis.yml000066400000000000000000000000641315075330300147310ustar00rootroot00000000000000language: node_js node_js: - '6' script: - npm test equal-2.0.0/LICENSE000066400000000000000000000020531315075330300136250ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 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. equal-2.0.0/README.md000066400000000000000000000011451315075330300141000ustar00rootroot00000000000000equal ===== [![Build Status](https://travis-ci.org/shouldjs/equal.svg?branch=master)](https://travis-ci.org/shouldjs/equal) Deep equality comparison implementation for should.js. **Not supported outside of should.js** Function returns an array of failed equality checks if array is empty it means objects are equal: ```js > var eq = require('.'); undefined > var a = {a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10}, ... b = {a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:7,i:9,j:10}; undefined > eq(a, b) [ EqualityFail { a: 8, b: 7, reason: 'A is not equal to B', path: [ 'h' ], showReason: false } ] > ``` equal-2.0.0/format.js000066400000000000000000000002511315075330300144440ustar00rootroot00000000000000export default function format(msg) { var args = arguments; for (var i = 1, l = args.length; i < l; i++) { msg = msg.replace(/%s/, args[i]); } return msg; } equal-2.0.0/index.js000066400000000000000000000200271315075330300142660ustar00rootroot00000000000000import t from "should-type"; import format from "./format"; var hasOwnProperty = Object.prototype.hasOwnProperty; function EqualityFail(a, b, reason, path) { this.a = a; this.b = b; this.reason = reason; this.path = path; } function typeToString(tp) { return tp.type + (tp.cls ? "(" + tp.cls + (tp.sub ? " " + tp.sub : "") + ")" : ""); } var PLUS_0_AND_MINUS_0 = "+0 is not equal to -0"; var DIFFERENT_TYPES = "A has type %s and B has type %s"; var EQUALITY = "A is not equal to B"; var EQUALITY_PROTOTYPE = "A and B have different prototypes"; var WRAPPED_VALUE = "A wrapped value is not equal to B wrapped value"; var FUNCTION_SOURCES = "function A is not equal to B by source code value (via .toString call)"; var MISSING_KEY = "%s has no key %s"; var SET_MAP_MISSING_KEY = "Set/Map missing key %s"; var DEFAULT_OPTIONS = { checkProtoEql: true, checkSubType: true, plusZeroAndMinusZeroEqual: true, collectAllFails: false }; function setBooleanDefault(property, obj, opts, defaults) { obj[property] = typeof opts[property] !== "boolean" ? defaults[property] : opts[property]; } var METHOD_PREFIX = "_check_"; function EQ(opts, a, b, path) { opts = opts || {}; setBooleanDefault("checkProtoEql", this, opts, DEFAULT_OPTIONS); setBooleanDefault("plusZeroAndMinusZeroEqual", this, opts, DEFAULT_OPTIONS); setBooleanDefault("checkSubType", this, opts, DEFAULT_OPTIONS); setBooleanDefault("collectAllFails", this, opts, DEFAULT_OPTIONS); this.a = a; this.b = b; this._meet = opts._meet || []; this.fails = opts.fails || []; this.path = path || []; } function ShortcutError(fail) { this.name = "ShortcutError"; this.message = "fail fast"; this.fail = fail; } ShortcutError.prototype = Object.create(Error.prototype); EQ.checkStrictEquality = function(a, b) { this.collectFail(a !== b, EQUALITY); }; EQ.add = function add(type, cls, sub, f) { var args = Array.prototype.slice.call(arguments); f = args.pop(); EQ.prototype[METHOD_PREFIX + args.join("_")] = f; }; EQ.prototype = { check: function() { try { this.check0(); } catch (e) { if (e instanceof ShortcutError) { return [e.fail]; } throw e; } return this.fails; }, check0: function() { var a = this.a; var b = this.b; // equal a and b exit early if (a === b) { // check for +0 !== -0; return this.collectFail(a === 0 && 1 / a !== 1 / b && !this.plusZeroAndMinusZeroEqual, PLUS_0_AND_MINUS_0); } var typeA = t(a); var typeB = t(b); // if objects has different types they are not equal if (typeA.type !== typeB.type || typeA.cls !== typeB.cls || typeA.sub !== typeB.sub) { return this.collectFail(true, format(DIFFERENT_TYPES, typeToString(typeA), typeToString(typeB))); } // as types the same checks type specific things var name1 = typeA.type, name2 = typeA.type; if (typeA.cls) { name1 += "_" + typeA.cls; name2 += "_" + typeA.cls; } if (typeA.sub) { name2 += "_" + typeA.sub; } var f = this[METHOD_PREFIX + name2] || this[METHOD_PREFIX + name1] || this[METHOD_PREFIX + typeA.type] || this.defaultCheck; f.call(this, this.a, this.b); }, collectFail: function(comparison, reason, showReason) { if (comparison) { var res = new EqualityFail(this.a, this.b, reason, this.path); res.showReason = !!showReason; this.fails.push(res); if (!this.collectAllFails) { throw new ShortcutError(res); } } }, checkPlainObjectsEquality: function(a, b) { // compare deep objects and arrays // stacks contain references only // var meet = this._meet; var m = this._meet.length; while (m--) { var st = meet[m]; if (st[0] === a && st[1] === b) { return; } } // add `a` and `b` to the stack of traversed objects meet.push([a, b]); // TODO maybe something else like getOwnPropertyNames var key; for (key in b) { if (hasOwnProperty.call(b, key)) { if (hasOwnProperty.call(a, key)) { this.checkPropertyEquality(key); } else { this.collectFail(true, format(MISSING_KEY, "A", key)); } } } // ensure both objects have the same number of properties for (key in a) { if (hasOwnProperty.call(a, key)) { this.collectFail(!hasOwnProperty.call(b, key), format(MISSING_KEY, "B", key)); } } meet.pop(); if (this.checkProtoEql) { //TODO should i check prototypes for === or use eq? this.collectFail(Object.getPrototypeOf(a) !== Object.getPrototypeOf(b), EQUALITY_PROTOTYPE, true); } }, checkPropertyEquality: function(propertyName) { var _eq = new EQ(this, this.a[propertyName], this.b[propertyName], this.path.concat([propertyName])); _eq.check0(); }, defaultCheck: EQ.checkStrictEquality }; EQ.add(t.NUMBER, function(a, b) { this.collectFail((a !== a && b === b) || (b !== b && a === a) || (a !== b && a === a && b === b), EQUALITY); }); [t.SYMBOL, t.BOOLEAN, t.STRING].forEach(function(tp) { EQ.add(tp, EQ.checkStrictEquality); }); EQ.add(t.FUNCTION, function(a, b) { // functions are compared by their source code this.collectFail(a.toString() !== b.toString(), FUNCTION_SOURCES); // check user properties this.checkPlainObjectsEquality(a, b); }); EQ.add(t.OBJECT, t.REGEXP, function(a, b) { // check regexp flags var flags = ["source", "global", "multiline", "lastIndex", "ignoreCase", "sticky", "unicode"]; while (flags.length) { this.checkPropertyEquality(flags.shift()); } // check user properties this.checkPlainObjectsEquality(a, b); }); EQ.add(t.OBJECT, t.DATE, function(a, b) { //check by timestamp only (using .valueOf) this.collectFail(+a !== +b, EQUALITY); // check user properties this.checkPlainObjectsEquality(a, b); }); [t.NUMBER, t.BOOLEAN, t.STRING].forEach(function(tp) { EQ.add(t.OBJECT, tp, function(a, b) { //primitive type wrappers this.collectFail(a.valueOf() !== b.valueOf(), WRAPPED_VALUE); // check user properties this.checkPlainObjectsEquality(a, b); }); }); EQ.add(t.OBJECT, function(a, b) { this.checkPlainObjectsEquality(a, b); }); [t.ARRAY, t.ARGUMENTS, t.TYPED_ARRAY].forEach(function(tp) { EQ.add(t.OBJECT, tp, function(a, b) { this.checkPropertyEquality("length"); this.checkPlainObjectsEquality(a, b); }); }); EQ.add(t.OBJECT, t.ARRAY_BUFFER, function(a, b) { this.checkPropertyEquality("byteLength"); this.checkPlainObjectsEquality(a, b); }); EQ.add(t.OBJECT, t.ERROR, function(a, b) { this.checkPropertyEquality("name"); this.checkPropertyEquality("message"); this.checkPlainObjectsEquality(a, b); }); EQ.add(t.OBJECT, t.BUFFER, function(a) { this.checkPropertyEquality("length"); var l = a.length; while (l--) { this.checkPropertyEquality(l); } //we do not check for user properties because //node Buffer have some strange hidden properties }); function checkMapByKeys(a, b) { var iteratorA = a.keys(); for (var nextA = iteratorA.next(); !nextA.done; nextA = iteratorA.next()) { var key = nextA.value; var hasKey = b.has(key); this.collectFail(!hasKey, format(SET_MAP_MISSING_KEY, key)); if (hasKey) { var valueB = b.get(key); var valueA = a.get(key); eq(valueA, valueB, this); } } } function checkSetByKeys(a, b) { var iteratorA = a.keys(); for (var nextA = iteratorA.next(); !nextA.done; nextA = iteratorA.next()) { var key = nextA.value; var hasKey = b.has(key); this.collectFail(!hasKey, format(SET_MAP_MISSING_KEY, key)); } } EQ.add(t.OBJECT, t.MAP, function(a, b) { this._meet.push([a, b]); checkMapByKeys.call(this, a, b); checkMapByKeys.call(this, b, a); this._meet.pop(); this.checkPlainObjectsEquality(a, b); }); EQ.add(t.OBJECT, t.SET, function(a, b) { this._meet.push([a, b]); checkSetByKeys.call(this, a, b); checkSetByKeys.call(this, b, a); this._meet.pop(); this.checkPlainObjectsEquality(a, b); }); export default function eq(a, b, opts) { return new EQ(opts, a, b).check(); } eq.EQ = EQ; equal-2.0.0/package.json000066400000000000000000000022431315075330300151070ustar00rootroot00000000000000{ "name": "should-equal", "version": "2.0.0", "description": "Deep comparison of 2 instances for should.js", "main": "cjs/should-equal.js", "jsnext:main": "es6/should-equal.js", "module": "es6/should-equal.js", "scripts": { "test": "mocha --ui bdd -R mocha-better-spec-reporter test.js", "cjs": "rollup --format=cjs --output=cjs/should-equal.js index.js", "es6": "rollup --format=es --output=es6/should-equal.js index.js", "build": "npm run cjs && npm run es6", "prepare": "npm run build", "pretest": "npm run build" }, "repository": { "type": "git", "url": "https://github.com/shouldjs/equal.git" }, "keywords": ["should.js", "deep", "equal"], "author": "Denis Bardadym ", "license": "MIT", "bugs": { "url": "https://github.com/shouldjs/equal/issues" }, "homepage": "https://github.com/shouldjs/equal", "devDependencies": { "eslint": "^3.0.0", "eslint-config-shouldjs": "^1.0.2", "mocha": "^3.5.0", "mocha-better-spec-reporter": "^3.1.0", "rollup": "0.34.7" }, "dependencies": { "should-type": "^1.4.0" }, "files": ["cjs/*", "es6/*", "README.md", "LICENSE"] } equal-2.0.0/test.js000066400000000000000000000254101315075330300141370ustar00rootroot00000000000000var assert = require("assert"); var equal = require("."); function eq(a, b, opts) { var r = equal(a, b, opts); if (r.length !== 0) { r = r[0]; var msg = r.reason + " at " + r.path + " " + r.a + " =/= " + r.b; assert.equal(a, b, msg); } } function ne(a, b, opts) { var r = equal(a, b, opts); assert.ok(r.length !== 0); } /* 1. simple tests */ /* 1.1. positive */ it("NaN eqs NaN", function() { return eq(NaN, NaN); }); it("finite integer n eqs n", function() { return eq(1234, 1234); }); it("empty list eqs empty list", function() { return eq([], []); }); it("empty obj eqs empty obj", function() { return eq({}, {}); }); it("number eqs number of same value", function() { return eq(123.45678, 123.45678); }); it("regex lit's w same pattern, flags are eq", function() { return eq(/^abc[a-zA-Z]/, /^abc[a-zA-Z]/); }); it("pods w same properties are eq", function() { return eq( { a: "b", c: "d" }, { a: "b", c: "d" } ); }); it("pods that only differ wrt prop ord are eq", function() { return eq( { a: "b", c: "d" }, { c: "d", a: "b" } ); }); /* 1.2. negative */ it("obj doesn't eq list", function() { return ne({}, []); }); it("obj in a list doesn't eq list in list", function() { return ne([{}], [[]]); }); it("integer n doesn't eq rpr n", function() { return ne(1234, "1234"); }); it("integer n doesn't eq n + 1", function() { return ne(1234, 1235); }); it("empty list doesn't eq false", function() { return ne([], false); }); it("list w an integer doesn't eq one w rpr n", function() { return ne([3], ["3"]); }); it("regex lit's w diff. patterns, same flags aren't eq", function() { return ne(/^abc[a-zA-Z]/, /^abc[a-zA-Z]x/); }); it("regex lit's w same patterns, diff. flags aren't eq", function() { return ne(/^abc[a-zA-Z]/, /^abc[a-zA-Z]/i); }); it("number obj not eqs primitive number of same value", function() { return ne(5, new Number(5)); }); it("string obj not eqs primitive string of same value", function() { return ne("helo", new String("helo")); }); it("(1) bool obj not eqs primitive bool of same value", function() { return ne(false, new Boolean(false)); }); it("(2) bool obj not eqs primitive bool of same value", function() { return ne(true, new Boolean(true)); }); it("number obj eqs the same valued number object", function() { return eq(new Number(5), new Number(5)); }); /* 2. complex tests */ it("obj w undef member not eqs other obj w/out same member", function() { var d, e; d = { x: void 0 }; e = {}; return ne(d, e); }); it("fn1: functions w same source are eq", function() { var d, e; d = function(a, b, c) { return a * b * c; }; e = function(a, b, c) { return a * b * c; }; return eq(d, e); }); it("fn2: functions w diff source aren't eq", function() { var d, e; d = function(a, b, c) { return a * b * c; }; e = function(a, b, s) { return a * b * s; }; return ne(d, e); }); it("fn3: equal functions w equal props are eq", function() { var d, e; d = function() { return null; }; d.foo = { some: "meaningless", properties: "here" }; e = function() { return null; }; e.foo = { some: "meaningless", properties: "here" }; return eq(d, e); }); it("fn4: equal functions w unequal props aren't eq", function() { var d, e; d = function() { return null; }; d.foo = { some: "meaningless", properties: "here" }; e = function() { return null; }; e.foo = { some: "meaningless", properties: "here!!!" }; return ne(d, e); }); it("list w named member eqs other list w same member", function() { var d, e; d = ["foo", null, 3]; d["extra"] = 42; e = ["foo", null, 3]; e["extra"] = 42; return eq(d, e); }); it("list w named member doesn't eq list w same member, other value", function() { var d, e; d = ["foo", null, 3]; d["extra"] = 42; e = ["foo", null, 3]; e["extra"] = 108; return ne(d, e); }); it("date eqs other date pointing to same time", function() { var d, e; d = new Date("1995-12-17T03:24:00"); e = new Date("1995-12-17T03:24:00"); return eq(d, e); }); it("date does not eq other date pointing to other time", function() { var d, e; d = new Date("1995-12-17T03:24:00"); e = new Date("1995-12-17T03:24:01"); return ne(d, e); }); it("str obj w props eq same str, same props", function() { var d, e; d = new String("helo test"); d["abc"] = 42; e = new String("helo test"); e["abc"] = 42; return eq(d, e); }); it("str obj w props not eq same str, other props", function() { var d, e; d = new String("helo test"); d["abc"] = 42; e = new String("helo test"); e["def"] = 42; return ne(d, e); }); it("str obj w props eq same str, same props (circ)", function() { var c, d, e; c = ["a list"]; c.push(c); d = new String("helo test"); d["abc"] = c; e = new String("helo test"); e["abc"] = c; return eq(d, e); }); it("str obj w props not eq same str, other props (circ)", function() { var c, d, e; c = ["a list"]; c.push(c); d = new String("helo test"); d["abc"] = c; e = new String("helo test"); e["def"] = c; return ne(d, e); }); /*it("(1) circ arrays w similar layout, same values aren't eq", function() { var d, e; d = [1, 2, 3]; d.push(d); e = [1, 2, 3]; e.push(d); return ne(d, e); });*/ it("(2) circ arrays w same layout, same values are eq", function() { var d, e; d = [1, 2, 3]; d.push(d); e = [1, 2, 3]; e.push(e); return eq(d, e); }); it("(fkling1) arrays w eq subarrays are eq", function() { var a, b, bar, foo; a = [1, 2, 3]; b = [1, 2, 3]; foo = [a, a]; bar = [b, b]; return eq(foo, bar); }); /*it("(fkling2) arrays w eq subarrays but diff distribution aren't eq"] = function() { var a, b, bar, foo; a = [1, 2, 3]; b = [1, 2, 3]; foo = [a, a]; bar = [a, b]; return ne(foo, bar); };*/ /* joshwilsdon's test (https://github.com/joyent/node/issues/7161) */ it("joshwilsdon", function() { var count, d1, d2, errors, idx1, idx2, v1, v2, _i, _j, _len, _ref; d1 = [ NaN, void 0, null, true, false, Infinity, 0, 1, "a", "b", { a: 1 }, { a: "a" }, [ { a: 1 } ], [ { a: true } ], { a: 1, b: 2 }, [1, 2], [1, 2, 3], { a: "1" }, { a: "1", b: "2" } ]; d2 = [ NaN, void 0, null, true, false, Infinity, 0, 1, "a", "b", { a: 1 }, { a: "a" }, [ { a: 1 } ], [ { a: true } ], { a: 1, b: 2 }, [1, 2], [1, 2, 3], { a: "1" }, { a: "1", b: "2" } ]; errors = []; count = 0; for (idx1 = _i = 0, _len = d1.length; _i < _len; idx1 = ++_i) { v1 = d1[idx1]; for (idx2 = _j = idx1, _ref = d2.length; idx1 <= _ref ? _j < _ref : _j > _ref; idx2 = idx1 <= _ref ? ++_j : --_j) { count += 1; v2 = d2[idx2]; if (idx1 === idx2) { if (!eq(v1, v2)) { //errors.push("eq " + (rpr(v1)) + ", " + (rpr(v2))); } } else { if (!ne(v1, v2)) { //errors.push("ne " + (rpr(v1)) + ", " + (rpr(v2))); } } } } return [count, errors]; }); it("node buffer", function() { eq(new Buffer("abc"), new Buffer("abc")); ne(new Buffer("abc"), new Buffer("abc1")); ne(new Buffer("abd"), new Buffer("abc")); }); it("RegExp with props", function() { var re1 = /a/; re1.lastIndex = 3; ne(re1, /a/); }); it("Date with props", function() { var now = Date.now(); var d1 = new Date(now); var d2 = new Date(now); d1.a = 10; ne(d1, d2); }); it("Check object prototypes", function() { var nbRoot = { toString: function() { return this.first + " " + this.last; } }; function nameBuilder(first, last) { this.first = first; this.last = last; return this; } nameBuilder.prototype = nbRoot; function nameBuilder2(first, last) { this.first = first; this.last = last; return this; } nameBuilder2.prototype = nbRoot; var nb1 = new nameBuilder("Ryan", "Dahl"); var nb2 = new nameBuilder2("Ryan", "Dahl"); eq(nb1, nb2); nameBuilder2.prototype = Object; nb2 = new nameBuilder2("Ryan", "Dahl"); ne(nb1, nb2); }); if (typeof Uint8Array !== "undefined") { it("typed arrays and array buffer", function() { var arr1 = new Uint8Array([21, 31]); var arr2 = new Uint8Array([21, 31]); eq(arr1, arr2); eq(arr1.buffer, arr2.buffer); }); } if (typeof Set !== "undefined") { it("es6 sets", function() { var s1 = new Set([1, 2, 3]); var s2 = new Set([1, 2, 3]); var s3 = new Set(["a", "b", "c"]); var s4 = new Set([]); eq(s1, s2); ne(s1, s3); ne(s1, s4); var k1 = { a: 1 }; var s5 = new Set([k1, k1, k1]); var s6 = new Set([k1, k1, k1]); ne(s1, s5); eq(s5, s6); eq(s6, s5); }); } if (typeof Map !== "undefined") { it("es6 maps", function() { var m1 = new Map([[1, 1], [2, 2], [3, 3]]); var m2 = new Map([[1, 1], [2, 2], [3, 3]]); var m3 = new Map([[1, 2], [2, 3], [3, 4]]); eq(m1, m1); eq(m1, m2); ne(m3, m2); var key_a10 = { a: 10 }; var key_a11 = { a: 11 }; var key_a12 = { a: 12 }; var key_a13 = { a: 13 }; var m4 = new Map([[key_a10, 2], [key_a11, 2], [key_a12, 2]]); var m5 = new Map([[key_a11, 2], [key_a12, 2], [key_a13, 2]]); var m6 = new Map([[key_a11, 2], [key_a12, 2], [key_a13, 2]]); ne(m4, m5); eq(m5, m6); var m7 = new Map([[{}, 1], [{}, 2]]); var m8 = new Map([[{}, 2], [{}, 3]]); var m9 = new Map([[{}, 2], [{}, 3]]); ne(m7, m8); ne(m9, m8); }); } if (typeof WeakSet !== "undefined") { it("es WeakMap, WeakSet", function() { var a = new WeakSet(); var b = new WeakSet(); eq(a, b); }); } it("should treat -0 and +0 as not equal when param not passed", function() { eq(-0, +0); ne(-0, +0, { plusZeroAndMinusZeroEqual: false }); }); if (typeof Symbol !== "undefined") { it("should work with Symbols", function() { ne([Symbol()], [Symbol()]); eq([Symbol.for("a")], [Symbol.for("a")]); ne(Symbol(), Symbol()); ne(Symbol("abc"), Symbol("abc")); eq(Symbol.for("a"), Symbol.for("a")); ne(Symbol.for("a"), Symbol.for("b")); }); } if (typeof Buffer !== "undefined") { it("should support node Buffer", function() { var b1 = new Buffer("abc", "utf8"); var b2 = new Buffer("abc", "utf8"); var b3 = new Buffer("acc", "utf8"); eq(b1, b2); ne(b1, b3); }); } it("should not assume object with only the same properties are equal", function() { var F = function(a, b) { this.a = a; this.b = b; }; var f = new F(19, 11); var f1 = { a: 19, b: 11 }; ne(f, f1); eq(f, f1, { checkProtoEql: false }); });