Files
novalon-website/src/app/(marketing)/solutions/solutions-content-v3.test.tsx
T
zhangxiang 5c1b883abf feat(ui): CTAButton 可复用组件 + P5 全站 CTA 签名切换(17 页面 27 处)
feat(ui): 抽离 CTAButton 为 src/components/ui/cta-button.tsx
- 4 variant 覆盖全站 CTA 场景:
  * primary:品牌红填充 pill(hero 主行动 / 底部大 CTA)
  * secondary:描边墨色 pill(浅色 section 次行动)
  * dark:描边浅色 pill(深色区块 bg-dark-bg 内次行动,如 brand/team 底部 CTA)
  * inline:透明 + 小箭头(卡片内次行动)
- 品牌箭头签名化:内置 ArrowRight + hover translate-x 微交互
- 200-300ms transition,对齐项目动效四原则
- data-testid 透传避免破坏既有测试

style(cta): P5 全站 CTA 签名切换(17 页面 27 处)
- home V15 / news×3 / products×4(含 erp)/ cases / services×2 / solutions×2
- brand / methodology / team(hero 2 + L4 2 = 4 处,含 dark variant)
- error / not-found(保留 onClick 功能按钮与卡片式导航 StaticLink)
- 清理:StaticLink / Button / ArrowRight / ChevronRight unused import

test(marketing): services/solutions lucide-react mock 补 ArrowRight
- CTAButton 内部用 ArrowRight,原 mock 漏导导致渲染崩(4 测试失败已修)
- 对齐 home-content-v15.test.tsx 既有 mock 范式
2026-08-31 17:25:08 +08:00

150 lines
4.9 KiB
TypeScript

import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import SolutionsContentV3 from './solutions-content-v3';
import type { Solution } from '@/lib/constants/solutions';
import type { Product } from '@/lib/constants/products';
jest.mock('framer-motion', () => ({
motion: {
div: ({ children, initial, animate, whileInView, viewport, className, ...props }: any) => (
<div
data-testid="motion-div"
data-initial={JSON.stringify(initial)}
data-animate={JSON.stringify(animate)}
data-while-inview={JSON.stringify(whileInView)}
data-viewport={JSON.stringify(viewport)}
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>
),
span: ({ children, className, ...props }: any) => (
<span data-testid="motion-span" className={className} {...props}>{children}</span>
),
a: ({ children, href, className, ...props }: any) => (
<a href={href} className={className} data-testid="motion-a" {...props}>{children}</a>
),
},
useInView: jest.fn(() => true),
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
jest.mock('@/components/ui/static-link', () => ({
StaticLink: ({ children, href, className, ...props }: any) => (
<a href={href} className={className} data-testid="static-link" {...props}>{children}</a>
),
}));
jest.mock('lucide-react', () => {
const mockIcon = (name: string) => {
const Icon = (props: any) => <svg data-testid={`icon-${name.toLowerCase()}`} className={props.className} />;
Icon.displayName = name;
return Icon;
};
return {
ArrowRight: mockIcon('arrow-right'),
ArrowUpRight: mockIcon('arrow-up-right'),
Factory: mockIcon('factory'),
ShoppingCart: mockIcon('shopping-cart'),
Heart: mockIcon('heart'),
GraduationCap: mockIcon('graduation-cap'),
Shield: mockIcon('shield'),
Truck: mockIcon('truck'),
};
});
const mockSolutions: Solution[] = [
{
id: 'manufacturing',
industry: '制造业',
title: '智能制造数字化转型方案',
subtitle: '端到端制造数字化',
description: '面向制造企业的整体转型方案',
challenges: ['数据孤岛', '库存积压'],
solutions: ['ERP 集成', 'BI 分析'],
relatedProducts: ['erp', 'bi'],
valueProposition: {
headline: '价值主张',
points: [{ icon: 'Factory', title: '降本增效', description: '描述' }],
},
suiteCombination: {
primaryProducts: ['erp'],
complementaryServices: ['consulting'],
rationale: '理由',
},
painPoints: ['数据孤岛'],
outcomes: [{ value: '30%', label: '效率提升' }],
recommendedProducts: ['erp', 'bi'],
},
{
id: 'retail',
industry: '贸易零售',
title: '零售数字化解决方案',
subtitle: '全渠道零售数字化',
description: '面向零售企业的数字化方案',
challenges: ['渠道割裂'],
solutions: ['CRM 统一'],
relatedProducts: ['crm', 'bi'],
valueProposition: {
headline: '价值主张',
points: [{ icon: 'ShoppingCart', title: '提升转化', description: '描述' }],
},
suiteCombination: {
primaryProducts: ['crm'],
complementaryServices: ['consulting'],
rationale: '理由',
},
painPoints: ['渠道割裂'],
outcomes: [{ value: '3x', label: '转化率提升' }],
recommendedProducts: ['crm', 'bi'],
},
];
const mockProducts: Product[] = [
{
id: 'erp',
title: '睿新ERP管理系统',
description: 'ERP',
image: '/erp.png',
category: '企业旗舰系列',
categoryId: 'enterprise',
status: '已发布',
overview: '概述',
features: [],
benefits: [],
process: [],
specs: [],
tags: [],
heroThemeId: 'erp',
caseStudies: [],
dataProofs: [],
certifications: [],
bundle: 'enterprise',
},
];
describe('SolutionsContentV3 - 非对称方案卡片', () => {
it('makes the first solution a featured full-width card', () => {
render(<SolutionsContentV3 solutions={mockSolutions} products={mockProducts} />);
expect(screen.getByTestId('solution-card-manufacturing')).toHaveAttribute('data-featured', 'true');
expect(screen.getByTestId('solution-card-manufacturing').className).toContain('md:col-span-2');
expect(screen.getByTestId('solution-card-retail')).toHaveAttribute('data-featured', 'false');
});
it('renders solution industries', () => {
render(<SolutionsContentV3 solutions={mockSolutions} products={mockProducts} />);
expect(screen.getByText('制造业')).toBeInTheDocument();
expect(screen.getByText('贸易零售')).toBeInTheDocument();
});
});