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,96 @@
// @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, whileHover, 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-hover={JSON.stringify(whileHover)}
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', () => ({
ArrowUpRight: (props: any) => <svg data-testid="icon-arrow-up-right" className={props.className} />,
}));
// ─── Mock Data ────────────────────────────────────────────────────────────
const mockInsight = {
tag: '白皮书',
title: '2025年数字化转型趋势',
desc: '深入分析企业数字化转型的最新趋势与挑战',
date: '2025年1月',
};
// ─── Tests ───────────────────────────────────────────────────────────────
describe('InsightCard', () => {
it('should render tag, title, and description', async () => {
const { InsightCard } = await import('./insight-card');
render(<InsightCard {...mockInsight} />);
expect(screen.getByText('白皮书')).toBeInTheDocument();
expect(screen.getByText('2025年数字化转型趋势')).toBeInTheDocument();
expect(screen.getByText('深入分析企业数字化转型的最新趋势与挑战')).toBeInTheDocument();
});
it('should render date when provided', async () => {
const { InsightCard } = await import('./insight-card');
render(<InsightCard {...mockInsight} />);
expect(screen.getByText('2025年1月')).toBeInTheDocument();
});
it('should not render date when not provided', async () => {
const { InsightCard } = await import('./insight-card');
render(<InsightCard {...mockInsight} date={undefined} />);
expect(screen.queryByText('2025年1月')).not.toBeInTheDocument();
});
it('should render "阅读全文" for featured variant', async () => {
const { InsightCard } = await import('./insight-card');
render(<InsightCard {...mockInsight} featured />);
expect(screen.getByText('阅读全文')).toBeInTheDocument();
});
it('should render background image for featured variant', async () => {
const { InsightCard } = await import('./insight-card');
const { container } = render(
<InsightCard {...mockInsight} featured image="/images/insights/trends.jpg" />
);
const img = container.querySelector('img');
expect(img).toBeInTheDocument();
expect(img).toHaveAttribute('src', '/images/insights/trends.jpg');
});
it('should render link element when provided', async () => {
const { InsightCard } = await import('./insight-card');
render(<InsightCard {...mockInsight} link={<span data-testid="custom-link">查看更多</span>} />);
expect(screen.getByTestId('custom-link')).toBeInTheDocument();
expect(screen.getByText('查看更多')).toBeInTheDocument();
});
it('should apply custom className', async () => {
const { InsightCard } = await import('./insight-card');
const { container } = render(<InsightCard {...mockInsight} className="custom-card" />);
const card = container.querySelector('.custom-card');
expect(card).toBeInTheDocument();
});
});