Skip to content
Merged
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
21 changes: 15 additions & 6 deletions core/src/assert/funcs/equal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,18 +177,27 @@ interface IEqualOptions {
visiting: any[];
}

/**
* Wraps _strictEquals to ensure it always returns a boolean (never null).
* For types that should only be equal if reference-equal (Promise, function, WeakMap, etc.),
* this treats non-reference-equal values as not equal.
*/
const _strictEqualsBool = (value: any, expected: any, options: IEqualOptions): boolean => {
return _strictEquals(value, expected) === true;
};

const _typeEquals: { [key: string]: (value: any, expected: any, options: IEqualOptions) => boolean } = {
"string": _valueOfEquals,
"number": _valueOfEquals,
"boolean": _valueOfEquals,
"undefined": _valueOfEquals,
"date": _valueOfEquals,

"promise": _strictEquals,
"function": _strictEquals,
"symbol": _strictEquals,
"weakmap": _strictEquals,
"weakset": _strictEquals,
"promise": _strictEqualsBool,
"function": _strictEqualsBool,
"symbol": _strictEqualsBool,
"weakmap": _strictEqualsBool,
"weakset": _strictEqualsBool,

"error": _matchKeys(["name", "message", "code"]),

Expand All @@ -215,7 +224,7 @@ function _getTypeComparer(value: any): (value: any, expected: any, options: IEqu
objType = strLower(objToString(value).slice(8, -1));
}

let compareFn = _typeEquals[objType] || _typeEquals[theType];
let compareFn = (objType && _typeEquals[objType]) || _typeEquals[theType];
if (!compareFn) {
try {
if (isObject(value) && isFunction(value[Symbol.iterator])) {
Expand Down
37 changes: 37 additions & 0 deletions core/test/src/assert/assert.equals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1163,4 +1163,41 @@ describe("assert.deepStrictEqual", function () {
assert.deepStrictEqual([{ a: 1 }], [{ a: "1" }]);
}, "expected [{a:1}] to deeply and strictly equal [{a:\"1\"}]");
});

it("Objects/arrays containing different Promises/functions should fail", function () {
// Regression test for issue #273
// _deepEquals() must not return null for types like Promise/function/WeakMap/etc
// in nested comparisons, as null is treated as "equal" (not === false)

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const func1 = () => 1;
const func2 = () => 2;

// Different promises in nested objects should fail
checkError(function () {
assert.deepStrictEqual({ p: promise1 }, { p: promise2 });
}, /expected .* to deeply and strictly equal .*/);

// Different functions in nested objects should fail
checkError(function () {
assert.deepStrictEqual({ f: func1 }, { f: func2 });
}, /expected .* to deeply and strictly equal .*/);

// Different promises in arrays should fail
checkError(function () {
assert.deepStrictEqual([promise1], [promise2]);
}, /expected .* to deeply and strictly equal .*/);

// Different functions in arrays should fail
checkError(function () {
assert.deepStrictEqual([func1], [func2]);
}, /expected .* to deeply and strictly equal .*/);

// Same reference should pass
assert.deepStrictEqual({ p: promise1 }, { p: promise1 });
assert.deepStrictEqual({ f: func1 }, { f: func1 });
assert.deepStrictEqual([promise1], [promise1]);
assert.deepStrictEqual([func1], [func1]);
});
});
Loading