/** * @fileoverview Unit Tests for PivotTable React Component * * Comprehensive test suite for the PivotTable component functionality including: * - Component rendering with various data scenarios * - Proper styling and CSS class application * - Indentation behavior for nested dimensions * - Empty data and error state handling * - Metric value formatting and display * - Column header formatting * * Uses Jest and React Testing Library for component testing. * This test focuses on component behavior rather than data transformation logic. */ import React from "react"; import { render, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; import { PivotTable, type PivotTableProps, } from "@/src/features/widgets/chart-library/PivotTable"; import { type DataPoint } from "@/src/features/widgets/chart-library/chart-props"; describe("PivotTable Component", () => { describe("Basic Rendering", () => { test("renders table with simple data", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "gpt-4", metric: 100, }, { time_dimension: "2024-01-01", dimension: "gpt-3.5", metric: 75, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model"], metrics: ["metric"], }, }; render(); // Check table structure exists expect(screen.getByRole("table")).toBeInTheDocument(); // Should have dimension column and metric column expect(screen.getAllByRole("columnheader")).toHaveLength(2); }); test("displays data when no configuration provided", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 50, }, ]; const props: PivotTableProps = { data, }; render(); // Should still render a table expect(screen.getByRole("table")).toBeInTheDocument(); }); }); describe("Column Header Formatting", () => { test("formats single dimension header correctly", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model_name"], metrics: ["request_count"], }, }; render(); expect( screen.getByRole("columnheader", { name: "Model Name" }), ).toBeInTheDocument(); expect( screen.getByRole("columnheader", { name: "Request Count" }), ).toBeInTheDocument(); }); test("formats multiple dimension headers correctly", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model_name", "time_period"], metrics: ["request_count", "avg_duration"], }, }; render(); expect( screen.getByRole("columnheader", { name: "Model Name / Time Period" }), ).toBeInTheDocument(); expect( screen.getByRole("columnheader", { name: "Request Count" }), ).toBeInTheDocument(); expect( screen.getByRole("columnheader", { name: "Avg Duration" }), ).toBeInTheDocument(); }); test("defaults to 'Dimension' when no dimensions configured", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: [], metrics: ["metric"], }, }; render(); expect( screen.getByRole("columnheader", { name: "Dimension" }), ).toBeInTheDocument(); }); }); describe("Empty Data Handling", () => { test("displays 'No data available' for empty data array", () => { const props: PivotTableProps = { data: [], config: { dimensions: ["model"], metrics: ["metric"], }, }; render(); expect(screen.getByText("No data available")).toBeInTheDocument(); expect(screen.queryByRole("table")).not.toBeInTheDocument(); }); test("displays 'No data available' for null data", () => { const props: PivotTableProps = { data: null as any, config: { dimensions: ["model"], metrics: ["metric"], }, }; render(); expect(screen.getByText("No data available")).toBeInTheDocument(); expect(screen.queryByRole("table")).not.toBeInTheDocument(); }); test("displays 'No data available' for undefined data", () => { const props: PivotTableProps = { data: undefined as any, config: { dimensions: ["model"], metrics: ["metric"], }, }; render(); expect(screen.getByText("No data available")).toBeInTheDocument(); expect(screen.queryByRole("table")).not.toBeInTheDocument(); }); }); describe("Responsive Design", () => { test("includes overflow-auto class for responsive scrolling", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model"], metrics: ["metric"], }, }; const { container } = render(); const pivotTableContainer = container.firstChild as HTMLElement; expect(pivotTableContainer).toHaveClass("h-full", "overflow-auto"); }); }); describe("Accessibility", () => { test("provides proper table structure for screen readers", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "gpt-4", metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model"], metrics: ["metric"], }, }; render(); // Check table has proper semantic structure expect(screen.getByRole("table")).toBeInTheDocument(); expect(screen.getAllByRole("columnheader")).toHaveLength(2); // Should have at least header row expect(screen.getAllByRole("row").length).toBeGreaterThanOrEqual(1); }); test("provides proper cell associations", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model"], metrics: ["metric"], }, }; render(); // Header cells should be properly associated const headers = screen.getAllByRole("columnheader"); expect(headers.length).toBeGreaterThan(0); // Each header should be part of a row headers.forEach((header) => { expect(header.closest("tr")).toBeInTheDocument(); }); }); }); describe("Component Props", () => { test("handles missing config gracefully", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, // No config provided }; render(); // Should still render a table expect(screen.getByRole("table")).toBeInTheDocument(); }); test("passes accessibilityLayer prop correctly", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test", metric: 100, }, ]; const props: PivotTableProps = { data, accessibilityLayer: true, }; // Should render without error expect(() => render()).not.toThrow(); }); }); describe("Data Processing Integration", () => { test("handles various metric data types", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: "test1", metric: 100, // number }, { time_dimension: "2024-01-01", dimension: "test2", metric: [ [10, 20], [30, 40], ], // nested array }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model"], metrics: ["metric"], }, }; // Should render without error expect(() => render()).not.toThrow(); expect(screen.getByRole("table")).toBeInTheDocument(); }); test("handles missing dimension data", () => { const data: DataPoint[] = [ { time_dimension: "2024-01-01", dimension: undefined, // missing dimension metric: 100, }, ]; const props: PivotTableProps = { data, config: { dimensions: ["model"], metrics: ["metric"], }, }; // Should render without error expect(() => render()).not.toThrow(); expect(screen.getByRole("table")).toBeInTheDocument(); }); }); });