chore: sync marketing pages, CMS extensions, tests and project docs
同步工作区剩余变更,主要包括: - 营销页面组件与布局持续优化(about/news/services/solutions/team 等) - 详情页四层叙事组件、布局组件、UI 组件调整 - CMS 数据模型、API 路由、权限、工作流、站内通知、媒体管理扩展 - 新增/补充单元测试与 E2E 测试(cms-workflow.spec.ts 等) - ESLint 9 迁移、jest/tsconfig 配置更新、依赖调整 - 新增 ADR、CMS 评估文档、Release Review / Acceptance 报告 - 移除水墨装饰组件与大体积未使用字体文件
This commit is contained in:
@@ -2,9 +2,17 @@ import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import TermsOfServicePage from './page';
|
||||
|
||||
jest.mock('@/lib/cms/data-server', () => ({
|
||||
getPublishedItems: jest.fn(),
|
||||
}));
|
||||
|
||||
import { getPublishedItems } from '@/lib/cms/data-server';
|
||||
const mockedGetPublishedItems = getPublishedItems as jest.MockedFunction<typeof getPublishedItems>;
|
||||
|
||||
describe('TermsOfServicePage', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockedGetPublishedItems.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
@@ -49,4 +57,50 @@ describe('TermsOfServicePage', () => {
|
||||
expect(h2s.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CMS integration', () => {
|
||||
it('should render CMS hero title and description when available', async () => {
|
||||
mockedGetPublishedItems.mockResolvedValue([
|
||||
{
|
||||
id: 'legal-terms',
|
||||
modelId: 'model-legal-page',
|
||||
modelCode: 'legal-page',
|
||||
title: '服务条款',
|
||||
slug: 'terms',
|
||||
locale: 'zh-CN',
|
||||
status: 'published',
|
||||
data: {
|
||||
pageType: 'terms',
|
||||
heroTitle: 'CMS 服务条款标题',
|
||||
heroDescription: 'CMS 服务条款描述',
|
||||
lastUpdated: '2026-07-17',
|
||||
content: '<div data-testid="cms-content"><h2>CMS 条款内容</h2></div>',
|
||||
},
|
||||
version: 1,
|
||||
sortOrder: 0,
|
||||
createdBy: 'system',
|
||||
updatedBy: 'system',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
] as unknown as Awaited<ReturnType<typeof getPublishedItems>>);
|
||||
|
||||
const page = await TermsOfServicePage();
|
||||
render(page);
|
||||
|
||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('CMS 服务条款标题');
|
||||
expect(screen.getByText('CMS 服务条款描述')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('cms-content')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should fall back to inline content when CMS is unavailable', async () => {
|
||||
mockedGetPublishedItems.mockRejectedValue(new Error('DB connection failed'));
|
||||
|
||||
const page = await TermsOfServicePage();
|
||||
render(page);
|
||||
|
||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('服务条款');
|
||||
expect(screen.getByText('引言')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+13
-2
@@ -7,6 +7,9 @@ export const metadata: Metadata = {
|
||||
description: `${COMPANY_INFO.name}服务条款`,
|
||||
};
|
||||
|
||||
// ISR:生产环境每 1 小时自动重新验证,CMS 发布/更新时通过 /api/cms/revalidate 主动刷新
|
||||
export const revalidate = 3600;
|
||||
|
||||
function TermsContent() {
|
||||
return (
|
||||
<div className="prose prose-lg max-w-none">
|
||||
@@ -169,6 +172,8 @@ function TermsContent() {
|
||||
|
||||
export default async function TermsOfServicePage() {
|
||||
let cmsContent: string | null = null;
|
||||
let heroTitle = '服务条款';
|
||||
let heroDescription = '请仔细阅读以下服务条款,使用我们的服务即表示您同意这些条款';
|
||||
|
||||
try {
|
||||
const items = await getPublishedItems('legal-page');
|
||||
@@ -176,6 +181,12 @@ export default async function TermsOfServicePage() {
|
||||
if (termsItem && typeof termsItem.data.content === 'string' && termsItem.data.content.length > 0) {
|
||||
cmsContent = termsItem.data.content as string;
|
||||
}
|
||||
if (termsItem && typeof termsItem.data.heroTitle === 'string' && termsItem.data.heroTitle.length > 0) {
|
||||
heroTitle = termsItem.data.heroTitle as string;
|
||||
}
|
||||
if (termsItem && typeof termsItem.data.heroDescription === 'string' && termsItem.data.heroDescription.length > 0) {
|
||||
heroDescription = termsItem.data.heroDescription as string;
|
||||
}
|
||||
} catch {
|
||||
// CMS not available, fall back to inline content
|
||||
}
|
||||
@@ -185,10 +196,10 @@ export default async function TermsOfServicePage() {
|
||||
<div className="bg-gradient-to-br from-[var(--color-brand)] via-[var(--color-brand)]/80 to-[var(--color-hero-dark-end)] py-20">
|
||||
<div className="container-wide">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-white mb-4">
|
||||
服务条款
|
||||
{heroTitle}
|
||||
</h1>
|
||||
<p className="text-xl text-white/90 max-w-2xl">
|
||||
请仔细阅读以下服务条款,使用我们的服务即表示您同意这些条款
|
||||
{heroDescription}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user