Skip to content
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
9 changes: 5 additions & 4 deletions src/jsc/bindings/JSMockFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje
}

JSC::ArgList args = JSC::ArgList(callframe);
JSValue thisValue = callframe->thisValue();
JSValue thisValue = callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict());
JSC::JSArray* argumentsArray = nullptr;
{
JSC::ObjectInitializationScope object(vm);
Expand Down Expand Up @@ -1271,7 +1271,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionGetter_mockGetLastCall, (JSC::JSGlobalObj
{
auto& vm = JSC::getVM(globalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSValue thisObject = callframe->thisValue();
JSValue thisObject = callframe->thisValue().toThis(globalObject, JSC::ECMAMode::strict());
if (!thisObject.isObject()) [[unlikely]] {
return JSValue::encode(jsUndefined());
}
Expand Down Expand Up @@ -1444,17 +1444,18 @@ BUN_DEFINE_HOST_FUNCTION(JSMock__jsNow, (JSC::JSGlobalObject * globalObject, JSC
BUN_DEFINE_HOST_FUNCTION(JSMock__jsSetSystemTime, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
{
JSValue argument0 = callframe->argument(0);
JSValue thisValue = callframe->thisValue().toThis(globalObject, ECMAMode::strict());

// JSGlobalObject::overridenDateNow's "no override" sentinel is NaN (see
// JSGlobalObject::jsDateNow()), so every real timestamp, including 0 and
// pre-epoch negatives, overrides; an omitted arg, NaN, or invalid Date resets.
if (auto* dateInstance = dynamicDowncast<DateInstance>(argument0)) {
globalObject->overridenDateNow = dateInstance->internalNumber();
return JSValue::encode(callframe->thisValue());
return JSValue::encode(thisValue);
}
globalObject->overridenDateNow = argument0.isNumber() ? argument0.asNumber() : PNaN;

return JSValue::encode(callframe->thisValue());
return JSValue::encode(thisValue);
}

BUN_DEFINE_HOST_FUNCTION(JSMock__jsRestoreAllMocks, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe))
Expand Down
25 changes: 25 additions & 0 deletions test/js/bun/test/mock-fn.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,31 @@ describe("mock()", () => {

expect(bar()()).toBe(true);
});

test("this is undefined for a bare call through a closure-captured binding", () => {
const fn = jest.fn().mockReturnThis();
const captured = 1;
// Referencing `fn` from an inner function forces it into the enclosing
// lexical environment, so `fn()` below resolves it through that scope.
function keepCaptured() {
return [fn, captured];
}
expect(fn()).toBeUndefined();
expect(fn.mock.contexts).toEqual([undefined]);
});

test("this seen by the implementation is undefined for a closure-captured bare call", () => {
let receivedThis = "not called";
const fn = jest.fn(function () {
receivedThis = this;
});
function keepCaptured() {
return fn;
}
fn();
expect(receivedThis).toBeUndefined();
expect(fn.mock.contexts).toEqual([undefined]);
});
});

describe("spyOn", () => {
Expand Down
10 changes: 10 additions & 0 deletions test/js/bun/test/test-timers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ test("we can go back in time", () => {
expect(now.toISOString()).toBe(orig.toISOString());
});

test("setSystemTime returns undefined for a bare call through a closure-captured binding", () => {
const { setSystemTime } = jest;
// Referencing `setSystemTime` from an inner function forces it into the
// enclosing lexical environment, so the bare call resolves through that scope.
function keepCaptured() {
return setSystemTime;
}
expect(setSystemTime()).toBeUndefined();
});

test("setSystemTime accepts pre-epoch and epoch times and resets with no argument", () => {
const realBefore = Date.now();
jest.useFakeTimers();
Expand Down
Loading