Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assert: introduce deepStrictEqual #639

Closed
wants to merge 1 commit into from
Closed
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
17 changes: 14 additions & 3 deletions doc/api/assert.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,30 @@ 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])

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])

Expand Down
36 changes: 26 additions & 10 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -175,49 +181,51 @@ 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);
}
}

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still going to suffer from the things discussed in #621

Are we sure this shouldn't just check primitives? Otherwise we might as well just do a regular strictEquals().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what you mean. recursively call deepStrictEqual on prototypes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked at the tests and it seems as I would expect. I think I mis-understood points made in the previous thread.

var aIsArgs = isArguments(a),
bIsArgs = isArguments(b);
if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
return false;
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;
}
Expand All @@ -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);

Expand Down
115 changes: 115 additions & 0 deletions test/parallel/test-assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down