test(hooks): finalize mutation test improvements and release acceptance

- Raise use-swipe-gesture mutation score to 66.13% (target 65%+)
- Maintain use-reduced-motion mutation score at 76.32% (target 50%+)
- Fix use-reduced-motion.ts ESLint set-state-in-effect warning
- Add UJ-10 deep searcher journey (category browse → article read → content discovery)
- Add 40 new test files (analytics, detail, sections, ui, lib components)
- Update test-strategy-plan.md to v2.0 (sync test count to 1591)
- Sync README.md with final release metrics

Quality gates: TS 0 errors, ESLint 0 errors, 121 suites / 1591 tests passed
This commit is contained in:
2026-08-02 19:39:27 +08:00
parent 7c0af54897
commit 602ed6a671
203 changed files with 8338 additions and 81 deletions
@@ -0,0 +1,152 @@
// @ts-nocheck
import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
// ─── Mocks ───────────────────────────────────────────────────────────────
jest.mock('framer-motion', () => ({
motion: {
div: ({ children, initial, animate, variants, whileInView, viewport, transition, className, ...props }: any) => (
<div
data-testid="motion-div"
data-initial={JSON.stringify(initial)}
data-animate={JSON.stringify(animate)}
data-variants={JSON.stringify(variants)}
data-while-inview={JSON.stringify(whileInView)}
data-viewport={JSON.stringify(viewport)}
data-transition={JSON.stringify(transition)}
className={className}
{...props}
>
{children}
</div>
),
h1: ({ children, className, ...props }: any) => (
<h1 data-testid="motion-h1" className={className} {...props}>{children}</h1>
),
p: ({ children, className, ...props }: any) => (
<p data-testid="motion-p" className={className} {...props}>{children}</p>
),
},
useInView: jest.fn(() => true),
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
jest.mock('@/hooks/use-reduced-motion', () => ({
useReducedMotion: jest.fn(() => false),
}));
// ─── Tests ───────────────────────────────────────────────────────────────
describe('ListPageHero', () => {
it('should render title', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(<ListPageHero title="产品服务" subtitle="企业数字化转型全方位解决方案" />);
expect(screen.getByText('产品服务')).toBeInTheDocument();
});
it('should render subtitle', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(<ListPageHero title="产品服务" subtitle="企业数字化转型全方位解决方案" />);
expect(screen.getByText('企业数字化转型全方位解决方案')).toBeInTheDocument();
});
it('should render description when provided', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(
<ListPageHero
title="产品服务"
subtitle="企业数字化转型全方位解决方案"
description="覆盖企业全业务场景的产品矩阵"
/>
);
expect(screen.getByText('覆盖企业全业务场景的产品矩阵')).toBeInTheDocument();
});
it('should not render description when not provided', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(<ListPageHero title="产品服务" subtitle="企业数字化转型全方位解决方案" />);
expect(screen.queryByText('覆盖企业全业务场景的产品矩阵')).not.toBeInTheDocument();
});
it('should render badge when provided', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(
<ListPageHero
title="产品服务"
subtitle="企业数字化转型全方位解决方案"
badge={{ text: '新品发布', variant: 'brand' }}
/>
);
expect(screen.getByText('新品发布')).toBeInTheDocument();
});
it('should not render badge when not provided', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(<ListPageHero title="产品服务" subtitle="企业数字化转型全方位解决方案" />);
expect(screen.queryByText('新品发布')).not.toBeInTheDocument();
});
it('should render stats when provided', async () => {
const { ListPageHero } = await import('./list-page-hero');
const stats = [
{ value: '6', label: '企业套装' },
{ value: '3', label: '专业产品' },
];
render(
<ListPageHero
title="产品服务"
subtitle="企业数字化转型全方位解决方案"
stats={stats}
/>
);
expect(screen.getByText('6')).toBeInTheDocument();
expect(screen.getByText('企业套装')).toBeInTheDocument();
expect(screen.getByText('3')).toBeInTheDocument();
expect(screen.getByText('专业产品')).toBeInTheDocument();
});
it('should not render stats when not provided', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(<ListPageHero title="产品服务" subtitle="企业数字化转型全方位解决方案" />);
expect(screen.queryByText('6')).not.toBeInTheDocument();
});
it('should render with badge variant "blue"', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(
<ListPageHero
title="解决方案"
subtitle="行业解决方案"
badge={{ text: '推荐', variant: 'blue' }}
/>
);
expect(screen.getByText('推荐')).toBeInTheDocument();
});
it('should render with badge variant "green"', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(
<ListPageHero
title="服务"
subtitle="专业服务"
badge={{ text: '热门', variant: 'green' }}
/>
);
expect(screen.getByText('热门')).toBeInTheDocument();
});
it('should render with badge variant "neutral"', async () => {
const { ListPageHero } = await import('./list-page-hero');
render(
<ListPageHero
title="案例"
subtitle="客户案例"
badge={{ text: '案例', variant: 'neutral' }}
/>
);
const neutralBadges = screen.getAllByText('案例');
expect(neutralBadges.length).toBeGreaterThanOrEqual(1);
});
});
+154
View File
@@ -0,0 +1,154 @@
// @ts-nocheck
import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
// ─── Mocks ───────────────────────────────────────────────────────────────
jest.mock('framer-motion', () => ({
motion: {
div: ({ children, initial, animate, variants, whileInView, viewport, transition, className, ...props }: any) => (
<div
data-testid="motion-div"
data-initial={JSON.stringify(initial)}
data-animate={JSON.stringify(animate)}
data-variants={JSON.stringify(variants)}
data-while-inview={JSON.stringify(whileInView)}
data-viewport={JSON.stringify(viewport)}
data-transition={JSON.stringify(transition)}
className={className}
{...props}
>
{children}
</div>
),
},
useInView: jest.fn(() => true),
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
jest.mock('next/link', () => ({
__esModule: true,
default: ({ children, href, className, ...props }: any) => (
<a href={href} className={className} data-testid="next-link" {...props}>
{children}
</a>
),
}));
jest.mock('next/image', () => ({
__esModule: true,
default: ({ src, alt, className, fill, ...props }: any) => (
<img
src={src}
alt={alt}
className={className}
data-fill={fill ? 'true' : undefined}
data-testid="next-image"
{...props}
/>
),
}));
jest.mock('lucide-react', () => ({
ArrowRight: (props: any) => <svg data-testid="icon-arrow-right" className={props.className} />,
CheckCircle2: (props: any) => <svg data-testid="icon-check-circle-2" className={props.className} />,
}));
// ─── Mock Data ────────────────────────────────────────────────────────────
const mockProduct = {
id: 'erp',
title: 'ERP 企业资源管理系统',
description: '覆盖企业全业务流程的数字化管理平台,实现财务、供应链、生产一体化管理',
image: '/images/products/erp.jpg',
category: '企业套装',
status: '已发布' as const,
features: ['财务管理', '供应链管理', '人力资源管理'],
};
// ─── Tests ───────────────────────────────────────────────────────────────
describe('ProductCard (detail)', () => {
it('should render product title', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
expect(screen.getByText('ERP 企业资源管理系统')).toBeInTheDocument();
});
it('should render product description', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
expect(
screen.getByText('覆盖企业全业务流程的数字化管理平台,实现财务、供应链、生产一体化管理')
).toBeInTheDocument();
});
it('should render category badge', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
expect(screen.getByText('企业套装')).toBeInTheDocument();
});
it('should render status badge when status is "已发布"', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
expect(screen.getByText('已发布')).toBeInTheDocument();
});
it('should not render status badge when status is not "已发布"', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={{ ...mockProduct, status: '研发中' }} />);
expect(screen.queryByText('已发布')).not.toBeInTheDocument();
});
it('should render product image', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
const img = screen.getByTestId('next-image');
expect(img).toHaveAttribute('src', '/images/products/erp.jpg');
expect(img).toHaveAttribute('alt', 'ERP 企业资源管理系统');
});
it('should render feature tags', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
expect(screen.getByText('财务管理')).toBeInTheDocument();
expect(screen.getByText('供应链管理')).toBeInTheDocument();
expect(screen.getByText('人力资源管理')).toBeInTheDocument();
});
it('should render "了解详情" link', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
expect(screen.getByText('了解详情')).toBeInTheDocument();
});
it('should link to correct product page', async () => {
const { ProductCard } = await import('./product-card');
render(<ProductCard product={mockProduct} />);
const link = screen.getByTestId('next-link');
expect(link).toHaveAttribute('href', '/products/erp');
});
it('should render without features section when features is empty', async () => {
const { ProductCard } = await import('./product-card');
render(
<ProductCard product={{ ...mockProduct, features: [] }} />
);
// Should still render title and description
expect(screen.getByText('ERP 企业资源管理系统')).toBeInTheDocument();
// Feature tags should not be present
mockProduct.features.forEach((f) => {
expect(screen.queryByText(f)).not.toBeInTheDocument();
});
});
it('should render without features section when features is undefined', async () => {
const { ProductCard } = await import('./product-card');
render(
<ProductCard product={{ ...mockProduct, features: undefined }} />
);
expect(screen.getByText('ERP 企业资源管理系统')).toBeInTheDocument();
});
});
@@ -0,0 +1,134 @@
// @ts-nocheck
import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
// ─── Mocks ───────────────────────────────────────────────────────────────
jest.mock('framer-motion', () => ({
motion: {
div: ({ children, initial, animate, variants, whileInView, viewport, transition, className, ...props }: any) => (
<div
data-testid="motion-div"
data-initial={JSON.stringify(initial)}
data-animate={JSON.stringify(animate)}
data-variants={JSON.stringify(variants)}
data-while-inview={JSON.stringify(whileInView)}
data-viewport={JSON.stringify(viewport)}
data-transition={JSON.stringify(transition)}
className={className}
{...props}
>
{children}
</div>
),
},
useInView: jest.fn(() => true),
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
jest.mock('lucide-react', () => {
const icons: Record<string, any> = {};
const names = ['CheckCircle2', 'Zap', 'Clock', 'Users', 'Award', 'TrendingUp'];
for (const name of names) {
const Icon = (props: any) => (
<svg
data-testid={`icon-${name.toLowerCase().replace(/\s+/g, '-')}`}
className={props?.className}
/>
);
Icon.displayName = name;
icons[name] = Icon;
}
return icons;
});
// ─── Mock Data ────────────────────────────────────────────────────────────
const mockService = {
id: 'consulting',
title: '战略咨询',
description: '企业数字化转型战略规划服务',
icon: 'lightbulb',
overview: '从战略到执行,全方位助力企业数字化转型',
features: [
'需求调研:深入了解企业现状与痛点',
'方案设计:制定个性化数字化转型方案',
'实施指导:全程跟踪指导确保落地',
],
benefits: [
'提升运营效率 30%',
'降低运营成本 20%',
'增强市场竞争力',
],
process: [
'诊断评估:现状分析与问题诊断',
'战略规划:制定转型路线图',
'落地实施:敏捷迭代推进',
],
heroThemeId: 'consulting',
caseStudies: [],
dataProofs: [],
};
// ─── Tests ───────────────────────────────────────────────────────────────
describe('ServiceValueSection', () => {
it('should render service title in heading', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('为什么选择我们的战略咨询?')).toBeInTheDocument();
});
it('should render overview text', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('从战略到执行,全方位助力企业数字化转型')).toBeInTheDocument();
});
it('should render "服务能力" label', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('服务能力')).toBeInTheDocument();
});
it('should render "核心能力" label', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('核心能力')).toBeInTheDocument();
});
it('should render feature items', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('深入了解企业现状与痛点')).toBeInTheDocument();
expect(screen.getByText('制定个性化数字化转型方案')).toBeInTheDocument();
});
it('should render "服务优势" label', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('服务优势')).toBeInTheDocument();
});
it('should render benefit items', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('提升运营效率 30%')).toBeInTheDocument();
expect(screen.getByText('降低运营成本 20%')).toBeInTheDocument();
});
it('should render "服务流程" label', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('服务流程')).toBeInTheDocument();
});
it('should render process steps', async () => {
const { ServiceValueSection } = await import('./service-value');
render(<ServiceValueSection service={mockService} />);
expect(screen.getByText('诊断评估')).toBeInTheDocument();
expect(screen.getByText('战略规划')).toBeInTheDocument();
expect(screen.getByText('落地实施')).toBeInTheDocument();
});
});
@@ -0,0 +1,154 @@
// @ts-nocheck
import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
// ─── Mocks ───────────────────────────────────────────────────────────────
jest.mock('framer-motion', () => ({
motion: {
div: ({ children, initial, animate, variants, whileInView, viewport, transition, className, ...props }: any) => (
<div
data-testid="motion-div"
data-initial={JSON.stringify(initial)}
data-animate={JSON.stringify(animate)}
data-variants={JSON.stringify(variants)}
data-while-inview={JSON.stringify(whileInView)}
data-viewport={JSON.stringify(viewport)}
data-transition={JSON.stringify(transition)}
className={className}
{...props}
>
{children}
</div>
),
li: ({ children, className, ...props }: any) => (
<li data-testid="motion-li" className={className} {...props}>{children}</li>
),
},
useInView: jest.fn(() => true),
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
jest.mock('lucide-react', () => {
const icons: Record<string, any> = {};
const names = ['CheckCircle2', 'Lightbulb', 'Target', 'TrendingUp'];
for (const name of names) {
const Icon = (props: any) => (
<svg
data-testid={`icon-${name.toLowerCase().replace(/\s+/g, '-')}`}
className={props?.className}
/>
);
Icon.displayName = name;
icons[name] = Icon;
}
return icons;
});
// ─── Mock Data ────────────────────────────────────────────────────────────
const mockSolution = {
id: 'manufacturing',
industry: '制造业',
title: '智能制造解决方案',
subtitle: '助力制造业数字化转型',
description: '面向制造业的一站式数字化转型方案,涵盖生产、质量、设备全流程',
challenges: [
'生产流程不透明,无法实时掌握车间状态',
'设备利用率低,缺乏预防性维护机制',
'质量管控依赖人工,缺陷率居高不下',
],
solutions: [
'部署MES系统,实现生产过程透明化',
'接入IoT设备传感器,构建设备健康管理平台',
'引入AI质检系统,提升缺陷检测准确率',
],
relatedProducts: ['erp', 'crm'],
valueProposition: {
headline: '让制造更智能,让管理更高效',
points: [
{ icon: 'trending-up', title: '效率提升', description: '生产效率提升30%,订单交付周期缩短50%' },
{ icon: 'shield', title: '质量保障', description: '产品合格率提升至99%,缺陷率降低80%' },
{ icon: 'zap', title: '成本优化', description: '运营成本降低25%,设备维护成本降低40%' },
],
},
suiteCombination: {
primaryProducts: ['ERP系统', 'MES系统', 'WMS系统'],
complementaryServices: ['数据采集与集成', '设备物联网接入', 'AI质检系统部署'],
rationale: 'ERP+MES+IoT实现从订单到交付的全流程数字化,打通信息孤岛,构建智能制造底座',
},
};
// ─── Tests ───────────────────────────────────────────────────────────────
describe('SolutionValueSection', () => {
it('should render industry name in heading', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('制造业')).toBeInTheDocument();
expect(screen.getByText(/为.*量身定制/)).toBeInTheDocument();
});
it('should render "行业方案" label', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('行业方案')).toBeInTheDocument();
});
it('should render description', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('面向制造业的一站式数字化转型方案,涵盖生产、质量、设备全流程')).toBeInTheDocument();
});
it('should render "行业痛点挑战" section', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('行业痛点挑战')).toBeInTheDocument();
expect(screen.getByText('生产流程不透明,无法实时掌握车间状态')).toBeInTheDocument();
expect(screen.getByText('设备利用率低,缺乏预防性维护机制')).toBeInTheDocument();
});
it('should render "睿新致远解决方案" section', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('睿新致远解决方案')).toBeInTheDocument();
expect(screen.getByText('部署MES系统,实现生产过程透明化')).toBeInTheDocument();
});
it('should render value proposition section', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('价值主张')).toBeInTheDocument();
expect(screen.getByText('让制造更智能,让管理更高效')).toBeInTheDocument();
});
it('should render value proposition points', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('效率提升')).toBeInTheDocument();
expect(screen.getByText('质量保障')).toBeInTheDocument();
expect(screen.getByText('成本优化')).toBeInTheDocument();
});
it('should render "产品组合" section', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('产品组合')).toBeInTheDocument();
expect(screen.getByText('推荐搭配方案')).toBeInTheDocument();
});
it('should render suite combination products', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText('ERP系统')).toBeInTheDocument();
expect(screen.getByText('MES系统')).toBeInTheDocument();
});
it('should render combination rationale', async () => {
const { SolutionValueSection } = await import('./solution-value');
render(<SolutionValueSection solution={mockSolution} />);
expect(screen.getByText(/ERP\+MES\+IoT实现从订单到交付的全流程数字化/)).toBeInTheDocument();
});
});