feat(FN-3786): add reports dashboard view and comparison tooling
Adds a Reports dashboard view (`ReportsView`) to the reports plugin with a full supporting stack of components (`ReportComparisonDrawer`, `ReportDetailPanel`, `ReportFiltersBar`, `ReportListItem`, `ReportEmptyState`), hooks (`useReports`, `useReportPreview`, `useReportSectionDiff`, `useViewportMode` Fusion-Task-Id: FN-3786
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import * as preview from "../useReportPreview.js";
|
||||
import { ReportComparisonDrawer } from "../components/ReportComparisonDrawer.js";
|
||||
|
||||
describe("ReportComparisonDrawer", () => {
|
||||
it("renders compare ui", () => {
|
||||
vi.spyOn(preview, "useReportPreview").mockReturnValue({ html: "<article />", loading: false, error: null });
|
||||
const { getByText } = render(<ReportComparisonDrawer reports={[{ id: "R-1", title: "A" }, { id: "R-2", title: "B" }] as never} leftId="R-1" rightId="R-2" onPick={vi.fn()} onClose={vi.fn()} />);
|
||||
expect(getByText("Compare reports")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import * as preview from "../useReportPreview.js";
|
||||
import { ReportDetailPanel } from "../components/ReportDetailPanel.js";
|
||||
|
||||
describe("ReportDetailPanel", () => {
|
||||
it("renders report", () => {
|
||||
vi.spyOn(preview, "useReportPreview").mockReturnValue({ html: "<article />", loading: false, error: null });
|
||||
const { getByText } = render(<ReportDetailPanel report={{ id: "R-1", title: "Report", cadence: "daily", status: "published", periodStart: "2026-01-01", periodEnd: "2026-01-02" } as never} />);
|
||||
expect(getByText("Report")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { fireEvent, render, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ReportFiltersBar } from "../components/ReportFiltersBar.js";
|
||||
|
||||
const filters = { cadence: "all", status: "all", from: "", to: "", q: "", agentId: "" } as const;
|
||||
|
||||
describe("ReportFiltersBar", () => {
|
||||
it("emits changes", async () => {
|
||||
const onChange = vi.fn();
|
||||
const { getByPlaceholderText } = render(<ReportFiltersBar filters={{ ...filters }} onChange={onChange} agents={[]} />);
|
||||
fireEvent.change(getByPlaceholderText("Search title"), { target: { value: "hello" } });
|
||||
await waitFor(() => expect(onChange).toBeCalled());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import * as reportsHook from "../useReports.js";
|
||||
import { ReportsView } from "../ReportsView.js";
|
||||
|
||||
describe("ReportsView", () => {
|
||||
it("renders list and compare toggle", () => {
|
||||
vi.spyOn(reportsHook, "useReports").mockReturnValue({
|
||||
filters: { cadence: "all", status: "all", from: "", to: "", q: "", agentId: "" },
|
||||
setFilters: vi.fn(),
|
||||
reports: [{ id: "R-1", title: "A", cadence: "daily", status: "published", periodStart: "2026-01-01", periodEnd: "2026-01-02", metadata: {} }],
|
||||
loading: false,
|
||||
selectedId: "R-1",
|
||||
selectedReport: { id: "R-1", title: "A", cadence: "daily", status: "published", periodStart: "2026-01-01", periodEnd: "2026-01-02", metadata: {} },
|
||||
selectId: vi.fn(),
|
||||
compareMode: false,
|
||||
compareA: undefined,
|
||||
compareB: undefined,
|
||||
enterCompareMode: vi.fn(),
|
||||
closeCompareMode: vi.fn(),
|
||||
setCompareSlot: vi.fn(),
|
||||
} as never);
|
||||
const { getByText } = render(<ReportsView addToast={vi.fn()} />);
|
||||
fireEvent.click(getByText("Compare"));
|
||||
expect(getByText("Reports")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { getReportExportUrl, getReportPreviewHtml, listReports } from "../api.js";
|
||||
|
||||
describe("api", () => {
|
||||
it("lists reports", async () => {
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: async () => ({ reports: [{ id: "R-1" }] }) }));
|
||||
const reports = await listReports();
|
||||
expect(reports).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("reads preview html", async () => {
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, text: async () => "<article/>" }));
|
||||
await expect(getReportPreviewHtml("R-1")).resolves.toContain("article");
|
||||
});
|
||||
|
||||
it("builds export url", () => {
|
||||
expect(getReportExportUrl("R-1")).toContain("/reports/R-1/export.html");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { diffReportSections } from "../useReportSectionDiff.js";
|
||||
|
||||
describe("diffReportSections", () => {
|
||||
it("classifies changed sections", () => {
|
||||
const a = { metadata: { wins: ["a"] } } as never;
|
||||
const b = { metadata: { wins: ["b"] } } as never;
|
||||
const diff = diffReportSections(a, b);
|
||||
expect(diff.changed.find((item) => item.id === "system-wins")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import * as api from "../api.js";
|
||||
import { useReports } from "../useReports.js";
|
||||
|
||||
describe("useReports", () => {
|
||||
it("loads reports", async () => {
|
||||
vi.spyOn(api, "listReports").mockResolvedValue([{ id: "R-1", title: "A" } as never]);
|
||||
vi.spyOn(api, "getReport").mockResolvedValue({ id: "R-1", title: "A" } as never);
|
||||
const { result } = renderHook(() => useReports({ addToast: vi.fn() }));
|
||||
await waitFor(() => expect(result.current.reports).toHaveLength(1));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user