diff --git a/lib/assert.js b/lib/assert.js index 75b04d359..41032c0f4 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -358,3 +358,67 @@ 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.} actual The actual array to test + * @param {Array.} 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; +}; diff --git a/lib/types.js b/lib/types.js index 879edd8b3..3992b2d6b 100644 --- a/lib/types.js +++ b/lib/types.js @@ -149,6 +149,7 @@ 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), expect: function (num) { expecting = num; },