pax_global_header00006660000000000000000000000064126610137650014521gustar00rootroot0000000000000052 comment=7dbefccb03cbe8f5ecfedda41cb20d1ae3514e5c samsam-1.2.1/000077500000000000000000000000001266101376500130035ustar00rootroot00000000000000samsam-1.2.1/.gitignore000066400000000000000000000000231266101376500147660ustar00rootroot00000000000000node_modules .idea samsam-1.2.1/.travis.yml000066400000000000000000000001561266101376500151160ustar00rootroot00000000000000language: node_js sudo: false node_js: - "0.10" - "0.12" - "4" - "5" before_install: - npm i -g npm samsam-1.2.1/AUTHORS000066400000000000000000000001211266101376500140450ustar00rootroot00000000000000Christian Johansen (christian@cjohansen.no) August Lilleaas (august@augustl.com) samsam-1.2.1/Gruntfile.js000066400000000000000000000011011266101376500152710ustar00rootroot00000000000000module.exports = function (grunt) { grunt.loadNpmTasks("grunt-buster"); grunt.initConfig({ "buster": { "browser": { "test": { "config-group": "browser" } }, "node": { "test": { "config-group": "node" } } } }); grunt.registerTask("test", "Clean build, minify and run tests", ["buster:node:test", "buster:browser:server", "buster:browser:phantomjs", "buster:browser:test"] ); }; samsam-1.2.1/LICENSE000066400000000000000000000031241266101376500140100ustar00rootroot00000000000000(The BSD License) Copyright (c) 2010-2012, Christian Johansen, christian@cjohansen.no and August Lilleaas, august.lilleaas@gmail.com. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Christian Johansen nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. samsam-1.2.1/Readme.md000066400000000000000000000145251266101376500145310ustar00rootroot00000000000000# samsam [![Build status](https://secure.travis-ci.org/busterjs/samsam.png?branch=master)](http://travis-ci.org/busterjs/samsam) > Same same, but different `samsam` is a collection of predicate and comparison functions useful for identifiying the type of values and to compare values with varying degrees of strictness. `samsam` is a general-purpose library with no dependencies. It works in browsers (including old and rowdy ones, like IE6) and Node. It will define itself as an AMD module if you want it to (i.e. if there's a `define` function available). `samsam` was originally extracted from the `referee `_ assertion library, which ships with the Buster.JS testing framework. ## Predicate functions ### `isArguments(object)` Returns `true` if `object` is an `arguments` object, `false` otherwise. ### `isNegZero(value)` Returns `true` if `value` is `-0`. ## `isElement(object)` Returns `true` if `object` is a DOM element node. Unlike Underscore.js/lodash, this function will return `false` if `object` is an *element-like* object, i.e. a regular object with a `nodeType` property that holds the value `1`. ###`isDate(object)` 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`. ## Comparison functions ###`identical(x, y)` Strict equality check according to `EcmaScript Harmony's `egal`. **From the Harmony wiki:** > An egal function simply makes available the internal `SameValue` function from section 9.12 of the ES5 spec. If two values are egal, then they are not observably distinguishable. `identical` returns `true` when `===` is `true`, except for `-0` and `+0`, where it returns `false`. Additionally, it returns `true` when `NaN` is compared to itself. ### `deepEqual(obj1, obj2)` Deep equal comparison. Two values are "deep equal" if: * They are identical * They are both date objects representing the same time * They are both arrays containing elements that are all deepEqual * They are objects with the same set of properties, and each property in `obj1` is deepEqual to the corresponding property in `obj2` ### `match(object, matcher)` Partial equality check. Compares `object` with matcher according a wide set of rules: **String matcher** In its simplest form, `match` performs a case insensitive substring match. When the matcher is a string, `object` is converted to a string, and the function returns `true` if the matcher is a case-insensitive substring of `object` as a string. ```javascript samsam.match("Give me something", "Give"); //true samsam.match("Give me something", "sumptn"); // false samsam.match({ toString: function () { return "yeah"; } }, "Yeah!"); // true ``` The last example is not symmetric. When the matcher is a string, the `object` is coerced to a string - in this case using `toString`. Changing the order of the arguments would cause the matcher to be an object, in which case different rules apply (see below). **Boolean matcher** Performs a strict (i.e. `===`) match with the object. So, only `true` matches `true`, and only `false` matches `false`. **Regular expression matcher** When the matcher is a regular expression, the function will pass if `object.test(matcher)` is `true`. `match` is written in a generic way, so any object with a `test` method will be used as a matcher this way. ```javascript samsam.match("Give me something", /^[a-z\s]$/i); // true samsam.match("Give me something", /[0-9]/); // false samsam.match({ toString: function () { return "yeah!"; } }, /yeah/); // true samsam.match(234, /[a-z]/); // false ``` **Number matcher** When the matcher is a number, the assertion will pass if `object == matcher`. ```javascript samsam.match("123", 123); // true samsam.match("Give me something", 425); // false samsam.match({ toString: function () { return "42"; } }, 42); // true samsam.match(234, 1234); // false ``` **Function matcher** When the matcher is a function, it is called with `object` as its only argument. `match` returns `true` if the function returns `true`. A strict match is performed against the return value, so a boolean `true` is required, truthy is not enough. ```javascript // true samsam.match("123", function (exp) { return exp == "123"; }); // false samsam.match("Give me something", function () { return "ok"; }); // true samsam.match({ toString: function () { return "42"; } }, function () { return true; }); // false samsam.match(234, function () {}); ``` **Object matcher** As mentioned above, if an object matcher defines a `test` method, `match` will return `true` if `matcher.test(object)` returns truthy. If the matcher does not have a test method, a recursive match is performed. If all properties of `matcher` matches corresponding properties in `object`, `match` returns `true`. Note that the object matcher does not care if the number of properties in the two objects are the same - only if all properties in the matcher recursively matches ones in `object`. ```javascript // true samsam.match("123", { test: function (arg) { return arg == 123; } }); // false samsam.match({}, { prop: 42 }); // true samsam.match({ name: "Chris", profession: "Programmer" }, { name: "Chris" }); // false samsam.match(234, { name: "Chris" }); ``` **DOM elements** `match` can be very helpful when comparing DOM elements, because it allows you to compare several properties with one call: ```javascript var el = document.getElementById("myEl"); samsam.match(el, { tagName: "h2", className: "item", innerHTML: "Howdy" }); ``` ## Changelog **1.1.2** (11.12.2014) * Fix for issue [#359 - `assert.match` does not support objects with `null` properties`](https://github.com/busterjs/buster/issues/359) * Implementation of feature request [#64 - assert.match and parentNode](https://github.com/busterjs/buster/issues/64) **1.1.1** (26.03.2014) * [Make `isArguments` work with arguments from `"strict mode"` functions](https://github.com/busterjs/samsam/commit/72903613af90f39474f8388ed8957eaea4cf46ae) * [Fix type error for nested object in function `match`](https://github.com/busterjs/samsam/commit/9d3420a11e9b3c65559945e60ca56980820db20f) * Fix for issue [#366 - Assertion match fails with data attribute](https://github.com/busterjs/buster/issues/366) samsam-1.2.1/appveyor.yml000066400000000000000000000004531266101376500153750ustar00rootroot00000000000000environment: matrix: - nodejs_version: "0.10" - nodejs_version: "0.12" - nodejs_version: "4" - nodejs_version: "5" install: - ps: Install-Product node $env:nodejs_version - npm install -g npm - npm install test_script: - node --version - npm --version - npm test build: off samsam-1.2.1/autolint.js000066400000000000000000000006341266101376500152030ustar00rootroot00000000000000module.exports = { paths: [ "lib/*.js", "test/*.js" ], linterOptions: { node: true, browser: true, plusplus: true, vars: true, nomen: true, forin: true, sloppy: true, eqeq: true, predef: [ "_", "define", "assert", "refute", "buster" ] } }; samsam-1.2.1/buster.js000066400000000000000000000004651266101376500146520ustar00rootroot00000000000000var config = module.exports; config["browser"] = { rootPath: ".", environment: "browser", src: [ "lib/samsam.js" ], tests: [ "test/samsam-test.js" ] }; config["node"] = { rootPath: ".", environment: "node", tests: [ "test/samsam-test.js" ] }; samsam-1.2.1/lib/000077500000000000000000000000001266101376500135515ustar00rootroot00000000000000samsam-1.2.1/lib/samsam.js000066400000000000000000000332031266101376500153710ustar00rootroot00000000000000((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; var div = typeof document !== "undefined" && document.createElement("div"); function isNaN(value) { // Unlike global isNaN, this avoids type coercion // typeof check avoids IE host object issues, hat tip to // lodash var val = value; // JsLint thinks value !== value is "weird" return typeof value === "number" && value !== val; } function getClass(value) { // Returns the internal [[Class]] by calling Object.prototype.toString // with the provided value as this. Return value is a string, naming the // internal class, e.g. "Array" return o.toString.call(value).split(/[ \]]/)[1]; } /** * @name samsam.isArguments * @param Object object * * Returns ``true`` if ``object`` is an ``arguments`` object, * ``false`` otherwise. */ function isArguments(object) { if (getClass(object) === 'Arguments') { return true; } 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 * @param Object object * * Returns ``true`` if ``object`` is a DOM element node. Unlike * Underscore.js/lodash, this function will return ``false`` if ``object`` * is an *element-like* object, i.e. a regular object with a ``nodeType`` * property that holds the value ``1``. */ function isElement(object) { if (!object || object.nodeType !== 1 || !div) { return false; } try { object.appendChild(div); object.removeChild(div); } catch (e) { return false; } return true; } /** * @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 * @param Object value * * Returns ``true`` if ``value`` is ``-0``. */ function isNegZero(value) { return value === 0 && 1 / value === -Infinity; } /** * @name samsam.equal * @param Object obj1 * @param Object obj2 * * Returns ``true`` if two objects are strictly equal. Compared to * ``===`` there are two exceptions: * * - NaN is considered equal to NaN * - -0 and +0 are not considered equal */ function identical(obj1, obj2) { if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); } } function isSet(val) { if (typeof Set !== 'undefined' && val instanceof Set) { return true; } } function isSubset(s1, s2, compare) { var values1 = Array.from(s1); var values2 = Array.from(s2); for (var i = 0; i < values1.length; i++) { var includes = false; for (var j = 0; j < values2.length; j++) { if (compare(values2[j], values1[i])) { includes = true; break; } } if (!includes) { return false; } } return true; } /** * @name samsam.deepEqual * @param Object obj1 * @param Object obj2 * * Deep equal comparison. Two values are "deep equal" if: * * - They are equal, according to samsam.identical * - They are both date objects representing the same time * - They are both arrays containing elements that are all deepEqual * - They are objects with the same set of properties, and each property * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` * * Supports cyclic objects. */ function deepEqualCyclic(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 = {}; /** * 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) { if (typeof value === 'object' && value !== null && !(value instanceof Boolean) && !(value instanceof Date) && !(value instanceof Number) && !(value instanceof RegExp) && !(value instanceof String)) { return true; } return false; } /** * 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) { var i; for (i = 0; i < objects.length; i++) { if (objects[i] === obj) { return i; } } return -1; } // does the recursion for the deep equal check return (function deepEqual(obj1, obj2, path1, path2) { var type1 = typeof obj1; var type2 = typeof obj2; // == null also matches undefined if (obj1 === obj2 || isNaN(obj1) || isNaN(obj2) || obj1 == null || obj2 == null || type1 !== "object" || type2 !== "object") { return identical(obj1, obj2); } // 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; } } if (isSet(obj1) || isSet(obj2)) { if (!isSet(obj1) || !isSet(obj2) || obj1.size !== obj2.size) { return false; } return isSubset(obj1, obj2, deepEqual); } 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')); } function arrayContains(array, subset, compare) { if (subset.length === 0) { return true; } var i, l, j, k; for (i = 0, l = array.length; i < l; ++i) { if (compare(array[i], subset[0])) { for (j = 0, k = subset.length; j < k; ++j) { if ((i + j) >= l) { return false; } if (!compare(array[i + j], subset[j])) { return false; } } return true; } } return false; } /** * @name samsam.match * @param Object object * @param Object matcher * * Compare arbitrary value ``object`` with matcher. */ function match(object, matcher) { if (matcher && typeof matcher.test === "function") { return matcher.test(object); } if (typeof matcher === "function") { return matcher(object) === true; } if (typeof matcher === "string") { matcher = matcher.toLowerCase(); var notNull = typeof object === "string" || !!object; return notNull && (String(object)).toLowerCase().indexOf(matcher) >= 0; } if (typeof matcher === "number") { return matcher === object; } if (typeof matcher === "boolean") { return matcher === object; } if (typeof(matcher) === "undefined") { return typeof(object) === "undefined"; } if (matcher === null) { return object === null; } if (isSet(object)) { return isSubset(matcher, object, match); } if (getClass(object) === "Array" && getClass(matcher) === "Array") { return arrayContains(object, matcher, match); } if (matcher && typeof matcher === "object") { if (matcher === object) { return true; } var prop; for (prop in matcher) { var value = object[prop]; if (typeof value === "undefined" && typeof object.getAttribute === "function") { value = object.getAttribute(prop); } if (matcher[prop] === null || typeof matcher[prop] === 'undefined') { if (value !== matcher[prop]) { return false; } } else if (typeof value === "undefined" || !match(value, matcher[prop])) { return false; } } return true; } throw new Error("Matcher was not a string, a number, a " + "function, a boolean or an object"); } return { isArguments: isArguments, isElement: isElement, isDate: isDate, isNegZero: isNegZero, identical: identical, deepEqual: deepEqualCyclic, match: match, keys: keys }; }); samsam-1.2.1/package.json000066400000000000000000000016741266101376500153010ustar00rootroot00000000000000{ "name": "samsam", "version": "1.2.1", "description": "Value identification and comparison functions", "homepage": "http://docs.busterjs.org/en/latest/modules/samsam/", "author": "Christian Johansen", "contributors": [ { "name": "Christian Johansen", "email": "christian@cjohansen.no", "url": "http://cjohansen.no" }, { "name": "August Lilleaas", "email": "august.lilleaas@gmail.com", "url": "http://augustl.com" }, { "name": "Daniel Wittner", "email": "d.wittner@gmx.de", "url": "https://github.com/dwittner" } ], "license": "BSD-3-Clause", "main": "./lib/samsam", "repository": { "type": "git", "url": "https://github.com/busterjs/samsam.git" }, "scripts": { "test": "grunt test" }, "devDependencies": { "grunt": "0.4.x", "grunt-buster": "0.4.x", "grunt-cli": "0.1.x", "buster": "0.7.x", "phantomjs": "1.9.x" } } samsam-1.2.1/test/000077500000000000000000000000001266101376500137625ustar00rootroot00000000000000samsam-1.2.1/test/samsam-test.js000066400000000000000000000375301266101376500165660ustar00rootroot00000000000000if (typeof module === "object" && typeof require === "function") { var buster = require("buster"); var samsam = require("../lib/samsam"); } (function () { var assert = buster.assert; function tests(method, body) { var tc = {}; function pass(name) { var args = Array.prototype.slice.call(arguments, 1); tc["should return true for " + name] = function () { assert(samsam[method].apply(samsam, args)); }; } function fail(name) { var args = Array.prototype.slice.call(arguments, 1); tc["should return false for " + name] = function () { assert(!samsam[method].apply(samsam, args)); }; } function shouldThrow(name) { var args = Array.prototype.slice.call(arguments, 1); try { samsam[method].apply(samsam, args); buster.assertion.fail("Expected to throw"); } catch (e) { assert(true); } } function add(name, func) { tc[name] = func; } body(pass, fail, shouldThrow, add); buster.testCase(method, tc); } tests("isElement", function (pass, fail) { if (typeof document !== "undefined") { pass("DOM element node", document.createElement("div")); fail("DOM text node", document.createTextNode("Hello")); } fail("primitive", 42); fail("object", {}); fail("node-like object", { nodeType: 1 }); }); tests("isNegZero", function (pass, fail) { pass("-0", -0); fail("0", 0); fail("object", {}); }); tests("identical", function (pass, fail) { var object = { id: 42 }; pass("same object", object, object); pass("same primitive", 42, 42); fail("-0 and 0", -0, 0); pass("NaN and NaN", NaN, NaN); }); tests("deepEqual", function (pass, fail) { var func = function () {}; var obj = {}; var arr = []; var date = new Date(); var sameDate = new Date(date.getTime()); var anotherDate = new Date(date.getTime() - 10); var sameDateWithProp = new Date(date.getTime()); sameDateWithProp.prop = 42; pass("object to itself", obj, obj); pass("strings", "Hey", "Hey"); pass("numbers", 32, 32); pass("booleans", false, false); pass("null", null, null); pass("undefined", undefined, undefined); pass("function to itself", func, func); fail("functions", function () {}, function () {}); pass("array to itself", arr, arr); pass("date objects with same date", date, sameDate); fail("date objects with different dates", date, anotherDate); fail("date objects to null", date, null); fail("date with different custom properties", date, sameDateWithProp); fail("strings and numbers with coercion", "4", 4); fail("numbers and strings with coercion", 4, "4"); fail("number object with coercion", 32, new Number(32)); fail("number object reverse with coercion", new Number(32), 32); fail("falsy values with coercion", 0, ""); fail("falsy values reverse with coercion", "", 0); fail("string boxing with coercion", "4", new String("4")); fail("string boxing reverse with coercion", new String("4"), "4"); pass("NaN to NaN", NaN, NaN); fail("-0 to +0", -0, +0); fail("-0 to 0", -0, 0); fail("objects with different own properties", { id: 42 }, { id: 42, di: 24 }); fail("objects with different own properties #2", { id: undefined }, { di: 24 }); fail("objects with different own properties #3", { id: 24 }, { di: undefined }); pass("objects with one property", { id: 42 }, { id: 42 }); pass("objects with one object property", { obj: { id: 42 } }, { obj: { id: 42 } }); fail("objects with one property with different values", { id: 42 }, { id: 24 }); var deepObject = { id: 42, name: "Hey", sayIt: function () { return this.name; }, child: { speaking: function () {} } }; pass("complex objects", deepObject, { sayIt: deepObject.sayIt, child: { speaking: deepObject.child.speaking }, id: 42, name: "Hey" }); pass("arrays", [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }], [1, 2, "Hey there", func, { id: 42, prop: [2, 3] }]); fail("nested array with shallow array", [["hey"]], ["hey"]); var arr1 = [1, 2, 3]; var arr2 = [1, 2, 3]; arr1.prop = 42; fail("arrays with different custom properties", arr1, arr2); pass("regexp literals", /a/, /a/); pass("regexp objects", new RegExp("[a-z]+"), new RegExp("[a-z]+")); var re1 = new RegExp("[a-z]+"); var re2 = new RegExp("[a-z]+"); re2.id = 42; fail("regexp objects with custom properties", re1, re2); fail("different objects", { id: 42 }, {}); fail("object to null", {}, null); fail("object to undefined", {}, undefined); fail("object to false", {}, false); fail("false to object", false, {}); fail("object to true", {}, true); fail("true to object", true, {}); fail("'empty' object to date", {}, new Date()); fail("'empty' object to string object", {}, String()); fail("'empty' object to number object", {}, Number()); fail("'empty' object to empty array", {}, []); function gather() { return arguments; } var arrayLike = { length: 4, "0": 1, "1": 2, "2": {}, "3": [] }; pass("arguments to array", [1, 2, {}, []], gather(1, 2, {}, [])); pass("array to arguments", gather(), []); pass("arguments to array like object", arrayLike, gather(1, 2, {}, [])); if (typeof Set !== "undefined") { pass("sets with the same content", new Set([1, 2, 3]), new Set([2, 1, 3])); fail("sets with different content", new Set([1, 2, 3]), new Set([2, 5, 3])); } }); /** * 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) { pass("matching regexp", "Assertions", /[a-z]/); pass("generic object and test method returning true", "Assertions", { test: function () { return true; } }); fail("non-matching regexp", "Assertions 123", /^[a-z]$/); pass("matching boolean", true, true); fail("mismatching boolean", true, false); fail("generic object with test method returning false", "Assertions", { test: function () { return false; } }); shouldThrow("match object === null", "Assertions 123", null); fail("match object === false", "Assertions 123", false); fail("matching number against string", "Assertions 123", 23); fail("matching number against similar string", "23", 23); pass("matching number against itself", 23, 23); pass("matcher function returns true", "Assertions 123", function (obj) { return true; }); fail("matcher function returns false", "Assertions 123", function (obj) { return false; }); fail("matcher function returns falsy", "Assertions 123", function () {}); fail("matcher does not return explicit true", "Assertions 123", function () { return "Hey"; }); add("should call matcher with object", function () { var spy = this.spy(); samsam.match("Assertions 123", spy); assert.calledWith(spy, "Assertions 123"); }); pass("matcher is substring of matchee", "Diskord", "or"); pass("matcher is string equal to matchee", "Diskord", "Diskord"); pass("strings ignoring case", "Look ma, case-insensitive", "LoOk Ma, CaSe-InSenSiTiVe"); fail("match string is not substring of matchee", "Vim", "Emacs"); fail("match string is not substring of object", {}, "Emacs"); fail("matcher is not substring of object.toString", { toString: function () { return "Vim"; } }, "Emacs"); fail("null and empty string", null, ""); fail("undefined and empty string", undefined, ""); fail("false and empty string", false, ""); fail("0 and empty string", 0, ""); fail("NaN and empty string", NaN, ""); var object = { id: 42, name: "Christian", doIt: "yes", speak: function () { return this.name; } }; pass("object containing all properties in matcher", object, { id: 42, doIt: "yes" }); var object2 = { id: 42, name: "Christian", doIt: "yes", owner: { someDude: "Yes", hello: "ok" }, speak: function () { return this.name; } }; pass("nested matcher", object2, { owner: { someDude: "Yes", hello: function (value) { return value == "ok"; } } }); pass("empty strings", "", ""); pass("empty strings as object properties", { foo: "" }, { foo: "" }); pass("similar arrays", [1, 2, 3], [1, 2, 3]); pass("array subset", [1, 2, 3], [2, 3]); pass("single-element array subset", [1, 2, 3], [1]); pass("matching array subset", [1, 2, 3, { id: 42 }], [{ id: 42 }]); fail("mis-matching array 'subset'", [1, 2, 3], [2, 3, 4]); fail("mis-ordered array 'subset'", [1, 2, 3], [1, 3]); fail("mis-ordered, but similar arrays of objects", [{a: 'a'}, {a: 'aa'}], [{a: 'aa'}, {a: 'a'}]); pass("empty arrays", [], []); pass("objects with empty arrays", { xs: [] }, { xs: [] }); fail("nested objects with different depth", { a: 1 }, { b: { c: 2 } }); pass("dom elements with matching data attributes", { getAttribute: function (name) { if (name === "data-path") { return "foo.bar"; } } }, { "data-path": "foo.bar" }); fail("dom elements with not matching data attributes", { getAttribute: function (name) { if (name === "data-path") { return "foo.foo"; } } }, { "data-path": "foo.bar" }); pass("equal null properties", { foo: null }, { foo: null }); fail("unmatched null property", {}, { foo: null }); fail("matcher with unmatched null property", { foo: 'arbitrary' }, { foo: null }); pass("equal undefined properties", { foo: undefined }, { foo: undefined }); fail("matcher with unmatched undefined property", { foo: 'arbitrary' }, { foo: undefined }); pass('unmatched undefined property', {}, { foo: undefined }); var obj = { foo: undefined }; pass("same object matches self", obj, obj); pass("null matches null", null, null); fail("null does not match undefined", null, undefined); pass("undefined matches undefined", undefined, undefined); fail("undefined does not match null", undefined, null); if (typeof Set !== 'undefined') { pass("sets with same content", new Set([1, 2, 3]), new Set([2, 3, 1])); pass("subset", new Set([1, 2, 3]), new Set([3, 1])); pass("subset complex types", new Set([1, {id: 42}, 3]), new Set([{id: 42}])); fail("sets with dissimilar content", new Set([1, 2, 3]), new Set([2, 5, 1])); fail("sets with different complex member", new Set([{id: 42}]), new Set([{id: 13}])); pass( "differently sorted complex objects", new Set([{ end: "2019-08-07T18:00:00Z", geoAvailability: { resId: "http://id.nrk.no/2015/guri/IPRights/geoavailability/NORGE", title: "NORGE" }, resId: "http://id.nrk.no/2015/guri/68cc0a15-2be1-4666-984f-b421b415326d/publicationEvent/0", start: "2015-04-03T14:00:00Z" }, { geoAvailability: { resId: "http://id.nrk.no/2015/guri/IPRights/geoavailability/NRK", title: "NRK" }, resId: "x-test:pubEvent-1", start: "2015-04-03T14:00:00Z" }]), new Set([{ geoAvailability: { resId: "http://id.nrk.no/2015/guri/IPRights/geoavailability/NRK", title: "NRK" }, resId: "x-test:pubEvent-1", start: "2015-04-03T14:00:00Z" }, { end: "2019-08-07T18:00:00Z", geoAvailability: { resId: "http://id.nrk.no/2015/guri/IPRights/geoavailability/NORGE", title: "NORGE" }, start: "2015-04-03T14:00:00Z" }]) ); } }); tests("isArguments", function (pass, fail) { pass("arguments object", arguments); fail("primitive", 42); fail("object", {}); pass("arguments object from strict-mode function", (function () { "use strict"; return arguments; }())); }); }());