[Pkg-javascript-commits] [node-samsam] 01/05: Import Upstream version 1.2.1

Sruthi Chandran srud-guest at moszumanska.debian.org
Wed Oct 12 10:34:10 UTC 2016


This is an automated email from the git hooks/post-receive script.

srud-guest pushed a commit to branch master
in repository node-samsam.

commit 5dc2cb26c86bb5bc5b8391722b59aac0005a705a
Author: Sruthi <srud at disroot.org>
Date:   Wed Oct 12 15:32:10 2016 +0530

    Import Upstream version 1.2.1
---
 .gitignore          |   2 +
 .travis.yml         |   9 ++
 AUTHORS             |   2 +
 Gruntfile.js        |  24 +++
 LICENSE             |  27 ++++
 Readme.md           | 226 +++++++++++++++++++++++++++
 appveyor.yml        |  18 +++
 autolint.js         |  23 +++
 buster.js           |  20 +++
 lib/samsam.js       | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 package.json        |  40 +++++
 test/samsam-test.js | 435 +++++++++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 1263 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb79dd5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+.idea
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..896e270
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,9 @@
+language: node_js
+sudo: false
+node_js:
+  - "0.10"
+  - "0.12"
+  - "4"
+  - "5"
+before_install:
+  - npm i -g npm
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..13df013
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Christian Johansen (christian at cjohansen.no)
+August Lilleaas (august at augustl.com)
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..617cac3
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,24 @@
+module.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"]
+    );
+
+};
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f00310b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+(The BSD License)
+
+Copyright (c) 2010-2012, Christian Johansen, christian at cjohansen.no and
+August Lilleaas, august.lilleaas at 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.
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..a2a56c3
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,226 @@
+# 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 <http://github.com/busterjs/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)
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..c5c6866
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,18 @@
+environment:
+  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
diff --git a/autolint.js b/autolint.js
new file mode 100644
index 0000000..4f9755e
--- /dev/null
+++ b/autolint.js
@@ -0,0 +1,23 @@
+module.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"
+        ]
+    }
+};
diff --git a/buster.js b/buster.js
new file mode 100644
index 0000000..1d09645
--- /dev/null
+++ b/buster.js
@@ -0,0 +1,20 @@
+var 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"
+    ]
+};
diff --git a/lib/samsam.js b/lib/samsam.js
new file mode 100644
index 0000000..35685d7
--- /dev/null
+++ b/lib/samsam.js
@@ -0,0 +1,437 @@
+((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
+    };
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..cd9928c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,40 @@
+{
+  "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 at cjohansen.no",
+      "url": "http://cjohansen.no"
+    },
+    {
+      "name": "August Lilleaas",
+      "email": "august.lilleaas at gmail.com",
+      "url": "http://augustl.com"
+    },
+    {
+      "name": "Daniel Wittner",
+      "email": "d.wittner at 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"
+  }
+}
diff --git a/test/samsam-test.js b/test/samsam-test.js
new file mode 100644
index 0000000..f0fd4f6
--- /dev/null
+++ b/test/samsam-test.js
@@ -0,0 +1,435 @@
+if (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; }()));
+    });
+}());

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-samsam.git



More information about the Pkg-javascript-commits mailing list