13 KiB
v0.7 — Mock Investigation Mode for UI Development
Purpose
A local mock/demo mode lets you develop and test the Confidence Engine UI without running Ollama. It intercepts API calls at the frontend layer and replays pre-recorded scenario fixtures, producing identical response shapes whether real or mocked.
Quick Start
# Enable mock mode
export NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS=true
# Optional: choose a delay profile (default: normal = 700ms)
export NEXT_PUBLIC_CONFIDENCE_MOCK_DELAY=normal # instant | normal | slow
# Optional: choose a scenario (default: complete = jumps to end after start)
export NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCK_SCENARIO=complete # complete | error | ""
npm run dev
Navigate to the confidence-engine UI and enter any scenario text — the response will come from fixtures, not Ollama.
Environment Variables
| Variable | Required | Values | Default | Description |
|---|---|---|---|---|
NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS |
yes | "true" or anything else |
disabled | Enables mock mode when set to "true" |
NEXT_PUBLIC_CONFIDENCE_MOCK_DELAY |
no | "instant", "normal", "slow" |
"normal" (700ms) |
Simulated latency for realistic loading UX |
NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCK_SCENARIO |
no | "complete", "error", "" |
"" (sequential turns) |
Which scenario to replay |
Scenarios
Default (Sequential Turns)
Replays 6 turns of the "Complaints + Production" investigation:
| Turn | What happens |
|---|---|
| 0 | Start — two observations, three unknown nodes. Question: "Were both percentages calculated from comparable baseline counts?" |
| 1 | User confirms same period → new observation added. Question: "Did the complaint rate per unit produced improve or worsen?" |
| 2 | User provides baselines (100→135 complaints, 1000→1400 production) → new observation. Question: "Was there any change in how complaints were recorded?" |
| 3 | User confirms rate improved (10/1000→9.6/1000) → new observation. Unknown u-4 resolved by process of elimination. no-question — needs more evidence. |
| 4 | (not reached in default — shown only when stepping past turn 3) |
| 5 | User confirms same reporting rules → final completion with full summary. |
After the initial analysis is submitted, each update call advances to the next turn. In "complete" mode, the first update jumps directly to turn 5 (final).
Complete Mode (NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCK_SCENARIO=complete)
After the start response (turn 0), every subsequent update returns the final completion state (turn 5) immediately. Useful for quickly verifying end-to-end UI flow.
Error Mode (NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCK_SCENARIO=error)
All API calls return a structured error response with:
success: falsestage: "provider"error: "Mock provider error: structured response unavailable."providerErrors: [...]
Useful for testing the UI's error display paths.
Architecture
ScenarioForm (client)
├── MOCK_ENABLED (compile-time env resolution via Next.js build injection)
├── useMockGlobals() → window.__MOCK_* runtime globals
├── mockFetch ──► lib/mocks/confidence-engine/mock-client.js
│ └── Self-contained interceptor (no external deps, pure ESM)
│ ├── handleStartCase() — returns turn 0 or error
│ └── handleUpdateCase() — advances turns (0→5)
└── fetch ────────────────► real API routes /api/cases/start | /api/cases/update
- mock-client.js is self-contained: all turn data is defined inline. It intercepts POST requests to
/api/cases/startand/api/cases/update. Any other URL is passed through unchanged. - Uses
window.__MOCK_*globals (set byuseMockGlobals()hook in ScenarioForm) for runtime env access from the browser. Falls back toprocess.env.*on the server side. - UI integration in
scenario-form.jsx: one compile-time boolean (MOCK_ENABLED), one React hook (useMockGlobals), two ternary replacements offetch. No other components need changes.
Mock Indicator
When mock mode is active, a "Mock mode active" label appears inside the Developer details panel (bottom of the workspace). It does not appear in user-facing UI areas.
The indicator uses mockFetch's built-in guard:
- In client components: reads from
window.__MOCK_ENABLED(set byuseMockGlobals). - The real API path is preserved — when mock mode is off, mockFetch simply delegates to native
fetch.
Files
| File | Purpose |
|---|---|
lib/mocks/confidence-engine/mock-client.js |
Self-contained interceptor + 6-turn scenario data (pure ESM) |
components/scenario-form.jsx |
Minimal integration: MOCK_ENABLED guard, useMockGlobals hook, mockFetch dispatch |
.env.example |
Documented env var reference |
docs/v0.7-ui-mock-mode.md |
This file |
Safety Rules
- No secrets: Mock fixtures contain only fictional scenario data. No API keys, passwords, or PII.
- No
.env.localcommit: The.gitignorealready excludes.env.local. Copy from.env.examplefor local overrides. - Real path preserved: Setting
NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKSto anything other than"true"returns to the real Ollama API — zero code change needed.
Turn Fixture Schema
Each turn snapshot (inline in mock-client.js) contains:
{
nodes: [{ id, label, description, kind, status, confidence, confidenceAssessment?, value?, unit?, evidenceIds?, dependsOn?, affects?, parentId?, childIds? }],
edges: [{ id, fromNodeId, toNodeId, relationship, confidence, description }],
resolved: string[], // IDs of nodes now marked "resolved"
active: string | null, // ID of the current unknown node (or null)
question: string | null, // Question text for this turn
noQReason: string | null, // Why no question is asked (turns 4+ in complete mode)
summary: string // Current summary text
}
The situationGraph returned by the fixtures matches the real route response shape exactly, ensuring the UI renders identically.
Playwright E2E Scenario Automation
Purpose
Playwright tests replay main mock investigation journeys to validate:
- UI layout transitions — idle → loading → success/error terminal/recovery states
- State machine behaviour — loading overlays, question changes, history rendering
- Terminal states — CompletionCard (genuine completion) and EvidenceLimitCard
- Recovery states — ProviderUnavailableCard, MalformedResponseCard content visibility
- History rendering — turn count increases with each answer submission
These tests do NOT validate reasoning correctness, candidate selection quality, confidence accuracy, or question quality. They are purely UI-layout and state-transition checks.
Quick Start
# Enable mock mode for all Playwright tests
export NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS=true
# Run all E2E tests (headless, screenshot on failure only)
npx playwright test
# Run a single spec file
npx playwright test tests/e2e/happy-path.spec.js
# Run in headed mode for debugging
npx playwright test --headed
# Show browser console logs
PWDEBUG=1 npx playwright test
Note: Playwright is already listed as a devDependency (v1.62.1). No additional package installation is needed.
Screenshot Location
Screenshots are automatically saved to test-results/ in the project root when a test fails. They are named by test title and project. Pass-by-default tests produce no screenshots.
To capture screenshots for every scenario (useful for visual regression), run with the --retries=0 flag and inspect test-results/:
npx playwright test --retries=0
ls test-results/
# → happy-path-mock-mode/
# → recovery-states-mock-mode/
# → long-investigation-mock-mode/
Scenario Fixtures
All scenario data lives in tests/e2e/fixtures/investigation-scenarios.js. This file separates content (central statements, answer sequences, expected headings) from test logic. It is shared across all four spec files:
| Fixture | Central Statement | Mock Mode | Turns | Terminal State |
|---|---|---|---|---|
| Happy path — multi-turn comparison | Product A vs B ratings | default (env var) | 3 turns | No terminal |
| Happy path — complete investigation | Complaints + production | complete |
1 update turn | Investigation complete |
| Evidence limit (stuck early) | Manufacturing quality | default (env var) | Start only | Evidence limit reached |
| Provider error | Complaints + production | error |
Start only | Error panel visible |
| Malformed response | Complaints + production | error |
Start only | Error panel visible |
| Long investigation — European market entry | SaaS market entry | default (env var) | 4 answer turns | Investigation complete |
| Contradiction fixture | Outsourcing consultants | default (env var) | 1 update turn | No terminal |
| Diagnosis fixture | Customer churn | default (env var) | Start only | No terminal |
| Comparison fixture | Product A vs B ratings | default (env var) | 2 update turns | No terminal |
| Prioritisation fixture (team relocation) | London → Manchester | default (env var) | 1 update turn | No terminal |
What Each Spec File Validates
| Spec file | Scenario coverage | Key checks |
|---|---|---|
happy-path.spec.js |
multi-turn, complete, evidence-limit, reset flow, loading transitions | Idle→loading→success transitions, CompletionCard/EvidenceLimitCard visible, history turn count increases, reset button works |
recovery-states.spec.js |
provider error (start + update), malformed response | Error panels visible, workspace stays rendered, session persists after error, multiple successive errors don't crash UI |
long-investigation.spec.js |
long 5-turn, contradiction, diagnosis, comparison, prioritisation | Multi-turn loading state cycling, answer placeholders update, terminal renders without answer textarea, workspace stability across rapid updates |
Accessible Selectors
All tests use role-based selectors (getByRole, getByLabel) and attribute selectors — never CSS class names:
// ✅ Role-based (good)
page.getByRole("button", { name: "Analyse" })
page.getByRole("textbox").first() // textarea with placeholder="Describe..."
page.getByRole("heading", { name: /Investigation complete/ })
// ❌ CSS class selectors (brittle) — NEVER USED
page.locator(".bg-gray-900 .px-6") // fragile to theme changes
Test Structure Pattern
Each test follows a consistent flow:
test("scenario name", async ({ page }) => {
// 1. Navigate
await page.goto("/");
// 2. Enable mock mode (set window globals)
await page.evaluate(() => {
window.__MOCK_ENABLED = true;
window.__MOCK_DELAY = "instant"; // instant | normal | slow
window.__MOCK_SCENARIO = "complete"; // scenario key or ""
});
// 3. Enter central statement
const textarea = page.locator("textarea[placeholder*=Describe]");
await textarea.fill(scenario.centralStatement);
// 4. Click Analyse
await page.getByRole("button", { name: "Analyse" }).click();
// 5. Wait for loading overlay visible, then hidden
await expect(page.locator('[data-testid="loading-overlay"]')).toBeVisible();
await expect(page.locator('[data-testid="loading-overlay"]')).toBeHidden({ timeout: 30_000 });
// 6. Verify workspace state (terminal card, error panel, or question textarea)
await expect(page.locator('[data-testid="reasoning-workspace"]')).toBeVisible();
});
Playwright Config Reference
See playwright.config.js for the full configuration. Key settings:
| Setting | Value | Purpose |
|---|---|---|
testDir |
"./tests/e2e" |
All spec files under this directory |
fullyParallel |
false |
Tests run sequentially to avoid race conditions with mock state |
timeout |
60_000 |
60s per test (long enough for slow delay mode) |
expect.timeout |
15_000 |
15s for individual assertions |
screenshot |
"only-on-failure" |
No disk writes on passing tests |
headless |
true |
CI-safe by default |
projects[0].name |
"mock-mode" |
Distinguishes output in test results directory |
retries |
0 |
No retries — failures are deterministic with mock data |
Adding a New Scenario Fixture
- Add the scenario definition to
tests/e2e/fixtures/investigation-scenarios.js:
const newScenario = {
name: "New fixture description",
centralStatement: "The statement to analyse...",
mockMode: "", // "" = default, or set window.__MOCK_SCENARIO in test
turnCount: 2,
answerSequence: [
{ text: "Answer to first question.", expectedHeadingAfterTurn: "Current investigation" },
],
terminalState: null, // or "Investigation complete" / "Current evidence limit reached"
screenshots: [
{ label: "initial-state", afterAction: "before-start" },
{ label: "after-answer-1", afterAction: "after-answer-1" },
],
};
export const INVESTIGATION_SCENARIOS = [...INVESTIGATION_SCENARIOS, newScenario];
- Add a test case in the appropriate spec file that references the fixture.
Running Tests During Development
While developing mock responses or UI components, run tests with headed mode:
# Watch all specs and re-run on file changes
npx playwright test --headed --ui
The Playwright Test UI (shown via --ui) lets you step through each assertion, inspect the live DOM, and replay failed steps.