文件
OmniCard/backend/test/modules/card/seo.test.ts
wikiboss eec259a3a7 feat: 完成全系统30个功能点开发与E2E全链路验收 (8轮调度32个子代理闭环)
- 模块一: 出海案例作品集、博客动态发布、人名发音语音条、RTL阿语多语言切换
- 模块二: AI顾问人设配置、行业术语库润色、RAG切片沙盒调试、流式会话与意向留资卡片
- 模块三: 全球访客雷达大屏、多维加权意向算分、外部CRM对接推送、团队公海池流转
- 模块四: 移动端NFC安全挂失、NFC动态路由规则配置、动态防伪验签、展会制卡Zip批量导出
- 模块五: 组织架构与名片分配、移动端个人中心设置、离职交接与历史线索迁移、安全审计日志
- 模块六: Stripe国际信用卡订阅Webhook、独立二级域名与SSL检测、坐席配额预警、多币种发票
- 模块七: 纸质名片OCR解析入库、国际商务日程RFC 5545 ICS日历、微官网离线SWR预加载、展会实时排行榜大屏
- 模块八: 全球出海SEO OpenGraph元标签注入与社交爬虫探测、30点端到端全链路业务流集成测试套件
2026-09-20 23:47:47 +08:00

189 行
7.8 KiB
TypeScript

