diff options
| -rw-r--r-- | packages/bun-types/bun-test.d.ts | 30 | ||||
| -rw-r--r-- | src/bun.js/bindings/JSMockFunction.cpp | 12 | ||||
| -rw-r--r-- | src/bun.js/test/jest.zig | 31 | ||||
| -rw-r--r-- | test/js/bun/test/test-timers.test.ts | 35 | 
4 files changed, 96 insertions, 12 deletions
| diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts index 156585766..5182f7e93 100644 --- a/packages/bun-types/bun-test.d.ts +++ b/packages/bun-types/bun-test.d.ts @@ -29,6 +29,36 @@ declare module "bun:test" {      <T extends AnyFunction>(Function: T): Mock<T>;    }; +  /** +   * Control the system time used by: +   * - `Date.now()` +   * - `new Date()` +   * - `Intl.DateTimeFormat().format()` +   * +   * In the future, we may add support for more functions, but we haven't done that yet. +   * +   * @param now The time to set the system time to. If not provided, the system time will be reset. +   * @returns `this` +   * @since v0.6.13 +   * +   * ## Set Date to a specific time +   * +   * ```js +   * import { setSystemTime } from 'bun:test'; +   * +   * setSystemTime(new Date('2020-01-01T00:00:00.000Z')); +   * console.log(new Date().toISOString()); // 2020-01-01T00:00:00.000Z +   * ``` +   * ## Reset Date to the current time +   * +   * ```js +   * import { setSystemTime } from 'bun:test'; +   * +   * setSystemTime(); +   * ``` +   */ +  export function setSystemTime(now?: Date | number): ThisType<void>; +    interface Jest {      restoreAllMocks(): void;      fn<T extends AnyFunction>(func?: T): Mock<T>; diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index 0e24e761c..3a84f0139 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -66,6 +66,18 @@ JSC_DECLARE_HOST_FUNCTION(jsMockFunctionMockRejectedValueOnce);  JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementationCleanup);  JSC_DECLARE_HOST_FUNCTION(jsMockFunctionWithImplementation); +// This is a stub. Exists so that the same code can be run in Jest +extern "C" EncodedJSValue JSMock__jsUseFakeTimers(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +{ +    return JSValue::encode(callFrame->thisValue()); +} + +extern "C" EncodedJSValue JSMock__jsUseRealTimers(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +{ +    globalObject->overridenDateNow = -1; +    return JSValue::encode(callFrame->thisValue()); +} +  extern "C" EncodedJSValue JSMock__jsNow(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)  {      return JSValue::encode(jsNumber(globalObject->jsDateNow())); diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 727466835..55600ded8 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -333,7 +333,7 @@ pub const Jest = struct {      pub fn Bun__Jest__createTestModuleObject(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {          JSC.markBinding(@src()); -        const module = JSC.JSValue.createEmptyObject(globalObject, 12); +        const module = JSC.JSValue.createEmptyObject(globalObject, 13);          const test_fn = JSC.NewFunction(globalObject, ZigString.static("test"), 2, TestScope.call, false);          module.put( @@ -431,17 +431,40 @@ pub const Jest = struct {              Expect.getConstructor(globalObject),          ); +        const setSystemTime = JSC.NewFunction(globalObject, ZigString.static("setSystemTime"), 0, JSMock__jsSetSystemTime, false); +        module.put( +            globalObject, +            ZigString.static("setSystemTime"), +            setSystemTime, +        ); +        const useFakeTimers = JSC.NewFunction(globalObject, ZigString.static("useFakeTimers"), 0, JSMock__jsUseFakeTimers, false); +        const useRealTimers = JSC.NewFunction(globalObject, ZigString.static("useRealTimers"), 0, JSMock__jsUseRealTimers, false); +          const mockFn = JSC.NewFunction(globalObject, ZigString.static("fn"), 1, JSMock__jsMockFn, false);          const spyOn = JSC.NewFunction(globalObject, ZigString.static("spyOn"), 2, JSMock__jsSpyOn, false);          const restoreAllMocks = JSC.NewFunction(globalObject, ZigString.static("restoreAllMocks"), 2, JSMock__jsRestoreAllMocks, false);          module.put(globalObject, ZigString.static("mock"), mockFn); -        const jest = JSValue.createEmptyObject(globalObject, 3); +        const jest = JSValue.createEmptyObject(globalObject, 7);          jest.put(globalObject, ZigString.static("fn"), mockFn);          jest.put(globalObject, ZigString.static("spyOn"), spyOn);          jest.put(globalObject, ZigString.static("restoreAllMocks"), restoreAllMocks); +        jest.put( +            globalObject, +            ZigString.static("setSystemTime"), +            setSystemTime, +        ); +        jest.put( +            globalObject, +            ZigString.static("useFakeTimers"), +            useFakeTimers, +        ); +        jest.put( +            globalObject, +            ZigString.static("useRealTimers"), +            useRealTimers, +        );          jest.put(globalObject, ZigString.static("now"), JSC.NewFunction(globalObject, ZigString.static("now"), 0, JSMock__jsNow, false)); -        jest.put(globalObject, ZigString.static("setSystemTime"), JSC.NewFunction(globalObject, ZigString.static("setSystemTime"), 0, JSMock__jsSetSystemTime, false));          module.put(globalObject, ZigString.static("jest"), jest);          module.put(globalObject, ZigString.static("spyOn"), spyOn); @@ -462,6 +485,8 @@ pub const Jest = struct {      extern fn JSMock__jsSetSystemTime(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;      extern fn JSMock__jsRestoreAllMocks(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;      extern fn JSMock__jsSpyOn(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; +    extern fn JSMock__jsUseFakeTimers(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue; +    extern fn JSMock__jsUseRealTimers(*JSC.JSGlobalObject, *JSC.CallFrame) JSC.JSValue;      pub fn call(          _: void, diff --git a/test/js/bun/test/test-timers.test.ts b/test/js/bun/test/test-timers.test.ts index 64da0abda..963467dee 100644 --- a/test/js/bun/test/test-timers.test.ts +++ b/test/js/bun/test/test-timers.test.ts @@ -1,11 +1,28 @@  test("we can go back in time", () => { -  const dateNow = Date.now; -  //   jest.useFakeTimers(); -  jest.setSystemTime(new Date("2020-01-01T00:00:00.000Z")); -  expect(new Date().toISOString()).toBe("2020-01-01T00:00:00.000Z"); -  expect(Date.now()).toBe(1577836800000); -  expect(dateNow).toBe(Date.now); - -  jest.setSystemTime(); -  expect(new Date().toISOString()).not.toBe("2020-01-01T00:00:00.000Z"); +  const DateBeforeMocked = Date; +  const orig = new Date(); +  orig.setHours(0, 0, 0, 0); +  jest.useFakeTimers(); +  jest.setSystemTime(new Date("1995-12-19T00:00:00.000Z")); + +  expect(new Date().toISOString()).toBe("1995-12-19T00:00:00.000Z"); +  expect(Date.now()).toBe(819331200000); + +  if (typeof Bun !== "undefined") { +    // In bun, the Date object remains the same despite being mocked. +    // This prevents a whole bunch of subtle bugs in tests. +    expect(DateBeforeMocked).toBe(Date); +    expect(DateBeforeMocked.now).toBe(Date.now); + +    // Jest doesn't property mock new Intl.DateTimeFormat().format() +    expect(new Intl.DateTimeFormat().format()).toBe("12/19/1995"); +  } else { +    expect(DateBeforeMocked).not.toBe(Date); +    expect(DateBeforeMocked.now).not.toBe(Date.now); +  } + +  jest.useRealTimers(); +  const now = new Date(); +  now.setHours(0, 0, 0, 0); +  expect(now.toISOString()).toBe(orig.toISOString());  }); | 
