import React from "react"; import "@testing-library/jest-dom"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { api } from "./api"; import OperationsPage from "./views/OperationsPage"; jest.mock("./api", () => ({ api: { get: jest.fn(), post: jest.fn(), delete: jest.fn(), interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } }, }, getApiErrorMessage: (_error: unknown, fallback?: string) => fallback || "Request failed.", })); const mockedApi = api as jest.Mocked; const operation = { id: "11111111-1111-1111-1111-111111111111", taskType: "strategy_snapshot", status: "running", createdAtUtc: "2026-08-02T12:00:00Z", progressStage: "Analysing role", progressPercent: 40, canCancel: true, canRetry: false, }; const notification = { id: "22222222-2222-2222-2222-222222222222", operationId: operation.id, kind: "operation_failed", title: "Operation failed", message: "A background operation failed. Review it for details.", createdAtUtc: "2026-08-02T12:05:00Z", readAtUtc: null, }; beforeEach(() => { jest.clearAllMocks(); mockedApi.get.mockImplementation((url: string) => Promise.resolve({ data: url.startsWith("/operations") ? [operation] : [notification], } as any)); mockedApi.post.mockResolvedValue({ data: {} } as any); mockedApi.delete.mockResolvedValue({ data: {} } as any); }); test("shows persistent operation and notification states with accessible actions", async () => { render(); expect(await screen.findByText("strategy_snapshot")).toBeInTheDocument(); expect(screen.getByText("Analysing role")).toBeInTheDocument(); expect(screen.getByRole("progressbar", { name: "strategy_snapshot progress" })).toHaveAttribute("aria-valuenow", "40"); expect(screen.getByText("Operation failed")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Cancel" })); await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(`/operations/${operation.id}/cancel`)); const markRead = screen.getByRole("button", { name: "Mark read" }); await waitFor(() => expect(markRead).toBeEnabled()); fireEvent.click(markRead); await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(`/notifications/${notification.id}/read`)); const dismiss = screen.getByRole("button", { name: "Dismiss" }); await waitFor(() => expect(dismiss).toBeEnabled()); fireEvent.click(dismiss); await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith(`/notifications/${notification.id}`)); }); test("shows honest empty and failure states", async () => { mockedApi.get.mockResolvedValueOnce({ data: [] } as any).mockResolvedValueOnce({ data: [] } as any); const { unmount } = render(); expect(await screen.findByText("No notifications.")).toBeInTheDocument(); expect(screen.getByText("No background operations yet.")).toBeInTheDocument(); unmount(); mockedApi.get.mockRejectedValue(new Error("offline")); render(); expect(await screen.findByRole("alert")).toHaveTextContent("Operations could not be loaded."); });