292 lines
13 KiB
Markdown
292 lines
13 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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: false`
|
|
- `stage: "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/start` and `/api/cases/update`. Any other URL is passed through unchanged.
|
|
- Uses `window.__MOCK_*` globals (set by `useMockGlobals()` hook in ScenarioForm) for runtime env access from the browser. Falls back to `process.env.*` on the server side.
|
|
- **UI integration** in `scenario-form.jsx`: one compile-time boolean (`MOCK_ENABLED`), one React hook (`useMockGlobals`), two ternary replacements of `fetch`. 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 by `useMockGlobals`).
|
|
- 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.local` commit**: The `.gitignore` already excludes `.env.local`. Copy from `.env.example` for local overrides.
|
|
- **Real path preserved**: Setting `NEXT_PUBLIC_CONFIDENCE_ENGINE_MOCKS` to 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:
|
|
|
|
```js
|
|
{
|
|
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
|
|
|
|
```bash
|
|
# 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/`:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```js
|
|
// ✅ 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:
|
|
|
|
```js
|
|
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
|
|
|
|
1. Add the scenario definition to `tests/e2e/fixtures/investigation-scenarios.js`:
|
|
|
|
```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];
|
|
```
|
|
|
|
2. 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:
|
|
|
|
```bash
|
|
# 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.
|