Files
ai-directory-platform/tests/seo-generator.test.ts
T
AI Directory Platform e46b4b52a3 feat: AI Directory Platform 底座升级 + 五套主题站点
- 数据模型: vote/review/submission/claim/use_case/audience/collection/event (+tool 冗余统计列)
- 社区: 投票(限流+原子toggle+API) / 评论(评分+优缺点+Helpful+聚合回写) / 提交(重复检测+审批发布) / 认领(Owner白名单编辑)
- Ranking Engine: trending/popular/most-voted/most-saved/highest-rated/new/editors-choice (纯函数可测)
- 发现体系: Use Case + Audience + Collection 三维发现 / /ai-tools-for / /best SEO 页
- Programmatic SEO: 页面生成器 / sitemap 270条 / AggregateRating/FAQPage/Breadcrumb/ItemList schema / alternatives + vs 对比页
- Analytics: event 事件流 + 外链跳转埋点 + /admin/analytics CTR 与搜索词分析
- Admin: submissions/claims/reviews/discovery/vote-audit/analytics 六个新后台 + 仪表盘扩展
- 主题系统: themes registry + 五套站点 (classic/product/editorial/niche/minimal) 构建期注入
- 认证: GitHub OAuth (env门控) + 登录回跳 redirect
- i18n: 新增 7 个命名空间中英文案 (546 键一致)
- 测试: bun test 32 用例 (ranking/slug/去重/SEO生成器) + Playwright e2e 脚本
- 种子: 14分类/16用例/9受众/40工具/6用户/投票/评论/6合集/7718事件
2026-09-08 17:38:06 +00:00

83 lines
2.6 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.
import { describe, test, expect } from "bun:test";
import { generateSeoPage, generateToolFaqs } from "../src/lib/seo/page-generator";
describe("generateSeoPage", () => {
const base = {
name: "Blogging",
kind: "use-case" as const,
brandName: "AI Stack",
toolCount: 12,
freeCount: 4,
topToolNames: ["ChatGPT", "Claude", "Grammarly"],
year: 2026,
};
test("生成 title / h1 / intro / faqs", () => {
const page = generateSeoPage(base);
expect(page.title).toContain("Best AI Tools for Blogging in 2026");
expect(page.h1).toBe("Best AI Tools for Blogging");
expect(page.intro).toContain("12");
expect(page.faqs.length).toBeGreaterThanOrEqual(3);
});
test("description 含工具数与排序说明", () => {
const page = generateSeoPage(base);
expect(page.description).toContain("12");
expect(page.description).toContain("votes");
});
test("FAQ 具体化:引用真实工具名", () => {
const page = generateSeoPage(base);
const firstAnswer = page.faqs[0].answer;
expect(firstAnswer).toContain("ChatGPT");
});
test("free 数量反映到 FAQ", () => {
const page = generateSeoPage(base);
const freeFaq = page.faqs.find((f) => f.question.startsWith("Are there free"));
expect(freeFaq?.answer).toContain("4");
});
test("use case + audience 生成交叉引用 FAQ", () => {
const page = generateSeoPage({ ...base, audienceName: "Marketers" });
const crossFaq = page.faqs.find((f) => f.question.toLowerCase().includes("marketer"));
expect(crossFaq).toBeDefined();
});
test("零数据实体也能安全生成(无 NaN/undefined)", () => {
const page = generateSeoPage({
name: "Research",
kind: "audience",
brandName: "X",
toolCount: 0,
});
expect(page.intro).not.toContain("undefined");
expect(page.title).not.toContain("NaN");
});
});
describe("generateToolFaqs", () => {
test("由定价生成免费问答", () => {
const faqs = generateToolFaqs({ name: "ChatGPT", pricing: "freemium" });
const freeFaq = faqs.find((f) => f.question.includes("free"));
expect(freeFaq?.answer).toContain("freemium");
});
test("含替代品问答", () => {
const faqs = generateToolFaqs(
{ name: "Cursor", pricing: "freemium" },
["Windsurf", "Copilot"]
);
const altFaq = faqs.find((f) => f.question.includes("alternatives"));
expect(altFaq?.answer).toContain("Windsurf");
});
test("描述自动补句号", () => {
const faqs = generateToolFaqs({
name: "X",
description: "It does things",
});
expect(faqs[0].answer).toBe("It does things.");
});
});