feat(confidence-engine): v0.54b integrate Investigation Overview UI seam + bounded scroll cleanup

- Wire transient overview state from ScenarioForm to ReasoningWorkspace
- Inline rendering of investigation overview below milestone invitation
- Remove obsolete scrollIntoView after overview request (scrolled away from rendered content)
- All four overview props consumed in ReasoningWorkspace render path
- Targeted Vitest: 9/9 PASS (tests/ui/investigation-overview-ui.test.jsx)
- Production build: compiles successfully
This commit is contained in:
2026-09-02 15:14:20 +01:00
parent 83818c0c71
commit 745026f0a0
4 changed files with 307 additions and 14 deletions
+187
View File
@@ -0,0 +1,187 @@
import { describe, expect, it, vi } from "vitest";
import React from "react";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
// jsdom does not implement scrollIntoView — mock it globally
Element.prototype.scrollIntoView = vi.fn();
// ---------------------------------------------------------------------------
// Test target — bounded to ReasoningWorkspace milestone overview seam
// ---------------------------------------------------------------------------
function makeGraphResult(overrides = {}) {
return {
success: true,
situationGraph: {
centralStatement: "Complaints increased while production increased.",
currentSummary: "Nodes: 2 observation | Unknowns: 0 resolved",
activeUnknownNodeId: null,
resolvedNodeIds: ["n-unknown"],
nodes: [
{ id: "n-1", label: "Complaints up 35%", kind: "observation", status: "supported" },
{ id: "n-unknown", label: "Complaint rate denominator", kind: "unknown", status: "resolved" },
],
edges: [],
reconstruction: { plausibleInterpretations: [{ id: "i1", description: "Denominator was narrowed" }] },
},
selectedQuestion: null,
updatedSituationGraph: null,
newlySurfacedNodeIds: [],
diagnostics: { modelName: "test", validationStatus: "valid" },
...overrides,
};
}
let ReasoningWorkspace;
vi.mock("react-i18next", () => ({
useTranslation: () => ({ t: (k) => k }),
initPromise: Promise.resolve(),
}));
describe("investigation overview UI seam (v0.54b)", () => {
beforeAll(async () => {
const mod = await import("@/components/reasoning-workspace.jsx");
ReasoningWorkspace = mod.default;
});
function renderWorkspace(overrides = {}) {
const props = {
scenario: "Complaints increased while production increased.",
status: "success",
updateStatus: "idle",
cuSynthesisLoading: false,
currentUnderstanding: "Production quality declined.",
result: makeGraphResult(),
answer: "",
setAnswer: vi.fn(),
onAnswerSubmit: vi.fn(),
lastSubmittedAnswer: "",
onRestart: vi.fn(),
focusedContributions: [],
onFocusedContribution: vi.fn(),
findings: [],
onUpdateFindingDisposition: vi.fn(),
onUpdateFindingProposition: vi.fn(),
onSummaryUpdate: vi.fn(),
onImmediateGraphChange: vi.fn(),
onSituationGraphChange: vi.fn(),
initialPostAnalyseStatus: "success",
overviewState: null,
setOverviewState: vi.fn(),
overviewLoading: false,
handleRequestOverview: vi.fn(),
...overrides,
};
return render(React.createElement(ReasoningWorkspace, props));
}
// 1. zero-Open-Questions milestone renders the review action
it("renders Review current understanding button when open unknowns are zero and clarified questions exist", async () => {
renderWorkspace();
await waitFor(() => {
const btn = screen.getByRole("button", { name: /Review current understanding/i });
expect(btn).toBeInTheDocument();
});
});
// 7. no overview is requested before the user clicks
it("does not request overview before user clicks (handleRequestOverview not called)", async () => {
const mockHandleRequest = vi.fn();
renderWorkspace({ handleRequestOverview: mockHandleRequest });
expect(mockHandleRequest).not.toHaveBeenCalled();
});
// 2. clicking it calls the overview endpoint once
it("calls handleRequestOverview on button click", async () => {
const mockHandleRequest = vi.fn();
renderWorkspace({ handleRequestOverview: mockHandleRequest });
const btn = await screen.findByRole("button", { name: /Review current understanding/i });
fireEvent.click(btn);
expect(mockHandleRequest).toHaveBeenCalledTimes(1);
});
// 3. loading state is visible
it("shows loading text while overviewLoading is true", async () => {
renderWorkspace({ overviewState: null, overviewLoading: true, handleRequestOverview: vi.fn() });
await waitFor(() => {
expect(screen.getByText(/Generating overview/i)).toBeInTheDocument();
});
});
// 4. returned understanding is rendered under the established-understanding section
it("renders overview understanding in What we understand section", async () => {
renderWorkspace({
overviewState: {
success: true,
understanding: "We understand that complaints rose alongside production increases.",
plausibleInterpretations: null,
},
});
await waitFor(() => {
const surface = screen.getByTestId("investigation-overview");
expect(surface).toBeInTheDocument();
});
const surface2 = document.querySelector('[data-testid="investigation-overview"]');
expect(surface2.textContent).toContain("What we understand");
expect(surface2.textContent).toContain("We understand that complaints rose alongside production increases.");
});
// 5. returned plausibleInterpretations is rendered separately and clearly qualified
it("renders plausible interpretations as a separate qualified section", async () => {
renderWorkspace({
overviewState: {
success: true,
understanding: "Production quality declined.",
plausibleInterpretations: "The denominator may have been narrowed, explaining the apparent complaint increase.",
},
});
const surface = screen.getByTestId("investigation-overview");
expect(surface).toBeInTheDocument();
expect(surface.textContent).toContain("What remains plausible");
expect(surface.textContent).toContain("The denominator may have been narrowed");
});
// 6. existing Current Understanding remains
it("existing Current Understanding text remains visible alongside overview", async () => {
renderWorkspace({
currentUnderstanding: "Production quality declined.",
overviewState: {
success: true,
understanding: "We understand that complaints rose alongside production increases.",
plausibleInterpretations: null,
},
});
const cuSurface = document.querySelector('[id="cu-scroll-target"]');
expect(cuSurface).toBeInTheDocument();
expect(cuSurface.textContent).toContain("Production quality declined.");
});
// 8. overview response does not mutate Current Understanding
it("does not call setCurrentUnderstanding or mutate existing CU", async () => {
const mockSetCU = vi.fn();
renderWorkspace({
currentUnderstanding: "Production quality declined.",
overviewState: {
success: true,
understanding: "Overview synthesis result.",
plausibleInterpretations: null,
},
setCurrentUnderstanding: mockSetCU,
});
expect(mockSetCU).not.toHaveBeenCalled();
});
// 9. clarified questions remain visible
it("clarified questions remain rendered when overview is shown", async () => {
renderWorkspace({
overviewState: {
success: true,
understanding: "Overview.",
plausibleInterpretations: null,
},
});
expect(screen.getByText(/Questions we have clarified/i)).toBeInTheDocument();
});
});