/**
* @file TerminalPanel.test.tsx
* @description Unit tests for the TerminalPanel component
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import type { ReactElement } from "react";
// ==========================================
// Mocks
// ==========================================
// Mock XTerminal to avoid xterm.js DOM dependencies in panel tests
vi.mock("./XTerminal", () => ({
XTerminal: vi.fn(({ token, isVisible }: { token: string; isVisible: boolean }) => (
)),
}));
import { TerminalPanel } from "./TerminalPanel";
// ==========================================
// Tests
// ==========================================
describe("TerminalPanel", () => {
const onClose = vi.fn();
const onTabChange = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
// ==========================================
// Rendering
// ==========================================
describe("rendering", () => {
it("renders the terminal panel", () => {
render(() as ReactElement);
expect(screen.getByRole("region", { name: "Terminal panel" })).toBeInTheDocument();
});
it("renders with height 280 when open", () => {
render(() as ReactElement);
const panel = screen.getByRole("region", { name: "Terminal panel" });
expect(panel).toHaveStyle({ height: "280px" });
});
it("renders with height 0 when closed", () => {
const { container } = render(
() as ReactElement
);
const panel = container.querySelector('[role="region"][aria-label="Terminal panel"]');
expect(panel).toHaveStyle({ height: "0px" });
});
it("passes isVisible=true to XTerminal when open", () => {
render(() as ReactElement);
const xterm = screen.getByTestId("mock-xterminal");
expect(xterm).toHaveAttribute("data-visible", "true");
});
it("passes isVisible=false to XTerminal when closed", () => {
const { container } = render(
() as ReactElement
);
// Use container query since the element is inside an aria-hidden region
const xterm = container.querySelector('[data-testid="mock-xterminal"]');
expect(xterm).toHaveAttribute("data-visible", "false");
});
it("passes token to XTerminal", () => {
render(
() as ReactElement
);
const xterm = screen.getByTestId("mock-xterminal");
expect(xterm).toHaveAttribute("data-token", "my-auth-token");
});
});
// ==========================================
// Tab bar
// ==========================================
describe("tab bar", () => {
it("renders default tabs when none provided", () => {
render(() as ReactElement);
expect(screen.getByRole("tab", { name: "main" })).toBeInTheDocument();
});
it("renders custom tabs", () => {
const tabs = [
{ id: "tab1", label: "Terminal 1" },
{ id: "tab2", label: "Terminal 2" },
];
render(
(
) as ReactElement
);
expect(screen.getByRole("tab", { name: "Terminal 1" })).toBeInTheDocument();
expect(screen.getByRole("tab", { name: "Terminal 2" })).toBeInTheDocument();
});
it("marks the active tab as selected", () => {
const tabs = [
{ id: "tab1", label: "Tab 1" },
{ id: "tab2", label: "Tab 2" },
];
render(
(
) as ReactElement
);
const tab2 = screen.getByRole("tab", { name: "Tab 2" });
expect(tab2).toHaveAttribute("aria-selected", "true");
});
it("calls onTabChange when a tab is clicked", () => {
const tabs = [
{ id: "tab1", label: "Tab 1" },
{ id: "tab2", label: "Tab 2" },
];
render(
(
) as ReactElement
);
fireEvent.click(screen.getByRole("tab", { name: "Tab 2" }));
expect(onTabChange).toHaveBeenCalledWith("tab2");
});
});
// ==========================================
// Close button
// ==========================================
describe("close button", () => {
it("renders the close button", () => {
render(() as ReactElement);
expect(screen.getByRole("button", { name: "Close terminal" })).toBeInTheDocument();
});
it("calls onClose when close button is clicked", () => {
render(() as ReactElement);
fireEvent.click(screen.getByRole("button", { name: "Close terminal" }));
expect(onClose).toHaveBeenCalledTimes(1);
});
});
// ==========================================
// Accessibility
// ==========================================
describe("accessibility", () => {
it("has aria-hidden=true when closed", () => {
const { container } = render(
() as ReactElement
);
// When aria-hidden=true, testing-library role queries ignore the element's aria-label.
// Use a direct DOM query to verify the attribute.
const panel = container.querySelector('[role="region"][aria-label="Terminal panel"]');
expect(panel).toHaveAttribute("aria-hidden", "true");
});
it("has aria-hidden=false when open", () => {
render(() as ReactElement);
const panel = screen.getByRole("region", { name: "Terminal panel" });
expect(panel).toHaveAttribute("aria-hidden", "false");
});
it("has tablist role on the tab bar", () => {
render(() as ReactElement);
expect(screen.getByRole("tablist")).toBeInTheDocument();
});
});
});