UNPKG

orcs-design-system

Version:
342 lines (339 loc) 11.3 kB
import React from "react"; import { act } from "react"; import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import "@testing-library/jest-dom"; import CodeBlock from "./index"; import SystemThemeProvider from "../../SystemThemeProvider"; import { jsx as _jsx } from "react/jsx-runtime"; const renderWithTheme = (ui, options) => render(/*#__PURE__*/_jsx(SystemThemeProvider, { children: ui }), options); // Mock clipboard API const mockWriteText = jest.fn(); Object.assign(navigator, { clipboard: { writeText: mockWriteText } }); describe("CodeBlock", () => { beforeEach(() => { mockWriteText.mockClear(); }); describe("Basic rendering", () => { it("renders code content", () => { const code = "const hello = 'world';"; renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: code })); expect(screen.getByText(/const hello/)).toBeInTheDocument(); }); it("renders with JSON language by default for JSON content", () => { const jsonCode = JSON.stringify({ test: "data" }, null, 2); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "json", children: jsonCode })); expect(screen.getByText("json")).toBeInTheDocument(); }); it("handles string children correctly", () => { const code = "function test() { return true; }"; const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: code })); expect(container.querySelector("pre")).toBeInTheDocument(); }); }); describe("Header", () => { it("shows language label in header", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "typescript", children: "const x: number = 42;" })); expect(screen.getByText("typescript")).toBeInTheDocument(); }); it("hides header when showHeader is false", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { showHeader: false, language: "javascript", children: "console.log('test');" })); expect(screen.queryByText("javascript")).not.toBeInTheDocument(); }); it("shows header by default", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "python", children: "print('hello')" })); expect(screen.getByText("python")).toBeInTheDocument(); }); }); describe("Copy button", () => { it("renders copy button by default", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "const test = true;" })); expect(screen.getByText("Copy")).toBeInTheDocument(); }); it("hides copy button when showCopyButton is false", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { showCopyButton: false, children: "const test = true;" })); expect(screen.queryByText("Copy")).not.toBeInTheDocument(); }); it("copies code to clipboard when copy button is clicked", async () => { const code = "const hello = 'world';"; mockWriteText.mockResolvedValue(); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: code })); const copyButton = screen.getByText("Copy"); await act(async () => { fireEvent.click(copyButton); }); await waitFor(() => { expect(mockWriteText).toHaveBeenCalledWith(code); }); }); it("shows 'Copied!' text after successful copy", async () => { mockWriteText.mockResolvedValue(); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "test code" })); const copyButton = screen.getByText("Copy"); await act(async () => { fireEvent.click(copyButton); }); await waitFor(() => { expect(screen.getByText("Copied!")).toBeInTheDocument(); }); }); it("resets copy button text after timeout", async () => { jest.useFakeTimers(); mockWriteText.mockResolvedValue(); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "test code" })); const copyButton = screen.getByText("Copy"); await act(async () => { fireEvent.click(copyButton); }); await waitFor(() => { expect(screen.getByText("Copied!")).toBeInTheDocument(); }); await act(async () => { jest.advanceTimersByTime(2000); }); await waitFor(() => { expect(screen.getByText("Copy")).toBeInTheDocument(); }); jest.useRealTimers(); }); it("handles copy errors gracefully", async () => { const consoleError = jest.spyOn(console, "error").mockImplementation(); mockWriteText.mockRejectedValue(new Error("Copy failed")); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "test code" })); const copyButton = screen.getByText("Copy"); await act(async () => { fireEvent.click(copyButton); }); await waitFor(() => { expect(consoleError).toHaveBeenCalledWith("Failed to copy code:", expect.any(Error)); }); consoleError.mockRestore(); }); }); describe("Line numbers", () => { it("does not show line numbers by default", () => { const code = "line 1\nline 2\nline 3"; const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: code })); const lineNumbers = container.querySelectorAll('span[style*="width: 2em"]'); expect(lineNumbers.length).toBe(0); }); it("shows line numbers when showLineNumbers is true", () => { const code = "first\nsecond\nthird"; const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { showLineNumbers: true, children: code })); // Check for line numbers - they should be present in the rendered content // The LineNumber component renders spans with specific styling const preElement = container.querySelector("pre"); expect(preElement).not.toBeNull(); // Verify that line numbers appear (checking text content) const content = container.textContent; expect(content).toContain("1"); expect(content).toContain("2"); expect(content).toContain("3"); expect(content).toContain("first"); expect(content).toContain("second"); expect(content).toContain("third"); }); }); describe("Language support", () => { it("supports JSON language", () => { const json = JSON.stringify({ key: "value" }, null, 2); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "json", children: json })); expect(screen.getByText("json")).toBeInTheDocument(); }); it("supports JavaScript language", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "javascript", children: "const x = 42;" })); expect(screen.getByText("javascript")).toBeInTheDocument(); }); it("supports TypeScript language", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "typescript", children: "const x: number = 42;" })); expect(screen.getByText("typescript")).toBeInTheDocument(); }); it("supports Python language", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "python", children: "print('hello')" })); expect(screen.getByText("python")).toBeInTheDocument(); }); it("supports SQL language", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "sql", children: "SELECT * FROM users;" })); expect(screen.getByText("sql")).toBeInTheDocument(); }); it("supports Bash language", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { language: "bash", children: "echo \"hello\"" })); expect(screen.getByText("bash")).toBeInTheDocument(); }); }); describe("Custom styling", () => { it("applies custom maxHeight", () => { const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { maxHeight: "200px", children: "test code" })); const pre = container.querySelector("pre"); expect(pre).toHaveStyle({ maxHeight: "200px" }); }); it("uses default maxHeight of 500px when not specified", () => { const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "test code" })); const pre = container.querySelector("pre"); expect(pre).toHaveStyle({ maxHeight: "500px" }); }); it("accepts spacing props", () => { const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { mb: "r", mt: "s", children: "test code" })); const wrapper = container.firstChild; expect(wrapper).toBeInTheDocument(); }); }); describe("Accessibility", () => { it("copy button has accessible label", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "test code" })); const copyButton = screen.getByLabelText("Copy code"); expect(copyButton).toBeInTheDocument(); }); it("copy button shows 'Copied!' in label after clicking", async () => { mockWriteText.mockResolvedValue(); renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "test code" })); const copyButton = screen.getByLabelText("Copy code"); await act(async () => { fireEvent.click(copyButton); }); await waitFor(() => { expect(screen.getByLabelText("Copied!")).toBeInTheDocument(); }); }); }); describe("Edge cases", () => { it("handles empty string", () => { renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: "" })); const copyButton = screen.getByText("Copy"); expect(copyButton).toBeInTheDocument(); }); it("handles very long code", () => { const longCode = "x".repeat(10000); const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: longCode })); const pre = container.querySelector("pre"); expect(pre).toBeInTheDocument(); }); it("handles code with special characters", () => { const specialCode = 'const x = "<>&\'"'; renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: specialCode })); expect(screen.getByText(/const x/)).toBeInTheDocument(); }); it("handles multiline code", () => { const multiline = "function test() {\n return true;\n}"; renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { children: multiline })); expect(screen.getByText(/function test/)).toBeInTheDocument(); }); }); describe("Theme integration", () => { it("accepts custom theme prop", () => { const customTheme = { colors: { greyDarkest: "#000000" } }; const { container } = renderWithTheme(/*#__PURE__*/_jsx(CodeBlock, { theme: customTheme, children: "test code" })); expect(container.firstChild).toBeInTheDocument(); }); }); });