import React from "react"; import { afterEach, beforeEach, describe, it, expect, vi } from "vitest"; import { renderWithProviders as render, screen, waitFor, } from "../utils/test-utils"; import userEvent from "@testing-library/user-event"; import "@testing-library/jest-dom/vitest"; import CreateFlowTopNav from "../../app/components/navigation/CreateFlowTopNav"; import { componentTestSuite, ComponentTestSuiteConfig, } from "../utils/componentTestSuite"; // Mock next/navigation vi.mock("next/navigation", () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn(), prefetch: vi.fn(), back: vi.fn(), forward: vi.fn(), refresh: vi.fn(), }), })); type CreateFlowTopNavProps = React.ComponentProps; const baseProps: CreateFlowTopNavProps = {}; const config: ComponentTestSuiteConfig = { component: CreateFlowTopNav, name: "CreateFlowTopNav", props: baseProps, requiredProps: [], optionalProps: { hasShare: true, hasExport: true, hasEdit: true, saveDraftOnExit: true, onShare: vi.fn(), onSelectExportFormat: vi.fn(), onEdit: vi.fn(), hasManageStakeholders: true, onManageStakeholders: vi.fn(), onExit: vi.fn(), className: "test-class", }, primaryRole: "banner", testCases: { renders: true, accessibility: true, keyboardNavigation: false, disabledState: false, errorState: false, }, }; componentTestSuite(config); describe("CreateFlowTopNav (behavioral tests)", () => { it("renders Exit button by default", () => { render(); const exitButton = screen.getByRole("button", { name: "Exit" }); expect(exitButton).toBeInTheDocument(); }); it("shows Save & Exit when saveDraftOnExit is true", () => { render(); const exitButton = screen.getByRole("button", { name: "Save & Exit" }); expect(exitButton).toBeInTheDocument(); }); it.each([ ["Download Markdown", "markdown"], ["Download PDF", "pdf"], ["Download CSV", "csv"], ] as const)( "opens export menu and calls onSelectExportFormat for %s", async (menuLabel, expectedFormat) => { const user = userEvent.setup(); const handleExport = vi.fn(); render( , ); const exportButton = screen.getByRole("button", { name: "Export" }); await user.click(exportButton); const item = screen.getByRole("menuitem", { name: menuLabel }); await user.click(item); expect(handleExport).toHaveBeenCalledWith(expectedFormat); }, ); it("renders Share button when hasShare is true", () => { render(); const shareButton = screen.getByRole("button", { name: "Share" }); expect(shareButton).toBeInTheDocument(); }); it("does not render Share button when hasShare is false", () => { render(); expect( screen.queryByRole("button", { name: "Share" }), ).not.toBeInTheDocument(); }); it("renders Export button when hasExport is true", () => { render( , ); const exportButton = screen.getByRole("button", { name: "Export" }); expect(exportButton).toBeInTheDocument(); }); it("renders Edit button when hasEdit is true", () => { render(); const editButton = screen.getByRole("button", { name: "Edit" }); expect(editButton).toBeInTheDocument(); }); it("renders Manage Stakeholders when hasManageStakeholders is true", () => { render( , ); expect( screen.getByRole("button", { name: "Manage Stakeholders" }), ).toBeInTheDocument(); }); it("calls onManageStakeholders when Manage Stakeholders is clicked", async () => { const user = userEvent.setup(); const handler = vi.fn(); render( , ); await user.click( screen.getByRole("button", { name: "Manage Stakeholders" }), ); expect(handler).toHaveBeenCalledTimes(1); }); it("renders Duplicate button when hasDuplicate is true", () => { render( , ); expect( screen.getByRole("button", { name: "Duplicate" }), ).toBeInTheDocument(); }); it("uses exitLabel override when provided", () => { render(); expect(screen.getByRole("button", { name: "Return" })).toBeInTheDocument(); }); it("calls onExit when Exit button is clicked", async () => { const user = userEvent.setup(); const handleExit = vi.fn(); render(); const exitButton = screen.getByRole("button", { name: "Exit" }); await user.click(exitButton); expect(handleExit).toHaveBeenCalledTimes(1); }); }); describe("CreateFlowTopNav (viewport < sm2 / 440px)", () => { const defaultInnerWidth = 1200; beforeEach(() => { Object.defineProperty(window, "innerWidth", { writable: true, configurable: true, value: 320, }); }); afterEach(() => { Object.defineProperty(window, "innerWidth", { writable: true, configurable: true, value: defaultInnerWidth, }); }); const completedScreenProps = { hasShare: true, hasExport: true, hasEdit: true, saveDraftOnExit: true, onShare: vi.fn(), onSelectExportFormat: vi.fn(), onEdit: vi.fn(), onExit: vi.fn(), } as const; it("collapses secondary actions into a kebab menu", async () => { render(); await waitFor(() => { expect( screen.getByRole("button", { name: "More options" }), ).toBeInTheDocument(); }); expect( screen.queryByRole("button", { name: "Share" }), ).not.toBeInTheDocument(); expect( screen.queryByRole("button", { name: "Export" }), ).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Edit" })).not.toBeInTheDocument(); expect( screen.queryByRole("button", { name: "Save & Exit" }), ).not.toBeInTheDocument(); }); it("opens kebab menu with share, export formats, edit, and save & exit", async () => { const user = userEvent.setup(); render(); const kebab = await screen.findByRole("button", { name: "More options" }); await user.click(kebab); expect(screen.getByRole("menuitem", { name: "Share" })).toBeInTheDocument(); expect( screen.getByRole("menuitem", { name: "Download PDF" }), ).toBeInTheDocument(); expect( screen.getByRole("menuitem", { name: "Download CSV" }), ).toBeInTheDocument(); expect( screen.getByRole("menuitem", { name: "Download Markdown" }), ).toBeInTheDocument(); expect(screen.getByRole("menuitem", { name: "Edit" })).toBeInTheDocument(); expect( screen.getByRole("menuitem", { name: "Save & Exit" }), ).toBeInTheDocument(); }); it("invokes handlers from kebab menu items", async () => { const user = userEvent.setup(); const handleShare = vi.fn(); const handleEdit = vi.fn(); const handleExit = vi.fn(); render( , ); const kebab = await screen.findByRole("button", { name: "More options" }); await user.click(kebab); await user.click(screen.getByRole("menuitem", { name: "Share" })); expect(handleShare).toHaveBeenCalledTimes(1); await user.click(kebab); await user.click(screen.getByRole("menuitem", { name: "Edit" })); expect(handleEdit).toHaveBeenCalledTimes(1); await user.click(kebab); await user.click(screen.getByRole("menuitem", { name: "Save & Exit" })); expect(handleExit).toHaveBeenCalledTimes(1); }); });