112 lines
5.0 KiB
JavaScript
112 lines
5.0 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import vm from "node:vm";
|
|
|
|
const ROOT = path.resolve(process.cwd(), "web/storyforge-web-v4");
|
|
|
|
function loadHomepageModule() {
|
|
const source = fs.readFileSync(path.join(ROOT, "assets/storyforge-dashboard-home.js"), "utf8");
|
|
const context = {
|
|
window: {},
|
|
console,
|
|
escapeHtml: (value) => String(value ?? ""),
|
|
formatNumber: (value) => String(value ?? 0),
|
|
safeArray: (value) => Array.isArray(value) ? value : [],
|
|
button: (label, action, tone = "secondary") =>
|
|
`<button class="btn btn-${tone}" data-action="${action}">${label}</button>`
|
|
};
|
|
vm.createContext(context);
|
|
vm.runInContext(source, context);
|
|
return context.window.StoryForgeDashboardHome;
|
|
}
|
|
|
|
test("homepage v6 puts actions before overview and uses 1-primary-2-secondary structure", () => {
|
|
const mod = loadHomepageModule();
|
|
const html = mod.renderDashboardHome({
|
|
title: "项目总台",
|
|
workspaceLabel: "Kris",
|
|
currentProjectName: "品牌增长实验室",
|
|
contextLinks: [],
|
|
actionSourceLabel: "规则推荐",
|
|
summaryTabs: [
|
|
{ key: "project_progress", label: "项目进度", value: "3 / 5", hint: "2 项可继续推进", active: true },
|
|
{ key: "focus_accounts", label: "重点账号 / 对标", value: "2 个", hint: "1 个缺高分分析", active: false },
|
|
{ key: "production_jobs", label: "生产任务", value: "4 条", hint: "1 条待确认", active: false }
|
|
],
|
|
activeTabLabel: "项目进度",
|
|
primaryAction: {
|
|
title: "先补抖音重点对标的高分作品分析",
|
|
reason: "最近有新作品,但还没形成高分样本。",
|
|
badges: ["最优先", "预计 10 分钟判断", "关联:重点账号"],
|
|
goAction: "goto-discovery",
|
|
goLabel: "去找对标",
|
|
agentLabel: "交给主 Agent"
|
|
},
|
|
secondaryActions: [
|
|
{ title: "确认一个待执行的生产计划", reason: "素材和结论都在,只差最后确认。", goAction: "goto-production", goLabel: "去处理" },
|
|
{ title: "继续处理视频录制", reason: "录制源、导入配置和运行状态都集中在这里,适合先补录或维护。", goAction: "focus-live-recorder-maintenance", goLabel: "去录制" }
|
|
],
|
|
overviewBodyHtml: "<section>这里只展示当前 tab 的核心状态。</section>"
|
|
});
|
|
|
|
assert.ok(html.includes("今天先做什么"));
|
|
assert.ok(html.includes("项目概览"));
|
|
assert.ok(html.indexOf("今天先做什么") < html.indexOf("项目概览"));
|
|
assert.match(html, /先补抖音重点对标的高分作品分析/);
|
|
assert.match(html, /确认一个待执行的生产计划/);
|
|
assert.match(html, /继续处理视频录制/);
|
|
assert.match(html, /data-action="handoff-to-main-agent"/);
|
|
});
|
|
|
|
test("homepage model builds one primary action, two secondary actions, and a rule fallback label", () => {
|
|
const mod = loadHomepageModule();
|
|
assert.equal(typeof mod.createDashboardHomeModel, "function");
|
|
|
|
const model = mod.createDashboardHomeModel({
|
|
workspaceLabel: "Kris",
|
|
currentProjectName: "品牌增长实验室",
|
|
trackedAccountsCount: 2,
|
|
assistantCount: 1,
|
|
jobCount: 4,
|
|
actionSourceLabel: "规则推荐",
|
|
dashboardOverviewTab: "project_progress"
|
|
});
|
|
|
|
assert.equal(model.actionSourceLabel, "规则推荐");
|
|
assert.equal(model.secondaryActions.length, 2);
|
|
assert.match(model.primaryAction.title, /高分作品分析|继续补高分对标/);
|
|
assert.match(model.secondaryActions.map((item) => item.title).join(" "), /确认一个待执行的生产计划/);
|
|
assert.match(model.secondaryActions.map((item) => item.title).join(" "), /继续处理视频录制/);
|
|
});
|
|
|
|
test("homepage overview uses tab buttons and does not render legacy repeated sections", () => {
|
|
const mod = loadHomepageModule();
|
|
const html = mod.renderDashboardHome({
|
|
workspaceLabel: "Kris",
|
|
currentProjectName: "品牌增长实验室",
|
|
contextLinks: [],
|
|
actionSourceLabel: "主 Agent 优先推荐",
|
|
primaryAction: { title: "A", reason: "B", badges: [], goAction: "x", goLabel: "去处理", agentLabel: "交给主 Agent" },
|
|
secondaryActions: [],
|
|
summaryTabs: [
|
|
{ key: "project_progress", label: "项目进度", value: "3 / 5", hint: "2 项可继续推进", active: true }
|
|
],
|
|
activeTabLabel: "项目进度",
|
|
overviewBodyHtml: "<section>tab body</section>"
|
|
});
|
|
|
|
assert.ok(html.includes('data-action="select-dashboard-tab"'));
|
|
assert.ok(!html.includes("当前项目推进详情"));
|
|
assert.ok(!html.includes("重点账号 / 对标</h3><div class=\"panel-subtitle\">右栏保留"));
|
|
});
|
|
|
|
test("homepage handoff buttons carry runtime plan metadata instead of only opening chat", () => {
|
|
const source = fs.readFileSync(path.join(ROOT, "assets/storyforge-dashboard-home.js"), "utf8");
|
|
assert.match(source, /handoff-to-main-agent/);
|
|
assert.match(source, /data-source-screen=/);
|
|
assert.match(source, /data-source-action-key=/);
|
|
assert.match(source, /data-plan-steps=/);
|
|
});
|