feat: connect UI to situation graph start flow
This commit is contained in:
+42
-27
@@ -1,55 +1,70 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("v0.3 UI smoke test with live model response", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000");
|
||||
const BASE_URL = process.env.PLAYWRIGHT_BASE_URL || "http://localhost:3000";
|
||||
|
||||
test.setTimeout(300000);
|
||||
|
||||
test("graph-backed start flow smoke test", async ({ page }) => {
|
||||
await page.goto(BASE_URL);
|
||||
|
||||
// Page should load without error
|
||||
await expect(page.getByText(/Confidence Engine/i)).toBeVisible();
|
||||
|
||||
// Type the scenario
|
||||
const textarea = page.locator("textarea[placeholder*='Describe']");
|
||||
await textarea.fill("Complaints increased by 35% while production increased by 40%.");
|
||||
await textarea.fill(
|
||||
"Complaints increased by 35% while production increased by 40%.",
|
||||
);
|
||||
await expect(textarea).toHaveValue(
|
||||
"Complaints increased by 35% while production increased by 40%.",
|
||||
);
|
||||
|
||||
// Button should be enabled
|
||||
await expect(page.getByRole("button", { name: /Analyse/i })).toBeEnabled();
|
||||
|
||||
// Click Analyse and wait for diagnostics panel
|
||||
// Click Analyse and wait for graph-backed result
|
||||
await page.getByRole("button", { name: /Analyse/i }).click();
|
||||
|
||||
// Wait for result section (ReconstructionView rendered)
|
||||
await expect(page.getByRole("heading", { name: /Next Question/i })).toBeVisible({ timeout: 180000 });
|
||||
|
||||
// Take screenshot of result page
|
||||
await page.screenshot({ path: "tests-results/smoke-v0.3.png", fullPage: true });
|
||||
await expect(
|
||||
page.getByRole("heading", { name: /Selected Question/i }),
|
||||
).toBeVisible({ timeout: 180000 });
|
||||
await expect(
|
||||
page.getByRole("heading", { name: /Situation Graph/i }),
|
||||
).toBeVisible({ timeout: 180000 });
|
||||
await expect(page.getByText(/Central statement/i)).toBeVisible();
|
||||
await expect(page.getByText(/Active unknown/i)).toBeVisible();
|
||||
await expect(page.getByText(/Error:/i)).toHaveCount(0);
|
||||
|
||||
// Verify diagnostics panel exists and contains relevant info
|
||||
const diagPanel = page.locator('details summary').first();
|
||||
if (await diagPanel.isVisible()) {
|
||||
console.log("Raw response viewer:", await diagPanel.innerText().catch(() => "not visible"));
|
||||
}
|
||||
const rawJsonToggle = page.getByText(/Raw graph JSON/i);
|
||||
await expect(rawJsonToggle).toBeVisible();
|
||||
await rawJsonToggle.click();
|
||||
await expect(page.getByText(/centralStatement/i)).toBeVisible();
|
||||
|
||||
// Get full body text for verification
|
||||
const bodyText = await page.locator("body").innerText();
|
||||
|
||||
|
||||
console.log("\n=== UI Smoke Test Results ===");
|
||||
console.log("Page title:", await page.title());
|
||||
console.log("Body content length:", bodyText.length);
|
||||
|
||||
|
||||
// Check key content indicators
|
||||
const hasNextQ = bodyText.includes("Next Question");
|
||||
const hasComplaints = bodyText.includes("Complaint") || bodyText.includes("complaint");
|
||||
const hasProduction = bodyText.includes("production") || bodyText.includes("Production");
|
||||
const hasRateContext = bodyText.toLowerCase().includes("rate") ||
|
||||
bodyText.toLowerCase().includes("unit") ||
|
||||
bodyText.toLowerCase().includes("denominator") ||
|
||||
bodyText.toLowerCase().includes("per-unit");
|
||||
|
||||
console.log("Has Next Question heading:", hasNextQ);
|
||||
const hasSelectedQuestion = bodyText.includes("Selected Question");
|
||||
const hasComplaints =
|
||||
bodyText.includes("Complaint") || bodyText.includes("complaint");
|
||||
const hasProduction =
|
||||
bodyText.includes("production") || bodyText.includes("Production");
|
||||
const hasRateContext =
|
||||
bodyText.toLowerCase().includes("rate") ||
|
||||
bodyText.toLowerCase().includes("unit") ||
|
||||
bodyText.toLowerCase().includes("denominator") ||
|
||||
bodyText.toLowerCase().includes("per-unit");
|
||||
|
||||
console.log("Has selected question heading:", hasSelectedQuestion);
|
||||
console.log("Has complaints reference:", hasComplaints);
|
||||
console.log("Has production reference:", hasProduction);
|
||||
console.log("Has rate context (rate/unit/denominator):", hasRateContext);
|
||||
|
||||
// Basic structural checks
|
||||
expect(bodyText.length).toBeGreaterThan(200);
|
||||
expect(hasNextQ).toBe(true);
|
||||
}, { timeout: 300000 });
|
||||
expect(hasSelectedQuestion).toBe(true);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
import React from "react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import DiagnosticsView from "@/components/diagnostics-view.jsx";
|
||||
import SituationGraphView from "@/components/situation-graph-view.jsx";
|
||||
import {
|
||||
ScenarioResultPanels,
|
||||
submitScenarioForStartCase,
|
||||
} from "@/components/scenario-form.jsx";
|
||||
|
||||
function makeGraphResult(overrides = {}) {
|
||||
return {
|
||||
success: true,
|
||||
situationGraph: {
|
||||
centralStatement: "Complaints increased while production increased.",
|
||||
currentSummary:
|
||||
"Nodes: 2 observation, 1 unknown | Edges: 2 total | Unknowns: 1 unresolved",
|
||||
activeUnknownNodeId: "n-unknown",
|
||||
resolvedNodeIds: [],
|
||||
nodes: [
|
||||
{
|
||||
id: "n-1",
|
||||
label: "Complaints up 35%",
|
||||
description: "Complaints increased by 35%",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
value: 35,
|
||||
unit: "%",
|
||||
},
|
||||
{
|
||||
id: "n-2",
|
||||
label: "Production up 40%",
|
||||
description: "Production increased by 40%",
|
||||
kind: "observation",
|
||||
status: "supported",
|
||||
confidence: "high",
|
||||
value: 40,
|
||||
unit: "%",
|
||||
},
|
||||
{
|
||||
id: "n-unknown",
|
||||
label: "Complaint rate denominator",
|
||||
description: "Need the denominator for complaint rate",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
value: null,
|
||||
unit: null,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{ id: "e1", fromNodeId: "n-1", toNodeId: "n-unknown" },
|
||||
{ id: "e2", fromNodeId: "n-2", toNodeId: "n-unknown" },
|
||||
],
|
||||
},
|
||||
selectedQuestion: {
|
||||
question: "What denominator is being used for the complaint rate?",
|
||||
},
|
||||
diagnostics: {
|
||||
modelName: "test",
|
||||
responseDurationMs: 1234,
|
||||
validationStatus: "valid",
|
||||
promptVersion: "test-prompt",
|
||||
nodeCount: 3,
|
||||
edgeCount: 2,
|
||||
graphReferenceValidation: { valid: true, errors: [] },
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("scenario-form UI helpers", () => {
|
||||
it("submits to /api/cases/start", async () => {
|
||||
const fetchImpl = vi.fn().mockResolvedValue({ ok: true });
|
||||
|
||||
await submitScenarioForStartCase(fetchImpl, "Scenario text");
|
||||
|
||||
expect(fetchImpl).toHaveBeenCalledWith(
|
||||
"/api/cases/start",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("graph-backed UI rendering", () => {
|
||||
it("renders central statement from successful graph response", () => {
|
||||
const data = makeGraphResult();
|
||||
const html = renderToStaticMarkup(
|
||||
<SituationGraphView
|
||||
situationGraph={data.situationGraph}
|
||||
selectedQuestion={data.selectedQuestion}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("Central statement");
|
||||
expect(html).toContain("Complaints increased while production increased.");
|
||||
});
|
||||
|
||||
it("renders active unknown", () => {
|
||||
const data = makeGraphResult();
|
||||
const html = renderToStaticMarkup(
|
||||
<SituationGraphView
|
||||
situationGraph={data.situationGraph}
|
||||
selectedQuestion={data.selectedQuestion}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("Active unknown");
|
||||
expect(html).toContain("Complaint rate denominator");
|
||||
});
|
||||
|
||||
it("renders selected question exactly once", () => {
|
||||
const data = makeGraphResult();
|
||||
const html = renderToStaticMarkup(
|
||||
<SituationGraphView
|
||||
situationGraph={data.situationGraph}
|
||||
selectedQuestion={data.selectedQuestion}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
html.match(/What denominator is being used for the complaint rate\?/g) ||
|
||||
[],
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("renders diagnostics", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<DiagnosticsView result={makeGraphResult()} />,
|
||||
);
|
||||
|
||||
expect(html).toContain("Diagnostics");
|
||||
expect(html).toContain("test");
|
||||
expect(html).toContain("1234ms");
|
||||
expect(html).toContain("Node count");
|
||||
expect(html).toContain("Edge count");
|
||||
expect(html).toContain("Graph references");
|
||||
});
|
||||
|
||||
it("hides empty sections", () => {
|
||||
const base = makeGraphResult();
|
||||
const result = makeGraphResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...base.situationGraph,
|
||||
activeUnknownNodeId: null,
|
||||
nodes: [base.situationGraph.nodes[0]],
|
||||
edges: [],
|
||||
},
|
||||
});
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
<SituationGraphView
|
||||
situationGraph={result.situationGraph}
|
||||
selectedQuestion={result.selectedQuestion}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).not.toContain("Selected Question");
|
||||
expect(html).not.toContain("Active unknown");
|
||||
});
|
||||
|
||||
it("displays API error clearly", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ScenarioResultPanels
|
||||
status="error"
|
||||
result={{ error: "Invalid start-case request" }}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("Error: Invalid start-case request");
|
||||
});
|
||||
|
||||
it("renders expandable raw graph JSON", () => {
|
||||
const data = makeGraphResult();
|
||||
const html = renderToStaticMarkup(
|
||||
<SituationGraphView
|
||||
situationGraph={data.situationGraph}
|
||||
selectedQuestion={data.selectedQuestion}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("Raw graph JSON");
|
||||
expect(html).toContain(""centralStatement"");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user