1import type { PullRequestCheck } from "@t3tools/contracts";
2import { act, createElement } from "react";
3import { create, type ReactTestRenderer } from "react-test-renderer";
4import { expect, it, vi } from "vite-plus/test";
5
6import { usePullRequestChecksRefresh } from "./usePullRequestChecksRefresh";
7
8it.each(["pending", "action-required"] as const)(
9 "polls quiet PRs each minute, speeds up for %s or missing checks, resumes from idle, and stops for closed PRs",
10 (pendingStatus) => {
11 vi.useFakeTimers();
12 vi.setSystemTime(1_000_000);
13 const document = Object.assign(new EventTarget(), { visibilityState: "visible" });
14 vi.stubGlobal("document", document);
15 vi.stubGlobal("window", new EventTarget());
16 vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
17 const refresh = vi.fn();
18 let renderer: ReactTestRenderer | undefined;
19 function Probe({
20 status = "success",
21 enabled = true,
22 busy = false,
23 }: {
24 status?: PullRequestCheck["status"] | null;
25 enabled?: boolean;
26 busy?: boolean;
27 }) {
28 usePullRequestChecksRefresh({
29 refresh: busy ? null : refresh,
30 enabled,
31 key: `test-pr-checks:${pendingStatus}`,
32 checks: status === null ? [] : [{ name: "CI", status, description: null, url: null }],
33 });
34 return null;
35 }
36 const update = (props: Parameters<typeof Probe>[0]) => {
37 act(() => renderer?.update(createElement(Probe, props)));
38 };
39 const advance = (ms: number) => act(() => vi.advanceTimersByTime(ms));
40 try {
41 act(() => {
42 renderer = create(createElement(Probe));
43 });
44 advance(59_999);
45 expect(refresh).toHaveBeenCalledTimes(0);
46 advance(1);
47 expect(refresh).toHaveBeenCalledTimes(1);
48 update({ status: pendingStatus });
49 advance(44_999);
50 expect(refresh).toHaveBeenCalledTimes(1);
51 advance(1);
52 expect(refresh).toHaveBeenCalledTimes(2);
53 update({ status: "failure" });
54 advance(60_000);
55 expect(refresh).toHaveBeenCalledTimes(3);
56 update({ status: null });
57 for (let tick = 0; tick < 3; tick++) {
58 advance(45_000);
59 update({ status: null });
60 }
61 expect(refresh).toHaveBeenCalledTimes(6);
62 advance(44_999);
63 expect(refresh).toHaveBeenCalledTimes(6);
64 advance(1);
65 expect(refresh).toHaveBeenCalledTimes(7);
66 advance(6 * 60_000);
67 const beforeResume = refresh.mock.calls.length;
68 act(() => document.dispatchEvent(new Event("pointerdown")));
69 expect(refresh).toHaveBeenCalledTimes(beforeResume + 1);
70 document.visibilityState = "hidden";
71 act(() => document.dispatchEvent(new Event("visibilitychange")));
72 advance(60_000);
73 expect(refresh).toHaveBeenCalledTimes(beforeResume + 1);
74 document.visibilityState = "visible";
75 act(() => document.dispatchEvent(new Event("visibilitychange")));
76 expect(refresh).toHaveBeenCalledTimes(beforeResume + 2);
77 update({ enabled: false });
78 advance(120_000);
79 expect(refresh).toHaveBeenCalledTimes(beforeResume + 2);
80 } finally {
81 act(() => renderer?.unmount());
82 vi.useRealTimers();
83 vi.unstubAllGlobals();
84 }
85 },
86);