geo/frontend/__tests__/lib/action-suggestions.test.ts

158 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 行动建议系统统一测试
* 验证 ActionSuggestion 和 NextAction 使用统一的 ActionItem 类型
*/
import { describe, it, expect } from "vitest";
import { generateActionSuggestions } from "@/lib/dashboard-health";
import { generateNextActions } from "@/lib/next-action";
import type { ActionItem, ActionPriority } from "@/types/suggestion";
import type { ActionSuggestion } from "@/types/dashboard-health";
import type { NextAction } from "@/types/next-action";
describe("ActionItem 统一类型", () => {
it("ActionItem 类型应包含所有必要字段", async () => {
const suggestionModule = await import("@/types/suggestion");
// 验证类型可以被导入(编译时检查)
expect(suggestionModule).toBeDefined();
});
it("ActionPriority 应为 'primary' | 'secondary' | 'optional'", async () => {
const suggestionModule = await import("@/types/suggestion");
// ActionPriority 是类型,运行时不可直接检查,但我们可以验证模块导出
expect(suggestionModule).toBeDefined();
});
});
describe("ActionSuggestion 兼容 ActionItem", () => {
it("generateActionSuggestions 返回的对象应兼容 ActionItem 结构", () => {
const stats = {
platformScores: [
{
platform: "kimi",
score: 30,
competitor_score: 60,
competitor_name: "竞品A",
},
],
overallScore: 30,
hasQueries: true,
};
const suggestions = generateActionSuggestions(stats);
expect(suggestions.length).toBeGreaterThan(0);
for (const suggestion of suggestions) {
// ActionItem 必需字段
expect(suggestion).toHaveProperty("id");
expect(suggestion).toHaveProperty("title");
expect(suggestion).toHaveProperty("description");
expect(suggestion).toHaveProperty("icon");
expect(suggestion).toHaveProperty("href");
// type 字段应兼容 ActionPriority
const validPriorities: ActionPriority[] = [
"primary",
"secondary",
"optional",
];
expect(validPriorities).toContain(suggestion.type);
}
});
});
describe("NextAction 兼容 ActionItem", () => {
it("generateNextActions 返回的对象应兼容 ActionItem 结构", () => {
const context = {
hasData: true,
hasBrands: true,
brandCount: 1,
overallScore: 50,
scoreChange: -5,
competitorCount: 2,
hasQueryHistory: true,
currentPage: "dashboard" as const,
};
const actions = generateNextActions(context);
expect(actions.length).toBeGreaterThan(0);
for (const action of actions) {
// ActionItem 必需字段
expect(action).toHaveProperty("id");
expect(action).toHaveProperty("title");
expect(action).toHaveProperty("description");
expect(action).toHaveProperty("icon");
// NextAction 应有 href 字段(映射自 actionUrl
expect(action).toHaveProperty("href");
// priority 字段应兼容 ActionPriority
const validPriorities: ActionPriority[] = [
"primary",
"secondary",
"optional",
];
expect(validPriorities).toContain(action.priority);
}
});
it("NextAction 的 href 应与 actionUrl 一致", () => {
const context = {
hasData: false,
hasBrands: false,
brandCount: 0,
overallScore: 0,
scoreChange: 0,
competitorCount: 0,
hasQueryHistory: false,
currentPage: "dashboard" as const,
};
const actions = generateNextActions(context);
for (const action of actions) {
// href 应该存在且非空
expect(action.href).toBeTruthy();
expect(typeof action.href).toBe("string");
}
});
});
describe("两个系统生成结果的一致性", () => {
it("ActionSuggestion 和 NextAction 都能生成有效的行动建议", () => {
// dashboard-health 的 generateActionSuggestions
const dashboardSuggestions = generateActionSuggestions({
platformScores: [
{ platform: "kimi", score: 30, competitor_score: 60 },
],
overallScore: 30,
hasQueries: true,
});
// next-action 的 generateNextActions
const nextActions = generateNextActions({
hasData: true,
hasBrands: true,
brandCount: 1,
overallScore: 30,
scoreChange: -5,
competitorCount: 1,
hasQueryHistory: true,
currentPage: "dashboard",
});
// 两个系统都应该能生成建议
expect(dashboardSuggestions.length).toBeGreaterThan(0);
expect(nextActions.length).toBeGreaterThan(0);
// 所有建议都应有唯一 id
const dashboardIds = dashboardSuggestions.map((s) => s.id);
const nextActionIds = nextActions.map((a) => a.id);
expect(new Set(dashboardIds).size).toBe(dashboardIds.length);
expect(new Set(nextActionIds).size).toBe(nextActionIds.length);
});
});