diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index 1c760357186a..aace70d653e5 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -837,6 +837,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje return {}; } + const bool isConstruct = !callframe->newTarget().isUndefined(); JSC::ArgList args = JSC::ArgList(callframe); JSValue thisValue = callframe->thisValue(); JSC::JSArray* argumentsArray = nullptr; @@ -954,15 +955,24 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje fn->returnValues.set(vm, fn, returnValuesArray); } + if (isConstruct && !returnValue.isObject()) { + return JSValue::encode(thisValue.isObject() ? thisValue : JSC::constructEmptyObject(globalObject)); + } return JSValue::encode(returnValue); } case JSMockImplementation::Kind::ReturnValue: { JSValue returnValue = impl->underlyingValue.get(); setReturnValue(createMockResult(vm, globalObject, "return"_s, returnValue)); + if (isConstruct && !returnValue.isObject()) { + return JSValue::encode(thisValue.isObject() ? thisValue : JSC::constructEmptyObject(globalObject)); + } return JSValue::encode(returnValue); } case JSMockImplementation::Kind::ReturnThis: { setReturnValue(createMockResult(vm, globalObject, "return"_s, thisValue)); + if (isConstruct && !thisValue.isObject()) { + return JSValue::encode(JSC::constructEmptyObject(globalObject)); + } return JSValue::encode(thisValue); } case JSMockImplementation::Kind::RejectedValue: { @@ -978,6 +988,9 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje } setReturnValue(createMockResult(vm, globalObject, "return"_s, jsUndefined())); + if (isConstruct) { + return JSValue::encode(thisValue.isObject() ? thisValue : JSC::constructEmptyObject(globalObject)); + } return JSValue::encode(jsUndefined()); } diff --git a/test/js/bun/test/mock-fn-construct.test.ts b/test/js/bun/test/mock-fn-construct.test.ts new file mode 100644 index 000000000000..90afe68078ef --- /dev/null +++ b/test/js/bun/test/mock-fn-construct.test.ts @@ -0,0 +1,16 @@ +import { expect, jest, test } from "bun:test"; + +test("Reflect.construct on mock with non-object return value does not crash", () => { + const m = jest.fn(() => 42); + const result = Reflect.construct(m, []); + expect(result).toBeObject(); + expect(m.mock.results[0].value).toBe(42); +}); + +test("Reflect.construct on mock with mockReturnValue does not crash", () => { + const m = jest.fn(); + m.mockReturnValue(123); + const result = Reflect.construct(m, []); + expect(result).toBeObject(); + expect(m.mock.results[0].value).toBe(123); +});