mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(test): delete setTimeout.clock property when disabling fake timers (#26285)
## Summary - Fixes the `setTimeout.clock` property not being properly deleted after `jest.useRealTimers()` is called - Previously, the property was set to `false` instead of deleted, causing `hasOwnProperty` checks to return `true` - This broke React Testing Library and other libraries that check for fake timers using `Object.prototype.hasOwnProperty.call(setTimeout, 'clock')` ## Changes - Added `JSValue.deleteProperty()` binding in Zig to call JSC's `deleteProperty()` method - Updated `setFakeTimerMarker()` in `FakeTimers.zig` to delete the `clock` property when disabling fake timers - Updated existing test in `test/regression/issue/25869.test.ts` to verify correct behavior - Added new regression test in `test/regression/issue/26284.test.ts` ## Test plan - [x] Verified new test fails with system bun (before fix) - [x] Verified new test passes with debug build (after fix) - [x] Verified existing fake timer tests still pass - [x] Verified test for issue #25869 passes with fix Fixes #26284 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,13 +32,13 @@ test("setTimeout.clock is set after useFakeTimers", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("setTimeout.clock is set to false after useRealTimers", () => {
|
||||
test("setTimeout.clock is deleted after useRealTimers", () => {
|
||||
jest.useFakeTimers();
|
||||
jest.useRealTimers();
|
||||
// Note: The clock property remains on setTimeout but is set to false.
|
||||
// This differs from Jest/Sinon which removes the property entirely.
|
||||
// The value being false is sufficient for most use cases.
|
||||
expect((globalThis.setTimeout as any).clock).toBe(false);
|
||||
// The clock property should be deleted when disabling fake timers.
|
||||
// This matches Jest/Sinon behavior and ensures hasOwnProperty returns false.
|
||||
expect(Object.prototype.hasOwnProperty.call(globalThis.setTimeout, "clock")).toBe(false);
|
||||
expect((globalThis.setTimeout as any).clock).toBe(undefined);
|
||||
});
|
||||
|
||||
test("advanceTimersByTime(0) fires setTimeout(fn, 0) timers", async () => {
|
||||
|
||||
67
test/regression/issue/26284.test.ts
Normal file
67
test/regression/issue/26284.test.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
// https://github.com/oven-sh/bun/issues/26284
|
||||
// After useRealTimers(), hasOwnProperty('clock') should return false
|
||||
import { afterEach, expect, jest, test } from "bun:test";
|
||||
|
||||
// Simulates testing-library/react's jestFakeTimersAreEnabled() function
|
||||
function jestFakeTimersAreEnabled(): boolean {
|
||||
// @ts-expect-error - checking for Jest fake timers markers
|
||||
if (typeof jest !== "undefined" && jest !== null) {
|
||||
return (
|
||||
// @ts-expect-error - checking for mock function marker
|
||||
(globalThis.setTimeout as any)._isMockFunction === true ||
|
||||
Object.prototype.hasOwnProperty.call(globalThis.setTimeout, "clock")
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure fake timers are always cleaned up after each test
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
test("hasOwnProperty('clock') returns false before useFakeTimers", () => {
|
||||
expect(Object.prototype.hasOwnProperty.call(globalThis.setTimeout, "clock")).toBe(false);
|
||||
expect(jestFakeTimersAreEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
test("hasOwnProperty('clock') returns true after useFakeTimers", () => {
|
||||
jest.useFakeTimers();
|
||||
expect(Object.prototype.hasOwnProperty.call(globalThis.setTimeout, "clock")).toBe(true);
|
||||
expect((globalThis.setTimeout as any).clock).toBe(true);
|
||||
expect(jestFakeTimersAreEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
test("hasOwnProperty('clock') returns false after useRealTimers", () => {
|
||||
// First enable fake timers
|
||||
jest.useFakeTimers();
|
||||
expect(Object.prototype.hasOwnProperty.call(globalThis.setTimeout, "clock")).toBe(true);
|
||||
|
||||
// Then disable them
|
||||
jest.useRealTimers();
|
||||
|
||||
// The clock property should be deleted, not just set to false
|
||||
expect(Object.prototype.hasOwnProperty.call(globalThis.setTimeout, "clock")).toBe(false);
|
||||
expect((globalThis.setTimeout as any).clock).toBe(undefined);
|
||||
expect(jestFakeTimersAreEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
test("multiple useFakeTimers/useRealTimers cycles work correctly", () => {
|
||||
// Cycle 1
|
||||
jest.useFakeTimers();
|
||||
expect(jestFakeTimersAreEnabled()).toBe(true);
|
||||
jest.useRealTimers();
|
||||
expect(jestFakeTimersAreEnabled()).toBe(false);
|
||||
|
||||
// Cycle 2
|
||||
jest.useFakeTimers();
|
||||
expect(jestFakeTimersAreEnabled()).toBe(true);
|
||||
jest.useRealTimers();
|
||||
expect(jestFakeTimersAreEnabled()).toBe(false);
|
||||
|
||||
// Cycle 3
|
||||
jest.useFakeTimers();
|
||||
expect(jestFakeTimersAreEnabled()).toBe(true);
|
||||
jest.useRealTimers();
|
||||
expect(jestFakeTimersAreEnabled()).toBe(false);
|
||||
});
|
||||
Reference in New Issue
Block a user