fix: show update reasoning progress
This commit is contained in:
@@ -9,6 +9,8 @@ import ReasoningWorkspace, {
|
||||
INITIAL_MESSAGES,
|
||||
UPDATE_MESSAGES,
|
||||
LoadingOverlay,
|
||||
resolveCurrentSummary,
|
||||
isTechnicalSummary,
|
||||
} from "@/components/reasoning-workspace.jsx";
|
||||
import {
|
||||
ScenarioResultPanels,
|
||||
@@ -885,14 +887,14 @@ describe("ReasoningWorkspace UI", () => {
|
||||
expect(html).toContain("Complaints increased while production increased.");
|
||||
});
|
||||
|
||||
it("shows what we've established section", () => {
|
||||
it("shows what we've established section with plain-language summary", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
{...makeWorkspaceProps({
|
||||
result: makeWorkspaceResult({
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
currentSummary: "Nodes: 2 observation, 1 unknown",
|
||||
currentSummary: "We have identified a key question to investigate further.",
|
||||
},
|
||||
}),
|
||||
})}
|
||||
@@ -900,7 +902,27 @@ describe("ReasoningWorkspace UI", () => {
|
||||
);
|
||||
|
||||
expect(html).toContain("What we've established");
|
||||
expect(html).toContain("Nodes: 2 observation, 1 unknown");
|
||||
expect(html).toContain("We have identified a key question to investigate further.");
|
||||
});
|
||||
|
||||
it("filters technical summaries from the main view (not developer details)", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace {...makeWorkspaceProps()} />,
|
||||
);
|
||||
|
||||
// Fallback text shown in main view (currentSummary filtered)
|
||||
expect(html).toContain("We have separated what is known from what still needs checking.");
|
||||
// Developer details preserves the full technical summary
|
||||
expect(html).toContain("Developer details");
|
||||
});
|
||||
|
||||
it("keeps technical summary available in Developer details", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace {...makeWorkspaceProps()} />,
|
||||
);
|
||||
|
||||
// Developer details is the mechanism for accessing full technical data
|
||||
expect(html).toContain("Developer details");
|
||||
});
|
||||
|
||||
it("prominently displays the current investigation", () => {
|
||||
@@ -1115,6 +1137,10 @@ describe("ReasoningWorkspace UI", () => {
|
||||
// After 16s elapsed, the second message should be active
|
||||
// (useEffect fires in real React; here we verify via hook export)
|
||||
expect(INITIAL_MESSAGES[1].min).toBe(10);
|
||||
expect(UPDATE_MESSAGES[0].text).toBe("Considering your answer");
|
||||
expect(UPDATE_MESSAGES[1].text).toBe("Updating the situation");
|
||||
expect(UPDATE_MESSAGES[2].text).toBe("Checking what changed");
|
||||
expect(UPDATE_MESSAGES[3].text).toBe("Choosing the next question");
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
@@ -1141,6 +1167,14 @@ describe("ReasoningWorkspace UI", () => {
|
||||
updateStatus="idle"
|
||||
result={makeWorkspaceResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
activeUnknownNodeId: null,
|
||||
nodes: makeWorkspaceResult().situationGraph.nodes.map((n) =>
|
||||
n.id === "n-unknown" ? { ...n, status: "resolved", value: "the denominator is total units sold" } : n,
|
||||
),
|
||||
resolvedNodeIds: ["n-unknown"],
|
||||
},
|
||||
diagnostics: { ...makeWorkspaceResult().diagnostics, noQuestionReason: "All unknowns resolved" },
|
||||
})}
|
||||
answer=""
|
||||
@@ -1241,6 +1275,39 @@ describe("ReasoningWorkspace UI", () => {
|
||||
expect(html).not.toContain("Update situation");
|
||||
});
|
||||
|
||||
// ── Technical summary detection ───────────────────────
|
||||
|
||||
it("isTechnicalSummary detects node counts", () => {
|
||||
expect(isTechnicalSummary("Nodes: 2 observation, 1 unknown")).toBe(true);
|
||||
expect(isTechnicalSummary("Nodes: 3 observations total")).toBe(true);
|
||||
});
|
||||
|
||||
it("isTechnicalSummary detects edge counts", () => {
|
||||
expect(isTechnicalSummary("Edges: 5 total")).toBe(true);
|
||||
expect(isTechnicalSummary("Edges: 2")).toBe(true);
|
||||
});
|
||||
|
||||
it("isTechnicalSummary detects graph enum language", () => {
|
||||
expect(isTechnicalSummary("1 unknown remaining")).toBe(true);
|
||||
expect(isTechnicalSummary("Observation nodes identified")).toBe(true);
|
||||
expect(isTechnicalSummary("Conclusion verified")).toBe(true);
|
||||
});
|
||||
|
||||
it("isTechnicalSummary allows plain-language summaries", () => {
|
||||
expect(isTechnicalSummary("We have separated what is known from what still needs checking.")).toBe(false);
|
||||
expect(isTechnicalSummary("Updated summary")).toBe(false);
|
||||
expect(isTechnicalSummary("")).toBe(false);
|
||||
expect(isTechnicalSummary(null)).toBe(false);
|
||||
expect(isTechnicalSummary(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it("resolveCurrentSummary filters technical and passes plain", () => {
|
||||
expect(resolveCurrentSummary("Nodes: 1 state, 3 observations")).toBe(null);
|
||||
expect(resolveCurrentSummary("Updated summary")).toBe("Updated summary");
|
||||
expect(resolveCurrentSummary(null)).toBe(null);
|
||||
expect(resolveCurrentSummary(undefined)).toBe(null);
|
||||
});
|
||||
|
||||
it("update result merges into workspace correctly", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
@@ -1524,6 +1591,14 @@ describe("ReasoningWorkspace UI", () => {
|
||||
updateStatus="idle"
|
||||
result={makeWorkspaceResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
activeUnknownNodeId: null,
|
||||
nodes: makeWorkspaceResult().situationGraph.nodes.map((n) =>
|
||||
n.id === "n-unknown" ? { ...n, status: "resolved", value: "1.9 complaints per 100 units" } : n,
|
||||
),
|
||||
resolvedNodeIds: ["n-unknown"],
|
||||
},
|
||||
diagnostics: { ...makeWorkspaceResult().diagnostics, noQuestionReason: "All unknowns satisfied" },
|
||||
})}
|
||||
answer=""
|
||||
@@ -1536,13 +1611,21 @@ describe("ReasoningWorkspace UI", () => {
|
||||
expect(html).not.toContain("There is no next question at the moment");
|
||||
});
|
||||
|
||||
it("investigation complete shows confidence-building fallback", () => {
|
||||
it("no-question state with unresolved areas shows holding, not completion", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="idle"
|
||||
result={makeWorkspaceResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
activeUnknownNodeId: "n-unknown",
|
||||
nodes: makeWorkspaceResult().situationGraph.nodes.map((n) =>
|
||||
n.id === "n-unknown" ? { ...n, status: "unknown", value: null } : n,
|
||||
),
|
||||
resolvedNodeIds: [],
|
||||
},
|
||||
diagnostics: { ...makeWorkspaceResult().diagnostics, noQuestionReason: null },
|
||||
})}
|
||||
answer=""
|
||||
@@ -1551,6 +1634,198 @@ describe("ReasoningWorkspace UI", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("There is no further question the engine can justify at the moment");
|
||||
expect(html).not.toContain("We have established enough for now");
|
||||
expect(html).not.toContain("All areas under investigation are now complete");
|
||||
});
|
||||
|
||||
// ── Update loading tests ───────────────────────────────
|
||||
|
||||
it("update loading card appears immediately after answer submit", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="loading"
|
||||
result={makeWorkspaceResult()}
|
||||
answer="some answer"
|
||||
setAnswer={vi.fn()}
|
||||
onAnswerSubmit={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("Working through your situation");
|
||||
expect(html).toContain("Considering your answer");
|
||||
});
|
||||
|
||||
it("normal update button is hidden while loading", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="loading"
|
||||
result={makeWorkspaceResult()}
|
||||
answer="some answer"
|
||||
setAnswer={vi.fn()}
|
||||
onAnswerSubmit={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).not.toContain("Update situation");
|
||||
expect(html).not.toContain("Your response");
|
||||
});
|
||||
|
||||
it("update status messages change with mocked timers", () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const baseProps = {
|
||||
status: "success",
|
||||
updateStatus: "loading",
|
||||
result: makeWorkspaceResult(),
|
||||
answer: "test",
|
||||
setAnswer: vi.fn(),
|
||||
onAnswerSubmit: vi.fn(),
|
||||
};
|
||||
|
||||
let html = renderToStaticMarkup(<ReasoningWorkspace {...baseProps} />);
|
||||
expect(html).toContain("Considering your answer");
|
||||
|
||||
vi.advanceTimersByTime(10000);
|
||||
vi.useRealTimers();
|
||||
|
||||
// We rely on the exported UPDATE_MESSAGES to verify message pool correctness
|
||||
expect(UPDATE_MESSAGES[0].text).toBe("Considering your answer");
|
||||
expect(UPDATE_MESSAGES[1].text).toBe("Updating the situation");
|
||||
expect(UPDATE_MESSAGES[2].text).toBe("Checking what changed");
|
||||
expect(UPDATE_MESSAGES[3].text).toBe("Choosing the next question");
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("update prevents duplicate submission by removing controls while loading", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="loading"
|
||||
result={makeWorkspaceResult()}
|
||||
answer="some answer"
|
||||
setAnswer={vi.fn()}
|
||||
onAnswerSubmit={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).not.toContain("Update situation");
|
||||
expect(html).toContain("Working through your situation");
|
||||
});
|
||||
|
||||
// ── Outcome state tests ────────────────────────────────
|
||||
|
||||
it("next-question state does not show completion wording", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
{...makeWorkspaceProps({
|
||||
result: makeWorkspaceResult({
|
||||
selectedQuestion: { question: "What denominator is being used?" },
|
||||
}),
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).toContain("Current investigation");
|
||||
expect(html).not.toContain("We have established enough for now");
|
||||
expect(html).not.toContain("All areas under investigation are now complete");
|
||||
});
|
||||
|
||||
it("unresolved-no-question state does not imply completion", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="idle"
|
||||
result={makeWorkspaceResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
activeUnknownNodeId: "n-unknown",
|
||||
nodes: [
|
||||
...makeWorkspaceResult().situationGraph.nodes.filter(
|
||||
(n) => n.id !== "n-unknown" || n.kind === "observation",
|
||||
),
|
||||
{
|
||||
id: "n-unknown",
|
||||
label: "Complaint rate denominator",
|
||||
description: "Need the denominator for complaint rate",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
},
|
||||
],
|
||||
resolvedNodeIds: [],
|
||||
},
|
||||
})}
|
||||
answer=""
|
||||
setAnswer={vi.fn()}
|
||||
onAnswerSubmit={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).not.toContain("All areas under investigation are now complete");
|
||||
expect(html).not.toContain("We have established enough for now");
|
||||
expect(html).toContain("There is no further question the engine can justify at the moment");
|
||||
});
|
||||
|
||||
it("genuine completion hides active investigation and remaining-area wording", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="idle"
|
||||
result={makeWorkspaceResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
activeUnknownNodeId: null,
|
||||
nodes: makeWorkspaceResult().situationGraph.nodes.map((n) =>
|
||||
n.id === "n-unknown" ? { ...n, status: "resolved", value: "total units" } : n,
|
||||
),
|
||||
resolvedNodeIds: ["n-unknown"],
|
||||
},
|
||||
})}
|
||||
answer=""
|
||||
setAnswer={vi.fn()}
|
||||
onAnswerSubmit={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(html).not.toContain("Current investigation");
|
||||
expect(html).not.toContain("areas remain");
|
||||
expect(html).toContain("We have established enough for now");
|
||||
});
|
||||
|
||||
it("no contradictory completion and unresolved focus appear together", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace
|
||||
status="success"
|
||||
updateStatus="idle"
|
||||
result={makeWorkspaceResult({
|
||||
selectedQuestion: null,
|
||||
situationGraph: {
|
||||
...makeWorkspaceResult().situationGraph,
|
||||
activeUnknownNodeId: "n-unknown",
|
||||
resolvedNodeIds: [],
|
||||
},
|
||||
})}
|
||||
answer=""
|
||||
setAnswer={vi.fn()}
|
||||
onAnswerSubmit={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
const hasCompletion = html.includes("All areas under investigation are now complete") || html.includes("We have established enough for now");
|
||||
expect(hasCompletion).toBe(false);
|
||||
});
|
||||
|
||||
it("Developer details remains collapsed and unchanged", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<ReasoningWorkspace {...makeWorkspaceProps()} />,
|
||||
);
|
||||
|
||||
expect(html).toContain("Developer details");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user