import { MantineProvider } from "@mantine/core"; import { render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { usePubSub } from "create-pubsub/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { menuExpandedAccordionsPubSub, showFeatureTipsPubSub, } from "@/modules/pubSub"; vi.mock("create-pubsub/react", () => ({ usePubSub: vi.fn(), })); vi.mock("./AISettings/AISettingsForm", () => ({ default: () =>
, })); vi.mock("./SearchSettingsForm", () => ({ default: () =>
, })); vi.mock("./InterfaceSettingsForm", () => ({ default: () =>
, })); vi.mock("./VoiceSettingsForm", () => ({ default: () =>
, })); vi.mock("./ActionsForm", () => ({ default: () =>
, })); vi.mock("@/components/Settings/HistorySettings", () => ({ default: () =>
, })); interface MenuState { expandedAccordions: string[]; showFeatureTips: boolean; } function createMenuState(overrides: Partial = {}): MenuState { return { expandedAccordions: [], showFeatureTips: true, ...overrides, }; } function mockMenuState(state: MenuState) { const setShowFeatureTips = vi.fn(); vi.mocked(usePubSub).mockImplementation((pubSub: unknown) => { if (pubSub === menuExpandedAccordionsPubSub) return [state.expandedAccordions, vi.fn()]; if (pubSub === showFeatureTipsPubSub) return [state.showFeatureTips, setShowFeatureTips]; throw new Error("MenuDrawer.test.tsx: unexpected pubSub in usePubSub mock"); }); return { setShowFeatureTips }; } async function renderMenuDrawer(state: MenuState) { const handlers = mockMenuState(state); const MenuDrawer = (await import("./MenuDrawer")).default; const utils = render( , ); return { ...utils, ...handlers }; } describe("MenuDrawer", () => { beforeEach(() => { vi.clearAllMocks(); }); it("renders a description under each accordion control", async () => { await renderMenuDrawer(createMenuState()); const sections: Array<[title: string, description: string]> = [ ["AI Settings", "AI responses, where inference runs, and reasoning"], ["Search Settings", "Text and image results, and how many to show"], ["Interface Settings", "Appearance and search-box input"], ["History Settings", "How long and how many searches to keep"], ["Voice Settings", "The voice used when reading answers aloud"], ["Actions", "Clear stored data and view the log"], ]; // Scoped to the control so the test fails if a description moves into the // (keepMounted) panel, where it would no longer be visible while collapsed. for (const [title, description] of sections) { const control = screen.getByRole("button", { name: new RegExp(title) }); expect(within(control).getByText(description)).toBeInTheDocument(); } }); it("shows the tips with the search URL when enabled", async () => { await renderMenuDrawer(createMenuState({ showFeatureTips: true })); expect(screen.getByText("Tips")).toBeInTheDocument(); expect(screen.getByText(/Search from anywhere/)).toBeInTheDocument(); expect(screen.getByText(/\/\?q=%s/)).toBeInTheDocument(); expect( screen.getByText(/use the speaker button on an answer/), ).toBeInTheDocument(); }); it("hides the tips when showFeatureTips is false", async () => { await renderMenuDrawer(createMenuState({ showFeatureTips: false })); expect(screen.queryByText("Tips")).not.toBeInTheDocument(); }); it("persists the dismissal when the tips are closed", async () => { const user = userEvent.setup(); const { setShowFeatureTips } = await renderMenuDrawer( createMenuState({ showFeatureTips: true }), ); await user.click(screen.getByRole("button", { name: "Dismiss tips" })); expect(setShowFeatureTips).toHaveBeenCalledWith(false); }); });