Source: pipeline/prompts/journeyPrompt.js

/**
 * journeyPrompt.js — Multi-page journey prompt template
 *
 * Builds the AI prompt for generating end-to-end Playwright tests that span
 * multiple pages (e.g. Login → Dashboard → Action → Logout).
 *
 * When the journey includes `_observedActions` (produced by the state explorer
 * in `pipeline/stateExplorer.js`), the prompt includes the exact sequence of
 * actions that were observed to work during exploration. This gives the AI
 * concrete evidence of what interactions succeed, rather than having to guess.
 *
 * Returns { system, user } for structured message support.
 */

import { isLocalProvider } from "../../aiProvider.js";
import { resolveTestCountInstruction } from "../promptHelpers.js";
import { buildSystemPrompt, buildOutputSchemaBlock } from "./outputSchema.js";

/**
 * Format observed actions from the state explorer into a prompt block.
 * Only included when `journey._observedActions` is present (state explorer mode).
 *
 * @param {Array} observedActions — from flowToJourney()._observedActions
 * @returns {string}
 */
function buildObservedActionsBlock(observedActions) {
  if (!observedActions || observedActions.length === 0) return "";

  const steps = observedActions.map((act, i) => {
    let desc = `  Step ${i + 1}: On ${act.onPage}`;
    if (act.actionType === "fill") {
      desc += `, filled "${act.target}" with "${act.value}"`;
    } else if (act.actionType === "click" || act.actionType === "submit") {
      desc += `, clicked "${act.target}"`;
    } else if (act.actionType === "select") {
      desc += `, selected option in "${act.target}"`;
    } else if (act.actionType === "check") {
      desc += `, checked "${act.target}"`;
    } else {
      desc += `, performed ${act.actionType} on "${act.target}"`;
    }
    // Append selector hints so the AI can write targeted Playwright code
    // instead of guessing from ambiguous text labels alone.
    const hints = [];
    if (act.role)      hints.push(`role="${act.role}"`);
    if (act.testId)    hints.push(`data-testid="${act.testId}"`);
    if (act.ariaLabel) hints.push(`aria-label="${act.ariaLabel}"`);
    if (act.selector)  hints.push(`selector: ${act.selector}`);
    if (hints.length > 0) desc += `  [${hints.join(", ")}]`;
    if (act.resultPage && act.resultPage !== act.onPage) {
      desc += ` → navigated to ${act.resultPage}`;
    }
    return desc;
  }).join("\n");

  return `
OBSERVED ACTIONS (verified during live exploration — these interactions actually worked):
${steps}

IMPORTANT: Use the observed actions above as the basis for the POSITIVE test path.
The AI saw these actions succeed in a real browser. Reproduce them faithfully in playwrightCode.
When a step includes selector hints (role, data-testid, aria-label, selector), prefer those
over guessing from the text label — they were verified to resolve during exploration.
For NEGATIVE tests, vary the inputs (empty fields, wrong values) but use the same selectors/elements.`;
}

export function buildJourneyPrompt(journey, allSnapshots, { testCount = "ai_decides" } = {}) {
  const local = isLocalProvider();
  const pageContexts = journey.pages.map(page => {
    // Prefer fingerprint lookup for state-explorer journeys (multiple states
    // at the same URL). Falls back to URL lookup for legacy crawl journeys.
    const snapshot = (page._stateFingerprint && allSnapshots[page._stateFingerprint])
      || allSnapshots[page.url];
    // For local models (Ollama ≤8B) keep element data very compact to avoid
    // context overflow (HTTP 500). 5 pages × 4 elements is ~2K tokens — safe
    // for 8K-context models. Cloud models get the full 10 elements.
    const rawElems = (snapshot?.elements || []).slice(0, local ? 4 : 10);
    const elems = local
      ? rawElems.map(e => ({
          tag: e.tag, text: (e.text || "").slice(0, 30), type: e.type,
          role: e.role, name: e.name, testId: e.testId,
        }))
      : rawElems;
    return `
  Page: ${page.url}
  Title: ${page.title}
  Intent: ${page.dominantIntent}
  Key elements: ${JSON.stringify(elems, null, 2)}`;
  }).join("\n---");

  const firstUrl = journey.pages[0]?.url || "";

  // Include observed actions when available (state explorer mode)
  const observedBlock = buildObservedActionsBlock(journey._observedActions);

  const user = `JOURNEY: ${journey.name}
TYPE: ${journey.type}
DESCRIPTION: ${journey.description}

PAGES IN THIS JOURNEY:
${pageContexts}
${observedBlock}

${resolveTestCountInstruction(testCount, local)} end-to-end Playwright tests covering this journey from multiple angles.

Requirements:
1. Cover BOTH positive paths (happy paths) AND negative paths (error states, edge cases)
2. Each test must flow through multiple pages/steps logically
3. Include at least 3 meaningful assertions per test that verify SPECIFIC VISIBLE CONTENT
4. CRITICAL: Each test's playwrightCode MUST be fully self-contained — it MUST start with await page.goto('${firstUrl}', { waitUntil: 'domcontentloaded', timeout: 30000 }). Use the actual URL from the PAGE data above — never a placeholder.
5. Read the actual PAGE DATA above (titles, intents, elements) and assert against REAL content from those pages

${buildOutputSchemaBlock({ isJourney: true, journeyType: journey.type })}`;

  return { system: buildSystemPrompt(), user };
}