import { describe, expect, it, vi } from "vitest"; import React from "react"; import { render, screen, fireEvent } from "@testing-library/react"; import "@testing-library/jest-dom"; vi.mock("next/navigation", () => ({ useRouter: () => ({ push: () => {} }), })); vi.mock("@/lib/storage/investigation-storage", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, clearInvestigation: vi.fn(), }; }); // --------------------------------------------------------------------------- // Route-level assertions for v0.55 architecture + v0.56 action semantics // Portfolio = /index ; Investigation Report = dedicated page // --------------------------------------------------------------------------- function makeSnapshot(overrides = {}) { const situationGraph = { evidence: [ { id: "e1", claim: "Complaints rose.", type: "finding" }, { id: "e2", claim: "Production increased.", type: "finding" }, ], reconstruction: { plausibleInterpretations: ["The denominator may have been narrowed."], }, }; return { scenario: "Complaints increased while production increased.", situationGraph, selectedQuestion: null, summary: "Production quality declined.", updatedAt: new Date().toISOString(), schemaVersion: 1, investigationReport: null, findings: [], ...overrides, }; } describe("Portfolio page (v0.55 route architecture)", () => { let Portfolio; beforeAll(async () => { const mod = await import("@/app/page.jsx"); Portfolio = mod.default; }); it("shows + Create new investigation when no investigation exists", async () => { localStorage.clear(); render(React.createElement(Portfolio)); expect(await screen.findByText(/Create new investigation/i)).toBeInTheDocument(); // Open/Continue investigation buttons absent when no investigation exists expect(screen.queryByText(/Open investigation/i)).not.toBeInTheDocument(); expect(screen.queryByText(/Continue investigation/i)).not.toBeInTheDocument(); }); it("does not show Restart investigation when no investigation exists", async () => { localStorage.clear(); render(React.createElement(Portfolio)); expect(screen.queryByText(/Restart investigation/i)).not.toBeInTheDocument(); }); it("shows Continue investigation card when one persisted investigation exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(Portfolio)); expect(await screen.findByText(/Continue investigation/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("does not show Open investigation text when one persisted investigation exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(Portfolio)); expect(screen.queryByText(/Open investigation/i)).not.toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("shows View report only when a persisted report exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify( makeSnapshot({ investigationReport: { understanding: "We understand complaints rose.", hasPlausibleInterpretations: false, }, }), ), ); render(React.createElement(Portfolio)); expect(await screen.findByText(/View report/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("does not show View report when no report persists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(Portfolio)); expect(screen.queryByText(/View report/i)).not.toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("shows Restart investigation button when one persisted investigation exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(Portfolio)); expect(await screen.findByText(/Restart investigation/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("card does not contain Create new investigation when one persisted investigation exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(Portfolio)); // Portfolio-level "+ Create new investigation" is still present (below card) expect(await screen.findByText(/Create new investigation/i)).toBeInTheDocument(); // But the investigations section card does NOT contain it (no duplicate) const card = document.querySelector("section.mb-10"); const btns = Array.from(card.querySelectorAll('a,button')); expect(btns.some(el => /Create new investigation/i.test(el.textContent))).toBe(false); localStorage.removeItem("confidence-engine-investigation"); }); }); describe("Investigation Report page (v0.55 route architecture)", () => { let ReportPage; beforeAll(async () => { const mod = await import("@/app/investigations/[id]/report/page.jsx"); ReportPage = mod.default; }); it("renders persisted report with Situation and What we understand", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify( makeSnapshot({ investigationReport: { understanding: "We understand complaints rose alongside production increases.", hasPlausibleInterpretations: false, }, }), ), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/Investigation Report/i)).toBeInTheDocument(); expect(await screen.findByText(/Situation/i)).toBeInTheDocument(); expect(screen.getByText(/What we understand/i)).toBeInTheDocument(); expect(screen.getByText("We understand complaints rose alongside production increases.")).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("does not show pending placeholder during pre-hydration", async () => { render(React.createElement(ReportPage)); // During pre-hydration, the page should render Investigation Report title // but NOT the "Report generation pending" placeholder (which is for post-hydration no-report) const pending = await screen.findByText(/Investigation Report/i); expect(pending).toBeInTheDocument(); }); it("shows skeleton after hydration when genuinely no report exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/Investigation Report/i)).toBeInTheDocument(); // After hydration, if no report, skeleton should appear (not during pre-hydration) const pending = await screen.findByText(/Report generation pending/i); expect(pending).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("conditionally renders What remains plausible only when hasPlausibleInterpretations is true", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify( makeSnapshot({ investigationReport: { understanding: "Some understanding.", hasPlausibleInterpretations: false, plausibleInterpretations: "", }, }), ), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/Investigation Report/i)).toBeInTheDocument(); expect(screen.queryByText(/What remains plausible/i)).not.toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("renders What remains plausible when hasPlausibleInterpretations is true", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify( makeSnapshot({ investigationReport: { understanding: "Some understanding.", hasPlausibleInterpretations: true, plausibleInterpretations: "The denominator may have been narrowed.", }, }), ), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/What remains plausible/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("renders Back to investigation link", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify( makeSnapshot({ investigationReport: { understanding: "Some understanding.", hasPlausibleInterpretations: false, }, }), ), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/Back to investigation/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("renders Back to portfolio link", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify( makeSnapshot({ investigationReport: { understanding: "Some understanding.", hasPlausibleInterpretations: false, }, }), ), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/Back to portfolio/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); it("shows skeleton loading state when no persisted report exists", async () => { localStorage.setItem( "confidence-engine-investigation", JSON.stringify(makeSnapshot()), ); render(React.createElement(ReportPage)); expect(await screen.findByText(/Investigation Report/i)).toBeInTheDocument(); // Skeleton should not have actual understanding content expect(screen.queryByText(/Report generation pending/i)).toBeInTheDocument(); localStorage.removeItem("confidence-engine-investigation"); }); }); describe("Investigation page (v0.55 route architecture)", () => { let InvestigationPage; beforeAll(async () => { const mod = await import("@/app/investigations/[id]/page.jsx"); InvestigationPage = mod.default; }); it("renders Back to portfolio link", async () => { render(React.createElement(InvestigationPage)); expect(await screen.findByText(/Back to portfolio/i)).toBeInTheDocument(); }); });