|
| 1 | +import { afterEach, expect, mock, test } from "bun:test"; |
| 2 | +import { act } from "react"; |
| 3 | +import { createRoot, type Root } from "react-dom/client"; |
| 4 | +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; |
| 5 | +import { starterPack } from "@corbits/triage-contracts"; |
| 6 | +import PolicySwitches from "./PolicySwitches.tsx"; |
| 7 | + |
| 8 | +const REPO = "acme/widgets"; |
| 9 | +const pack = starterPack(REPO); |
| 10 | +let root: Root | undefined; |
| 11 | +const originalFetch = globalThis.fetch; |
| 12 | + |
| 13 | +// Other DOM test files replace this module for the whole test process, so this file sets its own. |
| 14 | +mock.module("../../lib/portal.tsx", () => ({ |
| 15 | + usePortal() { |
| 16 | + return { snapshot: { workspace: { tenantId: "tenant" } } }; |
| 17 | + }, |
| 18 | +})); |
| 19 | + |
| 20 | +afterEach(function unmount() { |
| 21 | + globalThis.fetch = originalFetch; |
| 22 | + act(() => root?.unmount()); |
| 23 | + document.body.innerHTML = ""; |
| 24 | +}); |
| 25 | + |
| 26 | +type Sent = { url: string; body: unknown }; |
| 27 | + |
| 28 | +function fakeHub(sent: Sent[]): void { |
| 29 | + globalThis.fetch = async function answer(input: RequestInfo | URL, init?: RequestInit) { |
| 30 | + const url = String(input); |
| 31 | + sent.push({ url, body: typeof init?.body === "string" ? JSON.parse(init.body) : undefined }); |
| 32 | + if (url.endsWith("/policy/patch")) return Response.json({ before: "rules: []\n", after: "rules: [] # off\n" }); |
| 33 | + return Response.json({ status: "ok", pack, source: {}, automatic: [], wanted: [], mergeRisks: [], codeReview: false }); |
| 34 | + } as typeof fetch; |
| 35 | +} |
| 36 | + |
| 37 | +async function settle() { |
| 38 | + for (let i = 0; i < 5; i += 1) await act(() => new Promise((resolve) => setTimeout(resolve, 0))); |
| 39 | +} |
| 40 | + |
| 41 | +test("turning a rule off lists the change and previews exactly that change", async () => { |
| 42 | + const sent: Sent[] = []; |
| 43 | + fakeHub(sent); |
| 44 | + const host = document.createElement("div"); |
| 45 | + document.body.append(host); |
| 46 | + root = createRoot(host); |
| 47 | + const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); |
| 48 | + act(() => root?.render( |
| 49 | + <QueryClientProvider client={client}> |
| 50 | + <PolicySwitches |
| 51 | + repo={REPO} |
| 52 | + source="github" |
| 53 | + file={{ path: ".corbits-triage.yml", sha: "sha", url: "https://github.com/acme/widgets" }} |
| 54 | + pack={pack} |
| 55 | + reviewOn={false} |
| 56 | + hasModel |
| 57 | + reviewMissing={undefined} |
| 58 | + pull={null} |
| 59 | + pending={[]} |
| 60 | + disabled={false} |
| 61 | + /> |
| 62 | + </QueryClientProvider>, |
| 63 | + )); |
| 64 | + |
| 65 | + const conflicts = document.querySelector<HTMLButtonElement>('[role="switch"][aria-label="Flag merge conflicts with the base branch"]'); |
| 66 | + if (!conflicts) throw new Error("no switch for the merge conflicts rule"); |
| 67 | + act(() => conflicts.click()); |
| 68 | + await settle(); |
| 69 | + |
| 70 | + const tray = document.querySelector("aside.tray"); |
| 71 | + expect(tray?.querySelector("b")?.textContent).toBe("1 change. Open a pull request to apply it."); |
| 72 | + expect([...(tray?.querySelectorAll("li") ?? [])].map((item) => item.textContent)).toEqual(["Turn off “Flag merge conflicts with the base branch”."]); |
| 73 | + const patch = sent.find((request) => request.url.endsWith("/policy/patch")); |
| 74 | + expect(patch?.body).toEqual({ repo: REPO, source: "github", changes: [{ kind: "rule", id: "no-merge-conflicts", enabled: false }], preview: true }); |
| 75 | + expect(tray?.textContent).toContain("After it merges, the policy reads:"); |
| 76 | +}); |
0 commit comments