Comparing version 0.1.1 to 1.0.0
@@ -1,8 +0,6 @@ | ||
((typeof define === "function" && define.amd && function (m) { | ||
define("lodash", m); | ||
}) || (typeof module === "object" && | ||
typeof require === "function" && function (m) { | ||
module.exports = m(require("lodash")); | ||
}) || function (m) { this.samsam = m(_); } | ||
)(function (_) { | ||
((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || | ||
(typeof module === "object" && | ||
function (m) { module.exports = m(); }) || // Node | ||
function (m) { this.samsam = m(); } // Browser globals | ||
)(function () { | ||
var o = Object.prototype; | ||
@@ -27,2 +25,24 @@ var div = typeof document !== "undefined" && document.createElement("div"); | ||
/** | ||
* @name samsam.isArguments | ||
* @param Object object | ||
* | ||
* Returns ``true`` if ``object`` is an ``arguments`` object, | ||
* ``false`` otherwise. | ||
*/ | ||
function isArguments(object) { | ||
if (typeof object !== "object" || typeof object.length !== "number" || | ||
getClass(object) === "Array") { | ||
return false; | ||
} | ||
if (typeof object.callee == "function") { return true; } | ||
try { | ||
object[object.length] = 6; | ||
delete object[object.length]; | ||
} catch (e) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
/** | ||
* @name samsam.isElement | ||
@@ -48,2 +68,30 @@ * @param Object object | ||
/** | ||
* @name samsam.keys | ||
* @param Object object | ||
* | ||
* Return an array of own property names. | ||
*/ | ||
function keys(object) { | ||
var ks = [], prop; | ||
for (prop in object) { | ||
if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } | ||
} | ||
return ks; | ||
} | ||
/** | ||
* @name samsam.isDate | ||
* @param Object value | ||
* | ||
* Returns true if the object is a ``Date``, or *date-like*. Duck typing | ||
* of date objects work by checking that the object has a ``getTime`` | ||
* function whose return value equals the return value from the object's | ||
* ``valueOf``. | ||
*/ | ||
function isDate(value) { | ||
return typeof value.getTime == "function" && | ||
value.getTime() == value.valueOf(); | ||
} | ||
/** | ||
* @name samsam.isNegZero | ||
@@ -75,2 +123,3 @@ * @param Object value | ||
/** | ||
@@ -88,54 +137,169 @@ * @name samsam.deepEqual | ||
* in ``obj1`` is deepEqual to the corresponding property in ``obj2`` | ||
* | ||
* Supports cyclic objects. | ||
*/ | ||
function deepEqual(obj1, obj2) { | ||
var type1 = typeof obj1; | ||
var type2 = typeof obj2; | ||
function deepEqualCyclic(obj1, obj2) { | ||
// == null also matches undefined | ||
if (obj1 === obj2 || | ||
isNaN(obj1) || isNaN(obj2) || | ||
obj1 == null || obj2 == null || | ||
type1 !== "object" || type2 !== "object") { | ||
return identical(obj1, obj2); | ||
} | ||
// used for cyclic comparison | ||
// contain already visited objects | ||
var objects1 = [], | ||
objects2 = [], | ||
// contain pathes (position in the object structure) | ||
// of the already visited objects | ||
// indexes same as in objects arrays | ||
paths1 = [], | ||
paths2 = [], | ||
// contains combinations of already compared objects | ||
// in the manner: { "$1['ref']$2['ref']": true } | ||
compared = {}; | ||
// Elements are only equal if identical(expected, actual) | ||
if (isElement(obj1) || isElement(obj2)) { return false; } | ||
/** | ||
* used to check, if the value of a property is an object | ||
* (cyclic logic is only needed for objects) | ||
* only needed for cyclic logic | ||
*/ | ||
function isObject(value) { | ||
var isDate1 = _.isDate(obj1), isDate2 = _.isDate(obj2); | ||
if (isDate1 || isDate2) { | ||
if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { | ||
return false; | ||
if (typeof value === 'object' && value !== null && | ||
!(value instanceof Boolean) && | ||
!(value instanceof Date) && | ||
!(value instanceof Number) && | ||
!(value instanceof RegExp) && | ||
!(value instanceof String)) { | ||
return true; | ||
} | ||
} | ||
if (obj1 instanceof RegExp && obj2 instanceof RegExp) { | ||
if (obj1.toString() !== obj2.toString()) { return false; } | ||
return false; | ||
} | ||
var class1 = getClass(obj1); | ||
var class2 = getClass(obj2); | ||
var keys1 = _.keys(obj1); | ||
var keys2 = _.keys(obj2); | ||
/** | ||
* returns the index of the given object in the | ||
* given objects array, -1 if not contained | ||
* only needed for cyclic logic | ||
*/ | ||
function getIndex(objects, obj) { | ||
if (_.isArguments(obj1) || _.isArguments(obj2)) { | ||
if (obj1.length !== obj2.length) { return false; } | ||
} else { | ||
if (type1 !== type2 || class1 !== class2 || | ||
keys1.length !== keys2.length) { | ||
return false; | ||
var i; | ||
for (i = 0; i < objects.length; i++) { | ||
if (objects[i] === obj) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
} | ||
var key, i, l; | ||
// does the recursion for the deep equal check | ||
return (function deepEqual(obj1, obj2, path1, path2) { | ||
var type1 = typeof obj1; | ||
var type2 = typeof obj2; | ||
for (i = 0, l = keys1.length; i < l; i++) { | ||
key = keys1[i]; | ||
if (!o.hasOwnProperty.call(obj2, key) || | ||
!deepEqual(obj1[key], obj2[key])) { | ||
return false; | ||
// == null also matches undefined | ||
if (obj1 === obj2 || | ||
isNaN(obj1) || isNaN(obj2) || | ||
obj1 == null || obj2 == null || | ||
type1 !== "object" || type2 !== "object") { | ||
return identical(obj1, obj2); | ||
} | ||
} | ||
return true; | ||
// Elements are only equal if identical(expected, actual) | ||
if (isElement(obj1) || isElement(obj2)) { return false; } | ||
var isDate1 = isDate(obj1), isDate2 = isDate(obj2); | ||
if (isDate1 || isDate2) { | ||
if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { | ||
return false; | ||
} | ||
} | ||
if (obj1 instanceof RegExp && obj2 instanceof RegExp) { | ||
if (obj1.toString() !== obj2.toString()) { return false; } | ||
} | ||
var class1 = getClass(obj1); | ||
var class2 = getClass(obj2); | ||
var keys1 = keys(obj1); | ||
var keys2 = keys(obj2); | ||
if (isArguments(obj1) || isArguments(obj2)) { | ||
if (obj1.length !== obj2.length) { return false; } | ||
} else { | ||
if (type1 !== type2 || class1 !== class2 || | ||
keys1.length !== keys2.length) { | ||
return false; | ||
} | ||
} | ||
var key, i, l, | ||
// following vars are used for the cyclic logic | ||
value1, value2, | ||
isObject1, isObject2, | ||
index1, index2, | ||
newPath1, newPath2; | ||
for (i = 0, l = keys1.length; i < l; i++) { | ||
key = keys1[i]; | ||
if (!o.hasOwnProperty.call(obj2, key)) { | ||
return false; | ||
} | ||
// Start of the cyclic logic | ||
value1 = obj1[key]; | ||
value2 = obj2[key]; | ||
isObject1 = isObject(value1); | ||
isObject2 = isObject(value2); | ||
// determine, if the objects were already visited | ||
// (it's faster to check for isObject first, than to | ||
// get -1 from getIndex for non objects) | ||
index1 = isObject1 ? getIndex(objects1, value1) : -1; | ||
index2 = isObject2 ? getIndex(objects2, value2) : -1; | ||
// determine the new pathes of the objects | ||
// - for non cyclic objects the current path will be extended | ||
// by current property name | ||
// - for cyclic objects the stored path is taken | ||
newPath1 = index1 !== -1 | ||
? paths1[index1] | ||
: path1 + '[' + JSON.stringify(key) + ']'; | ||
newPath2 = index2 !== -1 | ||
? paths2[index2] | ||
: path2 + '[' + JSON.stringify(key) + ']'; | ||
// stop recursion if current objects are already compared | ||
if (compared[newPath1 + newPath2]) { | ||
return true; | ||
} | ||
// remember the current objects and their pathes | ||
if (index1 === -1 && isObject1) { | ||
objects1.push(value1); | ||
paths1.push(newPath1); | ||
} | ||
if (index2 === -1 && isObject2) { | ||
objects2.push(value2); | ||
paths2.push(newPath2); | ||
} | ||
// remember that the current objects are already compared | ||
if (isObject1 && isObject2) { | ||
compared[newPath1 + newPath2] = true; | ||
} | ||
// End of cyclic logic | ||
// neither value1 nor value2 is a cycle | ||
// continue with next level | ||
if (!deepEqual(value1, value2, newPath1, newPath2)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}(obj1, obj2, '$1', '$2')); | ||
} | ||
@@ -208,8 +372,10 @@ | ||
return { | ||
isArguments: isArguments, | ||
isElement: isElement, | ||
isDate: isDate, | ||
isNegZero: isNegZero, | ||
identical: identical, | ||
deepEqual: deepEqual, | ||
deepEqual: deepEqualCyclic, | ||
match: match | ||
}; | ||
}); |
{ | ||
"name": "samsam", | ||
"version": "0.1.1", | ||
"version": "1.0.0", | ||
"description": "Value identification and comparison functions", | ||
"homepage": "http://busterjs.org/docs/buster-assertions", | ||
"author": { "name": "August Lilleaas and Christian Johansen" }, | ||
"author": "Christian Johansen", | ||
"contributors": [{ | ||
@@ -15,2 +15,6 @@ "name": "Christian Johansen", | ||
"url": "http://augustl.com" | ||
}, { | ||
"name": "Daniel Wittner", | ||
"email": "d.wittner@gmx.de", | ||
"url": "https://github.com/dwittner" | ||
}], | ||
@@ -25,8 +29,5 @@ "main": "./lib/samsam", | ||
}, | ||
"dependencies": { | ||
"lodash": "~0.4" | ||
}, | ||
"devDependencies": { | ||
"buster": "0.6.2" | ||
"buster": "0.6.11" | ||
} | ||
} |
@@ -159,4 +159,4 @@ if (typeof module === "object" && typeof require === "function") { | ||
fail("'empty' object to date", {}, new Date()); | ||
fail("'empty' object to string object", {}, new String()); | ||
fail("'empty' object to number object", {}, new Number()); | ||
fail("'empty' object to string object", {}, String()); | ||
fail("'empty' object to number object", {}, Number()); | ||
fail("'empty' object to empty array", {}, []); | ||
@@ -174,2 +174,77 @@ | ||
/** | ||
* Tests for cyclic objects. | ||
*/ | ||
tests("deepEqual", function (pass, fail) { | ||
(function () { | ||
var cyclic1 = {}, cyclic2 = {}; | ||
cyclic1.ref = cyclic1; | ||
cyclic2.ref = cyclic2; | ||
pass("equal cyclic objects (cycle on 2nd level)", cyclic1, cyclic2); | ||
}()); | ||
(function () { | ||
var cyclic1 = {}, cyclic2 = {}; | ||
cyclic1.ref = cyclic1; | ||
cyclic2.ref = cyclic2; | ||
cyclic2.ref2 = cyclic2; | ||
fail("different cyclic objects (cycle on 2nd level)", | ||
cyclic1, cyclic2); | ||
}()); | ||
(function () { | ||
var cyclic1 = {}, cyclic2 = {}; | ||
cyclic1.ref = {}; | ||
cyclic1.ref.ref = cyclic1; | ||
cyclic2.ref = {}; | ||
cyclic2.ref.ref = cyclic2; | ||
pass("equal cyclic objects (cycle on 3rd level)", cyclic1, cyclic2); | ||
}()); | ||
(function () { | ||
var cyclic1 = {}, cyclic2 = {}; | ||
cyclic1.ref = {}; | ||
cyclic1.ref.ref = cyclic1; | ||
cyclic2.ref = {}; | ||
cyclic2.ref.ref = cyclic2; | ||
cyclic2.ref.ref2 = cyclic2; | ||
fail("different cyclic objects (cycle on 3rd level)", | ||
cyclic1, cyclic2); | ||
}()); | ||
(function () { | ||
var cyclic1 = {}, cyclic2 = {}; | ||
cyclic1.ref = cyclic1; | ||
cyclic2.ref = cyclic1; | ||
pass("equal objects even though only one object is cyclic", | ||
cyclic1, cyclic2); | ||
}()); | ||
(function () { | ||
var cyclic1 = {}, cyclic2 = {}; | ||
cyclic1.ref = { | ||
ref: cyclic1 | ||
}; | ||
cyclic2.ref = {}; | ||
cyclic2.ref.ref = cyclic2.ref; | ||
pass("referencing different but equal cyclic objects", | ||
cyclic1, cyclic2); | ||
}()); | ||
(function () { | ||
var cyclic1 = {a: "a"}, cyclic2 = {a: "a"}; | ||
cyclic1.ref = { | ||
b: "b", | ||
ref: cyclic1 | ||
}; | ||
cyclic2.ref = { | ||
b: "b" | ||
}; | ||
cyclic2.ref.ref = cyclic2.ref; | ||
fail("referencing different and unequal cyclic objects", | ||
cyclic1, cyclic2); | ||
}()); | ||
}); | ||
tests("match", function (pass, fail, shouldThrow, add) { | ||
@@ -189,3 +264,3 @@ pass("matching regexp", "Assertions", /[a-z]/); | ||
fail("matching number against string", "Assertions 123", 23); | ||
pass("matching number against similar string", "23", 23); | ||
fail("matching number against similar string", "23", 23); | ||
pass("matching number against itself", 23, 23); | ||
@@ -269,2 +344,3 @@ pass("matcher function returns true", | ||
}); | ||
}()); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
33714
0
647
1
215
0
1
- Removedlodash@~0.4
- Removedlodash@0.4.2(transitive)