Files
stack/apps/web/src/components/widgets/__tests__/WidgetGrid.test.tsx
Jason Woltje cc56f2cbe1
All checks were successful
ci/woodpecker/push/web Pipeline was successful
feat(web): migrate dashboard to WidgetGrid with layout persistence (#497)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-02-24 00:50:24 +00:00

112 lines
3.5 KiB
TypeScript

/**
* WidgetGrid Component Tests
* Following TDD - write tests first!
*/
import { describe, it, expect, vi, beforeAll } from "vitest";
import { render, screen } from "@testing-library/react";
import { WidgetGrid } from "../WidgetGrid";
import type { WidgetPlacement } from "@mosaic/shared";
// ResizeObserver is not available in jsdom
beforeAll((): void => {
global.ResizeObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
});
// Mock react-grid-layout
vi.mock("react-grid-layout", () => ({
default: ({ children }: { children: React.ReactNode }): React.JSX.Element => (
<div data-testid="grid-layout">{children}</div>
),
Responsive: ({ children }: { children: React.ReactNode }): React.JSX.Element => (
<div data-testid="responsive-grid-layout">{children}</div>
),
}));
describe("WidgetGrid", (): void => {
const mockLayout: WidgetPlacement[] = [
{ i: "tasks-1", x: 0, y: 0, w: 2, h: 2 },
{ i: "calendar-1", x: 2, y: 0, w: 2, h: 2 },
];
const mockOnLayoutChange = vi.fn();
it("should render grid layout", (): void => {
render(<WidgetGrid layout={mockLayout} onLayoutChange={mockOnLayoutChange} />);
expect(screen.getByTestId("grid-layout")).toBeInTheDocument();
});
it("should render widgets from layout", (): void => {
render(<WidgetGrid layout={mockLayout} onLayoutChange={mockOnLayoutChange} />);
// Should render correct number of widgets
const widgets = screen.getAllByTestId(/widget-/);
expect(widgets).toHaveLength(mockLayout.length);
});
it("should call onLayoutChange when layout changes", (): void => {
const { rerender } = render(
<WidgetGrid layout={mockLayout} onLayoutChange={mockOnLayoutChange} />
);
const newLayout: WidgetPlacement[] = [
{ i: "tasks-1", x: 1, y: 0, w: 2, h: 2 },
{ i: "calendar-1", x: 2, y: 0, w: 2, h: 2 },
];
rerender(<WidgetGrid layout={newLayout} onLayoutChange={mockOnLayoutChange} />);
// Layout change handler should be set up (actual calls handled by react-grid-layout)
expect(mockOnLayoutChange).toBeDefined();
});
it("should support edit mode", (): void => {
render(<WidgetGrid layout={mockLayout} onLayoutChange={mockOnLayoutChange} isEditing={true} />);
// In edit mode, widgets should have edit controls
expect(screen.getByTestId("grid-layout")).toBeInTheDocument();
});
it("should support read-only mode", (): void => {
render(
<WidgetGrid layout={mockLayout} onLayoutChange={mockOnLayoutChange} isEditing={false} />
);
expect(screen.getByTestId("grid-layout")).toBeInTheDocument();
});
it("should render empty state when no widgets", (): void => {
render(<WidgetGrid layout={[]} onLayoutChange={mockOnLayoutChange} />);
expect(screen.getByText(/no widgets/i)).toBeInTheDocument();
});
it("should handle widget removal", (): void => {
const mockOnRemoveWidget = vi.fn();
render(
<WidgetGrid
layout={mockLayout}
onLayoutChange={mockOnLayoutChange}
onRemoveWidget={mockOnRemoveWidget}
isEditing={true}
/>
);
// Widget removal should be supported
expect(mockOnRemoveWidget).toBeDefined();
});
it("should apply custom className", (): void => {
const { container } = render(
<WidgetGrid layout={mockLayout} onLayoutChange={mockOnLayoutChange} className="custom-grid" />
);
expect(container.querySelector(".custom-grid")).toBeInTheDocument();
});
});