import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import HeroParticleField from './hero-particle-field';
jest.mock('@/hooks/use-reduced-motion', () => ({
useReducedMotion: jest.fn(() => false),
}));
describe('HeroParticleField - 动态粒子层(入场浮现后静止)', () => {
it('renders an aria-hidden, non-interactive canvas layer', () => {
render();
const canvas = screen.getByTestId('hero-particle-field');
expect(canvas.tagName).toBe('CANVAS');
expect(canvas).toHaveAttribute('aria-hidden', 'true');
expect(canvas.className).toContain('pointer-events-none');
});
it('does not throw under reduced motion (degrades to a single static frame)', () => {
const { useReducedMotion } = require('@/hooks/use-reduced-motion');
(useReducedMotion as jest.Mock).mockReturnValue(true);
expect(() => render()).not.toThrow();
// 静态降级下仍渲染 canvas(无 rAF 循环,仅一次绘制)
expect(screen.getByTestId('hero-particle-field').tagName).toBe('CANVAS');
});
it('bails safely when no 2d context is available (jsdom / SSR guard)', () => {
const ctxSpy = jest
.spyOn(HTMLCanvasElement.prototype, 'getContext')
.mockReturnValue(null as any);
expect(() => render()).not.toThrow();
ctxSpy.mockRestore();
});
});