import React from "react"; import { render, screen, fireEvent } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { expect, test, describe, it, vi, beforeEach } from "vitest"; import { axe, toHaveNoViolations } from "jest-axe"; import ContextMenu from "../../app/components/ContextMenu"; import ContextMenuItem from "../../app/components/ContextMenuItem"; import ContextMenuSection from "../../app/components/ContextMenuSection"; import ContextMenuDivider from "../../app/components/ContextMenuDivider"; expect.extend(toHaveNoViolations); describe("ContextMenu Component", () => { const defaultProps = { children: "Context Menu Content", }; describe("Rendering", () => { it("renders with default props", () => { render(); expect(screen.getByText("Context Menu Content")).toBeInTheDocument(); }); it("renders with custom className", () => { render(); const menu = screen.getByText("Context Menu Content").closest("div"); expect(menu).toHaveClass("custom-class"); }); it("applies correct base styles", () => { render(); const menu = screen.getByText("Context Menu Content").closest("div"); expect(menu).toHaveClass( "bg-black", "border", "rounded-[var(--measures-radius-medium)]", "shadow-lg", "p-[4px]", ); }); it("has solid black background", () => { render(); const menu = screen.getByText("Context Menu Content").closest("div"); expect(menu).toHaveStyle({ backgroundColor: "#000000" }); }); }); describe("Accessibility", () => { it("has no accessibility violations", async () => { const { container } = render( Menu Item , ); const results = await axe(container); expect(results).toHaveNoViolations(); }); it("has proper role", () => { render(); const menu = screen.getByText("Context Menu Content").closest("div"); expect(menu).toHaveAttribute("role", "menu"); }); }); }); describe("ContextMenuItem Component", () => { const defaultProps = { children: "Menu Item", onClick: vi.fn(), }; beforeEach(() => { vi.clearAllMocks(); }); describe("Rendering", () => { it("renders with default props", () => { render(); expect(screen.getByText("Menu Item")).toBeInTheDocument(); }); it("renders as selected when selected prop is true", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass( "bg-[var(--color-surface-default-secondary)]", "rounded-[var(--measures-radius-small)]", ); }); it("renders with submenu arrow when hasSubmenu prop is true", () => { render(); // Check for the right-pointing chevron SVG const item = screen.getByRole("menuitem"); const svg = item.querySelector("svg:last-child"); expect(svg).toBeInTheDocument(); }); it("renders with checkmark when selected prop is true", () => { render(); // Check for the checkmark SVG const item = screen.getByRole("menuitem"); const svg = item.querySelector("svg:first-child"); expect(svg).toBeInTheDocument(); }); it("applies correct size styles", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass("text-[10px]", "leading-[14px]"); }); it("applies medium size styles", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass("text-[14px]", "leading-[20px]"); }); it("applies large size styles", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass("text-[16px]", "leading-[24px]"); }); }); describe("Interaction", () => { it("calls onClick when clicked", async () => { const user = userEvent.setup(); render(); const item = screen.getByText("Menu Item"); await user.click(item); expect(defaultProps.onClick).toHaveBeenCalledTimes(1); }); it("does not call onClick when disabled", async () => { const user = userEvent.setup(); render(); const item = screen.getByText("Menu Item"); await user.click(item); expect(defaultProps.onClick).not.toHaveBeenCalled(); }); it("has hover effects", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass( "hover:!bg-[var(--color-surface-default-secondary)]", ); }); }); describe("Accessibility", () => { it("has no accessibility violations", async () => { const { container } = render( , ); const results = await axe(container); expect(results).toHaveNoViolations(); }); it("has proper role", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toBeInTheDocument(); }); }); describe("Styling", () => { it("applies correct text color", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass( "text-[var(--color-content-default-brand-primary)]", ); }); it("applies correct padding", () => { render(); const item = screen.getByRole("menuitem"); expect(item).toHaveClass("px-[8px]", "py-[4px]"); }); it("applies correct gap between checkmark and text", () => { render(); const item = screen.getByText("Menu Item").closest("div"); expect(item).toHaveClass("gap-[8px]"); }); }); }); describe("ContextMenuSection Component", () => { const defaultProps = { title: "Section Title", children: "Section Content", }; describe("Rendering", () => { it("renders with title and children", () => { render(); expect(screen.getByText("Section Title")).toBeInTheDocument(); expect(screen.getByText("Section Content")).toBeInTheDocument(); }); it("renders without title when not provided", () => { render(Section Content); expect(screen.getByText("Section Content")).toBeInTheDocument(); expect(screen.queryByText("Section Title")).not.toBeInTheDocument(); }); it("applies correct title styling", () => { render(); const title = screen.getByText("Section Title"); expect(title).toHaveClass( "text-[var(--color-content-default-primary)]", "font-medium", ); }); }); describe("Accessibility", () => { it("has no accessibility violations", async () => { const { container } = render(); const results = await axe(container); expect(results).toHaveNoViolations(); }); }); }); describe("ContextMenuDivider Component", () => { describe("Rendering", () => { it("renders divider", () => { render(); const divider = screen.getByRole("separator"); expect(divider).toBeInTheDocument(); }); it("applies correct styling", () => { render(); const divider = screen.getByRole("separator"); expect(divider).toHaveClass( "border-t", "border-[var(--color-border-default-tertiary)]", "my-1", ); }); }); describe("Accessibility", () => { it("has no accessibility violations", async () => { const { container } = render(); const results = await axe(container); expect(results).toHaveNoViolations(); }); }); }); describe("ContextMenu Components Integration", () => { const TestMenu = () => ( Item 1 Item 2 Item 3 ); it("renders all components together", () => { render(); expect(screen.getByText("First Section")).toBeInTheDocument(); expect(screen.getByText("Item 1")).toBeInTheDocument(); expect(screen.getByText("Item 2")).toBeInTheDocument(); expect(screen.getByText("Second Section")).toBeInTheDocument(); expect(screen.getByText("Item 3")).toBeInTheDocument(); expect(screen.getByRole("separator")).toBeInTheDocument(); }); it("has no accessibility violations when integrated", async () => { const { container } = render(); const results = await axe(container); expect(results).toHaveNoViolations(); }); });