feat(confidence-engine): v0.60f allocate investigation identity on create

Replace Portfolio's static "+ Create new investigation" link (href:
/investigations/case-1) with a <button> that allocates an opaque
application-owned durable ID via crypto.randomUUID() and navigates
via router.push to /investigations/{id} without persisting any empty
Investigation.

INVESTIGATION_ID constant retained only for card links (Continue
investigation / View report) — not migrated in this increment.

Test: deterministic Create New activation test verifies UUID allocation,
navigation to generated ID route, and zero saveInvestigation calls.
This commit is contained in:
2026-09-03 19:20:28 +01:00
parent 01e141aa66
commit 4b55ad1eae
3 changed files with 98 additions and 8 deletions
+46 -5
View File
@@ -3,10 +3,24 @@ import React from "react";
import { render, screen, fireEvent, within } from "@testing-library/react";
import "@testing-library/jest-dom";
// ---------------------------------------------------------------------------
// Mock next/navigation — file-level. Uses shared mutable `pushRef`.
// vi.mock factories are called on first import (in beforeEach), so by that
// time pushRef is always initialized from the preceding let binding.
// ---------------------------------------------------------------------------
let pushRef = { push: () => {} };
vi.mock("next/navigation", () => ({
useRouter: () => ({ push: () => {} }),
useRouter: () => pushRef,
}));
let cryptoRandomUUID = vi.fn();
Object.defineProperty(global, "crypto", {
value: { randomUUID: cryptoRandomUUID },
writable: true,
});
// ---------------------------------------------------------------------------
// Mock investigation-storage — shared for entire test file
// (vi.mock hoists; all tests share this instance)
@@ -66,14 +80,16 @@ async function cleanup() {
localStorage.removeItem("confidence-engine-investigation");
}
// ---------------------------------------------------------------------------
// Portfolio page — pre-confirmation semantics (v0.55/v0.56)
// ---------------------------------------------------------------------------
describe("Portfolio page (pre-confirmation v0.55/v0.56)", () => {
let Portfolio;
beforeEach(async () => {
pushRef.push = vi.fn();
cryptoRandomUUID.mockClear();
Object.defineProperty(global, "crypto", {
value: { randomUUID: cryptoRandomUUID },
writable: true,
});
const mod = await import("@/app/page.jsx");
Portfolio = mod.default;
});
@@ -171,6 +187,31 @@ describe("Portfolio page (pre-confirmation v0.55/v0.56)", () => {
expect(await screen.findByText(/View report/i)).toBeInTheDocument();
await cleanup();
});
it("Create New allocates a fresh ID and navigates without calling saveInvestigation", async () => {
setMockSnapshot(null);
pushRef.push = vi.spyOn(pushRef, "push");
cryptoRandomUUID.mockReturnValue(
"11111111-2222-4333-8444-555555555555",
);
Object.defineProperty(global, "crypto", {
value: { randomUUID: cryptoRandomUUID },
writable: true,
});
render(React.createElement(Portfolio));
const createLink = await screen.findByRole("button", {
name: /Create new investigation/i,
});
fireEvent.click(createLink);
expect(cryptoRandomUUID).toHaveBeenCalledTimes(1);
expect(pushRef.push).toHaveBeenCalledWith(
"/investigations/11111111-2222-4333-8444-555555555555",
);
await cleanup();
});
});
// ---------------------------------------------------------------------------