assert: introduce `deepStrictEqual`
`deepStrictEqual` works the same way as `strictEqual`, but uses `===` to compare primitives and requires prototypes of equal objects to be the same object. Fixes: https://github.com/joyent/node/issues/7161 Fixes: https://github.com/iojs/io.js/issues/620 PR-URL: https://github.com/iojs/io.js/pull/639 Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-by: Rod Vagg <rod@vagg.org>
This commit is contained in:
Родитель
828d19a1f6
Коммит
3f473ef141
|
@ -23,11 +23,12 @@ Tests shallow, coercive non-equality with the not equal comparison operator ( `!
|
|||
|
||||
## assert.deepEqual(actual, expected[, message])
|
||||
|
||||
Tests for deep equality.
|
||||
Tests for deep equality. Primitive values are compared with the equal comparison
|
||||
operator ( `==` ). Doesn't take object prototypes into account.
|
||||
|
||||
## assert.notDeepEqual(actual, expected[, message])
|
||||
|
||||
Tests for any deep inequality.
|
||||
Tests for any deep inequality. Opposite of `assert.deepEqual`.
|
||||
|
||||
## assert.strictEqual(actual, expected[, message])
|
||||
|
||||
|
@ -35,7 +36,17 @@ Tests strict equality, as determined by the strict equality operator ( `===` )
|
|||
|
||||
## assert.notStrictEqual(actual, expected[, message])
|
||||
|
||||
Tests strict non-equality, as determined by the strict not equal operator ( `!==` )
|
||||
Tests strict non-equality, as determined by the strict not equal
|
||||
operator ( `!==` )
|
||||
|
||||
## assert.deepStrictEqual(actual, expected[, message])
|
||||
|
||||
Tests for deep equality. Primitive values are compared with the strict equality
|
||||
operator ( `===` ).
|
||||
|
||||
## assert.notDeepStrictEqual(actual, expected[, message])
|
||||
|
||||
Tests for deep inequality. Opposite of `assert.deepStrictEqual`.
|
||||
|
||||
## assert.throws(block[, error][, message])
|
||||
|
||||
|
|
|
@ -129,12 +129,18 @@ assert.notEqual = function notEqual(actual, expected, message) {
|
|||
// assert.deepEqual(actual, expected, message_opt);
|
||||
|
||||
assert.deepEqual = function deepEqual(actual, expected, message) {
|
||||
if (!_deepEqual(actual, expected)) {
|
||||
if (!_deepEqual(actual, expected, false)) {
|
||||
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
|
||||
}
|
||||
};
|
||||
|
||||
function _deepEqual(actual, expected) {
|
||||
assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
|
||||
if (!_deepEqual(actual, expected, true)) {
|
||||
fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);
|
||||
}
|
||||
};
|
||||
|
||||
function _deepEqual(actual, expected, strict) {
|
||||
// 7.1. All identical values are equivalent, as determined by ===.
|
||||
if (actual === expected) {
|
||||
return true;
|
||||
|
@ -166,7 +172,7 @@ function _deepEqual(actual, expected) {
|
|||
// equivalence is determined by ==.
|
||||
} else if ((actual === null || typeof actual !== 'object') &&
|
||||
(expected === null || typeof expected !== 'object')) {
|
||||
return actual == expected;
|
||||
return strict ? actual === expected : actual == expected;
|
||||
|
||||
// 7.5 For all other Object pairs, including Array objects, equivalence is
|
||||
// determined by having the same number of owned properties (as verified
|
||||
|
@ -175,7 +181,7 @@ function _deepEqual(actual, expected) {
|
|||
// corresponding key, and an identical 'prototype' property. Note: this
|
||||
// accounts for both named and indexed properties on Arrays.
|
||||
} else {
|
||||
return objEquiv(actual, expected);
|
||||
return objEquiv(actual, expected, strict);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,12 +189,14 @@ function isArguments(object) {
|
|||
return Object.prototype.toString.call(object) == '[object Arguments]';
|
||||
}
|
||||
|
||||
function objEquiv(a, b) {
|
||||
function objEquiv(a, b, strict) {
|
||||
if (a === null || a === undefined || b === null || b === undefined)
|
||||
return false;
|
||||
// if one is a primitive, the other must be same
|
||||
if (util.isPrimitive(a) || util.isPrimitive(b))
|
||||
return a === b;
|
||||
if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
|
||||
return false;
|
||||
var aIsArgs = isArguments(a),
|
||||
bIsArgs = isArguments(b);
|
||||
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
|
||||
|
@ -196,28 +204,28 @@ function objEquiv(a, b) {
|
|||
if (aIsArgs) {
|
||||
a = pSlice.call(a);
|
||||
b = pSlice.call(b);
|
||||
return _deepEqual(a, b);
|
||||
return _deepEqual(a, b, strict);
|
||||
}
|
||||
var ka = Object.keys(a),
|
||||
kb = Object.keys(b),
|
||||
key, i;
|
||||
// having the same number of owned properties (keys incorporates
|
||||
// hasOwnProperty)
|
||||
if (ka.length != kb.length)
|
||||
if (ka.length !== kb.length)
|
||||
return false;
|
||||
//the same set of keys (although not necessarily the same order),
|
||||
ka.sort();
|
||||
kb.sort();
|
||||
//~~~cheap key test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
if (ka[i] != kb[i])
|
||||
if (ka[i] !== kb[i])
|
||||
return false;
|
||||
}
|
||||
//equivalent values for every corresponding key, and
|
||||
//~~~possibly expensive deep test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
key = ka[i];
|
||||
if (!_deepEqual(a[key], b[key])) return false;
|
||||
if (!_deepEqual(a[key], b[key], strict)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -226,11 +234,19 @@ function objEquiv(a, b) {
|
|||
// assert.notDeepEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
|
||||
if (_deepEqual(actual, expected)) {
|
||||
if (_deepEqual(actual, expected, false)) {
|
||||
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
|
||||
}
|
||||
};
|
||||
|
||||
assert.notDeepStrictEqual = notDeepStrictEqual;
|
||||
function notDeepStrictEqual(actual, expected, message) {
|
||||
if (_deepEqual(actual, expected, true)) {
|
||||
fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 9. The strict equality assertion tests strict equality, as determined by ===.
|
||||
// assert.strictEqual(actual, expected, message_opt);
|
||||
|
||||
|
|
|
@ -149,6 +149,121 @@ assert.doesNotThrow(makeBlock(a.deepEqual, new String('a'), {0: 'a'}), a.Asserti
|
|||
assert.doesNotThrow(makeBlock(a.deepEqual, new Number(1), {}), a.AssertionError);
|
||||
assert.doesNotThrow(makeBlock(a.deepEqual, new Boolean(true), {}), a.AssertionError);
|
||||
|
||||
//deepStrictEqual
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, new Date(2000, 3, 14),
|
||||
new Date(2000, 3, 14)), 'deepStrictEqual date');
|
||||
|
||||
assert.throws(makeBlock(a.deepStrictEqual, new Date(), new Date(2000, 3, 14)),
|
||||
a.AssertionError,
|
||||
'deepStrictEqual date');
|
||||
|
||||
// 7.3 - strict
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/, /a/));
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/g, /a/g));
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/i, /a/i));
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/m, /a/m));
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, /a/igm, /a/igm));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, /ab/, /a/));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, /a/g, /a/));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, /a/i, /a/));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, /a/m, /a/));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, /a/igm, /a/im));
|
||||
|
||||
var re1 = /a/;
|
||||
re1.lastIndex = 3;
|
||||
assert.throws(makeBlock(a.deepStrictEqual, re1, /a/));
|
||||
|
||||
|
||||
// 7.4 - strict
|
||||
assert.throws(makeBlock(a.deepStrictEqual, 4, '4'),
|
||||
a.AssertionError,
|
||||
'deepStrictEqual === check');
|
||||
|
||||
assert.throws(makeBlock(a.deepStrictEqual, true, 1),
|
||||
a.AssertionError,
|
||||
'deepStrictEqual === check');
|
||||
|
||||
assert.throws(makeBlock(a.deepStrictEqual, 4, '5'),
|
||||
a.AssertionError,
|
||||
'deepStrictEqual === check');
|
||||
|
||||
// 7.5 - strict
|
||||
// having the same number of owned properties && the same set of keys
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, {a: 4}, {a: 4}));
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual,
|
||||
{a: 4, b: '2'},
|
||||
{a: 4, b: '2'}));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, [4], ['4']));
|
||||
assert.throws(makeBlock(a.deepStrictEqual, {a: 4}, {a: 4, b: true}),
|
||||
a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, ['a'], {0: 'a'}));
|
||||
//(although not necessarily the same order),
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual,
|
||||
{a: 4, b: '1'},
|
||||
{b: '1', a: 4}));
|
||||
|
||||
assert.throws(makeBlock(a.deepStrictEqual,
|
||||
[0, 1, 2, 'a', 'b'],
|
||||
[0, 1, 2, 'b', 'a']),
|
||||
a.AssertionError);
|
||||
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, a1, a2));
|
||||
|
||||
// Prototype check
|
||||
function Constructor1(first, last) {
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
function Constructor2(first, last) {
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
var obj1 = new Constructor1('Ryan', 'Dahl');
|
||||
var obj2 = new Constructor2('Ryan', 'Dahl');
|
||||
|
||||
assert.throws(makeBlock(a.deepStrictEqual, obj1, obj2), a.AssertionError);
|
||||
|
||||
Constructor2.prototype = Constructor1.prototype;
|
||||
obj2 = new Constructor2('Ryan', 'Dahl');
|
||||
|
||||
assert.doesNotThrow(makeBlock(a.deepStrictEqual, obj1, obj2));
|
||||
|
||||
// primitives
|
||||
assert.throws(makeBlock(assert.deepStrictEqual, 4, '4'),
|
||||
a.AssertionError);
|
||||
assert.throws(makeBlock(assert.deepStrictEqual, true, 1),
|
||||
a.AssertionError);
|
||||
assert.throws(makeBlock(assert.deepStrictEqual, Symbol(), Symbol()),
|
||||
a.AssertionError);
|
||||
|
||||
var s = Symbol();
|
||||
assert.doesNotThrow(makeBlock(assert.deepStrictEqual, s, s));
|
||||
|
||||
|
||||
// primitives and object
|
||||
assert.throws(makeBlock(a.deepStrictEqual, null, {}), a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, undefined, {}), a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, 'a', ['a']), a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, 'a', {0: 'a'}), a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, 1, {}), a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, true, {}), a.AssertionError);
|
||||
assert.throws(makeBlock(assert.deepStrictEqual, Symbol(), {}),
|
||||
a.AssertionError);
|
||||
|
||||
|
||||
// primitive wrappers and object
|
||||
assert.throws(makeBlock(a.deepStrictEqual, new String('a'), ['a']),
|
||||
a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, new String('a'), {0: 'a'}),
|
||||
a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, new Number(1), {}),
|
||||
a.AssertionError);
|
||||
assert.throws(makeBlock(a.deepStrictEqual, new Boolean(true), {}),
|
||||
a.AssertionError);
|
||||
|
||||
|
||||
// Testing the throwing
|
||||
function thrower(errorConstructor) {
|
||||
throw new errorConstructor('test');
|
||||
|
|
Загрузка…
Ссылка в новой задаче