| 1 | import { EnvironmentId, type PullRequestCheck } from "@t3tools/contracts"; |
| 2 | import { act, cloneElement, type ReactElement, type ReactNode } from "react"; |
| 3 | import { create, type ReactTestRenderer } from "react-test-renderer"; |
| 4 | import { afterEach, expect, it, vi } from "vite-plus/test"; |
| 5 | |
| 6 | const state = vi.hoisted(() => ({ |
| 7 | status: "success" as PullRequestCheck["status"], |
| 8 | extraStatus: null as PullRequestCheck["status"] | null, |
| 9 | perform: vi.fn(), |
| 10 | })); |
| 11 | |
| 12 | vi.mock("~/state/entities", () => ({ useServerConfigs: () => new Map() })); |
| 13 | vi.mock("~/state/pullRequests", () => ({ pullRequestEnvironment: {} })); |
| 14 | vi.mock("~/hooks/useLiveRefresh", () => ({ useLiveRefresh: () => {} })); |
| 15 | vi.mock("~/state/query", () => ({ |
| 16 | useEnvironmentQuery: () => ({ |
| 17 | data: { |
| 18 | number: 1, |
| 19 | title: "Test PR", |
| 20 | state: "open", |
| 21 | isDraft: false, |
| 22 | mergeability: "mergeable", |
| 23 | headBranch: "feature", |
| 24 | baseBranch: "main", |
| 25 | changedFiles: 1, |
| 26 | additions: 1, |
| 27 | deletions: 0, |
| 28 | checks: [state.status, ...(state.extraStatus ? [state.extraStatus] : [])].map((status) => ({ |
| 29 | name: `CI-${status}`, |
| 30 | status, |
| 31 | description: null, |
| 32 | url: null, |
| 33 | })), |
| 34 | capabilities: { actions: ["merge"], mergeMethods: ["merge"] }, |
| 35 | viewerPermissions: { actions: ["merge"] }, |
| 36 | mergeCapabilities: { merge: true, squash: false, rebase: false }, |
| 37 | }, |
| 38 | dataUpdatedAt: 1, |
| 39 | isPending: false, |
| 40 | refresh: vi.fn(), |
| 41 | }), |
| 42 | })); |
| 43 | vi.mock("../pullRequest/usePullRequestActions", () => ({ |
| 44 | usePullRequestActionRunner: () => ({ actionPending: false, perform: state.perform }), |
| 45 | usePullRequestHandoffs: () => ({ handoff: null, startHandoff: vi.fn() }), |
| 46 | })); |
| 47 | vi.mock("../ui/popover", () => ({ |
| 48 | Popover: ({ children }: { children: ReactNode }) => children, |
| 49 | PopoverTrigger: ({ render, children }: { render: ReactElement; children: ReactNode }) => |
| 50 | cloneElement(render, undefined, children), |
| 51 | PopoverPopup: () => null, |
| 52 | })); |
| 53 | vi.mock("../ui/tooltip", () => ({ |
| 54 | Tooltip: ({ children }: { children: ReactNode }) => children, |
| 55 | TooltipTrigger: ({ render, children }: { render: ReactElement; children: ReactNode }) => |
| 56 | cloneElement(render, undefined, children), |
| 57 | TooltipPopup: () => null, |
| 58 | })); |
| 59 | vi.mock("../ui/alert-dialog", () => ({ |
| 60 | AlertDialog: ({ open, children }: { open: boolean; children: ReactNode }) => |
| 61 | open ? <div role="alertdialog">{children}</div> : null, |
| 62 | AlertDialogPopup: ({ children }: { children: ReactNode }) => children, |
| 63 | AlertDialogHeader: ({ children }: { children: ReactNode }) => children, |
| 64 | AlertDialogTitle: ({ children }: { children: ReactNode }) => <h2>{children}</h2>, |
| 65 | AlertDialogDescription: ({ children }: { children: ReactNode }) => children, |
| 66 | AlertDialogFooter: ({ children }: { children: ReactNode }) => children, |
| 67 | AlertDialogClose: () => null, |
| 68 | })); |
| 69 | |
| 70 | import { ThreadDetailsPrRow } from "./ThreadDetailsPrRow"; |
| 71 | |
| 72 | let renderer: ReactTestRenderer; |
| 73 | afterEach(() => { |
| 74 | act(() => renderer?.unmount()); |
| 75 | vi.unstubAllGlobals(); |
| 76 | state.status = "success"; |
| 77 | state.extraStatus = null; |
| 78 | }); |
| 79 | |
| 80 | it("requires a new merge click after passing checks become pending and pass again", () => { |
| 81 | vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); |
| 82 | const render = () => ( |
| 83 | <ThreadDetailsPrRow |
| 84 | environmentId={EnvironmentId.make("environment")} |
| 85 | pr={null} |
| 86 | number={1} |
| 87 | status={null} |
| 88 | project={null} |
| 89 | label="Test PR" |
| 90 | openAriaLabel="Open PR" |
| 91 | onOpen={vi.fn()} |
| 92 | /> |
| 93 | ); |
| 94 | const clickMerge = () => |
| 95 | act(() => { |
| 96 | renderer.root |
| 97 | .findAllByType("button") |
| 98 | .find((button) => button.children.includes("Merge"))! |
| 99 | .props.onClick(); |
| 100 | }); |
| 101 | const dialogs = () => renderer.root.findAllByProps({ role: "alertdialog" }); |
| 102 | |
| 103 | act(() => { |
| 104 | renderer = create(render()); |
| 105 | }); |
| 106 | clickMerge(); |
| 107 | expect(dialogs()).toHaveLength(1); |
| 108 | act(() => { |
| 109 | state.status = "pending"; |
| 110 | renderer.update(render()); |
| 111 | }); |
| 112 | expect(dialogs()).toHaveLength(0); |
| 113 | act(() => { |
| 114 | state.status = "success"; |
| 115 | renderer.update(render()); |
| 116 | }); |
| 117 | expect(dialogs()).toHaveLength(0); |
| 118 | clickMerge(); |
| 119 | expect(dialogs()).toHaveLength(1); |
| 120 | expect(state.perform).not.toHaveBeenCalled(); |
| 121 | }); |
| 122 | |
| 123 | it.each<[PullRequestCheck["status"], PullRequestCheck["status"], string]>([ |
| 124 | ["success", "skipped", ""], |
| 125 | ["failure", "cancelled", ""], |
| 126 | ["success", "action-required", ""], |
| 127 | ["success", "pending", "1/2"], |
| 128 | ["failure", "pending", "1/2"], |
| 129 | ])("shows a count only while checks run (%s, %s)", (status, extraStatus, count) => { |
| 130 | vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); |
| 131 | state.status = status; |
| 132 | state.extraStatus = extraStatus; |
| 133 | act(() => { |
| 134 | renderer = create( |
| 135 | <ThreadDetailsPrRow |
| 136 | environmentId={EnvironmentId.make("environment")} |
| 137 | pr={null} |
| 138 | number={1} |
| 139 | status={null} |
| 140 | project={null} |
| 141 | label="Test PR" |
| 142 | openAriaLabel="Open PR" |
| 143 | onOpen={vi.fn()} |
| 144 | />, |
| 145 | ); |
| 146 | }); |
| 147 | const text = renderer.root |
| 148 | .findAllByType("span") |
| 149 | .map((span) => span.children.filter((child) => typeof child === "string").join("")); |
| 150 | expect(text.filter((value) => /^\d+\/\d+$/.test(value))).toEqual(count ? [count] : []); |
| 151 | }); |