From c2105bc0ac4914dfff9a704e6fa7a62a400bfc80 Mon Sep 17 00:00:00 2001 From: Rob Date: Sat, 6 Apr 2024 22:27:27 -0400 Subject: [PATCH] feat: binds more Vi utilities, and creates tests --- src/Vitest.res | 121 +++++++++++++++++++++++++++++--- tests/vi.test.res | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 9 deletions(-) create mode 100644 tests/vi.test.res diff --git a/src/Vitest.res b/src/Vitest.res index 2f7d577..064ca99 100644 --- a/src/Vitest.res +++ b/src/Vitest.res @@ -968,6 +968,11 @@ external afterEachPromise: (@uncurry (unit => promise<'a>), Js.Undefined.t) let afterEachPromise = (~timeout=?, callback) => afterEachPromise(callback, timeout->Js.Undefined.fromOption) +@module("vitest") +external afterAll: (unit => 'a, Js.Undefined.t) => unit = "afterAll" + +let afterAll = (~timeout=?, callback) => afterAll(callback, timeout->Js.Undefined.fromOption) + @module("vitest") external afterAllPromise: (@uncurry (unit => promise<'a>), Js.Undefined.t) => unit = "afterAll" @@ -1109,7 +1114,9 @@ module Matchers = ( @inline let toMatch = (expected, list) => { - expected->dangerously_reinforce_assertion(Belt.List.toArray)->Array.toMatch(list->Belt.List.toArray) + expected + ->dangerously_reinforce_assertion(Belt.List.toArray) + ->Array.toMatch(list->Belt.List.toArray) } } @@ -1160,6 +1167,13 @@ module Assert = { %%private(@module("vitest") @val external assert_obj: t = "assert") + @module("vitest") + external assert_: (bool, Js.undefined) => unit = "assert" + let assert_ = (~message=?, value) => assert_(value, message->Js.Undefined.fromOption) + + @module("vitest") @scope("expect") + external unreachable: (~message: string=?, unit) => unit = "unreachable" + @send external equal: (t, 'a, 'a, Js.undefined) => unit = "equal" @inline @@ -1180,29 +1194,118 @@ module Vi = { @send external advanceTimersByTime: (t, int) => t = "advanceTimersByTime" @inline let advanceTimersByTime = ms => vi_obj->advanceTimersByTime(ms) + @send external advanceTimersByTimeAsync: (t, int) => promise = "advanceTimersByTimeAsync" + let advanceTimersByTimeAsync = time => vi_obj->advanceTimersByTimeAsync(time) + @send external advanceTimersToNextTimer: t => t = "advanceTimersToNextTimer" @inline let advanceTimersToNextTimer = () => vi_obj->advanceTimersToNextTimer + @send external advanceTimersToNextTimerAsync: t => promise = "advanceTimersToNextTimerAsync" + let advanceTimersToNextTimerAsync = () => vi_obj->advanceTimersToNextTimerAsync + + @send external getTimerCount: t => int = "getTimerCount" + let getTimerCount = () => vi_obj->getTimerCount + + @send external clearAllTimers: t => t = "clearAllTimers" + let clearAllTimers = () => vi_obj->clearAllTimers + + @send external runAllTicks: t => t = "runAllTicks" + let runAllTicks = () => vi_obj->runAllTicks + @send external runAllTimers: t => t = "runAllTimers" @inline let runAllTimers = () => vi_obj->runAllTimers + @send external runAllTimersAsync: t => promise = "runAllTimersAsync" + let runAllTimersAsync = () => vi_obj->runAllTimersAsync + @send external runOnlyPendingTimers: t => t = "runOnlyPendingTimers" @inline let runOnlyPendingTimers = () => vi_obj->runOnlyPendingTimers - @send external useFakeTimers: t => t = "useFakeTimers" - @inline let useFakeTimers = () => vi_obj->useFakeTimers + @send external runOnlyPendingTimersAsync: t => promise = "runOnlyPendingTimersAsync" + let runOnlyPendingTimersAsync = () => vi_obj->runOnlyPendingTimersAsync + + @send + external setSystemTime: (t, @unwrap [#Date(Js.Date.t) | #String(string) | #Int(int)]) => t = + "setSystemTime" + let setSystemTime = time => vi_obj->setSystemTime(time) + + /** + https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/sinonjs__fake-timers/index.d.ts + */ + type fakeTimersConfig = { + now?: Js.Date.t, // or int + toFake?: array, + loopLimit?: int, + shouldAdvanceTime?: bool, + advanceTimeDelta?: int, + shouldClearNativeTimers?: bool, + } + + @send external useFakeTimers: (t, ~config: fakeTimersConfig=?, unit) => t = "useFakeTimers" + @inline let useFakeTimers = (~config=?, ()) => vi_obj->useFakeTimers(~config?, ()) @send external useRealTimers: t => t = "useRealTimers" @inline let useRealTimers = () => vi_obj->useRealTimers - @send external mockCurrentDate: (t, Js.Date.t) => t = "mockCurrentDate" - @inline let mockCurrentDate = date => vi_obj->mockCurrentDate(date) + @send external isFakeTimers: t => bool = "isFakeTimers" + let isFakeTimers = () => vi_obj->isFakeTimers + + @send @return(nullable) + external getMockedSystemTime: t => option = "getMockedSystemTime" + let getMockedSystemTime = () => vi_obj->getMockedSystemTime + + @send external getRealSystemTime: t => float = "getRealSystemTime" + let getRealSystemTime = () => vi_obj->getRealSystemTime + + type waitForOptions = { + timeout?: int, + interval?: int, + } + + @send external waitFor: (t, unit => 'a, ~options: waitForOptions=?) => promise<'a> = "waitFor" + + /** + @since(vitest >= 0.34.5) + */ + let waitFor = (callback, ~timeout=?, ~interval=?, ()) => + waitFor(vi_obj, callback, ~options={?timeout, ?interval}) + + @send + external waitForAsync: (t, unit => promise<'a>, ~options: waitForOptions=?) => promise<'a> = + "waitFor" + + /** + @since(vitest >= 0.34.5) + */ + let waitForAsync = (callback, ~timeout=?, ~interval=?, ()) => + waitForAsync(vi_obj, callback, ~options={?timeout, ?interval}) + + type waitUntilOptions = { + timeout?: int, + interval?: int, + } + + @send + external waitUntil: (t, unit => 'a, ~options: waitUntilOptions=?) => promise<'a> = "waitUntil" + + /** + @since(vitest >= 0.34.5) + */ + let waitUntil = (callback, ~timeout=?, ~interval=?, ()) => + waitUntil(vi_obj, callback, ~options={?timeout, ?interval}) + + @send + external waitUntilAsync: (t, unit => promise<'a>, ~options: waitUntilOptions=?) => promise<'a> = + "waitUntil" - @send external restoreCurrentDate: (t, Js.Date.t) => t = "restoreCurrentDate" - @inline let restoreCurrentDate = date => vi_obj->restoreCurrentDate(date) + /** + @since(vitest >= 0.34.5) + */ + let waitUntilAsync = (callback, ~timeout=?, ~interval=?, ()) => + waitUntilAsync(vi_obj, callback, ~options={?timeout, ?interval}) - @send external getMockedDate: t => Js.null = "getMockedDate" - @inline let getMockedDate = () => vi_obj->getMockedDate->Js.Null.toOption + @send external hoisted: (t, unit => 'a) => 'a = "hoisted" + let hoisted = callback => hoisted(vi_obj, callback) } @scope("import.meta") @val diff --git a/tests/vi.test.res b/tests/vi.test.res new file mode 100644 index 0000000..3db462b --- /dev/null +++ b/tests/vi.test.res @@ -0,0 +1,171 @@ +open Vitest + +@val external nextTick: (unit => unit) => unit = "process.nextTick" + +describe("Vi", () => { + beforeEach(() => { + let _ = Vi.useRealTimers() + }) + + afterAll(() => { + let _ = Vi.useRealTimers() + }) + + let _promise = () => Js.Promise2.resolve() + + itAsync("should compile fake timers correctly", async _t => { + let _ = Vi.useFakeTimers() + Vi.isFakeTimers()->expect->Expect.toBe(true) + + let called = ref(false) + let called2 = ref(false) + + let _ = Js.Global.setTimeout(() => called := true, 100) + let _ = Js.Global.setTimeout(() => called2 := true, 200) + let _ = Vi.advanceTimersByTime(10) + called->expect->Expect.toEqual({contents: false}) + called2->expect->Expect.toEqual({contents: false}) + + let _ = Vi.advanceTimersByTime(100) + called->expect->Expect.toEqual({contents: true}) + called2->expect->Expect.toEqual({contents: false}) + called := false + + let _ = await Vi.advanceTimersByTimeAsync(1000) + called2->expect->Expect.toEqual({contents: true}) + called2 := false + Vi.getTimerCount()->expect->Expect.toBe(0) + + let _ = Js.Global.setTimeout(() => called := true, 1000) + let _ = Js.Global.setTimeout(() => called2 := true, 2000) + Vi.getTimerCount()->expect->Expect.toBe(2) + let _ = Vi.runAllTimers() + called->expect->Expect.toEqual({contents: true}) + called2->expect->Expect.toEqual({contents: true}) + called := false + called2 := false + + let _ = Js.Global.setTimeout(() => called := true, 1000) + let _ = Js.Global.setTimeout(() => called2 := true, 2000) + Vi.getTimerCount()->expect->Expect.toBe(2) + let _ = await Vi.runAllTimersAsync() + called->expect->Expect.toEqual({contents: true}) + called2->expect->Expect.toEqual({contents: true}) + called := false + called2 := false + + let _ = Js.Global.setTimeout(() => called := true, 1000) + Vi.getTimerCount()->expect->Expect.toBe(1) + let _ = Vi.runOnlyPendingTimers() + called->expect->Expect.toEqual({contents: true}) + called := false + + let _ = Js.Global.setTimeout(() => called := true, 1000) + Vi.getTimerCount()->expect->Expect.toBe(1) + let _ = await Vi.runOnlyPendingTimersAsync() + called->expect->Expect.toEqual({contents: true}) + called := false + + let _ = Js.Global.setTimeout(() => called := true, 1000) + let _ = Js.Global.setTimeout(() => called2 := true, 2000) + Vi.getTimerCount()->expect->Expect.toBe(2) + let _ = Vi.advanceTimersToNextTimer() + called->expect->Expect.toEqual({contents: true}) + called2.contents->expect->Expect.toBe(false) + + let _ = await Vi.advanceTimersToNextTimerAsync() + called2.contents->expect->Expect.toBe(true) + + let _ = Js.Global.setTimeout(() => called := true, 1000) + Vi.getTimerCount()->expect->Expect.toBe(1) + let _ = Vi.clearAllTimers() + Vi.getTimerCount()->expect->Expect.toBe(0) + + nextTick(() => called := true) + let _ = Vi.runAllTicks() + called->expect->Expect.toEqual({contents: true}) + called := false + }) + + itAsync("should compile waitFor correctly", async _t => { + let called = ref(false) + let _ = Js.Global.setTimeout(() => called := true, 100) + await Vi.waitFor(() => Assert.assert_(called.contents == true), ()) + called->expect->Expect.toEqual({contents: true}) + + let called = ref(false) + let _ = Js.Global.setTimeout(() => called := true, 100) + await Vi.waitFor(() => Assert.assert_(called.contents == true), ~timeout=200, ()) + called->expect->Expect.toEqual({contents: true}) + + let called = ref(false) + let _ = Js.Global.setTimeout(() => called := true, 100) + await Vi.waitFor(() => Assert.assert_(called.contents == true), ~interval=50, ()) + called->expect->Expect.toEqual({contents: true}) + + let called = ref(false) + let _ = Js.Global.setTimeout(() => called := true, 100) + await Vi.waitFor(() => Assert.assert_(called.contents == true), ~timeout=200, ~interval=50, ()) + called->expect->Expect.toEqual({contents: true}) + + let run = async () => { + let called = ref(false) + let _ = Js.Global.setTimeout(() => called := true, 100) + await Vi.waitFor(() => Assert.assert_(called.contents == true), ~timeout=50, ()) + called->expect->Expect.toEqual({contents: false}) + } + + await run() + ->expect + ->Expect.Promise.rejects + ->Expect.Promise.toThrow + }) + + itAsync("should compile waitForAsync correctly", async _t => { + let _ = Vi.useFakeTimers() + + let sleep = ms => { + Js.Promise2.make( + (~resolve, ~reject as _) => { + let _ = Js.Global.setTimeout(() => resolve(), ms) + }, + ) + } + + await Vi.waitForAsync(() => sleep(1), ()) + await Vi.waitForAsync(() => sleep(20), ~timeout=100, ()) + await Vi.waitForAsync(() => sleep(50), ~interval=50, ()) + await Vi.waitForAsync(() => sleep(150), ~timeout=200, ~interval=50, ()) + + let run = () => Vi.waitForAsync(() => sleep(100), ~timeout=50, ()) + + await run() + ->expect + ->Expect.Promise.rejects + ->Expect.Promise.toThrow + }) + + it("compile mocking system time correctly", _t => { + Vi.getMockedSystemTime()->expect->Expect.toBeNone + + let date = Js.Date.makeWithYMD(~year=2021., ~month=1., ~date=1., ()) + let _ = Vi.setSystemTime(#Date(date)) + // FIXME: Expect.toBeSome is not working + /* + AssertionError: expected 2021-02-01T05:00:00.000Z to not deeply equal 2021-02-01T05:00:00.000Z + ❯ Object.toBeSome src/Vitest.mjs:671:21 + 669| expected.not.toBeUndefined(); + 670| if (some !== undefined) { + 671| return expected.toEqual(Caml_option.valFromOption(some)); + | ^ + 672| } + */ + Vi.getMockedSystemTime()->Belt.Option.getExn->expect->Expect.toStrictEqual(date) + Vi.getRealSystemTime()->expect->Expect.Float.toBeGreaterThan(Js.Date.getTime(date)) + + Vi.getRealSystemTime()->expect->Expect.Float.toBeGreaterThanOrEqual(0.0) + let _ = Vi.useRealTimers() + Vi.isFakeTimers()->expect->Expect.toBe(false) + Vi.getMockedSystemTime()->expect->Expect.toBeNone + }) +})