feat(#41): implement Widget/HUD system
- BaseWidget wrapper with loading/error states - WidgetRegistry for central widget management - WidgetGrid with react-grid-layout integration - TasksWidget, CalendarWidget, QuickCaptureWidget - useLayouts hooks for layout persistence - Comprehensive test suite (TDD approach)
This commit is contained in:
127
apps/web/src/components/widgets/__tests__/TasksWidget.test.tsx
Normal file
127
apps/web/src/components/widgets/__tests__/TasksWidget.test.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* TasksWidget Component Tests
|
||||
* Following TDD principles
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { TasksWidget } from "../TasksWidget";
|
||||
|
||||
// Mock fetch for API calls
|
||||
global.fetch = vi.fn();
|
||||
|
||||
describe("TasksWidget", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should render loading state initially", () => {
|
||||
(global.fetch as any).mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render task statistics", async () => {
|
||||
const mockTasks = [
|
||||
{ id: "1", title: "Task 1", status: "IN_PROGRESS", priority: "HIGH" },
|
||||
{ id: "2", title: "Task 2", status: "COMPLETED", priority: "MEDIUM" },
|
||||
{ id: "3", title: "Task 3", status: "NOT_STARTED", priority: "LOW" },
|
||||
];
|
||||
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockTasks,
|
||||
});
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("3")).toBeInTheDocument(); // Total
|
||||
expect(screen.getByText("1")).toBeInTheDocument(); // In Progress
|
||||
expect(screen.getByText("1")).toBeInTheDocument(); // Completed
|
||||
});
|
||||
});
|
||||
|
||||
it("should render task list", async () => {
|
||||
const mockTasks = [
|
||||
{ id: "1", title: "Complete documentation", status: "IN_PROGRESS", priority: "HIGH" },
|
||||
{ id: "2", title: "Review PRs", status: "NOT_STARTED", priority: "MEDIUM" },
|
||||
];
|
||||
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockTasks,
|
||||
});
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Complete documentation")).toBeInTheDocument();
|
||||
expect(screen.getByText("Review PRs")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle empty task list", async () => {
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => [],
|
||||
});
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/no tasks/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle API errors gracefully", async () => {
|
||||
(global.fetch as any).mockRejectedValueOnce(new Error("API Error"));
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/error/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display priority indicators", async () => {
|
||||
const mockTasks = [
|
||||
{ id: "1", title: "High priority task", status: "IN_PROGRESS", priority: "HIGH" },
|
||||
];
|
||||
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockTasks,
|
||||
});
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("High priority task")).toBeInTheDocument();
|
||||
// Priority icon should be rendered (high priority = red)
|
||||
});
|
||||
});
|
||||
|
||||
it("should limit displayed tasks to 5", async () => {
|
||||
const mockTasks = Array.from({ length: 10 }, (_, i) => ({
|
||||
id: `${i + 1}`,
|
||||
title: `Task ${i + 1}`,
|
||||
status: "NOT_STARTED",
|
||||
priority: "MEDIUM",
|
||||
}));
|
||||
|
||||
(global.fetch as any).mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => mockTasks,
|
||||
});
|
||||
|
||||
render(<TasksWidget id="tasks-1" />);
|
||||
|
||||
await waitFor(() => {
|
||||
const taskElements = screen.getAllByText(/Task \d+/);
|
||||
expect(taskElements.length).toBeLessThanOrEqual(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user