import { LexicalComposer } from "@lexical/react/LexicalComposer"; import { ContentEditable } from "@lexical/react/LexicalContentEditable"; import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary"; import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, it, expect, vi, beforeEach } from "vitest"; import ToolbarPlugin from "./ToolbarPlugin"; // Mocks vi.mock("../../icon", () => ({ Icon: ({ name }: { name: string }) => {name}, })); vi.mock("../../button", () => ({ Button: ({ children, onClick, StartIcon }: any) => ( ), })); vi.mock("../../dropdown", () => ({ Dropdown: ({ children }: any) =>
{children}
, DropdownMenuTrigger: ({ children }: any) =>
{children}
, DropdownMenuContent: ({ children }: any) =>
{children}
, DropdownMenuItem: ({ children }: any) =>
{children}
, })); vi.mock("./AddVariablesDropdown", () => ({ AddVariablesDropdown: () =>
AddVariablesDropdown
, })); const initialConfig = { namespace: "MyEditor", theme: {}, onError: (error: Error) => console.error(error), }; const TestWrapper = ({ children }: { children: string | JSX.Element }) => ( } placeholder={
Enter some text...
} ErrorBoundary={LexicalErrorBoundary} /> {children}
); describe("ToolbarPlugin", () => { const defaultProps = { editable: true, getText: vi.fn(() => ""), setText: vi.fn(), firstRender: true, setFirstRender: vi.fn(), updateTemplate: false, }; beforeEach(() => { vi.clearAllMocks(); }); it("renders correctly with default props", () => { render( ); expect(screen.getByTestId("dropdown")).toBeDefined(); expect(screen.getByTestId("button-bold")).toBeDefined(); expect(screen.getByTestId("button-italic")).toBeDefined(); expect(screen.getByTestId("button-link")).toBeDefined(); }); it("does not render when editable is false", () => { render( ); expect(screen.queryByTestId("dropdown")).toBeNull(); }); it("renders variables dropdown when variables prop is provided", () => { render( ); expect(screen.getByTestId("add-variables-dropdown")).toBeDefined(); }); it("excludes toolbar items when specified", () => { render( ); expect(screen.queryByTestId("button-bold")).toBeNull(); expect(screen.queryByTestId("button-italic")).toBeNull(); expect(screen.getByTestId("button-link")).toBeDefined(); }); it("changes block type when dropdown item is clicked", async () => { render( ); fireEvent.click(screen.getByTestId("dropdown-trigger")); await waitFor(() => { expect(screen.getByTestId("dropdown-content")).toBeDefined(); }); const h1Button = screen.getByText("Large Heading"); fireEvent.click(h1Button); }); it("toggles bold formatting when bold button is clicked", () => { render( ); const boldButton = screen.getByTestId("button-bold"); fireEvent.click(boldButton); }); it("toggles italic formatting when italic button is clicked", () => { render( ); const italicButton = screen.getByTestId("button-italic"); fireEvent.click(italicButton); }); it("toggles link when link button is clicked", async () => { render( ); const linkButton = screen.getByTestId("button-link"); fireEvent.click(linkButton); }); // Additional tests it("calls setText when editor content changes", async () => { render( ); const editableContent = screen.getByRole("textbox"); await userEvent.type(editableContent, "Hello, World!"); expect(defaultProps.setText).toHaveBeenCalled(); }); it("updates editor content when updateTemplate prop changes", async () => { const getText = vi.fn(() => "

Initial content

"); const setText = vi.fn(); const setFirstRender = vi.fn(); const { rerender } = render( ); // Wait for initial render await waitFor(() => { expect(setFirstRender).toHaveBeenCalledWith(false); }); // Update getText mock and trigger update getText.mockReturnValue("

Updated content

"); await act(async () => { rerender( ); }); // Wait for content update await waitFor( () => { expect(setText).toHaveBeenCalledWith(expect.stringContaining("Updated content")); }, { timeout: 5000 } ); }); it("initializes editor with provided text", async () => { const getText = vi.fn(() => "

Initial content

"); const setText = vi.fn(); const setFirstRender = vi.fn(); render( ); await waitFor( () => { expect(setFirstRender).toHaveBeenCalledWith(false); expect(setText).toHaveBeenCalledWith(expect.stringContaining("Initial content")); }, { timeout: 3000 } ); }); it("calls setText after content update", async () => { const setText = vi.fn(); await act(async () => { render( "

Test content

"} />
); }); await waitFor(() => { expect(setText).toHaveBeenCalled(); }); }); });