Files
stack/apps/web/src/components/tasks/TaskItem.test.tsx
Jason Woltje ac1f2c176f
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix: Resolve all ESLint errors and warnings in web package
Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings.

Changes:
- Fixed 144 issues: nullish coalescing, return types, unused variables
- Fixed 118 issues: unnecessary conditions, type safety, template literals
- Fixed 79 issues: non-null assertions, unsafe assignments, empty functions
- Fixed 67 issues: explicit return types, promise handling, enum comparisons
- Fixed 45 final warnings: missing return types, optional chains
- Fixed 25 typecheck-related issues: async/await, type assertions, formatting
- Fixed JSX.Element namespace errors across 90+ files

All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems.

Files modified: 118 components, tests, hooks, and utilities

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 00:10:03 -06:00

229 lines
8.0 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { TaskItem } from "./TaskItem";
import { TaskStatus, TaskPriority, type Task } from "@mosaic/shared";
describe("TaskItem", (): void => {
const baseTask: Task = {
id: "task-1",
title: "Test task",
description: "Task description",
status: TaskStatus.IN_PROGRESS,
priority: TaskPriority.MEDIUM,
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"),
};
it("should render task title", (): void => {
render(<TaskItem task={baseTask} />);
expect(screen.getByText("Test task")).toBeInTheDocument();
});
it("should render task description when present", (): void => {
render(<TaskItem task={baseTask} />);
expect(screen.getByText("Task description")).toBeInTheDocument();
});
it("should show status indicator for active task", (): void => {
render(<TaskItem task={{ ...baseTask, status: TaskStatus.IN_PROGRESS }} />);
expect(screen.getByText("🟢")).toBeInTheDocument();
});
it("should show status indicator for not started task", (): void => {
render(<TaskItem task={{ ...baseTask, status: TaskStatus.NOT_STARTED }} />);
expect(screen.getByText("⚪")).toBeInTheDocument();
});
it("should show status indicator for paused task", (): void => {
render(<TaskItem task={{ ...baseTask, status: TaskStatus.PAUSED }} />);
expect(screen.getByText("⏸️")).toBeInTheDocument();
});
it("should display priority badge", (): void => {
render(<TaskItem task={{ ...baseTask, priority: TaskPriority.HIGH }} />);
expect(screen.getByText("High priority")).toBeInTheDocument();
});
it("should not use demanding language", (): void => {
const { container } = render(<TaskItem task={baseTask} />);
const text = container.textContent;
expect(text).not.toMatch(/overdue/i);
expect(text).not.toMatch(/urgent/i);
expect(text).not.toMatch(/must/i);
expect(text).not.toMatch(/critical/i);
});
it("should show 'Target passed' for past due dates", (): void => {
const pastTask = {
...baseTask,
dueDate: new Date("2026-01-27"), // Past date
};
render(<TaskItem task={pastTask} />);
expect(screen.getByText(/target passed/i)).toBeInTheDocument();
});
it("should show 'Approaching target' for near due dates", (): void => {
const soonTask = {
...baseTask,
dueDate: new Date(Date.now() + 12 * 60 * 60 * 1000), // 12 hours from now
};
render(<TaskItem task={soonTask} />);
expect(screen.getByText(/approaching target/i)).toBeInTheDocument();
});
describe("error states", (): void => {
it("should handle task with missing title", (): void => {
const taskWithoutTitle = {
...baseTask,
title: "",
};
const { container } = render(<TaskItem task={taskWithoutTitle} />);
// Should render without crashing, even with empty title
expect(container.querySelector(".bg-white")).toBeInTheDocument();
});
it("should handle task with missing description", (): void => {
const taskWithoutDescription = {
...baseTask,
description: null,
};
render(<TaskItem task={taskWithoutDescription} />);
expect(screen.getByText("Test task")).toBeInTheDocument();
// Description paragraph should not be rendered when null
expect(screen.queryByText("Task description")).not.toBeInTheDocument();
});
it("should handle task with invalid status", (): void => {
const taskWithInvalidStatus = {
...baseTask,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
status: "invalid-status" as any,
};
const { container } = render(<TaskItem task={taskWithInvalidStatus} />);
// Should render without crashing even with invalid status
expect(container.querySelector(".bg-white")).toBeInTheDocument();
expect(screen.getByText("Test task")).toBeInTheDocument();
});
it("should handle task with invalid priority", (): void => {
const taskWithInvalidPriority = {
...baseTask,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
priority: "invalid-priority" as any,
};
const { container } = render(<TaskItem task={taskWithInvalidPriority} />);
// Should render without crashing even with invalid priority
expect(container.querySelector(".bg-white")).toBeInTheDocument();
expect(screen.getByText("Test task")).toBeInTheDocument();
});
it("should handle task with missing dueDate", (): void => {
const taskWithoutDueDate = {
...baseTask,
dueDate: null,
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
render(<TaskItem task={taskWithoutDueDate as any} />);
expect(screen.getByText("Test task")).toBeInTheDocument();
});
it("should handle task with invalid dueDate", (): void => {
const taskWithInvalidDate = {
...baseTask,
dueDate: new Date("invalid-date"),
};
const { container } = render(<TaskItem task={taskWithInvalidDate} />);
expect(container.querySelector(".bg-white")).toBeInTheDocument();
expect(screen.getByText("Test task")).toBeInTheDocument();
});
it("should handle task with very long title", (): void => {
const longTitle = "A".repeat(500);
const taskWithLongTitle = {
...baseTask,
title: longTitle,
};
render(<TaskItem task={taskWithLongTitle} />);
expect(screen.getByText(longTitle)).toBeInTheDocument();
});
it("should handle task with special characters in title", (): void => {
const taskWithSpecialChars = {
...baseTask,
title: '<img src="x" onerror="alert(1)">',
};
const { container } = render(<TaskItem task={taskWithSpecialChars} />);
// Should render escaped HTML entities, not execute
// React escapes to &lt;img... &gt; which is safe
expect(container.innerHTML).toContain("&lt;img");
expect(container.innerHTML).not.toContain("<img src=");
// Text should be displayed as-is
expect(screen.getByText(/<img src="x" onerror="alert\(1\)">/)).toBeInTheDocument();
});
it("should handle task with HTML in description", (): void => {
const taskWithHtmlDesc = {
...baseTask,
description: '<b>Bold text</b><script>alert("xss")</script>',
};
const { container } = render(<TaskItem task={taskWithHtmlDesc} />);
// Should render as text, not HTML - React escapes by default
expect(container.innerHTML).not.toContain("<script>");
// Text should be displayed as-is
expect(screen.getByText(/Bold text/)).toBeInTheDocument();
});
it("should handle task with missing required IDs", (): void => {
const taskWithMissingIds = {
...baseTask,
id: "",
workspaceId: "",
};
const { container } = render(<TaskItem task={taskWithMissingIds} />);
expect(container.querySelector(".bg-white")).toBeInTheDocument();
expect(screen.getByText("Test task")).toBeInTheDocument();
});
it("should handle task with extremely old due date", (): void => {
const veryOldTask = {
...baseTask,
dueDate: new Date("1970-01-01"),
};
render(<TaskItem task={veryOldTask} />);
expect(screen.getByText(/target passed/i)).toBeInTheDocument();
});
it("should handle task with far future due date", (): void => {
const farFutureTask = {
...baseTask,
dueDate: new Date("2099-12-31"),
};
const { container } = render(<TaskItem task={farFutureTask} />);
expect(container.querySelector(".bg-white")).toBeInTheDocument();
expect(screen.getByText("Test task")).toBeInTheDocument();
});
});
});