import { CardSeoService, SeoOptions } from '../../../src/modules/card/service/seo';
import { CardProfile } from '../../../src/comm/types/omni';
describe('Point 29-A: SEO & OpenGraph 社交预览元标签注入引擎', () => {
let seoService: CardSeoService;
beforeEach(() => {
seoService = new CardSeoService();
});
const mockCard: Partial<CardProfile> = {
id: 101,
name: 'Sarah Connor',
title: 'Chief Technology Officer',
company: 'Omni Global Tech',
slogan: 'Building the next generation of AI-driven digital identities.',
avatarUrl: 'https://cdn.omnicard.ai/avatars/sarah.jpg',
slug: 'sarah-connor',
};
describe('1. generateOpenGraphMeta 标签生成规范', () => {
it('应为完整名片生成标准化的 OpenGraph 与 Twitter Card 标签集合', () => {
const options: SeoOptions = {
baseUrl: 'https://omnicard.ai',
locale: 'en_US',
siteName: 'OmniCard AI',
};
const meta = seoService.generateOpenGraphMeta(mockCard, options);
// OpenGraph 基础标准规范
expect(meta['og:title']).toBe('Sarah Connor - Chief Technology Officer | Omni Global Tech');
expect(meta['og:description']).toBe(
'Building the next generation of AI-driven digital identities.'
);
expect(meta['og:image']).toBe('https://cdn.omnicard.ai/avatars/sarah.jpg');
expect(meta['og:url']).toBe('https://omnicard.ai/c/sarah-connor');
expect(meta['og:type']).toBe('profile');
expect(meta['og:locale']).toBe('en_US');
expect(meta['og:site_name']).toBe('OmniCard AI');
// Twitter Card 协议
expect(meta['twitter:card']).toBe('summary_large_image');
expect(meta['twitter:title']).toBe('Sarah Connor - Chief Technology Officer | Omni Global Tech');
expect(meta['twitter:description']).toBe(
'Building the next generation of AI-driven digital identities.'
);
expect(meta['twitter:image']).toBe('https://cdn.omnicard.ai/avatars/sarah.jpg');
});
it('当提供 currentUrl 时,og:url 应优先采用 currentUrl', () => {
const meta = seoService.generateOpenGraphMeta(mockCard, {
currentUrl: 'https://custom.domain.com/profile/sarah',
});
expect(meta['og:url']).toBe('https://custom.domain.com/profile/sarah');
});
it('当缺少 slogan 时,og:description 应优雅拼接 title 与 company', () => {
const cardWithoutSlogan: Partial<CardProfile> = {
name: 'Alex Rivera',
title: 'Global Sales Director',
company: 'Nexa Global',
};
const meta = seoService.generateOpenGraphMeta(cardWithoutSlogan);
expect(meta['og:description']).toBe('Global Sales Director @ Nexa Global');
expect(meta['og:title']).toBe('Alex Rivera - Global Sales Director | Nexa Global');
});
});
describe('2. 多语言规范与 OpenGraph Locale 格式化', () => {
it('应将各类格式的多语言编码格式化为 RFC/OpenGraph 标准 ll_CC 格式', () => {
expect(seoService.formatOgLocale('zh-CN')).toBe('zh_CN');
expect(seoService.formatOgLocale('en-US')).toBe('en_US');
expect(seoService.formatOgLocale('ar-SA')).toBe('ar_SA');
expect(seoService.formatOgLocale('ja')).toBe('ja_JP');
expect(seoService.formatOgLocale('en')).toBe('en_US');
expect(seoService.formatOgLocale('zh')).toBe('zh_CN');
expect(seoService.formatOgLocale('ar')).toBe('ar_SA');
expect(seoService.formatOgLocale(undefined)).toBe('en_US');
});
});
describe('3. renderHtmlWithOgMeta 模板注入与 RTL 支持', () => {
const template = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>OmniCard</title>
</head>
<body>
<div id="app"></div>
</body>
</html>`;
it('应将 meta 标签与 title 安全注入到 <head> 中', () => {
const html = seoService.renderHtmlWithOgMeta(template, mockCard, {
baseUrl: 'https://omnicard.ai',
locale: 'en_US',
});
expect(html).toContain('<meta property="og:title" content="Sarah Connor - Chief Technology Officer | Omni Global Tech" />');
expect(html).toContain('<meta property="og:type" content="profile" />');
expect(html).toContain('<meta property="og:image" content="https://cdn.omnicard.ai/avatars/sarah.jpg" />');
expect(html).toContain('<meta name="twitter:card" content="summary_large_image" />');
expect(html).toContain('<title>Sarah Connor - Chief Technology Officer | Omni Global Tech</title>');
expect(html).toContain('<html lang="en">');
});
it('当语言为阿拉伯语时,应自动为 <html> 标签注入 dir="rtl" 与 lang="ar"', () => {
const arCard: Partial<CardProfile> = {
name: 'طارق المنصور',
title: 'مدير تطوير الأعمال',
company: 'التقنية العالمية',
};
const html = seoService.renderHtmlWithOgMeta(template, arCard, {
locale: 'ar-SA',
});
expect(html).toContain('dir="rtl"');
expect(html).toContain('lang="ar"');
expect(html).toContain('<meta property="og:locale" content="ar_SA" />');
expect(html).toContain('طارق المنصور');
});
it('当显式传入 options.isRtl = true 时,也应注入 dir="rtl"', () => {
const html = seoService.renderHtmlWithOgMeta(template, mockCard, {
isRtl: true,
});
expect(html).toContain('dir="rtl"');
});
});
describe('4. 防 XSS 注入与特殊字符安全转义', () => {
it('当姓名、职位或描述包含恶意 HTML/JS 脚本或注入符号时,应进行严格转义', () => {
const xssCard: Partial<CardProfile> = {
name: '<script>alert("xss")</script>',
title: '"><img src=x onerror=alert(1)>',
company: 'Tech & "Solutions" <LLC>',
slogan: '\'OR 1=1 -- <iframe src="evil.com">',
};
const meta = seoService.generateOpenGraphMeta(xssCard);
expect(meta['og:title']).not.toContain('<script>');
expect(meta['og:title']).not.toContain('onerror=');
const template = '<html><head></head><body></body></html>';
const renderedHtml = seoService.renderHtmlWithOgMeta(template, xssCard);
// 验证特殊字符均被 escape,属性双引号不可跳脱
expect(renderedHtml).not.toContain('<script>alert');
expect(renderedHtml).not.toContain('<img src=x');
expect(renderedHtml).not.toContain('<iframe');
expect(renderedHtml).toContain('&quot;');
expect(renderedHtml).toContain('&lt;');
expect(renderedHtml).toContain('&gt;');
expect(renderedHtml).toContain('&amp;');
});
});
describe('5. 空值与边界安全兜底', () => {
it('当 card 为 null 或 undefined 时,应使用默认值兜底且绝不报错', () => {
const metaNull = seoService.generateOpenGraphMeta(null);
expect(metaNull['og:title']).toBe('OmniCard AI');
expect(metaNull['og:description']).toBe(
'OmniCard AI - Global Digital Business Card & Micro Portal'
);
expect(metaNull['og:image']).toBe('https://omnicard.ai/assets/og-default.png');
expect(metaNull['og:type']).toBe('profile');
expect(metaNull['twitter:card']).toBe('summary_large_image');
const metaUndefined = seoService.generateOpenGraphMeta(undefined, {
siteName: 'Custom Brand',
defaultImage: 'https://example.com/fallback.jpg',
});
expect(metaUndefined['og:title']).toBe('Custom Brand');
expect(metaUndefined['og:image']).toBe('https://example.com/fallback.jpg');
});
it('当 template 不含标准 <head> 标签时,renderHtmlWithOgMeta 能够安全兜底注入', () => {
const rawHtml = '<div>Raw Body Content</div>';
const result = seoService.renderHtmlWithOgMeta(rawHtml, mockCard);
expect(result).toContain('<meta property="og:title"');
expect(result).toContain('<div>Raw Body Content</div>');
});
});
});