import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { Dashboard } from "../Dashboard"; import type { DashboardShortcut } from "../../types"; // Stub the composed widgets so the test exercises Dashboard's own behavior // (shortcut CRUD) without rendering widgets or their data queries. vi.mock("../../components/WidgetInstance", () => ({ WidgetInstanceCard: () =>
, })); vi.mock("../../components/WidgetConfigDialog", () => ({ WidgetConfigDialog: () =>
, })); const navigate = vi.fn(); vi.mock("react-router-dom", () => ({ useNavigate: () => navigate, })); vi.mock("../../hooks/useSettings", () => ({ useMonitoringSettings: () => ({ data: [] }), })); vi.mock("../../hooks/useWidgets", () => ({ useWidgetInstances: () => ({ data: [] }), })); vi.mock("../../hooks/useServices", () => ({ useServiceInstances: () => ({ data: [] }), })); const saveShortcutMutate = vi.fn().mockResolvedValue({}); const deleteShortcutMutate = vi.fn(); let shortcuts: DashboardShortcut[] = []; vi.mock("../../hooks/useDashboard", () => ({ useActivity: () => ({ data: undefined }), useDashboardShortcuts: () => ({ data: shortcuts }), useSaveDashboardShortcut: () => ({ mutateAsync: saveShortcutMutate }), useDeleteDashboardShortcut: () => ({ mutate: deleteShortcutMutate }), })); function websiteShortcut( overrides: Partial = {}, ): DashboardShortcut { return { id: "s1", label: "Wiki", shortcut_type: "website", enabled: true, icon: "📚", url: "example.com", task_id: "", machine_id: "", user_id: "", notes: "Team wiki", created_at: 0, updated_at: 0, ...overrides, } as DashboardShortcut; } beforeEach(() => { navigate.mockReset(); saveShortcutMutate.mockClear(); deleteShortcutMutate.mockClear(); shortcuts = []; }); describe("Dashboard", () => { it("shows the empty-state alert when there are no shortcuts", () => { render(); expect(screen.getByText(/No shortcuts yet/)).toBeInTheDocument(); expect( screen.getByRole("button", { name: "Add shortcut" }), ).toBeInTheDocument(); }); it("renders a shortcut card and deletes it via the confirm dialog", async () => { shortcuts = [websiteShortcut()]; render(); expect(screen.getByText("Wiki")).toBeInTheDocument(); // Open the delete confirm. await userEvent.click(screen.getByRole("button", { name: "Delete" })); expect(screen.getByText("Delete shortcut?")).toBeInTheDocument(); // Confirm deletion -> delete mutation fires with the shortcut id. const dialogs = screen.getAllByRole("button", { name: "Delete" }); // The card "Delete" plus the confirm "Delete"; confirm is the last one. await userEvent.click(dialogs[dialogs.length - 1]); expect(deleteShortcutMutate).toHaveBeenCalledTimes(1); expect(deleteShortcutMutate).toHaveBeenCalledWith("s1"); }); it("creates a shortcut via the dialog and saves it", async () => { render(); await userEvent.click(screen.getByRole("button", { name: "Add shortcut" })); // Edit dialog opens in "New shortcut" mode. expect(screen.getByText("New shortcut")).toBeInTheDocument(); // Fill the label and save. await userEvent.type(screen.getByLabelText("Label"), "Grafana"); await userEvent.click( screen.getByRole("button", { name: "Save shortcut" }), ); expect(saveShortcutMutate).toHaveBeenCalledTimes(1); const saved = saveShortcutMutate.mock.calls[0][0]; expect(saved.label).toBe("Grafana"); expect(saved.shortcut_type).toBe("website"); }); });