Files
bun.sh/test/regression/issue/26284.test.ts
robobun dbad2857ea 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>
2026-01-20 12:51:54 -08:00

68 lines
2.3 KiB
TypeScript

// 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);
});