pax_global_header00006660000000000000000000000064126002550340014507gustar00rootroot0000000000000052 comment=b1610b6f1a2003a7c7a29c05d25a8620bbf543e3 js-egal-1.3.0/000077500000000000000000000000001260025503400130325ustar00rootroot00000000000000js-egal-1.3.0/.gitignore000066400000000000000000000000261260025503400150200ustar00rootroot00000000000000/node_modules/ /*.tgz js-egal-1.3.0/.npmignore000066400000000000000000000000241260025503400150250ustar00rootroot00000000000000/*.tgz /.travis.yml js-egal-1.3.0/.travis.yml000066400000000000000000000002071260025503400151420ustar00rootroot00000000000000sudo: false language: node_js node_js: - "stable" - "0.10" - "0.11" - "0.12" - "4" notifications: email: ["andri@dot.ee"] js-egal-1.3.0/CHANGELOG.md000066400000000000000000000047001260025503400146440ustar00rootroot00000000000000## 1.3.0 (Sep 22, 2015) - Adds support for calling `deepEgal` with a custom comparison function. Right now this is an advanced feature with its API not set in stone. ## 1.2.0 (Sep 21, 2015) - Fixes `deepEgal` to consider `Array.prototype` equivalent to an empty array. JavaScript is a prototypical language after all and the prototype of an array is an array. - Fixes `egal` to consider two objects inheriting from `null` without `constructor` properties, but `valueOf` functions, as plain objects and not as value objects. - Adds support for comparing value objects that return compound values from their `valueOf` function. That is, you no longer need to return a single primitive value from `valueOf`, but merely a _more_ primitive one than before: ```javascript function Point(x, y) { this.x = x; this.y = y } Point.prototype.valueOf = function() { return [this.x, this.y] } egal(new Point(42, 69), new Point(42, 69)) // => true egal(new Point(42, 69), new Point(13, 42)) // => false ``` ## 1.1.0 (Jun 13, 2015) - Adds `deepEgal` for comparing plain objects and arrays recursively. It's still type-safe, so value objects and instances of classes nested in plain objects as compared as `egal` — by value if possible (same constructor and has a `valueOf` function), by reference (`===`) otherwise. ```javascript var deepEgal = require("egal").deepEgal function Model(name) { this.name = name } deepEgal(42, 42) // => true deepEgal({name: "John"}, {name: "John"}) // => true deepEgal({stats: {age: 13}}, {{stats: age: 13}}) // => true deepEgal([1, 2, 3], [1, 2, 3]) // => true deepEgal(new Model("John"), new Model("John")) // => false deepEgal(new Date(2000, 5), new Date(2000, 5)) // => true ``` ## 1.0.0 (May 25, 2015) - No longer considers a primitive and boxed object of the same value to be equivalent. Two boxed objects of the same value will remain equivalent. ```javascript egal(true, new Boolean(true)) // => false egal(new Boolean(true), new Boolean(true)) // => true ``` Boxed objects tend to be *very* error prone and it's best you stick to primitives only. The following is a small example of problems with boxed objects: ```javascript new String("a") == new String("a") // => false new Boolean(true) == new Boolean(true) // => false Boolean(new Boolean(false)) // => true !!(new Boolean(false)) // => true ``` ## 0.1.337 (Nov 1, 2013) - First public release. Brotherhood awakes! js-egal-1.3.0/LICENSE000066400000000000000000000016431260025503400140430ustar00rootroot00000000000000Egal.js Copyright (C) 2013 Andri Möll This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. Additional permission under the GNU Affero GPL version 3 section 7: If you modify this Program, or any covered work, by linking or combining it with other code, such other code is not for that reason alone subject to any of the requirements of the GNU Affero GPL version 3. In summary: - You can use this program for no cost. - You can use this program for both personal and commercial reasons. - You do not have to share your own program's code which uses this program. - You have to share modifications (e.g bug-fixes) you've made to this program. For the full copy of the GNU Affero General Public License see: http://www.gnu.org/licenses. js-egal-1.3.0/Makefile000066400000000000000000000012431260025503400144720ustar00rootroot00000000000000NODE_OPTS = TEST_OPTS = love: @echo "Feel like makin' love." test: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot $(TEST_OPTS) spec: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec $(TEST_OPTS) autotest: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R dot --watch $(TEST_OPTS) autospec: @node $(NODE_OPTS) ./node_modules/.bin/_mocha -R spec --watch $(TEST_OPTS) pack: @file=$$(npm pack); echo "$$file"; tar tf "$$file" publish: npm publish tag: git tag "v$$(node -e 'console.log(require("./package").version)')" clean: rm -f *.tgz npm prune --production .PHONY: love .PHONY: test spec autotest autospec .PHONY: pack publish tag .PHONY: clean js-egal-1.3.0/README.md000066400000000000000000000151341260025503400143150ustar00rootroot00000000000000Egal.js ======= [![NPM version][npm-badge]](https://www.npmjs.com/package/egal) [![Build status][travis-badge]](https://travis-ci.org/moll/js-egal) Egal.js provides an `egal` function that tests **strict equality** (like `===`), but adds support for built-in and custom [**value objects**][value-object] in a **type-safe** way. It also has a `deepEgal` function for comparing **plain objects and arrays recursively** or deeply without giving up on type-safeness on the way. It also handles **circular references**. ### Tour When and why to use `egal` over the triple-equals `===` operator? - When you need to compare the **semantic equivalence** of value objects without requiring the same object identity. JavaScript's `==` and `===` consider two different `Date` or `RegExp` objects unequal, even if they mean the same thing. - When you need to **compare custom value objects** in a type-safe way. Value objects are objects that have a [`valueOf`][valueof] function. Egal.js makes sure the two objects with `valueOf` are actually from the same constructor. - When you need to **compare objects or arrays recursively**, Egal.js has [`deepEgal`](#deep-comparison). #### Primitives A **primivitive** and its **boxed object** equivalent are considered different. Allowing unexpected boxed objects (e.g. `new Boolean(false)`) through is risky as they're extremely error prone (just think of `!!new Boolean(false)` returning `true`). Comparing two boxed objects of the same value, on the other hand, will work. #### Objects **Non-value objects**, like `Array` or `Object`, are compared by `egal` as `===` does it — based on object identity. For recursive or deep comparison, see [`deepEgal`](#deep-comparison). #### NaN **NaN**s (not-a-number) are **not equal** (matching how `===` behaves). This is because when you compare results of two mathematical operations that may both end up as `NaN`, you might inadvertently assume the calculations went fine. If you expect `NaN`, you can use JavaScript's built-in `isNaN` to test for that. #### Zeros **Negative and positive** zeros are **equal** (also matching how `===` behaves). You might end up with unexpected negative zeros via various calculations and when you don't need to distinguish between the two, you'll end up with too many false negatives. If you need to handle negative zeros differently, see the article on [Sameness in JavaScript][sameness]. #### Value Objects **Value objects** can also return **compound values**. That is, you need not return a single primitive value from `valueOf`, but merely a _more_ primitive one. Those values are compared with [`deepEgal`](#deep-comparison). ```javascript function Point(x, y) { this.x = x; this.y = y } Point.prototype.valueOf = function() { return [this.x, this.y] } egal(new Point(42, 69), new Point(42, 69)) // => true egal(new Point(42, 69), new Point(13, 42)) // => false ``` [npm-badge]: https://img.shields.io/npm/v/egal.svg [travis-badge]: https://travis-ci.org/moll/js-egal.png?branch=master [value-object]: https://en.wikipedia.org/wiki/Value_object [valueof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf [sameness]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Sameness Installing ---------- ### Installing on Node.js ``` npm install egal ``` ### Installing for the browser Egal.js doesn't yet have a build ready for the browser, but you might be able to use [Browserify][browserify] to have it run there till then. [browserify]: https://github.com/substack/node-browserify Using ----- Require Egal.js: ```javascript var egal = require("egal") ``` Then proceed with comparions: ```javascript egal(42, 42) // => true egal(new String("Hello!"), "Hello") // => true egal(new Date(2000, 5, 18), new Date(2000, 5, 18)) // => true egal(/abc/i, /abc/i) // => true ``` ### Value Objects To make and compare custom value objects, create a new constructor and give its prototype a `valueOf` function: ```javascript function Song(name) { this.name = name } Song.prototype.valueOf = function() { return this.name } egal(new Song("Play Guitar"), new Song("Play Guitar")) // => true egal(new Song("Play Guitar"), new Song("Crumblin' Down")) // => false ``` Egal.js makes sure the two instances are from the same constructor before comparing their `valueOf` outputs: ```javascript function Song(name) { this.name = name } Song.prototype.valueOf = function() { return this.name } function Car(name) { this.name = name } Car.prototype.valueOf = function() { return this.name } egal(new Song("KITT"), new Car("KITT")) // => false ``` Objects that are instances of a class (their `constructor` property set to something other than `Object`) but lack a `valueOf` function, thereby not being value objects, are compared by reference (`===`). ### Deep Comparison As of v1.1.0, Egal.js comes with a recursive or deep comparison function named `deepEgal`. It was mostly extracted from the [Must.js][must] testing library's `eql` function. ```javascript var deepEgal = require("egal").deepEgal function Model(name) { this.name = name } deepEgal(42, 42) // => true deepEgal({name: "John"}, {name: "John"}) // => true deepEgal({stats: {age: 13}}, {{stats: age: 13}}) // => true deepEgal([1, 2, 3], [1, 2, 3]) // => true deepEgal(new Model("John"), new Model("John")) // => false deepEgal(new Date(2000, 5), new Date(2000, 5)) // => true ``` The `deepEgal` function compares regular primitive values, model instances and value objects just like `egal`. Plain objects (those with no custom `constructor` property in their prototype), are compared recursively by their enumerable properties. Arrays are compared recursively by their contents (iterating over `length`). See above about [value objects](#value-objects) for more details on plain, instances and value objects. [must]: https://github.com/moll/js-must License ------- Egal.js is released under a *Lesser GNU Affero General Public License*, which in summary means: - You **can** use this program for **no cost**. - You **can** use this program for **both personal and commercial reasons**. - You **do not have to share your own program's code** which uses this program. - You **have to share modifications** (e.g bug-fixes) you've made to this program. For more convoluted language, see the `LICENSE` file. About ----- **[Andri Möll](http://themoll.com)** typed this and the code. [Monday Calendar](https://mondayapp.com) supported the engineering work. If you find Egal.js needs improving, please don't hesitate to type to me now at [andri@dot.ee][email] or [create an issue online][issues]. [email]: mailto:andri@dot.ee [issues]: https://github.com/moll/js-egal/issues js-egal-1.3.0/index.js000066400000000000000000000056321260025503400145050ustar00rootroot00000000000000var kindof = require("kindof") exports = module.exports = egal exports.deepEgal = deepEgal function egal(a, b) { if (a === b) return true var type switch (type = kindofPlain(a)) { case "date": if (type !== kindof(b)) return false return a.valueOf() === b.valueOf() case "regexp": if (type !== kindof(b)) return false return a.toString() === b.toString() case "object": if (type !== kindofPlain(b)) return false var constructor = getConstructorOf(a) if (constructor !== getConstructorOf(b)) return false if (!hasValueOf(a) || !hasValueOf(b)) return false return deepEgal(a.valueOf(), b.valueOf()) default: return false } } function maybeEgal(a, b) { if (egal(a, b)) return true var type = kindofPlain(a) switch (type) { case "array": case "plain": return type === kindofPlain(b) ? null : false default: return false } } function deepEgal(a, b, egal) { return deepEgalWith(typeof egal === "function" ? egal : maybeEgal, a, b) } function deepEgalWith(egal, a, b, aStack, bStack) { var equal = egal(a, b) if (equal != null) return Boolean(equal) var type = kindof(a) switch (type) { /* eslint no-fallthrough: 0 */ case "array": case "object": if (type === kindof(b)) break default: return false } var aPos = aStack && aStack.indexOf(a) var bPos = bStack && bStack.indexOf(b) if (aPos !== bPos) return false if (aPos != null && aPos >= 0) return true aStack = aStack ? aStack.concat([a]) : [a] bStack = bStack ? bStack.concat([b]) : [b] var i switch (type) { case "array": if (a.length !== b.length) return false if (a.length === 0) return true for (i = 0; i < a.length; ++i) if (!deepEgalWith(egal, a[i], b[i], aStack, bStack)) return false return true case "object": var aKeys = keys(a) var bKeys = keys(b) if (aKeys.length !== bKeys.length) return false if (aKeys.length === 0) return true aKeys.sort() bKeys.sort() for (i = 0; i < aKeys.length; ++i) if (aKeys[i] !== bKeys[i]) return false for (var key in a) if (!deepEgalWith(egal, a[key], b[key], aStack, bStack)) return false return true } } function kindofPlain(obj) { var type = kindof(obj) if (type === "object" && isObjectPlain(obj)) return "plain" return type } function isObjectPlain(obj) { var prototype = Object.getPrototypeOf(obj) if (prototype === null) return true if (!("constructor" in prototype)) return true return prototype.constructor === Object } function getConstructorOf(obj) { var prototype = Object.getPrototypeOf(obj) return prototype === null ? undefined : prototype.constructor } function hasValueOf(obj) { var valueOf = obj.valueOf return typeof valueOf === "function" && valueOf !== Object.prototype.valueOf } function keys(obj) { var all = [] for (var key in obj) all.push(key) return all } js-egal-1.3.0/package.json000066400000000000000000000015721260025503400153250ustar00rootroot00000000000000{ "name": "egal", "version": "1.3.0", "description": "Strict equality test (like ===) that handles both built-in and custom value objects (those with a valueOf function).", "keywords": [ "compare", "eql", "equal", "equality", "is" ], "homepage": "https://github.com/moll/js-egal", "bugs": "https://github.com/moll/js-egal/issues", "author": { "name": "Andri Möll", "email": "andri@dot.ee", "url": "http://themoll.com" }, "repository": { "type": "git", "url": "git://github.com/moll/js-egal.git" }, "licenses": [{ "type": "LAGPL", "url": "https://github.com/moll/js-egal/blob/master/LICENSE" }], "main": "index.js", "scripts": {"test": "make test"}, "dependencies": { "kindof": ">= 2.0.0 < 3" }, "devDependencies": { "mocha": ">= 1.12.0 < 2", "must": ">= 0.12.0 < 0.13", "sinon": ">= 1.15.3 < 2", "lodash.wrap": ">= 3.0.0 < 4" } } js-egal-1.3.0/test/000077500000000000000000000000001260025503400140115ustar00rootroot00000000000000js-egal-1.3.0/test/_boolean_test.js000066400000000000000000000015631260025503400171710ustar00rootroot00000000000000module.exports = function(egal) { // Allow using Boolean as constructor: /* jshint -W053 */ /* eslint no-new-wrappers: 0 */ describe("given Boolean", function() { it("must return true given identical primitives", function() { egal(true, true).must.be.true() }) it("must return false given unequivalent primitives", function() { egal(true, false).must.be.false() }) it("must return true given equivalent objects", function() { egal(new Boolean(true), new Boolean(true)).must.be.true() }) it("must return false given unequivalent objects", function() { egal(new Boolean(true), new Boolean(false)).must.be.false() }) it("must return false given equivalent primitive and object", function() { egal(true, new Boolean(true)).must.be.false() egal(false, new Boolean(false)).must.be.false() }) }) } js-egal-1.3.0/test/_date_test.js000066400000000000000000000007421260025503400164650ustar00rootroot00000000000000module.exports = function(egal) { describe("given Date", function() { it("must return true given equivalent dates", function() { egal(new Date(2000, 5), new Date(2000, 5)).must.be.true() }) it("must return false given unequivalent dates", function() { egal(new Date(2000, 5), new Date(1999, 5)).must.be.false() }) it("must return false given Date and number primitive", function() { egal(new Date(1337), 1337).must.be.false() }) }) } js-egal-1.3.0/test/_function_test.js000066400000000000000000000005321260025503400173720ustar00rootroot00000000000000module.exports = function(egal) { describe("given Function", function() { it("must return true given identical functions", function() { function fn() {} egal(fn, fn).must.be.true() }) it("must return false given equivalent functions", function() { egal(function() {}, function() {}).must.be.false() }) }) } js-egal-1.3.0/test/_null_test.js000066400000000000000000000005321260025503400165170ustar00rootroot00000000000000module.exports = function(egal) { it("must return true given nulls", function() { egal(null, null).must.be.true() }) it("must return true given undefineds", function() { egal(undefined, undefined).must.be.true() }) it("must return false given null and undefined", function() { egal(null, undefined).must.be.false() }) } js-egal-1.3.0/test/_number_test.js000066400000000000000000000056041260025503400170420ustar00rootroot00000000000000module.exports = function(egal) { // Allow using Number as constructor: /* jshint -W053 */ /* eslint no-new-wrappers: 0 */ describe("given Number", function() { it("must return true given identical primitives", function() { egal(42, 42).must.be.true() }) it("must return false given unequivalent primitives", function() { egal(42, 69).must.be.false() }) it("must return true given equivalent objects", function() { egal(new Number(42), new Number(42)).must.be.true() }) it("must return false given unequivalent objects", function() { egal(new Number(42), new Number(69)).must.be.false() }) it("must return false given equivalent primitive and object", function() { egal(42, new Number(42)).must.be.false() }) it("must return false given string primitive", function() { egal(42, "69").must.be.false() }) it("must return false given string object", function() { egal(42, new String("69")).must.be.false() }) describe("given -0", function() { it("must return true given primitives", function() { egal(-0, +0).must.be.true() }) it("must return true given objects", function() { egal(new Number(-0), new Number(+0)).must.be.true() }) it("must return false given primitive and object", function() { egal(-0, new Number(+0)).must.be.false() }) }) describe("given NaN", function() { it("must return false given primitives", function() { egal(NaN, NaN).must.be.false() }) it("must return false given objects", function() { egal(new Number(NaN), new Number(NaN)).must.be.false() }) it("must return false given number and NaN", function() { egal(42, NaN).must.be.false() }) }) describe("given Infinity", function() { it("must return true given identical primitivies", function() { egal(Infinity, Infinity).must.be.true() egal(-Infinity, -Infinity).must.be.true() }) it("must return false given unequivalent primitives", function() { egal(Infinity, -Infinity).must.be.false() }) it("must return true given equivalent objects", function() { egal(new Number(Infinity), new Number(Infinity)).must.be.true() egal(new Number(-Infinity), new Number(-Infinity)).must.be.true() }) it("must return false given unequivalent objects", function() { egal(new Number(Infinity), new Number(-Infinity)).must.be.false() }) it("must return false given equivalent primitive and object", function() { egal(Infinity, new Number(Infinity)).must.be.false() egal(new Number(-Infinity), -Infinity).must.be.false() }) it("must return false given number and infinity", function() { egal(42, Infinity).must.be.false() egal(42, -Infinity).must.be.false() }) }) }) } js-egal-1.3.0/test/_object_with_constructor_test.js000066400000000000000000000015221260025503400225130ustar00rootroot00000000000000module.exports = function(egal) { describe("given Object with constructor", function() { it("must return false given equivalent objects", function() { function Value() {} egal(new Value, new Value).must.be.false() }) it("must return false given different constructors", function() { function Value() {} function Price() {} egal(new Value, new Price).must.be.false() }) it("must return false given subclassed constructor", function() { function Value(value) { this.value = value } function MoreValue(value) { this.value = value } MoreValue.prototype = Object.create(Value.prototype, { constructor: {value: MoreValue, configurable: true, writable: true} }) var a = new Value(42) var b = new MoreValue(42) egal(a, b).must.be.false() }) }) } js-egal-1.3.0/test/_object_with_value_of_test.js000066400000000000000000000124061260025503400217310ustar00rootroot00000000000000module.exports = function(egal) { describe("given Object with valueOf", function() { it("must return true given equal value", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this.value } var a = new Value(42) var b = new Value(42) egal(a, b).must.be.true() }) it("must return true given equal value with unequivalent properties", function() { function Value(value, other) { this.value = value; this.other = other } Value.prototype.valueOf = function() { return this.value } var a = new Value(42, 1) var b = new Value(42, 2) egal(a, b).must.be.true() }) it("must return true given equal value but different valueOfs", function() { function Value(value) { this.valueOf = function() { return value } } var a = new Value(42) var b = new Value(42) egal(a, b).must.be.true() }) it("must return false given unequivalent values", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this.value } var a = new Value(42) var b = new Value(69) egal(a, b).must.be.false() }) it("must return false given differently typed values", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this.value } var a = new Value(42) var b = new Value("42") egal(a, b).must.be.false() }) it("must return true given equivalent array values", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return [42, this.value] } var a = new Value(42) var b = new Value(42) egal(a, b).must.be.true() }) it("must return false given unequivalent array values", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return [42, this.value] } var a = new Value(42) var b = new Value(69) egal(a, b).must.be.false() }) xit("must return false given valueOfs returning self", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this } var a = new Value(42) var b = new Value(42) egal(a, b).must.be.false() }) it("must return true given null inherited value objects", function() { function Value(value) { this.value = value } Value.prototype = Object.create(null, { constructor: {value: Value, configurable: true, writeable: true} }) Value.prototype.valueOf = function() { return this.value } var a = new Value(42) var b = new Value(42) egal(a, b).must.be.true() }) it("must return false given instance and plain object", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this.value } var a = new Value(42) var b = {valueOf: function() { return 42 }} egal(a, b).must.be.false() }) it("must return false given different constructors", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this.value } function Price(value) { this.value = value } Price.prototype.valueOf = function() { return this.value } var a = new Value(42) var b = new Price(42) egal(a, b).must.be.false() }) it("must return false given subclassed constructor", function() { function Value(value) { this.value = value } Value.prototype.valueOf = function() { return this.value } function MoreValue(value) { this.value = value } MoreValue.prototype = Object.create(Value.prototype, { constructor: {value: MoreValue, configurable: true, writable: true} }) var a = new Value(42) var b = new MoreValue(42) egal(a, b).must.be.false() }) it("must return false given overwritten constructor properties", function() { function A(value) { this.value = value } A.prototype.valueOf = function() { return this.value } function B(value) { this.value = value } B.prototype.valueOf = function() { return this.value } var a = new A(42) var b = new B(42) a.constructor = b.constructor = function() {} egal(a, b).must.be.false() }) it("must return false given non-function valueOfs", function() { function Value(value) { this.value = value } Value.prototype.valueOf = 42 var a = new Value(42) var b = new Value(42) egal(a, b).must.be.false() }) it("must return false given default Object.prototype.valueOf", function() { function Value(value) { this.value = value } var a = new Value(42) var b = new Value(42) egal(a, b).must.be.false() }) it("must return false given plain object", function() { var a = {valueOf: function() { return 1 }} var b = {valueOf: function() { return 1 }} egal(a, b).must.be.false() }) it("must return false given null inherited plain objects", function() { var a = Object.create(null); a.valueOf = function() { return 42 } var b = Object.create(null); b.valueOf = function() { return 42 } egal(a, b).must.be.false() }) }) } js-egal-1.3.0/test/_regexp_test.js000066400000000000000000000010431260025503400170350ustar00rootroot00000000000000module.exports = function(egal) { describe("given RegExp", function() { it("must return true given equivalent primitives", function() { egal(/a/, /a/).must.be.true() }) it("must return false given unequivalent primitives", function() { egal(/a/, /b/).must.be.false() }) it("must return false if given unequivalent flags", function() { egal(/a/ig, /a/i).must.be.false() }) it("must return false given RegExp and string primitive", function() { egal(/a/, "/a/").must.be.false() }) }) } js-egal-1.3.0/test/_string_test.js000066400000000000000000000020261260025503400170530ustar00rootroot00000000000000module.exports = function(egal) { // Allow using String as constructor: /* jshint -W053 */ /* eslint no-new-wrappers: 0 */ describe("given String", function() { it("must return true given identical primitives", function() { egal("ok", "ok").must.be.true() }) it("must return false given unequivalent primitives", function() { egal("ok", "no").must.be.false() }) it("must return true given equivalent objects", function() { egal(new String("ok"), new String("ok")).must.be.true() }) it("must return false given unequivalent objects", function() { egal(new String("ok"), new String("no")).must.be.false() }) it("must return false given equivalent primitive and object", function() { egal("ok", new String("ok")).must.be.false() }) it("must return false given number primitive", function() { egal("42", 42).must.be.false() }) it("must return false given number object", function() { egal("42", new Number(42)).must.be.false() }) }) } js-egal-1.3.0/test/_symbol_test.js000066400000000000000000000010661260025503400170550ustar00rootroot00000000000000var describe = typeof Symbol == "undefined" ? global.xdescribe : global.describe module.exports = function(egal) { describe("given Symbol", function() { it("must return true given the same symbol", function() { var symbol = Symbol() egal(symbol, symbol).must.be.true() }) it("must return false given two anonymous symbols", function() { egal(Symbol(), Symbol()).must.be.false() }) it("must return false given two named symbols", function() { egal(Symbol("iterator"), Symbol("iterator")).must.be.false() }) }) } js-egal-1.3.0/test/index_test.js000066400000000000000000000323151260025503400165210ustar00rootroot00000000000000var Sinon = require("sinon") var wrap = require("lodash.wrap") describe("egal", function() { var egal = wrap(require(".."), function(orig, a, b) { var equal = orig(a, b) orig(b, a).must.equal(equal) return equal }) require("./_null_test")(egal) require("./_boolean_test")(egal) require("./_number_test")(egal) require("./_string_test")(egal) require("./_symbol_test")(egal) require("./_regexp_test")(egal) require("./_date_test")(egal) require("./_function_test")(egal) require("./_object_with_value_of_test")(egal) it("must return false given an empty array and empty object", function() { // There was once an assertion library that considered {} equivalent to [] // for months! This will *never* happen under my watch! egal({}, []).must.be.false() }) describe("given Array", function() { it("must return true given same array", function() { var array = [] egal(array, array).must.be.true() }) it("must return false given empty arrays", function() { egal([], []).must.be.false() }) it("must return false given equivalent arrays", function() { egal([1], [1]).must.be.false() }) }) describe("given Object", function() { it("must return true given same object", function() { var object = {} egal(object, object).must.be.true() }) it("must return false given empty objects", function() { egal({}, {}).must.be.false() }) it("must return false given equivalent objects", function() { egal({a: 1}, {a: 1}).must.be.false() }) it("must return false given null inherited object", function() { var a = Object.create(null) var b = Object.create(null) egal(a, b).must.be.false() }) it("must return false given instance and plain object", function() { function Model() {} egal(new Model, {}).must.be.false() }) }) }) describe("deepEgal", function() { var deepEgal = wrap(require("..").deepEgal, function(orig, a, b, egal) { var equal = orig(a, b, egal) orig(b, a, egal).must.equal(equal) return equal }) it("must return false given an empty array and empty object", function() { deepEgal({}, []).must.be.false() }) require("./_null_test")(deepEgal) require("./_boolean_test")(deepEgal) require("./_number_test")(deepEgal) require("./_string_test")(deepEgal) require("./_symbol_test")(deepEgal) require("./_regexp_test")(deepEgal) require("./_date_test")(deepEgal) require("./_function_test")(deepEgal) require("./_object_with_constructor_test")(deepEgal) require("./_object_with_value_of_test")(deepEgal) describe("given Array", function() { it("must return true given equivalent empty arrays", function() { deepEgal([], []).must.be.true() }) it("must return true given empty array and Array.prototype", function() { deepEgal([], Array.prototype).must.be.true() }) it("must return true given equivalent arrays", function() { deepEgal([1], [1]).must.be.true() deepEgal([1, 2, 3], [1, 2, 3]).must.be.true() }) it("must return true given identical arrays", function() { var array = [] deepEgal(array, array).must.be.true() }) it("must return false given an empty and non-empty array", function() { deepEgal([], [1]).must.be.false() }) it("must return false given a smaller and a larger array", function() { deepEgal([1], [1, 2]).must.be.false() }) it("must return true given equivalent nested arrays", function() { deepEgal([1, [2], 3], [1, [2], 3]).must.be.true() }) it("must return false given unequivalent nested arrays", function() { deepEgal([1, [2], 3], [1, [42], 3]).must.be.false() }) describe("with circular references", function() { it("must return true if equal", function() { var a = [1, 2, 3] a.push(a) a.push(5) var b = [1, 2, 3] b.push(b) b.push(5) deepEgal(a, b).must.be.true() }) it("must return false if only one circular", function() { var a = [1, 2, 3] a.push(a) a.push(5) var b = [1, 2, 3, [1, 2, 3, 5], 5] deepEgal(a, b).must.be.false() }) it("must return false if circular to different levels", function() { var a = [1, 2, 3] a.push(a) var b = [1, 2, 3] var bInside = [1, 2, 3] bInside.push(bInside) b.push(bInside) deepEgal(a, b).must.be.false() }) }) describe("with nested values", function() { function nestedDeepEgal(a, b) { return deepEgal([a], [b]) } require("./_null_test")(nestedDeepEgal) require("./_boolean_test")(nestedDeepEgal) require("./_number_test")(nestedDeepEgal) require("./_string_test")(nestedDeepEgal) require("./_symbol_test")(nestedDeepEgal) require("./_regexp_test")(nestedDeepEgal) require("./_date_test")(nestedDeepEgal) require("./_function_test")(nestedDeepEgal) require("./_object_with_constructor_test")(nestedDeepEgal) require("./_object_with_value_of_test")(nestedDeepEgal) }) }) describe("given Object", function() { it("must return true given identical objects", function() { var obj = {a: 42, b: 69} deepEgal(obj, obj).must.be.true() }) it("must return true given empty objects", function() { deepEgal({}, {}).must.be.true() }) it("must return false given an empty and filled object", function() { deepEgal({}, {name: "John"}).must.be.false() }) it("must return false given a smaller and larger object", function() { var a = {a: 42, b: 69} var b = {a: 42} deepEgal(a, b).must.be.false() }) // This was a bug I discovered on Jun 12, 2015 related to not comparing // keys equivalence before comparing their values. it("must return false given equal amount of keys undefined keys", function() { deepEgal({name: undefined}, {age: undefined}).must.be.false() deepEgal({name: undefined}, {age: 13}).must.be.false() }) it("must return true given equivalent objects", function() { var a = {a: 42, b: 69} var b = {a: 42, b: 69} deepEgal(a, b).must.be.true() }) it("must return false given objects with differently typed properties", function() { var a = {a: "42", b: 69} var b = {a: 42, b: 69} deepEgal(a, b).must.be.false() }) it("must return true given an object with set constructor property", function() { var a = {constructor: 1337} var b = {constructor: 1337} deepEgal(a, b).must.be.true() }) it("must return true given a deep object", function() { var a = {life: {love: 69}} var b = {life: {love: 69}} deepEgal(a, b).must.be.true() }) it("must return false given an unequivalent deep object", function() { var a = {life: {love: 69}} var b = {life: {love: 42}} deepEgal(a, b).must.be.false() }) describe("with circular references", function() { it("must return true if equal", function() { var a = {life: {love: 69}} a.self = a var b = ({life: {love: 69}}) b.self = b deepEgal(a, b).must.be.true() }) it("must return false if only one circular", function() { var a = ({life: {love: 69}}) a.self = a var b = ({life: {love: 69}}) b.self = {life: {love: 69}, self: {}} deepEgal(a, b).must.be.false() }) it("must return false if circular to different levels", function() { var a = ({life: {love: 69}}) a.self = a var b = ({life: {love: 69}}) var bInside = ({life: {love: 69}}) bInside.self = bInside b.self = bInside deepEgal(a, b).must.be.false() }) }) describe("with inheritance", function() { it("must return true given empty inherited objects", function() { var a = Object.create({}) var b = Object.create({}) deepEgal(a, b).must.be.true() }) it("must return true given empty ancestored objects", function() { var a = Object.create(Object.create({})) var b = Object.create(Object.create({})) deepEgal(a, b).must.be.true() }) it("must return true given empty objects inherited from null", function() { var a = Object.create(null) var b = Object.create(null) deepEgal(a, b).must.be.true() }) it("must return true given empty objects ancestored from null", function() { var a = Object.create(Object.create(null)) var b = Object.create(Object.create(null)) deepEgal(a, b).must.be.true() }) it("must return true given equivalent inherited objects", function() { var a = Object.create({love: 42}) var b = Object.create({love: 42}) deepEgal(a, b).must.be.true() }) it("must return true given equivalent ancestored objects", function() { var a = Object.create(Object.create({love: 42})) var b = Object.create(Object.create({love: 42})) deepEgal(a, b).must.be.true() }) it("must return true given equivalent objects inherited from null", function() { var a = Object.create(null, {life: {value: 42, enumerable: true}}) var b = Object.create(null, {life: {value: 42, enumerable: true}}) deepEgal(a, b).must.be.true() }) it("must return true given equivalent objects ancestored from null", function() { var a = Object.create(Object.create(null, { life: {value: 42, enumerable: true} })) var b = Object.create(Object.create(null, { life: {value: 42, enumerable: true} })) deepEgal(a, b).must.be.true() }) it("must return false given unequivalent inherited objects", function() { var a = Object.create({love: 42}) var b = Object.create({love: 69}) deepEgal(a, b).must.be.false() }) it("must return false given unequivalent ancestored objects", function() { var a = Object.create(Object.create({love: 42})) var b = Object.create(Object.create({love: 69})) deepEgal(a, b).must.be.false() }) it("must return false given unequivalent objects inherited from null", function() { var a = Object.create(null, {life: {value: 42, enumerable: true}}) var b = Object.create(null, {life: {value: 69, enumerable: true}}) deepEgal(a, b).must.be.false() }) it("must return false given unequivalent objects ancestored from null", function() { var a = Object.create(Object.create(null, { life: {value: 42, enumerable: true} })) var b = Object.create(Object.create(null, { life: {value: 69, enumerable: true} })) deepEgal(a, b).must.be.false() }) }) describe("with nested values", function() { function nestedDeepEgal(a, b) { return deepEgal({key: a}, {key: b}) } require("./_null_test")(nestedDeepEgal) require("./_boolean_test")(nestedDeepEgal) require("./_number_test")(nestedDeepEgal) require("./_string_test")(nestedDeepEgal) require("./_symbol_test")(nestedDeepEgal) require("./_regexp_test")(nestedDeepEgal) require("./_date_test")(nestedDeepEgal) require("./_function_test")(nestedDeepEgal) require("./_object_with_constructor_test")(nestedDeepEgal) require("./_object_with_value_of_test")(nestedDeepEgal) }) }) describe("given egal function", function() { var deepEgal = require("..").deepEgal it("must be called with initial values", function() { var egal = Sinon.spy() var a = {}, b = {} deepEgal(a, b, egal) egal.callCount.must.equal(1) egal.args[0].length.must.equal(2) egal.args[0][0].must.equal(a) egal.args[0][1].must.equal(b) }) it("must not recurse if function returns true", function() { var egal = Sinon.spy(function() { return true }) deepEgal([42], [69], egal).must.be.true() egal.callCount.must.equal(1) egal.args[0].must.eql([[42], [69]]) }) it("must recurse if function returns null", function() { var egal = Sinon.spy(function() { return null }) deepEgal([42], [42], egal).must.be.false() egal.callCount.must.equal(2) egal.args[0].must.eql([[42], [42]]) egal.args[1].must.eql([42, 42]) }) it("must not recurse if function returns false", function() { var egal = Sinon.spy(function() { return false }) deepEgal([42], [69], egal).must.be.false() egal.callCount.must.equal(1) egal.args[0].must.eql([[42], [69]]) }) it("must be called when recursing to arrays", function() { var egal = Sinon.spy() var a = {}, b = {} deepEgal([a], [b], egal) egal.callCount.must.equal(2) egal.args[1].length.must.equal(2) egal.args[1][0].must.equal(a) egal.args[1][1].must.equal(b) }) it("must be called when recursing into objects", function() { var egal = Sinon.spy() var a = {}, b = {} deepEgal({key: a}, {key: b}, egal) egal.callCount.must.equal(2) egal.args[1].length.must.equal(2) egal.args[1][0].must.equal(a) egal.args[1][1].must.equal(b) }) }) }) js-egal-1.3.0/test/mocha.opts000066400000000000000000000000511260025503400160030ustar00rootroot00000000000000--recursive --require must --check-leaks