import React from "react";
import { describe, expect, it, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { ApprovalRead } from "@/api/generated/model";
import { BoardApprovalsPanel } from "./BoardApprovalsPanel";
vi.mock("@/auth/clerk", () => ({
useAuth: () => ({ isSignedIn: true }),
}));
vi.mock("recharts", () => {
type BoxProps = React.PropsWithChildren<{ className?: string }>;
const Box = ({ children, className }: BoxProps) => (
{children}
);
return {
ResponsiveContainer: Box,
Tooltip: Box,
Legend: Box,
PieChart: Box,
Pie: Box,
Cell: Box,
};
});
const renderWithQueryClient = (ui: React.ReactNode) => {
const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
return render(
{ui},
);
};
describe("BoardApprovalsPanel", () => {
it("renders nested linked-approval metadata and rubric scores", () => {
const approval = {
id: "approval-1",
board_id: "board-1",
action_type: "task.create",
confidence: 62,
status: "pending",
task_id: "task-1",
created_at: "2026-02-12T10:00:00Z",
resolved_at: null,
payload: {
linked_request: {
tasks: [
{
task_id: "task-1",
title: "Launch onboarding checklist",
description: "Create and validate the v1 onboarding checklist.",
},
{
task_id: "task-2",
title: "Publish onboarding checklist",
},
],
task_ids: ["task-1", "task-2"],
},
decision: { reason: "Needs explicit sign-off before rollout." },
analytics: {
rubric_scores: {
clarity: 25,
risk: 20,
dependencies: 15,
},
},
},
task_ids: ["task-1", "task-2"],
rubric_scores: null,
} as ApprovalRead;
renderWithQueryClient(
,
);
expect(
screen.getAllByText("Launch onboarding checklist").length,
).toBeGreaterThan(0);
expect(
screen.getByText("Create and validate the v1 onboarding checklist."),
).toBeInTheDocument();
expect(
screen.getByText("Needs explicit sign-off before rollout."),
).toBeInTheDocument();
expect(screen.getByText("62% score")).toBeInTheDocument();
expect(screen.getByText(/related tasks/i)).toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Launch onboarding checklist" }),
).toHaveAttribute("href", "/boards/board-1?taskId=task-1");
expect(
screen.getByRole("link", { name: "Publish onboarding checklist" }),
).toHaveAttribute("href", "/boards/board-1?taskId=task-2");
expect(screen.getByText(/rubric scores/i)).toBeInTheDocument();
expect(screen.getByText("Clarity")).toBeInTheDocument();
});
it("uses schema task_titles for related task links when payload titles are missing", () => {
const approval = {
id: "approval-2",
board_id: "board-1",
action_type: "task.update",
confidence: 88,
status: "pending",
task_id: "task-a",
task_ids: ["task-a", "task-b"],
task_titles: ["Prepare release notes", "Publish release notes"],
created_at: "2026-02-12T11:00:00Z",
resolved_at: null,
payload: {
task_ids: ["task-a", "task-b"],
reason: "Needs sign-off before publishing.",
},
rubric_scores: null,
} as ApprovalRead;
renderWithQueryClient(
,
);
expect(
screen.getByRole("link", { name: "Prepare release notes" }),
).toHaveAttribute("href", "/boards/board-1?taskId=task-a");
expect(
screen.getByRole("link", { name: "Publish release notes" }),
).toHaveAttribute("href", "/boards/board-1?taskId=task-b");
});
});