From 726aeb859171cc93cc5524d973e53076ca7966d4 Mon Sep 17 00:00:00 2001 From: Tomas Zijdemans Date: Sun, 6 Sep 2026 17:25:56 +0200 Subject: [PATCH] fix(testing): preserve Date subclass prototype under FakeTime --- testing/time.ts | 6 +++--- testing/time_test.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/testing/time.ts b/testing/time.ts index 53463e4a3e1a..af18bd0d09f9 100644 --- a/testing/time.ts +++ b/testing/time.ts @@ -75,10 +75,10 @@ function fakeTimeNow() { } const FakeDate = new Proxy(Date, { - construct(_target, args) { + construct(_target, args, newTarget) { if (args.length === 0) args.push(FakeDate.now()); - // @ts-expect-error this is a passthrough - return new _internals.Date(...args); + // Forward newTarget so `class X extends Date` keeps its prototype. + return Reflect.construct(_internals.Date, args, newTarget); }, apply(_target, _thisArg, _args) { return new _internals.Date(fakeTimeNow()).toString(); diff --git a/testing/time_test.ts b/testing/time_test.ts index 8414bbf200e0..09680f1d2940 100644 --- a/testing/time_test.ts +++ b/testing/time_test.ts @@ -107,6 +107,47 @@ Deno.test("FakeTime causes Date function to return the string representation of assertMatch(Date(), /(Fri|Thu) Jan 0(1|2) 1970/); }); +Deno.test("FakeTime preserves the prototype of Date subclasses", () => { + using _time = new FakeTime(24 * 60 * 60 * 1000); + class MyDate extends Date { + tag = "x"; + } + const date = new MyDate(); + assertInstanceOf(date, MyDate); + assertStrictEquals(Object.getPrototypeOf(date), MyDate.prototype); + assertEquals(date.tag, "x"); + assertEquals(date.toISOString(), "1970-01-02T00:00:00.000Z"); + assertEquals(new MyDate(1000).getTime(), 1000); +}); + +Deno.test("FakeTime causes new Date() to track the fake clock", () => { + using time = new FakeTime(9001); + assertEquals(new Date().getTime(), time.now); + time.tick(5000); + assertEquals(new Date().getTime(), time.now); +}); + +Deno.test("FakeTime passes explicit undefined through to Date", () => { + using _time = new FakeTime(9001); + assert(Number.isNaN(new Date(undefined as unknown as number).getTime())); +}); + +Deno.test("FakeTime constructor captured while faked works after restore", () => { + let Captured: DateConstructor; + let CapturedSub: new () => Date; + { + using _time = new FakeTime(9001); + Captured = Date; + CapturedSub = class extends Date {}; + } + const before = _internals.Date.now(); + const date = new Captured(); + const sub = new CapturedSub(); + assert(date.getTime() >= before); + assert(sub.getTime() >= before); + assertInstanceOf(sub, CapturedSub); +}); + Deno.test("FakeTime timeout functions unchanged if FakeTime is uninitialized", () => { assertStrictEquals(setTimeout, _internals.setTimeout); assertStrictEquals(clearTimeout, _internals.clearTimeout);