diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 63e17c18985ac5..f133ae6011e05d 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3795,6 +3795,27 @@ bool JSC__JSValue__isConstructor(JSC__JSValue JSValue0) return value.isConstructor(); } +bool JSC__JSValue__isInstanceOf(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, JSC__JSValue JSValue1) +{ + VM& vm = globalObject->vm(); + + auto scope = DECLARE_CATCH_SCOPE(vm); + + JSValue jsValue = JSValue::decode(JSValue0); + JSValue jsValue1 = JSValue::decode(JSValue1); + if (UNLIKELY(!jsValue1.isObject())) { + return false; + } + JSObject* jsConstructor = JSC::asObject(jsValue1); + if (UNLIKELY(!jsConstructor->structure()->typeInfo().implementsHasInstance())) + return false; + bool result = jsConstructor->hasInstance(globalObject, jsValue); + + RETURN_IF_EXCEPTION(scope, false); + + return result; +} + extern "C" JSC__JSValue JSC__JSValue__createRopeString(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* globalObject) { return JSValue::encode(JSC::jsString(globalObject, JSC::JSValue::decode(JSValue0).toString(globalObject), JSC::JSValue::decode(JSValue1).toString(globalObject))); diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 8a63058a57bd3f..3059ba09fb2cc8 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -3042,7 +3042,7 @@ pub const JSValue = enum(JSValueReprInt) { if (!this.isCell()) return false; - return JSC.C.JSValueIsInstanceOfConstructor(global, this.asObjectRef(), constructor.asObjectRef(), null); + return cppFn("isInstanceOf", .{ this, global, constructor }); } pub fn call(this: JSValue, globalThis: *JSGlobalObject, args: []const JSC.JSValue) JSC.JSValue { @@ -4122,6 +4122,7 @@ pub const JSValue = enum(JSValueReprInt) { "toZigException", "toZigString", "isConstructor", + "isInstanceOf", }; }; diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h index ae4ecaf5d6da96..243ae3706bae21 100644 --- a/src/bun.js/bindings/headers-cpp.h +++ b/src/bun.js/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1678254453 +//-- AUTOGENERATED FILE -- 1678855956 // clang-format off #pragma once diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 8312706e16dac5..35050c4e8d4f51 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1678254453 +//-- AUTOGENERATED FILE -- 1678855956 #pragma once #include @@ -325,6 +325,7 @@ CPP_DECL bool JSC__JSValue__isError(JSC__JSValue JSValue0); CPP_DECL bool JSC__JSValue__isException(JSC__JSValue JSValue0, JSC__VM* arg1); CPP_DECL bool JSC__JSValue__isGetterSetter(JSC__JSValue JSValue0); CPP_DECL bool JSC__JSValue__isHeapBigInt(JSC__JSValue JSValue0); +CPP_DECL bool JSC__JSValue__isInstanceOf(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL bool JSC__JSValue__isInt32(JSC__JSValue JSValue0); CPP_DECL bool JSC__JSValue__isInt32AsAnyInt(JSC__JSValue JSValue0); CPP_DECL bool JSC__JSValue__isIterable(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index ee0c4fb54e974c..1e3530d2228231 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -238,6 +238,7 @@ pub extern fn JSC__JSValue__isError(JSValue0: JSC__JSValue) bool; pub extern fn JSC__JSValue__isException(JSValue0: JSC__JSValue, arg1: *bindings.VM) bool; pub extern fn JSC__JSValue__isGetterSetter(JSValue0: JSC__JSValue) bool; pub extern fn JSC__JSValue__isHeapBigInt(JSValue0: JSC__JSValue) bool; +pub extern fn JSC__JSValue__isInstanceOf(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) bool; pub extern fn JSC__JSValue__isInt32(JSValue0: JSC__JSValue) bool; pub extern fn JSC__JSValue__isInt32AsAnyInt(JSValue0: JSC__JSValue) bool; pub extern fn JSC__JSValue__isIterable(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject) bool; diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index d8d4fb2468e239..33e7d8269a4b9e 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -2733,10 +2733,11 @@ pub const Expect = struct { } active_test_expectation_counter.actual += 1; + var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true }; + const expected_value = arguments[0]; - if (!expected_value.jsType().isFunction()) { - var fmt = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true }; - globalObject.throw("Expected value must be a function: {any}", .{expected_value.toFmt(globalObject, &fmt)}); + if (!expected_value.isConstructor()) { + globalObject.throw("Expected value must be a function: {any}", .{expected_value.toFmt(globalObject, &formatter)}); return .zero; } expected_value.ensureStillAlive(); @@ -2753,28 +2754,30 @@ pub const Expect = struct { if (pass) return thisValue; // handle failure - var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject, .quote_strings = true }; + const expected_fmt = expected_value.toFmt(globalObject, &formatter); const value_fmt = value.toFmt(globalObject, &formatter); if (not) { - const received_line = "Received: {any}\n"; - const fmt = comptime getSignature("toBeInstanceOf", "", true) ++ "\n\n" ++ received_line; + const expected_line = "Expected constructor: not {any}\n"; + const received_line = "Received value: {any}\n"; + const fmt = comptime getSignature("toBeInstanceOf", "", true) ++ "\n\n" ++ expected_line ++ received_line; if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{value_fmt}); + globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, value_fmt }); return .zero; } - globalObject.throw(Output.prettyFmt(fmt, false), .{value_fmt}); + globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, value_fmt }); return .zero; } - const received_line = "Received: {any}\n"; - const fmt = comptime getSignature("toBeInstanceOf", "", false) ++ "\n\n" ++ received_line; + const expected_line = "Expected constructor: {any}\n"; + const received_line = "Received value: {any}\n"; + const fmt = comptime getSignature("toBeInstanceOf", "", false) ++ "\n\n" ++ expected_line ++ received_line; if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{value_fmt}); + globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, value_fmt }); return .zero; } - globalObject.throw(Output.prettyFmt(fmt, false), .{value_fmt}); + globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, value_fmt }); return .zero; } diff --git a/test/js/bun/test/expect.test.ts b/test/js/bun/test/expect.test.ts index 8a7a6dde3a8a5b..9a70aca0a42ae0 100644 --- a/test/js/bun/test/expect.test.ts +++ b/test/js/bun/test/expect.test.ts @@ -7,18 +7,13 @@ describe("expect()", () => { const tests = [ { label: "string", - value: "", + value: new String(""), instanceOf: String, }, { label: "number", - value: 1, - instanceof: Number, - }, - { - label: "bigint", - value: 1n, - instanceof: BigInt, + value: new Number(1), + instanceOf: Number, }, { label: "object", @@ -28,22 +23,22 @@ describe("expect()", () => { { label: "function", value: () => {}, - instanceof: Function, + instanceOf: Function, }, { label: "Class", value: new Animal(), - instanceof: Animal, + instanceOf: Animal, }, { label: "extends Class", value: new Dog(), - instanceof: Dog, + instanceOf: Dog, }, { label: "super Class", value: new Dog(), - instanceof: Animal, + instanceOf: Animal, }, ]; for (const { label, value, instanceOf } of tests) {