import { render, screen } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { DiffView } from "./DiffView" import type { ChangesetResponse } from "@/api/types" const cs: ChangesetResponse = { updates: [{ key: "A www.example.com.", kind: "update", type: "A", name: "www.example.com.", desired: ["1.1.1.1"], actual: ["9.9.9.9"], readOnly: false, custom: false }], prunes: [{ key: "A old.example.com.", kind: "delete", type: "A", name: "old.example.com.", actual: ["2.2.2.2"], readOnly: false, custom: false }], customs: [], readOnly: [{ key: "NS example.com.", kind: "update", type: "NS", name: "example.com.", desired: ["ns1."], actual: ["ns2."], readOnly: true, custom: false }], inSyncCount: 3, } function noop() { /* unused in most tests */ } function renderDiff(overrides: Partial[0]> = {}) { return render( , ) } test("renders all sections with counts", () => { renderDiff() expect(screen.getByText(/www\.example\.com\./)).toBeInTheDocument() expect(screen.getByText(/old\.example\.com\./)).toBeInTheDocument() // Anchored (vs. the brief's bare /example\.com\./) — "www.example.com." and // "old.example.com." both contain "example.com." as a trailing substring, // so the unanchored pattern matches all three rows and getByText throws // "multiple elements found". Anchoring targets the read-only apex record // specifically, which is what this assertion is actually verifying. expect(screen.getByText(/^example\.com\.$/)).toBeInTheDocument() expect(screen.getByText(/3/)).toBeInTheDocument() // in-sync count }) test("marks read-only records", () => { renderDiff() expect(screen.getByText(/NS/)).toBeInTheDocument() }) test("renders a very long unbreakable value (DKIM key) without crashing", () => { // Real DKIM records ship a ~400-char unbroken p= blob. This must not // throw and the value must land in the DOM (wrapping itself is a CSS // concern verified manually, not via jsdom layout). const longValue = "v=DKIM1; k=rsa; p=" + "A".repeat(400) const csWithDkim: ChangesetResponse = { updates: [ { key: "TXT default._domainkey.example.com.", kind: "update", type: "TXT", name: "default._domainkey.example.com.", desired: [longValue], actual: [], readOnly: false, custom: false, }, ], prunes: [], customs: [], readOnly: [], inSyncCount: 0, } renderDiff({ changeset: csWithDkim, selectedUpdates: new Set() }) expect(screen.getByText(new RegExp(longValue))).toBeInTheDocument() }) test("does not crash when changeset fields are null", () => { // An empty changeset from an older/edge backend can arrive with null slices // instead of []. DiffView must normalise them, not blow up on .length/.map. const nullish = { updates: null, prunes: null, readOnly: null, inSyncCount: 5, } as unknown as ChangesetResponse renderDiff({ changeset: nullish, selectedUpdates: new Set() }) expect(screen.getByText(/5/)).toBeInTheDocument() expect(screen.getByText(/in sync/)).toBeInTheDocument() }) test("renders a checkbox for update and prune rows but not for read-only rows", () => { renderDiff() // 2 select-all (update + prune headers) + 2 row checkboxes (one update, one prune). // Read-only section contributes none: no select-all, no row checkbox. const checkboxes = screen.getAllByRole("checkbox") expect(checkboxes).toHaveLength(4) const updateRowCheckbox = screen.getByRole("checkbox", { name: /www\.example\.com\./ }) expect(updateRowCheckbox).toBeInTheDocument() const pruneRowCheckbox = screen.getByRole("checkbox", { name: /old\.example\.com\./ }) expect(pruneRowCheckbox).toBeInTheDocument() expect(screen.queryByRole("checkbox", { name: /example\.com\..*NS|NS.*example\.com\./ })).not.toBeInTheDocument() }) test("clicking an update row checkbox calls onToggleUpdate with the record key", async () => { const onToggleUpdate = vi.fn() const user = userEvent.setup() renderDiff({ onToggleUpdate }) await user.click(screen.getByRole("checkbox", { name: /www\.example\.com\./ })) expect(onToggleUpdate).toHaveBeenCalledWith("A www.example.com.") }) test("clicking a prune row checkbox calls onTogglePrune with the record key", async () => { const onTogglePrune = vi.fn() const user = userEvent.setup() renderDiff({ onTogglePrune }) await user.click(screen.getByRole("checkbox", { name: /old\.example\.com\./ })) expect(onTogglePrune).toHaveBeenCalledWith("A old.example.com.") }) test("select-all header checkbox is checked when all rows in the section are selected", () => { renderDiff({ selectedUpdates: new Set(["A www.example.com."]) }) const selectAll = screen.getByRole("checkbox", { name: /выбрать все.*updates/i }) expect(selectAll).toHaveAttribute("aria-checked", "true") }) test("select-all header checkbox calls onToggleAllUpdates(true) when clicked while none selected", async () => { const onToggleAllUpdates = vi.fn() const user = userEvent.setup() renderDiff({ selectedUpdates: new Set(), onToggleAllUpdates }) await user.click(screen.getByRole("checkbox", { name: /выбрать все.*updates/i })) expect(onToggleAllUpdates).toHaveBeenCalledWith(true) }) test("select-all header checkbox is indeterminate when only some update rows are selected", () => { const csWithMultipleUpdates: ChangesetResponse = { updates: [ { key: "A www.example.com.", kind: "update", type: "A", name: "www.example.com.", desired: ["1.1.1.1"], actual: ["9.9.9.9"], readOnly: false, custom: false }, { key: "A api.example.com.", kind: "update", type: "A", name: "api.example.com.", desired: ["1.1.1.2"], actual: ["9.9.9.8"], readOnly: false, custom: false }, { key: "A cdn.example.com.", kind: "update", type: "A", name: "cdn.example.com.", desired: ["1.1.1.3"], actual: ["9.9.9.7"], readOnly: false, custom: false }, ], prunes: [], customs: [], readOnly: [], inSyncCount: 0, } renderDiff({ changeset: csWithMultipleUpdates, // Partial selection: one of three keys — neither all nor none — is what // must drive the header checkbox into the indeterminate ("mixed") state. selectedUpdates: new Set(["A www.example.com."]), }) const selectAll = screen.getByRole("checkbox", { name: /выбрать все.*updates/i }) expect(selectAll).toHaveAttribute("aria-checked", "mixed") })