78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
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(
|
|
<QueryClientProvider client={createQueryClient()}>
|
|
<ConfigLockIndicator />
|
|
</QueryClientProvider>,
|
|
);
|
|
}
|
|
|
|
test("a definition names the value in words and the file key beside it", () => {
|
|
render(
|
|
<DefinitionList
|
|
items={[
|
|
{ label: "Port", zonKey: "dns.port", value: "53" },
|
|
{ label: "Authentication", value: "required" },
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
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);
|
|
|
|
const lock = await screen.findByText("Locked");
|
|
await waitFor(() =>
|
|
expect(lock.getAttribute("aria-label")).toBe(
|
|
`Locked. Managed by ${CONFIG_PATH}; edit the file and restart nxdns.`,
|
|
),
|
|
);
|
|
});
|
|
|
|
test("an unanswered status still locks, and says that is why", async () => {
|
|
renderIndicator("failed");
|
|
|
|
const lock = await screen.findByText("Locked");
|
|
await waitFor(() => expect(lock.getAttribute("aria-label")).toContain("Configuration status unavailable"));
|
|
});
|