import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import HomeContentV15 from './home-content-v15';
jest.mock('framer-motion', () => ({
motion: {
div: ({ children, initial, animate, className, style, ...props }: any) => (
{children}
),
h1: ({ children, className, ...props }: any) => (
{children}
),
p: ({ children, className, ...props }: any) => (
{children}
),
span: ({ children, className, ...props }: any) => (
{children}
),
a: ({ children, href, className, ...props }: any) => (
{children}
),
},
useInView: jest.fn(() => true),
AnimatePresence: ({ children }: any) => <>{children}>,
}));
jest.mock('next/image', () => ({
__esModule: true,
default: ({ src, alt, className }: any) => (
// eslint-disable-next-line @next/next/no-img-element
),
}));
jest.mock('@/components/ui/scroll-reveal', () => ({
ScrollReveal: ({ children, className }: any) => (
{children}
),
StaggerReveal: ({ children, className }: any) => (
{children}
),
}));
jest.mock('@/components/ui/badge', () => ({
Badge: ({ children, className }: any) => (
{children}
),
}));
jest.mock('lucide-react', () => {
const mockIcon = (name: string) => {
const Icon = (props: any) => ;
Icon.displayName = name;
return Icon;
};
return {
ArrowRight: mockIcon('arrow-right'),
ArrowUpRight: mockIcon('arrow-up-right'),
ArrowDownRight: mockIcon('arrow-down-right'),
Lightbulb: mockIcon('lightbulb'),
Database: mockIcon('database'),
Layers: mockIcon('layers'),
Code: mockIcon('code'),
Puzzle: mockIcon('puzzle'),
Server: mockIcon('server'),
Quote: mockIcon('quote'),
Newspaper: mockIcon('newspaper'),
Calendar: mockIcon('calendar'),
LayoutDashboard: mockIcon('layout-dashboard'),
BarChart3: mockIcon('bar-chart3'),
Package: mockIcon('package'),
Users: mockIcon('users'),
Settings: mockIcon('settings'),
Search: mockIcon('search'),
Bell: mockIcon('bell'),
TrendingUp: mockIcon('trending-up'),
AlertCircle: mockIcon('alert-circle'),
};
});
describe('HomeContentV15 - 浅色 Hero(暗黑模式 token 友好)', () => {
it('does not repeat the brand logo inside the hero (dedup with header)', () => {
render();
const hero = screen.getByTestId('hero-section');
expect(hero).toBeInTheDocument();
// hero 内不应再有 Logo(导航栏已展示,避免重复);slogan badge 保留
expect(hero.querySelector('img')).toBeNull();
});
it('mounts the brand-red dynamic particle layer (entrance-only, no continuous loop)', () => {
render();
const hero = screen.getByTestId('hero-section');
const particles = screen.getByTestId('hero-particle-field');
expect(particles.tagName).toBe('CANVAS');
expect(particles).toHaveAttribute('aria-hidden', 'true');
expect(hero.contains(particles)).toBe(true);
});
it('keeps a single primary CTA and demotes the secondary action to a text link', () => {
render();
expect(screen.getByTestId('hero-primary-cta')).toBeInTheDocument();
expect(screen.getByTestId('hero-primary-cta')).toHaveAttribute('href', '/contact');
expect(screen.getByTestId('hero-secondary-link')).toBeInTheDocument();
});
it('keeps hero declaration-first: stats sink out of hero into the results band', () => {
render();
const hero = screen.getByTestId('hero-section');
// 声明先行:hero 内不再内联数据条(一屏只负责一句话声明 + 一个行动)
expect(hero.querySelector('[data-testid="hero-stats"]')).toBeNull();
// 数据下沉为独立成果带,仍渲染(Glance 层滚动触发)
expect(screen.getByTestId('hero-stats')).toBeInTheDocument();
});
it('renders the abstract data-driven visual without product UI shell', () => {
render();
expect(screen.getByTestId('hero-product-visual')).toBeInTheDocument();
expect(screen.getByText('数据驱动 · 让转型可量化')).toBeInTheDocument();
});
});
describe('HomeContentV15 - 信任层与章节式叙事', () => {
it('renders verifiable trust signals with a source annotation', () => {
render();
expect(screen.getByTestId('trust-section')).toBeInTheDocument();
expect(screen.getByText('100%')).toBeInTheDocument();
expect(screen.getByText('成都')).toBeInTheDocument();
// 可验证来源标注存在(每个数据卡一个)
expect(screen.getAllByText(/数据来源:/i).length).toBeGreaterThanOrEqual(4);
});
it('renders an early-access co-creation label instead of fabricated testimonials', () => {
render();
expect(screen.getByTestId('early-access-banner')).toBeInTheDocument();
expect(screen.getAllByText('首批客户共创中').length).toBeGreaterThanOrEqual(1);
});
it('renders the problem-method-result narrative with chapter numbers', () => {
render();
expect(screen.getByTestId('narrative-section')).toBeInTheDocument();
expect(screen.getAllByText('01').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('02').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('03').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText(/从问题到结果/).length).toBeGreaterThan(0);
});
});
describe('HomeContentV15 - 行业洞察区(对标 Accenture Insights)', () => {
it('renders the insights pillar with typed tags and verifiable methodology content', () => {
render();
expect(screen.getByTestId('insights-section')).toBeInTheDocument();
// 类型标签(对标 Research Report / Perspective / Case Study)
expect(screen.getAllByText('方法论').length).toBeGreaterThanOrEqual(2);
expect(screen.getByText('观点')).toBeInTheDocument();
// 全部链接真实页面,不虚构研究报告
expect(screen.getByText('数字化转型诊断:从哪里开始,如何衡量')).toBeInTheDocument();
});
});
describe('HomeContentV15 - 创始人观点区(对标 Accenture CEO Quote)', () => {
it('renders the founder quote with attribution (no fabricated persona)', () => {
render();
expect(screen.getByTestId('founder-quote-section')).toBeInTheDocument();
expect(screen.getByText(/数字化不是一次性项目/)).toBeInTheDocument();
expect(screen.getByText('Novalon 创始团队')).toBeInTheDocument();
});
});
describe('HomeContentV15 - 新闻动态区(对标 Accenture News)', () => {
it('renders the news section with latest items and a view-all entry', () => {
render();
expect(screen.getByTestId('news-section')).toBeInTheDocument();
expect(screen.getByText('四川睿新致远科技有限公司正式成立')).toBeInTheDocument();
expect(screen.getByText('查看全部新闻')).toBeInTheDocument();
});
it('renders CMS-provided news when available', () => {
render();
expect(screen.getByText('CMS 新闻条目')).toBeInTheDocument();
});
});
describe('HomeContentV15 - 案例区 Client Spotlight(对标 Accenture Client Spotlight)', () => {
it('renders a co-creation spotlight card (verifiable, no fabricated clients) when no cases exist', () => {
render();
// 空态 spotlight:共创流程 3 步 + 深色卡 CTA
expect(screen.getAllByText('首批客户共创中').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('业务诊断').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('方案共创')).toBeInTheDocument();
expect(screen.getByText('验证发布')).toBeInTheDocument();
expect(screen.getAllByText('01').length).toBeGreaterThanOrEqual(1);
expect(screen.getByTestId('cases-co-creation-cta')).toHaveAttribute('href', '/contact');
});
it('renders a dark spotlight card with metrics for the featured case when data exists', () => {
const mockCases = [
{
slug: 'spotlight-demo',
industry: '制造业',
title: 'Spotlight 案例标题',
href: '/cases/spotlight-demo',
verified: true,
testimonial: {
quote: '这是一段客户引言,用于验证 spotlight 渲染。',
author: '客户 CIO',
role: '某制造企业',
},
metrics: [
{ value: '38%', label: '交付效率提升' },
{ value: '2x', label: '数据查询提速' },
{ value: '3周', label: '上线周期' },
],
},
{
slug: 'secondary-case',
industry: '贸易零售',
title: '次要案例标题',
href: '/cases/secondary-case',
metrics: [
{ value: '15%', label: '库存周转改善' },
{ value: '4h', label: '对账耗时' },
{ value: '9成', label: '预测准确率' },
],
},
];
render();
// spotlight 大卡:深色画布 + 引言 + 授权 badge + 量化指标
expect(screen.getByText('Spotlight 案例标题')).toBeInTheDocument();
expect(screen.getByText('这是一段客户引言,用于验证 spotlight 渲染。')).toBeInTheDocument();
expect(screen.getByText('已获客户授权')).toBeInTheDocument();
expect(screen.getByText('38%')).toBeInTheDocument();
expect(screen.getByText('查看完整案例')).toBeInTheDocument();
// 次要案例保持轻量卡片
expect(screen.getByText('次要案例标题')).toBeInTheDocument();
expect(screen.getAllByText('了解详情').length).toBeGreaterThanOrEqual(1);
});
});