feat(#37-41): Add domains, ideas, relationships, agents, widgets schema
Schema additions for issues #37-41: New models: - Domain (#37): Life domains (work, marriage, homelab, etc.) - Idea (#38): Brain dumps with pgvector embeddings - Relationship (#39): Generic entity linking (blocks, depends_on) - Agent (#40): ClawdBot agent tracking with metrics - AgentSession (#40): Conversation session tracking - WidgetDefinition (#41): HUD widget registry - UserLayout (#41): Per-user dashboard configuration Updated models: - Task, Event, Project: Added domainId foreign key - User, Workspace: Added new relations New enums: - IdeaStatus: CAPTURED, PROCESSING, ACTIONABLE, ARCHIVED, DISCARDED - RelationshipType: BLOCKS, BLOCKED_BY, DEPENDS_ON, etc. - AgentStatus: IDLE, WORKING, WAITING, ERROR, TERMINATED - EntityType: Added IDEA, DOMAIN Migration: 20260129182803_add_domains_ideas_agents_widgets
This commit is contained in:
156
apps/web/src/components/tasks/TaskList.test.tsx
Normal file
156
apps/web/src/components/tasks/TaskList.test.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { TaskList } from "./TaskList";
|
||||
import { TaskStatus, TaskPriority, type Task } from "@mosaic/shared";
|
||||
|
||||
describe("TaskList", () => {
|
||||
const mockTasks: Task[] = [
|
||||
{
|
||||
id: "task-1",
|
||||
title: "Review pull request",
|
||||
description: "Review and provide feedback on frontend PR",
|
||||
status: TaskStatus.IN_PROGRESS,
|
||||
priority: TaskPriority.HIGH,
|
||||
dueDate: new Date("2026-01-29"),
|
||||
creatorId: "user-1",
|
||||
assigneeId: "user-1",
|
||||
workspaceId: "workspace-1",
|
||||
projectId: null,
|
||||
parentId: null,
|
||||
sortOrder: 0,
|
||||
metadata: {},
|
||||
completedAt: null,
|
||||
createdAt: new Date("2026-01-28"),
|
||||
updatedAt: new Date("2026-01-28"),
|
||||
},
|
||||
{
|
||||
id: "task-2",
|
||||
title: "Update documentation",
|
||||
description: "Add setup instructions",
|
||||
status: TaskStatus.NOT_STARTED,
|
||||
priority: TaskPriority.MEDIUM,
|
||||
dueDate: new Date("2026-02-05"),
|
||||
creatorId: "user-1",
|
||||
assigneeId: "user-1",
|
||||
workspaceId: "workspace-1",
|
||||
projectId: null,
|
||||
parentId: null,
|
||||
sortOrder: 1,
|
||||
metadata: {},
|
||||
completedAt: null,
|
||||
createdAt: new Date("2026-01-28"),
|
||||
updatedAt: new Date("2026-01-28"),
|
||||
},
|
||||
];
|
||||
|
||||
it("should render empty state when no tasks", () => {
|
||||
render(<TaskList tasks={[]} isLoading={false} />);
|
||||
expect(screen.getByText(/no tasks scheduled/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render loading state", () => {
|
||||
render(<TaskList tasks={[]} isLoading={true} />);
|
||||
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render tasks list", () => {
|
||||
render(<TaskList tasks={mockTasks} isLoading={false} />);
|
||||
expect(screen.getByText("Review pull request")).toBeInTheDocument();
|
||||
expect(screen.getByText("Update documentation")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should group tasks by date", () => {
|
||||
render(<TaskList tasks={mockTasks} isLoading={false} />);
|
||||
// Should have date group sections (Today, This Week, etc.)
|
||||
// The exact sections depend on the current date, so just verify grouping works
|
||||
const sections = screen.getAllByRole("heading", { level: 2 });
|
||||
expect(sections.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should use PDA-friendly language", () => {
|
||||
render(<TaskList tasks={mockTasks} isLoading={false} />);
|
||||
// Should NOT contain demanding language
|
||||
const text = screen.getByRole("main").textContent;
|
||||
expect(text).not.toMatch(/overdue/i);
|
||||
expect(text).not.toMatch(/urgent/i);
|
||||
expect(text).not.toMatch(/must do/i);
|
||||
});
|
||||
|
||||
it("should display status indicators", () => {
|
||||
render(<TaskList tasks={mockTasks} isLoading={false} />);
|
||||
// Check for emoji status indicators (rendered as text)
|
||||
const listItems = screen.getAllByRole("listitem");
|
||||
expect(listItems.length).toBe(mockTasks.length);
|
||||
});
|
||||
|
||||
describe("error states", () => {
|
||||
it("should handle undefined tasks gracefully", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render(<TaskList tasks={undefined as any} isLoading={false} />);
|
||||
expect(screen.getByText(/no tasks scheduled/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle null tasks gracefully", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
render(<TaskList tasks={null as any} isLoading={false} />);
|
||||
expect(screen.getByText(/no tasks scheduled/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle tasks with missing required fields", () => {
|
||||
const malformedTasks = [
|
||||
{
|
||||
...mockTasks[0],
|
||||
title: "", // Empty title
|
||||
},
|
||||
];
|
||||
|
||||
render(<TaskList tasks={malformedTasks} isLoading={false} />);
|
||||
// Component should render without crashing
|
||||
expect(screen.getByRole("main")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle tasks with invalid dates", () => {
|
||||
const tasksWithBadDates = [
|
||||
{
|
||||
...mockTasks[0],
|
||||
dueDate: new Date("invalid-date"),
|
||||
},
|
||||
];
|
||||
|
||||
render(<TaskList tasks={tasksWithBadDates} isLoading={false} />);
|
||||
expect(screen.getByRole("main")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle extremely large task lists", () => {
|
||||
const largeTasks = Array.from({ length: 1000 }, (_, i) => ({
|
||||
...mockTasks[0],
|
||||
id: `task-${i}`,
|
||||
title: `Task ${i}`,
|
||||
}));
|
||||
|
||||
render(<TaskList tasks={largeTasks} isLoading={false} />);
|
||||
expect(screen.getByRole("main")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle tasks with very long titles", () => {
|
||||
const longTitleTask = {
|
||||
...mockTasks[0],
|
||||
title: "A".repeat(500),
|
||||
};
|
||||
|
||||
render(<TaskList tasks={[longTitleTask]} isLoading={false} />);
|
||||
expect(screen.getByText(/A{500}/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should handle tasks with special characters in title", () => {
|
||||
const specialCharTask = {
|
||||
...mockTasks[0],
|
||||
title: '<script>alert("xss")</script>',
|
||||
};
|
||||
|
||||
render(<TaskList tasks={[specialCharTask]} isLoading={false} />);
|
||||
// Should render escaped, not execute
|
||||
expect(screen.getByRole("main").innerHTML).not.toContain("<script>");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user