import { screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import PersonalSpacePage from "./PersonalSpacePage";
import { spaceApi, type SpaceWorkbench } from "@/api/space";
import { renderWithProviders } from "@/test/renderWithProviders";
vi.mock("@/api/space", () => ({
spaceApi: {
getWorkbench: vi.fn(),
},
}));
vi.mock("@/features/pmflow-os/components/MobileBottomNav", () => ({
MobileBottomNav: () => ,
}));
vi.mock("@/features/pmflow-os/components/AccountControls", () => ({
PMFlowAccountActions: () =>
,
}));
const emptyWorkbench: SpaceWorkbench = {
projects: [],
pendingConfirmations: [],
aiProgress: [],
releaseAttention: [],
feedbackAttention: [],
savedIdeas: [],
drafts: [],
recentProjects: [],
warnings: ["暂存想法只读聚合尚未接入后端合同,当前不使用 mock 数据冒充真实内容。"],
generatedAt: "2026-04-26T08:00:00.000Z",
};
describe("PersonalSpacePage", () => {
it("renders the read-only Space workbench sections without demo saved ideas", async () => {
vi.mocked(spaceApi.getWorkbench).mockResolvedValue(emptyWorkbench);
renderWithProviders(, ["/space"]);
expect(await screen.findAllByText("暂无可继续推进的项目")).not.toHaveLength(0);
expect(screen.getAllByText("个人工作面")).not.toHaveLength(0);
expect(screen.getAllByText("继续推进")).not.toHaveLength(0);
expect(screen.getAllByText("待我确认")).not.toHaveLength(0);
expect(screen.getAllByText("发布关注")).not.toHaveLength(0);
expect(screen.getAllByText("暂无发布验证记录")).not.toHaveLength(0);
expect(screen.getAllByText("发布后反馈关注")).not.toHaveLength(0);
expect(screen.getAllByText("暂无发布后反馈")).not.toHaveLength(0);
expect(screen.getAllByText("AI 正在推进")).not.toHaveLength(0);
expect(screen.getAllByText("暂存想法")).not.toHaveLength(0);
expect(screen.getAllByText("草案箱")).not.toHaveLength(0);
expect(screen.getAllByText("最近项目")).not.toHaveLength(0);
expect(screen.getAllByText("暂无暂存想法")).not.toHaveLength(0);
expect(screen.getByText("暂存想法只读聚合尚未接入后端合同,当前不使用 mock 数据冒充真实内容。")).toBeInTheDocument();
expect(screen.queryByText("AI 周报助手")).not.toBeInTheDocument();
});
it("keeps workbench actions as navigation-only links into the main flows", async () => {
vi.mocked(spaceApi.getWorkbench).mockResolvedValue({
...emptyWorkbench,
projects: [
{
id: "17",
title: "真实项目",
summary: "真实项目摘要",
status: "进行中",
stage: "开发",
progress: 62,
pendingCount: 1,
activeTaskCount: 2,
riskLabel: "中",
nextAction: "继续推进核心任务",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/pmflow-os?projectId=17",
},
],
pendingConfirmations: [
{
id: "change-91",
projectId: "17",
projectTitle: "真实项目",
title: "确认需求变更",
summary: "等待在主流程确认变更。",
source: "changeset",
stage: "需求",
riskLevel: "medium",
createdAt: "2026-04-26T08:00:00.000Z",
href: "/change-review?projectId=17&changeSetId=91",
},
],
aiProgress: [
{
id: "execution-17",
projectId: "17",
projectTitle: "真实项目",
title: "AI 正在推进执行链路",
summary: "正在聚合执行进度与回写状态。",
status: "coding",
progress: 52,
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/execution-bridge?projectId=17",
},
],
releaseAttention: [
{
id: "release-attention-17",
projectId: "17",
projectTitle: "真实项目",
source: "release_validation",
sourceLabel: "发布验证记录",
title: "发布验证阻塞",
summary: "测试失败,PR 未合并,不能发布。",
gateStatus: "blocked",
severity: "high",
reasons: ["测试失败: typecheck 未通过", "PR: 未合并"],
evidenceLabels: ["发布验证", "检查阻塞", "1/2 检查项通过"],
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/release-growth?projectId=17",
},
{
id: "release-validation-18",
projectId: "18",
projectTitle: "待确认项目",
source: "release_validation",
sourceLabel: "发布验证记录",
title: "发布验证待确认",
summary: "发布验证已通过,需要人工确认。",
gateStatus: "needs_confirmation",
severity: "medium",
reasons: [],
evidenceLabels: ["发布验证", "验证通过"],
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/release-growth?projectId=18",
},
{
id: "release-gate-19",
projectId: "19",
projectTitle: "候选项目",
source: "release_gate",
sourceLabel: "发布门禁",
title: "发布候选",
summary: "执行证据已进入发布判断。",
gateStatus: "ready",
severity: "low",
reasons: [],
evidenceLabels: ["测试通过", "PR"],
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/release-growth?projectId=19",
},
],
feedbackAttention: [
{
id: "feedback-signal-17-feedback-1",
projectId: "17",
projectTitle: "真实项目",
source: "feedback",
sourceLabel: "用户反馈",
title: "新反馈信号",
summary: "用户反馈集中在 onboarding 卡顿。",
value: "12",
severity: "medium",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/release-growth?projectId=17",
},
{
id: "feedback-signal-17-revenue-1",
projectId: "17",
projectTitle: "真实项目",
source: "revenue",
sourceLabel: "收入信号",
title: "收入信号更新",
summary: "付费转化新增。",
value: "USD 1200",
severity: "low",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/release-growth?projectId=17",
},
{
id: "feedback-signal-17-compliance-1",
projectId: "17",
projectTitle: "真实项目",
source: "compliance",
sourceLabel: "合规检查",
title: "合规阻塞关注",
summary: "隐私文案未通过。",
value: "blocked",
severity: "high",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/release-growth?projectId=17",
},
{
id: "feedback-iteration-17-change-92",
projectId: "17",
projectTitle: "真实项目",
source: "iteration",
sourceLabel: "下一轮建议",
title: "下一轮建议待处理",
summary: "基于发布后反馈生成下一轮优化建议。",
value: null,
severity: "medium",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/change-review?projectId=17&changeSetId=92",
},
],
drafts: [
{
id: "17-需求-draft-1",
projectId: "17",
projectTitle: "真实项目",
title: "待确认草案",
summary: "待确认草案摘要",
objectType: "需求",
status: "draft",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/pmflow-os?projectId=17",
},
],
recentProjects: [
{
id: "17",
title: "真实项目",
summary: "真实项目摘要",
status: "进行中",
stage: "开发",
progress: 62,
pendingCount: 1,
activeTaskCount: 2,
riskLabel: "中",
nextAction: "继续推进核心任务",
updatedAt: "2026-04-26T08:00:00.000Z",
href: "/pmflow-os?projectId=17",
},
],
});
renderWithProviders(, ["/space"]);
expect(await screen.findByRole("link", { name: /进入主流程/ })).toHaveAttribute(
"href",
"/pmflow-os?projectId=17"
);
expect(screen.getAllByRole("link", { name: /确认需求变更/ })[0]).toHaveAttribute(
"href",
"/change-review?projectId=17&changeSetId=91"
);
expect(screen.getAllByRole("link", { name: /AI 正在推进执行链路/ })[0]).toHaveAttribute(
"href",
"/execution-bridge?projectId=17"
);
expect(screen.getAllByRole("link", { name: /发布验证阻塞/ })[0]).toHaveAttribute(
"href",
"/release-growth?projectId=17"
);
expect(screen.getAllByRole("link", { name: /发布验证待确认/ })[0]).toHaveAttribute(
"href",
"/release-growth?projectId=18"
);
expect(screen.getAllByRole("link", { name: /发布候选/ })[0]).toHaveAttribute(
"href",
"/release-growth?projectId=19"
);
expect(screen.getAllByRole("link", { name: /新反馈信号/ })[0]).toHaveAttribute(
"href",
"/release-growth?projectId=17"
);
expect(screen.getAllByRole("link", { name: /收入信号更新/ })[0]).toHaveAttribute(
"href",
"/release-growth?projectId=17"
);
expect(screen.getAllByRole("link", { name: /合规阻塞关注/ })[0]).toHaveAttribute(
"href",
"/release-growth?projectId=17"
);
expect(screen.getAllByRole("link", { name: /下一轮建议待处理/ })[0]).toHaveAttribute(
"href",
"/change-review?projectId=17&changeSetId=92"
);
expect(screen.getAllByText("测试失败: typecheck 未通过")).not.toHaveLength(0);
expect(screen.queryByRole("button", { name: /创建发布验证|运行发布验证|重新验证/ })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: /创建反馈|写入反馈|记录收入|运行合规/ })).not.toBeInTheDocument();
});
});