255 lines
8.6 KiB
JavaScript
255 lines
8.6 KiB
JavaScript
/**
|
|
* Investigation scenarios — shared fixture data for all E2E Playwright tests.
|
|
*
|
|
* Each scenario defines:
|
|
* - name / centralStatement (the text entered into the scenario textarea)
|
|
* - mockMode (which mock mode to activate: "complete" | "error" | "" )
|
|
* - answerSequence [ { text, expectedHeadingAfterTurn } ]
|
|
* - expectedTerminalState (final card heading)
|
|
* - screenshots [ { label, expectedVisible } ]
|
|
*
|
|
* The test scripts drive the application through these scenarios using the
|
|
* Developer-details scenario selector (UI mock mode), or by setting env vars.
|
|
*/
|
|
|
|
/* ── Shared constants ─────────────────────────────────────── */
|
|
|
|
const BASE_SCENARIO_TEXT =
|
|
"Complaints increased by 35% while production increased by 40%.";
|
|
|
|
/* ── Scenario definitions ─────────────────────────────────── */
|
|
|
|
/**
|
|
* Multi-turn investigation with at least 2 turns.
|
|
* Uses the default sequential-turn mock (no special scenario flag).
|
|
*/
|
|
const happyPathComparison = {
|
|
name: "Happy path — multi-turn comparison",
|
|
centralStatement:
|
|
"Product A has a 4.2 star average rating while Product B averages 4.6 stars across 10,000+ reviews each.",
|
|
mockMode: "", // "" → sequential turns via default mock
|
|
turnCount: 3,
|
|
answerSequence: [
|
|
{
|
|
text: "Yes, both are standard 5-star scales.",
|
|
expectedHeadingAfterTurn: "Current investigation",
|
|
},
|
|
{
|
|
text: "The gap persists across verified purchase reviews.",
|
|
expectedHeadingAfterTurn: "Current investigation",
|
|
},
|
|
],
|
|
terminalState: null, // does not reach a terminal state (unknowns remain)
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "after-first-turn", afterAction: "after-answer-1" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Complete investigation — all unknowns resolved.
|
|
* Uses the "complete" mock mode (jumps to terminal after start).
|
|
*/
|
|
const happyPathComplete = {
|
|
name: "Happy path — complete investigation",
|
|
centralStatement: BASE_SCENARIO_TEXT,
|
|
mockMode: "complete",
|
|
turnCount: 1, // start gives turn 0, first update → final state
|
|
answerSequence: [
|
|
{
|
|
text: "The complaint rate fell from 2.0 per 100 to 1.9 per 100 units.",
|
|
expectedHeadingAfterTurn: "Investigation complete",
|
|
},
|
|
],
|
|
terminalState: "Investigation complete",
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "terminal-state", afterAction: "after-answer-1" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Evidence-limit state — unknowns remain but no next question.
|
|
*/
|
|
const evidenceLimit = {
|
|
name: "Evidence limit (stuck early)",
|
|
centralStatement:
|
|
"Should a mid-sized manufacturing company invest in automated quality inspection?",
|
|
mockMode: "", // single-turn default scenario
|
|
turnCount: 1,
|
|
answerSequence: [], // no answers needed — stuck after start
|
|
terminalState: "Current evidence limit reached",
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "terminal-state", afterAction: "after-start" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Provider error — all calls return a structured provider error.
|
|
*/
|
|
const providerError = {
|
|
name: "Provider error",
|
|
centralStatement: BASE_SCENARIO_TEXT,
|
|
mockMode: "error",
|
|
turnCount: 0, // start call also errors
|
|
answerSequence: [],
|
|
terminalState: null,
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "terminal-state", afterAction: "after-start" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Malformed response — the start produces a malformed shape.
|
|
* (We simulate this via error mode which skips all structured output.)
|
|
*/
|
|
const malformedResponse = {
|
|
name: "Malformed response",
|
|
centralStatement: BASE_SCENARIO_TEXT,
|
|
mockMode: "error", // reuses error mode for malformed path
|
|
turnCount: 0,
|
|
answerSequence: [],
|
|
terminalState: null,
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "terminal-state", afterAction: "after-start" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Long investigation — market entry, 5 turns.
|
|
*/
|
|
const longInvestigation = {
|
|
name: "Long investigation — European market entry",
|
|
centralStatement:
|
|
"Should we enter the European market with our SaaS analytics platform?",
|
|
mockMode: "", // sequential turns from scenario data (long array)
|
|
turnCount: 5,
|
|
answerSequence: [
|
|
{ text: "The market is valued at approximately €8B and growing.", expectedHeadingAfterTurn: "Current investigation" },
|
|
{ text: "Our platform does not currently support EU data residency.", expectedHeadingAfterTurn: "Current investigation" },
|
|
{ text: "Achieving compliance would take 6 months and $500K engineering investment.", expectedHeadingAfterTurn: "Current investigation" },
|
|
{ text: "Our real-time collaboration feature has no direct European equivalent.", expectedHeadingAfterTurn: "Investigation complete", isLast: true },
|
|
],
|
|
terminalState: "Investigation complete",
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "mid-investigation", afterAction: "after-answer-2" },
|
|
{ label: "terminal-state", afterAction: "after-answer-4" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Contradiction fixture — two opposing recommendations.
|
|
*/
|
|
const contradictionFixture = {
|
|
name: "Contradiction fixture",
|
|
centralStatement:
|
|
"Two consultants provided opposite recommendations about whether to outsource IT operations.",
|
|
mockMode: "",
|
|
turnCount: 2,
|
|
answerSequence: [
|
|
{ text: "Consultant A focused on cost over 2 years; Consultant B focused on quality over 5+ years.", expectedHeadingAfterTurn: "Current investigation" },
|
|
],
|
|
terminalState: null,
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "after-answer-1", afterAction: "after-answer-1" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Diagnosis fixture — churn diagnosis.
|
|
*/
|
|
const diagnosisFixture = {
|
|
name: "Diagnosis fixture",
|
|
centralStatement:
|
|
"Customer churn increased from 2% to 5% monthly over the last quarter.",
|
|
mockMode: "",
|
|
turnCount: 1,
|
|
answerSequence: [],
|
|
terminalState: null,
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "terminal-state", afterAction: "after-start" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Comparison fixture — product ratings with 3 turns.
|
|
*/
|
|
const comparisonFixture = {
|
|
name: "Comparison fixture",
|
|
centralStatement:
|
|
"Product A has a 4.2 star average rating while Product B averages 4.6 stars across 10,000+ reviews each.",
|
|
mockMode: "",
|
|
turnCount: 3,
|
|
answerSequence: [
|
|
{ text: "Both use the standard 5-star customer review scale.", expectedHeadingAfterTurn: "Current investigation" },
|
|
{ text: "Verified purchase gap remains approximately 0.3 stars.", expectedHeadingAfterTurn: "Current investigation" },
|
|
],
|
|
terminalState: null,
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "after-answer-1", afterAction: "after-answer-1" },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Prioritisation fixture — decision investigation.
|
|
*/
|
|
const prioritisationFixture = {
|
|
name: "Prioritisation fixture (team relocation)",
|
|
centralStatement:
|
|
"Should I relocate my engineering team from London to Manchester?",
|
|
mockMode: "",
|
|
turnCount: 2,
|
|
answerSequence: [
|
|
{ text: "Manchester has a growing tech ecosystem with 500+ roles posted monthly.", expectedHeadingAfterTurn: "Current investigation" },
|
|
],
|
|
terminalState: null,
|
|
screenshots: [
|
|
{ label: "initial-state", afterAction: "before-start" },
|
|
{ label: "after-answer-1", afterAction: "after-answer-1" },
|
|
],
|
|
};
|
|
|
|
/* ── Registry for dynamic test generation ─────────────────── */
|
|
|
|
export const INVESTIGATION_SCENARIOS = [
|
|
happyPathComparison,
|
|
happyPathComplete,
|
|
evidenceLimit,
|
|
providerError,
|
|
malformedResponse,
|
|
longInvestigation,
|
|
contradictionFixture,
|
|
diagnosisFixture,
|
|
comparisonFixture,
|
|
prioritisationFixture,
|
|
];
|
|
|
|
export const SCENARIO_LOOKUP = {};
|
|
INVESTIGATION_SCENARIOS.forEach((s) => {
|
|
SCENARIO_LOOKUP[s.name] = s;
|
|
});
|
|
|
|
/**
|
|
* Helper to get the answer text for a given turn index.
|
|
* Returns null if there is no answer for that turn.
|
|
*/
|
|
export function getAnswerForTurn(scenario, turnIndex) {
|
|
return scenario.answerSequence[turnIndex]?.text || null;
|
|
}
|
|
|
|
/**
|
|
* Return the list of screenshots in execution order.
|
|
*/
|
|
export function getScreenshotsOrdered(screenshotList) {
|
|
// Preserve insertion order from scenario definition
|
|
return screenshotList;
|
|
}
|
|
|
|
export default INVESTIGATION_SCENARIOS; |