Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,164 @@ assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
};

assert.ifError = function (err) { if (err) {throw err;}};


/**
* Return true if every element in the expected array also exists in the the actual
* array. The actual array may contain more elements that are not in the expected
* array. This implementation is very simple and not very efficient (Order(n^2)) so
* do not call this to compare large arrays.
*
* @param {Array.<Object>} actual The actual array to test
* @param {Array.<Object>} expected The array to test against
* @returns True if every element of the expected array exists in the actual array.
*/
function isEqualIgnoringOrder(actual, expected) {
var found = false;
for (var i = 0; i < expected.length; i++) {
var found = false;
for (var j = 0; j < actual.length; j++) {
try {
if (_deepEqual(actual[j], expected[i])) {
found = true;
break;
}
} catch (ignored) {
}
}
if (!found) {
return false;
}
}
return true;
}

function isArray(object) {
return typeof(object) === 'object' && Object.prototype.toString.call(object) === '[object Array]';
};

/**
* Performs a deep comparison of arrays, ignoring the order of the
* elements of the array. Essentially, this ensure that they have the same
* contents, possibly in a different order. Each of the elements of the
* array is compared with a deep equals. If this is called with objects
* that are not arrays, the assertion fails.
*
* @example // passing assertions
* assert.equalIgnoringOrder([5, 2, 8, 3], [2, 4, 5, 9]);
* assert.equalIgnoringOrder(["apples", "bananas", "oranges"], ["oranges", "apples", "bananas"]);
*
* @param {Object} actual The actual value
* @param {Object} expected The expected value
* @throws ArgumentsError
* @throws AssertionError
*/
assert.equalIgnoringOrder = function(actual, expected, message) {
if (!isArray(expected)) {
fail("Invalid expected argument to equalIgnoringOrder.");
} else if (isArray(actual)) {
if (isEqualIgnoringOrder(actual, expected) === false) {
fail(actual, expected, message, "equalIgnoringOrder", assert.equalIgnoringOrder);
}
} else {
fail(actual, expected, message, "equalIgnoringOrder", assert.equalIgnoringOrder);
}
return;
};

/**
* Performs a numeric equivalence within the given tolerance. If the
* difference between the arguments is within that tolerance, the assertion
* passes. Otherwise, it fails.
*
* @example // passing assertion
* assert.roughlyEqual(5.23456789, 5.2345947, 0.001);
*
* @param {Number} actual The actual value
* @param {Number} expected The expected value
* @param {Number} tolerance The tolerance for the difference between the two
* @throws ArgumentsError
* @throws AssertionError
*/
assert.roughlyEqual = function(actual, expected, tolerance, message) {
if (typeof(actual) !== "number" || typeof(expected) !== "number" || typeof(tolerance) !== "number") {
fail("Invalid expected argument to roughlyEquals.");
} else if (Math.abs(expected - actual) >= tolerance) {
fail(actual, expected, message, "roughlyEquals", assert.roughlyEquals);
}
return;
};

/**
* Check the actual result to see that every property that exists in the expected
* object also exists in the actual object and that it has the same value. If the
* expected object is a string, it names the property that should exist without
* giving the value. If the expected object is an array, each item in the expected
* array should exist in the actual array. If expected is an object, then the actual
* object must have all properties that the expected object has and with the same
* value. The actual object may have more properties that do not exist in
* expected, but this function
* is used to test for only the properties that the unit test cares about.
*
* @example // passing assertion
* assert.contains({a: 2, b: 3}, "b");
*
* @param {Object} actual The actual value to test which may be an array or an object
* @param {*} expected The name of the property that is expected to be within the object in the actual parameter
* @param {string} message message to print when the assertion fails
* @throws ArgumentsError
* @throws AssertionError
*/
assert.contains = function(actual, expected, message) {
if (isArray(actual)) {
if (typeof(expected) === "undefined") {
fail("Invalid expected argument to contains.");
}

if (typeof(expected) === "object") {
fail(actual, expected, message + " Expected is object and actual is array.", "contains", assert.contains);
} else if (isArray(expected)) {
for (var i = 0; i < expected.length; i++) {
if (actual.indexOf(expected[i]) < 0) {
fail(actual, expected, message + " Actual array does not contain array index " + i + " of expected.", "contains", assert.contains);
}
}
} else {
// primitive type -- check to see if it is in the actual array
if (actual.indexOf(expected) < 0) {
fail(actual, expected, message + " Expected value " + expected + " is not contained in the array in actual.", "contains", assert.contains);
}
}
} else if (typeof(actual) === "object") {
if (typeof(expected) === "object") {
for (p in expected) {
if (p && expected.hasOwnProperty(p)) {
if (typeof(actual[p]) === 'undefined') {
// "actual does not contain expected properties";
fail(actual[p], expected[p], message + " Expected contains property " + p + " and actual does not.", "contains", assert.contains);
} else if (typeof(expected[p]) === 'object') {
if (!_deepEqual(actual[p], expected[p])) {
fail(actual, expected, message, "contains", assert.notDeepEqual);
}
} else {
if (actual[p] != expected[p]) {
fail(actual, expected, message, "contains", assert.notDeepEqual);
}
}
}
}
} else if (isArray(expected)) {
fail(actual, expected, message + " Expected is array and actual is object.", "contains", assert.contains);
} else if (typeof(expected) === "string") {
if (typeof(actual[p]) === "undefined") {
fail(actual[p], expected[p], message + " Expected is looking for property " + expected + " and actual does not contain it.", "contains", assert.contains);
}
} else {
fail("Invalid expected argument to contains.");
}
} else {
fail("Invalid expected argument to contains.");
}
return;
};

3 changes: 3 additions & 0 deletions lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ exports.test = function (name, start, options, callback) {
ok: wrapAssert('ok', 'ok', 2),
same: wrapAssert('same', 'deepEqual', 3),
equals: wrapAssert('equals', 'equal', 3),
equalIgnoringOrder: wrapAssert('equalIgnoringOrder', 'equalIgnoringOrder', 3),
roughlyEqual: wrapAssert('roughlyEqual', 'roughlyEqual', 3),
contains: wrapAssert('contains', 'contains', 3),
expect: function (num) {
expecting = num;
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nodeunit",
"version": "0.11.2",
"version": "0.12.0",
"description": "Easy unit testing for node.js and the browser.",
"maintainers": [
{
Expand Down