import { render, screen, waitFor } from "@testing-library/react"; import { QueryClientProvider } from "@tanstack/react-query"; import { createQueryClient } from "@/lib/queryClient"; import DefinitionList from "@/ui/DefinitionList"; import ConfigLockIndicator from "./ConfigLockIndicator"; import { CONFIG_PATH, DATABASE, MANAGED_FILE, stubApi } from "./testFixtures"; import type { ConfigStatus } from "@/lib/types"; /** * The two shared pieces the redesign adds: the file-mode rendering of a scalar * and the compact lock a configuration control outside a configuration page * carries. */ afterEach(() => { vi.unstubAllGlobals(); }); function renderIndicator(status: ConfigStatus | "failed") { if (status === "failed") { stubApi(DATABASE, { responses: { "GET /api/config/status": new Response(JSON.stringify({ error: "unavailable" }), { status: 503 }), }, }); } else { stubApi(status); } render( , ); } test("a definition names the value in words and the file key beside it", () => { render( , ); const port = screen.getByText("Port"); expect(port.tagName).toBe("DT"); expect(port.textContent).toBe("Portdns.port"); // A derived value has no key: inventing one would send the reader looking // for a line that is not in the file. expect(screen.getByText("Authentication").textContent).toBe("Authentication"); expect(screen.getByText("required").tagName).toBe("DD"); }); test("the lock is silent when the database owns the configuration", async () => { renderIndicator(DATABASE); await waitFor(() => expect(screen.queryByText("Locked")).toBeNull()); }); test("under file authority the lock names the file, in words a reader hears", async () => { renderIndicator(MANAGED_FILE); // The reason is visible text beside the tag, not a title and not a label: a // tooltip reaches neither a keyboard nor a touch screen, and screen-reader-only // text is the same failure pointed the other way. const lock = await screen.findByText("Locked"); await waitFor(() => expect(screen.getByText(`Managed by ${CONFIG_PATH}; edit the file and restart nxdns`)).toBeTruthy(), ); expect(lock.getAttribute("title")).toBeNull(); expect(lock.getAttribute("aria-label")).toBeNull(); }); test("an unanswered status still locks, and says that is why", async () => { renderIndicator("failed"); await screen.findByText("Locked"); await waitFor(() => expect(screen.getByText(/^Configuration status unavailable/)).toBeTruthy()); });