import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; const ROOT = path.resolve(process.cwd(), "web/storyforge-web-v4"); const HTML = fs.readFileSync(path.join(ROOT, "index.html"), "utf8"); const APP = fs.readFileSync(path.join(ROOT, "assets/app.js"), "utf8"); const CSS = fs.readFileSync(path.join(ROOT, "assets/styles.css"), "utf8"); function extractBetween(source, startToken, endToken) { const start = source.indexOf(startToken); assert.notEqual(start, -1, `Missing token: ${startToken}`); const end = source.indexOf(endToken, start); assert.notEqual(end, -1, `Missing token: ${endToken}`); return source.slice(start, end); } test("settings navigation and screen are real routes", () => { assert.match(HTML, /data-screen-target="settings"/); assert.match(HTML, /data-screen="settings"/); assert.match(APP, /function renderSettingsScreen\(/); assert.match(APP, /screenMap\.settings\.innerHTML = renderSettingsScreen\(\);/); assert.match(APP, /window\.addEventListener\("hashchange"/); }); test("strategy navigation and screen are real routes", () => { assert.match(HTML, /data-screen-target="strategy"/); assert.match(HTML, /data-screen="strategy"/); assert.match(APP, /function renderStrategyScreen\(/); assert.match(APP, /screenMap\.strategy\.innerHTML = renderStrategyScreen\(\);/); }); test("automation screen stays user-facing and excludes admin-only panels", () => { const source = extractBetween(APP, "function renderAutomationScreen()", "function renderOwnedScreen()"); assert.doesNotMatch(source, /renderTenantQuotaPanel\(/); assert.doesNotMatch(source, /renderOneLinerActionRegistryPanel\(/); assert.doesNotMatch(source, /renderAdminOpsPanel\(/); assert.match(source, /renderDetailTabs\("automationDetailTab"/); }); test("agent screen excludes quota and registry panels and uses page tabs", () => { const source = extractBetween(APP, "function renderPlaybookScreen()", "function renderProductionScreen()"); assert.doesNotMatch(source, /renderTenantQuotaPanel\(/); assert.doesNotMatch(source, /renderOneLinerActionRegistryPanel\(/); assert.match(source, /renderDetailTabs\("playbookDetailTab"/); assert.match(source, /renderGovernanceSummaryCard\(/); assert.match(source, /open-user-global-policy/); assert.match(source, /open-user-platform-policy/); assert.match(source, /active_admin_override_notice/); }); test("discovery, production, and admin screens use page tabs for heavy content", () => { const discovery = extractBetween(APP, "function renderDiscoveryScreen()", "function renderTrackingScreen()"); const production = extractBetween(APP, "function renderProductionScreen()", "function renderReviewScreen()"); const admin = extractBetween(APP, "function renderAdminWorkbenchScreen()", "function renderDashboardScreen()"); const strategy = extractBetween(APP, "function renderStrategyScreen()", "function renderCreditsScreen()"); assert.match(discovery, /renderDetailTabs\("discoveryDetailTab"/); assert.match(production, /renderDetailTabs\("productionDetailTab"/); assert.match(admin, /renderDetailTabs\("adminWorkbenchTab"/); assert.match(admin, /renderAdminGovernanceSummaryPanel\(/); assert.match(admin, /覆盖与审计/); assert.match(strategy, /renderDetailTabs\("strategyDetailTab"/); assert.match(strategy, /renderPolicyAuditFeed\(/); }); test("projects screen uses an adaptive project grid instead of a fixed three-column squeeze", () => { const projects = extractBetween(APP, "function renderProjectsScreen()", "function getActiveDetailTab("); assert.match(projects, /project-status-grid/); assert.match(CSS, /\.project-status-grid\s*\{[\s\S]*repeat\(auto-fit,\s*minmax\(260px,\s*1fr\)\)/); assert.match(CSS, /\.entity-card\.pad\s*\{[\s\S]*padding:\s*15px/); }); test("dashboard and project screens distinguish auto-connecting from truly disconnected", () => { const dashboard = extractBetween(APP, "function renderDashboardScreen()", "function renderProjectsScreen()"); const projects = extractBetween(APP, "function renderProjectsScreen()", "function getActiveDetailTab("); const helper = extractBetween(APP, "function isAutoConnectionPending()", "async function refreshFromAuthModal()"); assert.match(APP, /function isAutoConnectionPending\(\)/); assert.match(APP, /function renderAutoConnectingScreen\(/); assert.match(helper, /正在自动连接工作区/); assert.match(dashboard, /isAutoConnectionPending\(\)/); assert.match(dashboard, /renderAutoConnectingScreen\("项目总台"/); assert.match(projects, /isAutoConnectionPending\(\)/); assert.match(projects, /renderAutoConnectingScreen\("我的项目"/); }); test("ensureAutoSession re-renders immediately when auto-connect starts", () => { const source = extractBetween(APP, "async function ensureAutoSession(options = {})", "async function refreshFromAuthModal()"); assert.match(source, /appState\.autoConnectAttempted = true;/); assert.match(source, /renderAll\(\);/); }); test("bootstrap does not trust a stored session from a different backend", () => { const helpers = extractBetween(APP, "function loadStoredSession()", "function compareDateDesc("); const ensure = extractBetween(APP, "async function ensureAutoSession(options = {})", "async function refreshFromAuthModal()"); const bootstrap = extractBetween(APP, "async function bootstrap()", "async function markTrackingDigestRead()"); assert.match(helpers, /function normalizeBackendUrlValue\(/); assert.match(helpers, /function hasSessionBackendMismatch\(/); assert.match(ensure, /const backendMismatch = hasSessionBackendMismatch\(backendUrl\);/); assert.match(ensure, /persistSession\(null\);/); assert.match(bootstrap, /const backendMismatch = hasSessionBackendMismatch\(\);/); assert.match(bootstrap, /await ensureAutoSession\(\{ force: backendMismatch \}\);/); }); test("oneliner submit failures stay inside the app instead of using a browser alert", () => { assert.doesNotMatch(APP, /alert\("OneLiner 调度失败:/); assert.match(APP, /presentActionFailure\(error,\s*"OneLiner 调度失败"\)/); }); test("agent control surfaces load governance endpoints for user and admin summaries", () => { const source = extractBetween(APP, "async function loadAgentControlSurfaces(projectId = \"\")", "async function loadOneLinerMessages(sessionId)"); assert.match(source, /\/v2\/oneliner\/governance\/effective/); assert.match(source, /\/v2\/oneliner\/runs/); assert.match(APP, /function hydrateSelectedOneLinerRun\(\)/); assert.match(APP, /\/v2\/oneliner\/runs\/\$\{encodeURIComponent\(runId\)\}/); assert.match(source, /\/v2\/oneliner\/governance\/user\/global/); assert.match(source, /\/v2\/oneliner\/governance\/user\/platforms\/\$\{encodeURIComponent\(governancePlatform\)\}/); assert.match(source, /\/v2\/admin\/oneliner\/governance\/system\/main-agent/); assert.match(source, /\/v2\/admin\/oneliner\/governance\/system\/platforms\/\$\{encodeURIComponent\(item\.value\)\}/); assert.match(source, /\/v2\/admin\/oneliner\/governance\/directory/); assert.match(source, /\/v2\/admin\/oneliner\/governance\/overrides/); assert.match(source, /Object\.prototype\.hasOwnProperty\.call\(existingTarget, "targetProjectId"\)/); assert.match(source, /const requestedProjectId = hasExistingProjectTarget/); assert.match(source, /const targetProjectId = requestedProjectId === ""/); }); test("oneliner panel includes a dedicated runtime header for agent runs", () => { const source = extractBetween(APP, "function ensureOneLinerUi()", "function renderOneLinerSessionTabs()"); const runtime = extractBetween(APP, "function renderOneLinerRunsHtml()", "function renderOneLinerMessagesHtml()"); assert.match(source, /data-role="oneliner-runs"/); assert.match(runtime, /confirm-oneliner-run/); assert.match(runtime, /cancel-oneliner-run/); assert.match(runtime, /当前计划/); assert.match(runtime, /renderOneLinerExecutionPayloadHtml\(currentRun\.result\)/); assert.match(runtime, /open-oneliner-run-result/); assert.match(runtime, /recommended_action/); }); test("oneliner meta and action handlers expose governance entry points", () => { const meta = extractBetween(APP, "function renderOneLinerUi()", "function openOneLinerPanel()"); const messages = extractBetween(APP, "function renderOneLinerMessagesHtml()", "function renderOneLinerUi()"); const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {"); assert.match(meta, /open-user-global-policy/); assert.match(meta, /renderOneLinerRunsHtml\(\)/); assert.match(meta, /policyScopeTagLabel/); assert.match(messages, /active_admin_override_notice/); assert.match(actions, /name === "open-user-global-policy"/); assert.match(actions, /name === "open-system-main-policy"/); assert.match(actions, /name === "handoff-to-main-agent"/); assert.match(actions, /name === "confirm-oneliner-run"/); assert.match(actions, /name === "open-oneliner-run-result"/); }); test("oneliner runtime remembers completed runs exactly once after hydration", () => { const hydrate = extractBetween(APP, "async function hydrateSelectedOneLinerRun()", "async function loadAgentControlSurfaces(projectId = \"\")"); const state = extractBetween(APP, "const appState = {", "};\n\nlet PLATFORM_RUNTIME"); assert.match(state, /lastCompletedOnelinerRunId/); assert.match(hydrate, /detail\.run_status === "done"/); assert.match(hydrate, /appState\.lastCompletedOnelinerRunId !== detail\.id/); assert.match(hydrate, /rememberAction\("主 Agent 已完成本轮"/); }); test("system governance saves refresh control surfaces after persisting", () => { const profile = extractBetween(APP, "function openOneLinerProfileAction()", "function parsePolicyJsonField(rawValue, label = \"策略 JSON\")"); const userGlobal = extractBetween(APP, "function openUserGlobalPolicyAction()", "function openUserPlatformPolicyAction(platform)"); const userPlatform = extractBetween(APP, "function openUserPlatformPolicyAction(platform)", "function openSystemMainPolicyAction()"); const main = extractBetween(APP, "function openSystemMainPolicyAction()", "function openSystemPlatformPolicyAction(platform)"); const platform = extractBetween(APP, "function openSystemPlatformPolicyAction(platform)", "function openPlatformAgentProfileAction(platform)"); assert.match(profile, /appState\.onelinerProfile = saved;/); assert.match(profile, /await loadAgentControlSurfaces\(project\.id\);/); assert.match(userGlobal, /appState\.userGlobalPolicy = saved;/); assert.match(userGlobal, /await loadAgentControlSurfaces\(project\.id\);/); assert.match(userPlatform, /appState\.userCurrentPlatformPolicy = saved;/); assert.match(userPlatform, /await loadAgentControlSurfaces\(project\.id\);/); assert.match(main, /const projectId = getOneLinerProjectId\(\);/); assert.match(main, /appState\.adminSystemMainPolicy = saved;/); assert.match(main, /await loadAgentControlSurfaces\(projectId\);/); assert.match(platform, /const projectId = getOneLinerProjectId\(\);/); assert.match(platform, /appState\.adminSystemPlatformPolicies = safeArray\(appState\.adminSystemPlatformPolicies\)/); assert.match(platform, /await loadAgentControlSurfaces\(projectId\);/); }); test("governance UI exposes admin override target picker and history rollback entrypoints", () => { const admin = extractBetween(APP, "function renderAdminGovernanceSummaryPanel()", "function renderPlatformAgentPanel()"); const targetPicker = extractBetween(APP, "async function openAdminOverrideTargetAction()", "function openAdminOverridePolicyAction()"); const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {"); assert.match(admin, /open-admin-override-target/); assert.match(admin, /open-admin-override-policy/); assert.match(admin, /open-admin-override-history/); assert.match(admin, /open-system-main-policy-history/); assert.match(admin, /open-system-platform-policy-history/); assert.match(APP, /function syncAdminOverrideProjectOptions\(/); assert.match(targetPicker, /onOpen: \(\) => \{/); assert.match(targetPicker, /userSelect\.addEventListener\("change"/); assert.match(targetPicker, /syncAdminOverrideProjectOptions\(userSelect\.value, ""\)/); assert.match(actions, /name === "open-admin-override-target"/); assert.match(actions, /name === "open-admin-override-policy"/); assert.match(actions, /name === "open-admin-override-history"/); assert.match(actions, /name === "open-system-main-policy-history"/); assert.match(actions, /name === "open-system-platform-policy-history"/); }); test("user governance UI exposes personal history and rollback entrypoints", () => { const playbook = extractBetween(APP, "function renderPlaybookScreen()", "function renderProductionScreen()"); const strategy = extractBetween(APP, "function renderStrategyScreen()", "function renderCreditsScreen()"); const automation = extractBetween(APP, "function renderAutomationScreen()", "function renderOwnedScreen()"); const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {"); assert.match(playbook, /open-user-global-policy-history/); assert.match(playbook, /open-user-platform-policy-history/); assert.match(playbook, /handoff-to-main-agent/); assert.match(playbook, /playbook-main-agent-handoff/); assert.match(strategy, /active_admin_override_notice/); assert.match(strategy, /管理员覆盖生效中/); assert.match(strategy, /handoff-to-main-agent/); assert.match(strategy, /strategy-main-agent-handoff/); assert.match(automation, /handoff-to-main-agent/); assert.match(automation, /automation-main-agent-handoff/); assert.match(actions, /name === "open-user-global-policy-history"/); assert.match(actions, /name === "open-user-platform-policy-history"/); }); test("main agent result rendering offers a direct route back into the recommended screen", () => { const execution = extractBetween(APP, "function renderOneLinerExecutionPayloadHtml(payload)", "function parseOneLinerActionPayloadValue(value)"); const lastAction = extractBetween(APP, "function renderLastActionCard()", "function getJobRecoveryCategory(job)"); assert.match(execution, /recommended_action/); assert.match(execution, /data-action="\$\{escapeHtml\(payload\.recommended_action\.action\)\}"/); assert.match(lastAction, /open-oneliner-run-result/); assert.match(lastAction, /recommended_action/); });