All checks were successful
Deploy / deploy (push) Successful in 2m26s
Per dashboard.md spec requirements: - RED background and text for REST decisions - YELLOW background and text for GENTLE/LIGHT/REDUCED decisions - GREEN background and text for TRAIN decisions Added 8 new tests for color-coded backgrounds (19 total). Updated IMPLEMENTATION_PLAN.md to mark spec gap as complete. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
273 lines
7.8 KiB
TypeScript
273 lines
7.8 KiB
TypeScript
// ABOUTME: Unit tests for DecisionCard component.
|
|
// ABOUTME: Tests rendering of decision status, icon, and reason.
|
|
import { render, screen } from "@testing-library/react";
|
|
import { describe, expect, it } from "vitest";
|
|
import type { Decision } from "@/types";
|
|
import { DecisionCard } from "./decision-card";
|
|
|
|
describe("DecisionCard", () => {
|
|
describe("rendering", () => {
|
|
it("renders the decision icon", () => {
|
|
const decision: Decision = {
|
|
status: "REST",
|
|
reason: "Your body needs recovery today",
|
|
icon: "🛌",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("🛌")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders the decision status", () => {
|
|
const decision: Decision = {
|
|
status: "TRAIN",
|
|
reason: "Good to go",
|
|
icon: "🏃",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("TRAIN")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders the decision reason", () => {
|
|
const decision: Decision = {
|
|
status: "GENTLE",
|
|
reason: "Take it easy today",
|
|
icon: "🧘",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("Take it easy today")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("different status types", () => {
|
|
it("renders REST status correctly", () => {
|
|
const decision: Decision = {
|
|
status: "REST",
|
|
reason: "HRV unbalanced - recovery day",
|
|
icon: "🛌",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("REST")).toBeInTheDocument();
|
|
expect(screen.getByText("🛌")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("HRV unbalanced - recovery day"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders GENTLE status correctly", () => {
|
|
const decision: Decision = {
|
|
status: "GENTLE",
|
|
reason: "Light movement recommended",
|
|
icon: "🧘",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("GENTLE")).toBeInTheDocument();
|
|
expect(screen.getByText("🧘")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("Light movement recommended"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders LIGHT status correctly", () => {
|
|
const decision: Decision = {
|
|
status: "LIGHT",
|
|
reason: "Keep intensity moderate",
|
|
icon: "🚶",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("LIGHT")).toBeInTheDocument();
|
|
expect(screen.getByText("🚶")).toBeInTheDocument();
|
|
expect(screen.getByText("Keep intensity moderate")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders REDUCED status correctly", () => {
|
|
const decision: Decision = {
|
|
status: "REDUCED",
|
|
reason: "Lower intensity today",
|
|
icon: "⬇️",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("REDUCED")).toBeInTheDocument();
|
|
expect(screen.getByText("⬇️")).toBeInTheDocument();
|
|
expect(screen.getByText("Lower intensity today")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders TRAIN status correctly", () => {
|
|
const decision: Decision = {
|
|
status: "TRAIN",
|
|
reason: "Full intensity training approved",
|
|
icon: "🏃",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
expect(screen.getByText("TRAIN")).toBeInTheDocument();
|
|
expect(screen.getByText("🏃")).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText("Full intensity training approved"),
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("color-coded backgrounds", () => {
|
|
it("renders REST with red background", () => {
|
|
const decision: Decision = {
|
|
status: "REST",
|
|
reason: "Recovery day",
|
|
icon: "🛌",
|
|
};
|
|
|
|
const { container } = render(<DecisionCard decision={decision} />);
|
|
|
|
const card = container.firstChild as HTMLElement;
|
|
expect(card).toHaveClass("bg-red-100", "border-red-300");
|
|
});
|
|
|
|
it("renders GENTLE with yellow background", () => {
|
|
const decision: Decision = {
|
|
status: "GENTLE",
|
|
reason: "Light movement",
|
|
icon: "🧘",
|
|
};
|
|
|
|
const { container } = render(<DecisionCard decision={decision} />);
|
|
|
|
const card = container.firstChild as HTMLElement;
|
|
expect(card).toHaveClass("bg-yellow-100", "border-yellow-300");
|
|
});
|
|
|
|
it("renders LIGHT with yellow background", () => {
|
|
const decision: Decision = {
|
|
status: "LIGHT",
|
|
reason: "Moderate intensity",
|
|
icon: "🚶",
|
|
};
|
|
|
|
const { container } = render(<DecisionCard decision={decision} />);
|
|
|
|
const card = container.firstChild as HTMLElement;
|
|
expect(card).toHaveClass("bg-yellow-100", "border-yellow-300");
|
|
});
|
|
|
|
it("renders REDUCED with yellow background", () => {
|
|
const decision: Decision = {
|
|
status: "REDUCED",
|
|
reason: "Lower intensity",
|
|
icon: "⬇️",
|
|
};
|
|
|
|
const { container } = render(<DecisionCard decision={decision} />);
|
|
|
|
const card = container.firstChild as HTMLElement;
|
|
expect(card).toHaveClass("bg-yellow-100", "border-yellow-300");
|
|
});
|
|
|
|
it("renders TRAIN with green background", () => {
|
|
const decision: Decision = {
|
|
status: "TRAIN",
|
|
reason: "Full intensity approved",
|
|
icon: "🏃",
|
|
};
|
|
|
|
const { container } = render(<DecisionCard decision={decision} />);
|
|
|
|
const card = container.firstChild as HTMLElement;
|
|
expect(card).toHaveClass("bg-green-100", "border-green-300");
|
|
});
|
|
|
|
it("renders reason with appropriate text color for REST", () => {
|
|
const decision: Decision = {
|
|
status: "REST",
|
|
reason: "Rest message",
|
|
icon: "🛌",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
const reason = screen.getByText("Rest message");
|
|
expect(reason).toHaveClass("text-red-700");
|
|
});
|
|
|
|
it("renders reason with appropriate text color for GENTLE", () => {
|
|
const decision: Decision = {
|
|
status: "GENTLE",
|
|
reason: "Gentle message",
|
|
icon: "🧘",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
const reason = screen.getByText("Gentle message");
|
|
expect(reason).toHaveClass("text-yellow-700");
|
|
});
|
|
|
|
it("renders reason with appropriate text color for TRAIN", () => {
|
|
const decision: Decision = {
|
|
status: "TRAIN",
|
|
reason: "Train message",
|
|
icon: "🏃",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
const reason = screen.getByText("Train message");
|
|
expect(reason).toHaveClass("text-green-700");
|
|
});
|
|
});
|
|
|
|
describe("styling", () => {
|
|
it("renders within a bordered container", () => {
|
|
const decision: Decision = {
|
|
status: "REST",
|
|
reason: "Test reason",
|
|
icon: "🛌",
|
|
};
|
|
|
|
const { container } = render(<DecisionCard decision={decision} />);
|
|
|
|
const card = container.firstChild as HTMLElement;
|
|
expect(card).toHaveClass("rounded-lg", "p-6");
|
|
});
|
|
|
|
it("renders status as bold heading", () => {
|
|
const decision: Decision = {
|
|
status: "TRAIN",
|
|
reason: "Test",
|
|
icon: "🏃",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
const heading = screen.getByRole("heading", { level: 2 });
|
|
expect(heading).toHaveTextContent("TRAIN");
|
|
expect(heading).toHaveClass("font-bold");
|
|
});
|
|
|
|
it("renders reason with status-specific color", () => {
|
|
const decision: Decision = {
|
|
status: "REST",
|
|
reason: "Reason with status color",
|
|
icon: "🛌",
|
|
};
|
|
|
|
render(<DecisionCard decision={decision} />);
|
|
|
|
const reason = screen.getByText("Reason with status color");
|
|
expect(reason).toHaveClass("text-red-700");
|
|
});
|
|
});
|
|
});
|