From 94fe397b6be4b563511bf189d32e2e21f8807ba2 Mon Sep 17 00:00:00 2001
From: brain-marchine <152466993+brain-marchine@users.noreply.github.com>
Date: Sun, 12 Jul 2026 16:56:41 +0800
Subject: [PATCH] test: cover admin pause/unpause toggle flow
Fixes #18
---
src/app/admin/page.test.tsx | 116 ++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 src/app/admin/page.test.tsx
diff --git a/src/app/admin/page.test.tsx b/src/app/admin/page.test.tsx
new file mode 100644
index 0000000..2ff725e
--- /dev/null
+++ b/src/app/admin/page.test.tsx
@@ -0,0 +1,116 @@
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import AdminPage from "./page";
+
+const mockFetch = (data: unknown) => {
+ global.fetch = jest.fn().mockResolvedValue({
+ ok: true,
+ text: () => Promise.resolve(JSON.stringify(data)),
+ } as unknown as Response);
+};
+
+afterEach(() => {
+ jest.restoreAllMocks();
+});
+
+describe("AdminPage semantics", () => {
+ it("exposes one main landmark and one page heading", async () => {
+ mockFetch({ paused: false });
+ render();
+
+ expect(document.querySelectorAll("#main-content")).toHaveLength(1);
+ expect(screen.getAllByRole("heading", { level: 1, name: /admin/i })).toHaveLength(1);
+ await screen.findByText("Live");
+ });
+
+ it("names the pause status panel with an accessible region", async () => {
+ mockFetch({ paused: true });
+ render();
+
+ await waitFor(() => {
+ expect(
+ screen.getByRole("region", { name: /router pause status/i }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it("reflects paused state with aria-pressed on the toggle", async () => {
+ mockFetch({ paused: true });
+ render();
+
+ const toggle = await screen.findByRole("button", { name: /unpause/i });
+ expect(toggle).toHaveAttribute("aria-pressed", "true");
+ });
+
+ it("reflects live state with aria-pressed false", async () => {
+ mockFetch({ paused: false });
+ render();
+
+ const toggle = await screen.findByRole("button", { name: /^pause$/i });
+ expect(toggle).toHaveAttribute("aria-pressed", "false");
+ });
+
+ it("marks the toggle busy and disabled while the request is in flight", async () => {
+ let resolvePost: (() => void) | undefined;
+ const fetchMock = jest
+ .fn()
+ .mockResolvedValueOnce({
+ ok: true,
+ text: () => Promise.resolve(JSON.stringify({ paused: false })),
+ } as unknown as Response)
+ .mockImplementationOnce(
+ () =>
+ new Promise((resolve) => {
+ resolvePost = () =>
+ resolve({
+ ok: true,
+ text: () => Promise.resolve("{}"),
+ } as unknown as Response);
+ }),
+ )
+ .mockResolvedValue({
+ ok: true,
+ text: () => Promise.resolve(JSON.stringify({ paused: true })),
+ } as unknown as Response);
+ global.fetch = fetchMock as unknown as typeof global.fetch;
+
+ render();
+ const toggle = await screen.findByRole("button", { name: /^pause$/i });
+ fireEvent.click(toggle);
+
+ await waitFor(() => {
+ expect(toggle).toHaveAttribute("aria-busy", "true");
+ expect(toggle).toBeDisabled();
+ expect(toggle).toHaveTextContent(/updating/i);
+ });
+
+ resolvePost?.();
+ await waitFor(() => {
+ expect(toggle).toHaveAttribute("aria-busy", "false");
+ expect(toggle).not.toBeDisabled();
+ });
+ });
+
+ it("re-enables the toggle after a failed request", async () => {
+ global.fetch = jest
+ .fn()
+ .mockResolvedValueOnce({
+ ok: true,
+ text: () => Promise.resolve(JSON.stringify({ paused: false })),
+ } as unknown as Response)
+ .mockResolvedValueOnce({
+ ok: false,
+ text: () =>
+ Promise.resolve(
+ JSON.stringify({ error: "server_error", message: "Pause failed" }),
+ ),
+ } as unknown as Response) as unknown as typeof global.fetch;
+
+ render();
+ const toggle = await screen.findByRole("button", { name: /^pause$/i });
+ fireEvent.click(toggle);
+
+ await screen.findByText(/Pause failed/i);
+ expect(toggle).toHaveAttribute("aria-busy", "false");
+ expect(toggle).not.toBeDisabled();
+ });
+});