diff options
author | 2022-12-01 19:36:47 -0800 | |
---|---|---|
committer | 2022-12-01 19:36:47 -0800 | |
commit | 92da72beb03dd686209428fcd0951b57d41e854c (patch) | |
tree | 3173a7671637c45d1456210eae519da8381b59c1 /src/bun.js | |
parent | a896d6c46c5a2ed6cf28c885d3077df972e7be43 (diff) | |
download | bun-92da72beb03dd686209428fcd0951b57d41e854c.tar.gz bun-92da72beb03dd686209428fcd0951b57d41e854c.tar.zst bun-92da72beb03dd686209428fcd0951b57d41e854c.zip |
bun test `toStrictEqual` (#1568)
* toStrictEqual and bug fix in deepEqual
* rebase Remove some dead bindings code
* remove debugging test
* canGetIndexQuickly for array holes
* isStrict template
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 142 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 6 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-handwritten.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.zig | 1 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 43 |
7 files changed, 152 insertions, 46 deletions
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index bcb42dc29..50a07f06e 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1803,7 +1803,7 @@ JSC_DEFINE_HOST_FUNCTION(functionBunDeepEquals, (JSGlobalObject * globalObject, Vector<std::pair<JSValue, JSValue>, 16> stack; - bool isEqual = Bun__deepEquals(globalObject, arg1, arg2, stack, &scope, true); + bool isEqual = Bun__deepEquals<false>(globalObject, arg1, arg2, stack, &scope, true); RETURN_IF_EXCEPTION(scope, {}); return JSValue::encode(jsBoolean(isEqual)); } diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 410855a8c..fe778ce42 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -152,6 +152,7 @@ static bool canPerformFastPropertyEnumerationForIterationBun(Structure* s) return true; } +template<bool isStrict> bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16>& stack, ThrowScope* scope, bool addToStack) { VM& vm = globalObject->vm(); @@ -184,6 +185,8 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, JSCell* c1 = v1.asCell(); JSCell* c2 = v2.asCell(); + JSObject* o1 = v1.getObject(); + JSObject* o2 = v2.getObject(); JSC::JSType c1Type = c1->type(); JSC::JSType c2Type = c2->type(); @@ -223,7 +226,7 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, RETURN_IF_EXCEPTION(*scope, false); // set has unique values, no need to count - if (Bun__deepEquals(globalObject, nextValue1, nextValue2, stack, scope, false)) { + if (Bun__deepEquals<isStrict>(globalObject, nextValue1, nextValue2, stack, scope, false)) { found = true; if (!nextValue1.isPrimitive()) { stack.append({ nextValue1, nextValue2 }); @@ -303,8 +306,8 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, JSValue value2 = nextValueObject2->getIndex(globalObject, static_cast<unsigned>(1)); RETURN_IF_EXCEPTION(*scope, false); - if (Bun__deepEquals(globalObject, key1, key2, stack, scope, false)) { - if (Bun__deepEquals(globalObject, nextValue1, nextValue2, stack, scope, false)) { + if (Bun__deepEquals<isStrict>(globalObject, key1, key2, stack, scope, false)) { + if (Bun__deepEquals<isStrict>(globalObject, nextValue1, nextValue2, stack, scope, false)) { found = true; if (!nextValue1.isPrimitive()) { stack.append({ nextValue1, nextValue2 }); @@ -443,7 +446,12 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, return (memcmp(vector, rightVector, byteLength) == 0); } - + case StringObjectType: { + if (!equal(JSObject::calculatedClassName(o1), JSObject::calculatedClassName(o2))) { + return false; + } + break; + } case JSFunctionType: { return false; } @@ -457,46 +465,50 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, bool v2Array = isArray(globalObject, v2); RETURN_IF_EXCEPTION(*scope, false); - JSObject* o1 = v1.getObject(); - JSObject* o2 = v2.getObject(); - if (v1Array != v2Array) return false; if (v1Array && v2Array) { JSC::JSArray* array1 = JSC::jsCast<JSC::JSArray*>(v1); JSC::JSArray* array2 = JSC::jsCast<JSC::JSArray*>(v2); + size_t length = array1->length(); if (length != array2->length()) { return false; } - if (array1->canDoFastIndexedAccess() && array2->canDoFastIndexedAccess()) { - for (size_t i = 0; i < length; i++) { - JSValue left = o1->getIndexQuickly(i); - RETURN_IF_EXCEPTION(*scope, false); - JSValue right = o2->getIndexQuickly(i); - RETURN_IF_EXCEPTION(*scope, false); - if (!Bun__deepEquals(globalObject, left, right, stack, scope, true)) { + for (uint64_t i = 0; i < length; i++) { + // array holes come back as empty values with tryGetIndexQuickly() + JSValue left = o1->canGetIndexQuickly(i) + ? o1->getIndexQuickly(i) + : o1->tryGetIndexQuickly(i); + RETURN_IF_EXCEPTION(*scope, false); + + JSValue right = o2->canGetIndexQuickly(i) + ? o2->getIndexQuickly(i) + : o2->tryGetIndexQuickly(i); + RETURN_IF_EXCEPTION(*scope, false); + + if constexpr (isStrict) { + if (left.isEmpty() && right.isEmpty()) { + continue; + } + if (left.isEmpty() || right.isEmpty()) { return false; } - - RETURN_IF_EXCEPTION(*scope, false); } - } else { - for (size_t i = 0; i < length; i++) { - JSValue left = o1->getIndex(globalObject, i); - RETURN_IF_EXCEPTION(*scope, false); - JSValue right = o2->getIndex(globalObject, i); - RETURN_IF_EXCEPTION(*scope, false); - - if (!Bun__deepEquals(globalObject, left, right, stack, scope, true)) { - return false; + if constexpr (!isStrict) { + if (((left.isEmpty() || right.isEmpty()) && (left.isUndefined() || right.isUndefined()))) { + continue; } + } - RETURN_IF_EXCEPTION(*scope, false); + if (!Bun__deepEquals<isStrict>(globalObject, left, right, stack, scope, true)) { + return false; } + + RETURN_IF_EXCEPTION(*scope, false); } JSC::PropertyNameArray a1(vm, PropertyNameMode::Symbols, PrivateSymbolMode::Include); @@ -505,8 +517,10 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, JSObject::getOwnPropertyNames(o2, globalObject, a2, DontEnumPropertiesMode::Exclude); size_t propertyLength = a1.size(); - if (propertyLength != a2.size()) { - return false; + if constexpr (isStrict) { + if (propertyLength != a2.size()) { + return false; + } } // take a property name from one, try to get it from both @@ -524,11 +538,17 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, JSValue prop2 = o2->getIfPropertyExists(globalObject, propertyName1); RETURN_IF_EXCEPTION(*scope, false); + if constexpr (!isStrict) { + if (prop1.isUndefined() && prop2.isEmpty()) { + continue; + } + } + if (!prop2) { return false; } - if (!Bun__deepEquals(globalObject, prop1, prop2, stack, scope, true)) { + if (!Bun__deepEquals<isStrict>(globalObject, prop1, prop2, stack, scope, true)) { return false; } @@ -544,6 +564,12 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, return true; } + if constexpr (isStrict) { + if (!equal(JSObject::calculatedClassName(o1), JSObject::calculatedClassName(o2))) { + return false; + } + } + JSC::Structure* o1Structure = o1->structure(); if (canPerformFastPropertyEnumerationForIterationBun(o1Structure)) { JSC::Structure* o2Structure = o2->structure(); @@ -552,8 +578,10 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, size_t count1 = 0; bool result = true; - if (o2Structure->inlineSize() + o2Structure->outOfLineSize() != o1Structure->inlineSize() + o1Structure->outOfLineSize()) { - return false; + if constexpr (isStrict) { + if (o2Structure->inlineSize() + o2Structure->outOfLineSize() != o1Structure->inlineSize() + o1Structure->outOfLineSize()) { + return false; + } } o1Structure->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { @@ -562,19 +590,25 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, } count1++; + JSValue left = o1->getDirect(entry.offset()); JSValue right = o2->getDirect(vm, JSC::PropertyName(entry.key())); + if constexpr (!isStrict) { + if (left.isUndefined() && right.isEmpty()) { + return true; + } + } + if (!right) { result = false; return false; } - JSValue left = o1->getDirect(entry.offset()); if (left == right || JSC::sameValue(globalObject, left, right)) { return true; } - if (!Bun__deepEquals(globalObject, left, right, stack, scope, true)) { + if (!Bun__deepEquals<isStrict>(globalObject, left, right, stack, scope, true)) { result = false; return false; } @@ -589,6 +623,12 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, return true; } + if constexpr (!isStrict) { + if (o2->getDirect(entry.offset()).isUndefined()) { + return true; + } + } + if (remain == 0) { result = false; return false; @@ -612,13 +652,15 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, o1->getPropertyNames(globalObject, a1, DontEnumPropertiesMode::Exclude); o2->getPropertyNames(globalObject, a2, DontEnumPropertiesMode::Exclude); - size_t propertyLength = a1.size(); - if (propertyLength != a2.size()) { - return false; + const size_t propertyArrayLength = a1.size(); + if constexpr (isStrict) { + if (propertyArrayLength != a2.size()) { + return false; + } } // take a property name from one, try to get it from both - for (size_t i = 0; i < propertyLength; i++) { + for (size_t i = 0; i < propertyArrayLength; i++) { Identifier i1 = a1[i]; PropertyName propertyName1 = PropertyName(i1); @@ -632,11 +674,17 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, JSValue prop2 = o2->getIfPropertyExists(globalObject, propertyName1); RETURN_IF_EXCEPTION(*scope, false); + if constexpr (!isStrict) { + if (prop1.isUndefined() && prop2.isEmpty()) { + continue; + } + } + if (!prop2) { return false; } - if (!Bun__deepEquals(globalObject, prop1, prop2, stack, scope, true)) { + if (!Bun__deepEquals<isStrict>(globalObject, prop1, prop2, stack, scope, true)) { return false; } @@ -1236,12 +1284,22 @@ bool JSC__JSValue__isSameValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1, bool JSC__JSValue__deepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject) { - JSC::JSValue v1 = JSC::JSValue::decode(JSValue0); - JSC::JSValue v2 = JSC::JSValue::decode(JSValue1); + JSValue v1 = JSValue::decode(JSValue0); + JSValue v2 = JSValue::decode(JSValue1); + + ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm()); + Vector<std::pair<JSValue, JSValue>, 16> stack; + return Bun__deepEquals<false>(globalObject, v1, v2, stack, &scope, true); +} + +bool JSC__JSValue__strictDeepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject) +{ + JSValue v1 = JSValue::decode(JSValue0); + JSValue v2 = JSValue::decode(JSValue1); ThrowScope scope = DECLARE_THROW_SCOPE(globalObject->vm()); - Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16> stack; - return Bun__deepEquals(globalObject, v1, v2, stack, &scope, true); + Vector<std::pair<JSValue, JSValue>, 16> stack; + return Bun__deepEquals<true>(globalObject, v1, v2, stack, &scope, true); } // This is the same as the C API version, except it returns a JSValue which may be a *Exception diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index d17cc5382..3b933f976 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -2977,6 +2977,10 @@ pub const JSValue = enum(JSValueReprInt) { return cppFn("deepEquals", .{ this, other, global }); } + pub fn strictDeepEquals(this: JSValue, other: JSValue, global: *JSGlobalObject) bool { + return cppFn("strictDeepEquals", .{ this, other, global }); + } + pub fn asString(this: JSValue) *JSString { return cppFn("asString", .{ this, @@ -3165,7 +3169,7 @@ pub const JSValue = enum(JSValueReprInt) { return this.asNullableVoid().?; } - pub const Extern = [_][]const u8{ "forEachProperty", "coerceToInt32", "fastGet_", "getStaticProperty", "createUninitializedUint8Array", "fromInt64NoTruncate", "fromUInt64NoTruncate", "toUInt64NoTruncate", "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable", "toBooleanSlow", "deepEquals", "getIfPropertyExistsFromPath" }; + pub const Extern = [_][]const u8{ "forEachProperty", "coerceToInt32", "fastGet_", "getStaticProperty", "createUninitializedUint8Array", "fromInt64NoTruncate", "fromUInt64NoTruncate", "toUInt64NoTruncate", "asPromise", "toInt64", "_then", "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt64", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable", "toBooleanSlow", "deepEquals", "strictDeepEquals", "getIfPropertyExistsFromPath" }; }; extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void; diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 9018615fc..5ce164a69 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -271,6 +271,7 @@ extern "C" size_t Bun__encoding__byteLengthUTF16(const UChar* ptr, size_t len, E extern "C" int64_t Bun__encoding__constructFromLatin1(void*, const unsigned char* ptr, size_t len, Encoding encoding); extern "C" int64_t Bun__encoding__constructFromUTF16(void*, const UChar* ptr, size_t len, Encoding encoding); +template<bool isStrict> bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSC::JSValue v1, JSC::JSValue v2, Vector<std::pair<JSC::JSValue, JSC::JSValue>, 16>& stack, JSC::ThrowScope* scope, bool addToStack); namespace Inspector { diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 75701502d..675d6df44 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1669880224 +//-- AUTOGENERATED FILE -- 1669793662 #pragma once #include <stddef.h> @@ -314,6 +314,7 @@ CPP_DECL JSC__JSValue JSC__JSValue__makeWithNameAndPrototype(JSC__JSGlobalObject CPP_DECL JSC__JSValue JSC__JSValue__parseJSON(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); CPP_DECL void JSC__JSValue__put(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, const ZigString* arg2, JSC__JSValue JSValue3); CPP_DECL void JSC__JSValue__putRecord(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, size_t arg4); +CPP_DECL bool JSC__JSValue__strictDeepEquals(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* arg2); CPP_DECL JSC__JSValue JSC__JSValue__symbolFor(JSC__JSGlobalObject* arg0, ZigString* arg1); CPP_DECL bool JSC__JSValue__symbolKeyFor(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2); CPP_DECL bool JSC__JSValue__toBoolean(JSC__JSValue JSValue0); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index b0bfadae9..808fa4fbc 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -238,6 +238,7 @@ pub extern fn JSC__JSValue__makeWithNameAndPrototype(arg0: ?*JSC__JSGlobalObject pub extern fn JSC__JSValue__parseJSON(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject) JSC__JSValue; pub extern fn JSC__JSValue__put(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, arg2: [*c]const ZigString, JSValue3: JSC__JSValue) void; pub extern fn JSC__JSValue__putRecord(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, arg2: [*c]ZigString, arg3: [*c]ZigString, arg4: usize) void; +pub extern fn JSC__JSValue__strictDeepEquals(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: ?*JSC__JSGlobalObject) bool; pub extern fn JSC__JSValue__symbolFor(arg0: ?*JSC__JSGlobalObject, arg1: [*c]ZigString) JSC__JSValue; pub extern fn JSC__JSValue__symbolKeyFor(JSValue0: JSC__JSValue, arg1: ?*JSC__JSGlobalObject, arg2: [*c]ZigString) bool; pub extern fn JSC__JSValue__toBoolean(JSValue0: JSC__JSValue) bool; diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index abde377db..351049f65 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -717,6 +717,48 @@ pub const Expect = struct { return .zero; } + pub fn toStrictEqual(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); + + const thisValue = callFrame.this(); + const _arguments = callFrame.arguments(1); + const arguments: []const JSValue = _arguments.ptr[0.._arguments.len]; + + if (arguments.len < 1) { + globalObject.throwInvalidArguments("toStrictEqual() requires 1 argument", .{}); + return .zero; + } + + if (this.scope.tests.items.len <= this.test_id) { + globalObject.throw("toStrictEqual() must be called in a test", .{}); + return .zero; + } + + active_test_expectation_counter.actual += 1; + + const expected = arguments[0]; + const value = Expect.capturedValueGetCached(thisValue) orelse { + globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); + return .zero; + }; + value.ensureStillAlive(); + + const not = this.op.contains(.not); + var pass = value.strictDeepEquals(expected, globalObject); + + if (not) pass = !pass; + if (pass) return thisValue; + + // handle failure + var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject }; + if (not) { + globalObject.throw("Expected values to not be strictly equal:\n\tExpected: {any}\n\tReceived: {any}", .{ expected.toFmt(globalObject, &fmt), value.toFmt(globalObject, &fmt) }); + } else { + globalObject.throw("Expected values to be strictly equal:\n\tExpected: {any}\n\tReceived: {any}", .{ expected.toFmt(globalObject, &fmt), value.toFmt(globalObject, &fmt) }); + } + return .zero; + } + pub fn toHaveProperty(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { defer this.postMatch(globalObject); @@ -805,7 +847,6 @@ pub const Expect = struct { pub const toMatchObject = notImplementedJSCFn; pub const toMatchSnapshot = notImplementedJSCFn; pub const toMatchInlineSnapshot = notImplementedJSCFn; - pub const toStrictEqual = notImplementedJSCFn; pub const toThrow = notImplementedJSCFn; pub const toThrowErrorMatchingSnapshot = notImplementedJSCFn; pub const toThrowErrorMatchingInlineSnapshot = notImplementedJSCFn; |