Skip to content

pnpm add -D vitest @testing-library/react @testing-library/jest-dom jsdom
Vitest
RTL
React Testing Library
jest-axe
tsx
1// vitest.config.ts
2import { defineConfig } from 'vitest/config';
3import react from '@vitejs/plugin-react';
4 
5export default defineConfig({
6 plugins: [react()],
7 test: {
8 environment: 'jsdom',
9 globals: true,
10 setupFiles: './test/setup.ts',
11 },
12});
tsx
1// test/setup.ts
2import '@testing-library/jest-dom';
3import { vi } from 'vitest';
4 
5// Mock IntersectionObserver for useMotion
6global.IntersectionObserver = vi.fn().mockImplementation(() => ({
7 observe: vi.fn(),
8 unobserve: vi.fn(),
9 disconnect: vi.fn(),
10}));
11 
12// Mock matchMedia for dark mode
13Object.defineProperty(window, 'matchMedia', {
14 value: vi.fn().mockImplementation((query) => ({
15 matches: false,
16 media: query,
17 addEventListener: vi.fn(),
18 removeEventListener: vi.fn(),
19 })),
20});
tsx
1// Button.test.tsx
2import { render, screen, fireEvent } from '@testing-library/react';
3import { Button } from '@hua-labs/hua';
4import { describe, it, expect, vi } from 'vitest';
5 
6describe('Button', () => {
7 it('renders correctly', () => {
8 render(<Button>Click me</Button>);
9 expect(screen.getByRole('button')).toHaveTextContent('Click me');
10 });
11 
12 it('handles click events', () => {
13 const handleClick = vi.fn();
14 render(<Button onClick={handleClick}>Click</Button>);
15 
16 fireEvent.click(screen.getByRole('button'));
17 expect(handleClick).toHaveBeenCalledTimes(1);
18 });
19 
20 it('applies variant styles', () => {
21 render(<Button variant="outline">Outline</Button>);
22 expect(screen.getByRole('button')).toHaveClass('border');
23 });
24});
tsx
1// useMotion.test.tsx
2import { renderHook } from '@testing-library/react';
3import { useMotion } from '@hua-labs/hua/framework';
4import { describe, it, expect } from 'vitest';
5 
6describe('useMotion', () => {
7 it('returns ref and style', () => {
8 const { result } = renderHook(() =>
9 useMotion({ type: 'fadeIn', duration: 500 })
10 );
11 
12 expect(result.current.ref).toBeDefined();
13 expect(result.current.style).toBeDefined();
14 });
15 
16 it('applies initial opacity for fadeIn', () => {
17 const { result } = renderHook(() =>
18 useMotion({ type: 'fadeIn' })
19 );
20 
21 // Before intersection, opacity should be 0
22 expect(result.current.style.opacity).toBe(0);
23 });
24});
tsx
1// i18n.test.tsx
2import { render, screen } from '@testing-library/react';
3import { I18nProvider, useTranslation } from '@hua-labs/hua';
4import { describe, it, expect } from 'vitest';
5 
6const TestComponent = () => {
7 const { t, currentLanguage } = useTranslation();
8 return (
9 <div>
10 <span data-testid="lang">{currentLanguage}</span>
11 <span data-testid="text">{t('common:hello')}</span>
12 </div>
13 );
14};
15 
16const config = {
17 defaultLanguage: 'ko',
18 supportedLanguages: [
19 { code: 'ko', name: 'Korean', nativeName: '한국어' },
20 { code: 'en', name: 'English', nativeName: 'English' },
21 ],
22 namespaces: ['common'],
23 loadPath: '/translations/{{lng}}/{{ns}}.json',
24};
25 
26describe('useTranslation', () => {
27 it('provides current language', () => {
28 render(
29 <I18nProvider config={config}>
30 <TestComponent />
31 </I18nProvider>
32 );
33 
34 expect(screen.getByTestId('lang')).toHaveTextContent('ko');
35 });
36});
tsx
1// accessibility.test.tsx
2import { render } from '@testing-library/react';
3import { axe, toHaveNoViolations } from 'jest-axe';
4import { Button, Card, Input } from '@hua-labs/hua';
5import { describe, it, expect } from 'vitest';
6 
7expect.extend(toHaveNoViolations);
8 
9describe('Accessibility', () => {
10 it('Button has no a11y violations', async () => {
11 const { container } = render(<Button>Click me</Button>);
12 const results = await axe(container);
13 expect(results).toHaveNoViolations();
14 });
15 
16 it('Input with label has no a11y violations', async () => {
17 const { container } = render(
18 <div>
19 <label htmlFor="email">Email</label>
20 <Input id="email" type="email" />
21 </div>
22 );
23 const results = await axe(container);
24 expect(results).toHaveNoViolations();
25 });
26 
27 it('Card with proper heading structure', async () => {
28 const { container } = render(
29 <Card>
30 <h2>Card Title</h2>
31 <p>Card content</p>
32 </Card>
33 );
34 const results = await axe(container);
35 expect(results).toHaveNoViolations();
36 });
37});