Skip to main content

testing-frontend

Use when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.

Quellinformationen

Repository
MadAppGang/claude-code
Letzte Quellaktivität
20. Januar 2026 um 08:16
Erkannte Sprache von SKILL.md
Englisch
Sterne
284
Forks
25

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
testing-frontend
version
1.0.0
description
Use when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.
keywords
["frontend testing","Vitest","React Testing Library","Vue Test Utils","component testing","user event testing","mocking","accessibility testing"]
plugin
dev
updated
2026-01-20T00:00:00.000Z
# Frontend Testing Patterns ## Overview Testing patterns for frontend applications using Vitest and React Testing Library / Vue Test Utils. ## Testing Philosophy ### User-Centric Testing Test behavior, not implementation. Query elements the way users would find them. ```tsx // BAD: Testing implementation expect(wrapper.state('isOpen')).toBe(true); expect(wrapper.find('.modal-class').exists()).toBe(true); // GOOD: Testing behavior expect(screen.getByRole('dialog')).toBeInTheDocument(); expect(screen.getByText('Modal Title')).toBeVisible(); ``` ### Query Priority Use queries in this order (most to least preferred): 1. `getByRole` - Accessible to everyone 2. `getByLabelText` - Form elements 3. `getByPlaceholderText` - Inputs 4. `getByText` - Non-interactive elements 5. `getByDisplayValue` - Form current values 6. `getByAltText` - Images 7. `getByTestId` - Last resort ## Component Testing (React) ### Basic Component Test ```tsx import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { UserCard } from './UserCard'; describe('UserCard', () => { const user = { id: '1', name: 'John Doe', email: 'john@example.com' }; it('renders user information', () => { render(<UserCard user={user} />); expect(screen.getByText('John Doe')).toBeInTheDocument(); expect(screen.getByText('john@example.com')).toBeInTheDocument(); }); it('calls onSelect when clicked', async () => { const onSelect = vi.fn(); const userEvt = userEvent.setup(); render(<UserCard user={user} onSelect={onSelect} />); await userEvt.click(screen.getByRole('button')); expect(onSelect).toHaveBeenCalledWith(user); }); }); ``` ### Testing Async Components ```tsx import { render, screen, waitFor } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { UserList } from './UserList'; // Mock API vi.mock('@/api', () => ({ getUsers: vi.fn(), })); describe('UserList', () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }); const wrapper = ({ children }) => ( <QueryClientProvider client={queryClient}> {children} </QueryClientProvider> ); beforeEach(() => { queryClient.clear(); }); it('shows loading state', () => { api.getUsers.mockImplementation(() => new Promise(() => {})); render(<UserList />, { wrapper }); expect(screen.getByText('Loading...')).toBeInTheDocument(); }); it('shows users when loaded', async () => { api.getUsers.mockResolvedValue([ { id: '1', name: 'John' }, { id: '2', name: 'Jane' }, ]); render(<UserList />, { wrapper }); await waitFor(() => { expect(screen.getByText('John')).toBeInTheDocument(); expect(screen.getByText('Jane')).toBeInTheDocument(); }); }); it('shows error message on failure', async () => { api.getUsers.mockRejectedValue(new Error('Network error')); render(<UserList />, { wrapper }); await waitFor(() => { expect(screen.getByText(/error/i)).toBeInTheDocument(); }); }); }); ``` ### Testing Forms ```tsx import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { LoginForm } from './LoginForm'; describe('LoginForm', () => { it('submits form with valid data', async () => { const onSubmit = vi.fn(); const user = userEvent.setup(); render(<LoginForm onSubmit={onSubmit} />); await user.type(screen.getByLabelText(/email/i), 'test@example.com'); await user.type(screen.getByLabelText(/password/i), 'password123'); await user.click(screen.getByRole('button', { name: /submit/i })); expect(onSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123', }); }); it('shows validation errors', async () => { const user = userEvent.setup(); render(<LoginForm onSubmit={vi.fn()} />); // Submit empty form await user.click(screen.getByRole('button', { name: /submit/i })); expect(screen.getByText(/email is required/i)).toBeInTheDocument(); expect(screen.getByText(/password is required/i)).toBeInTheDocument(); }); it('disables submit button while submitting', async () => { const user = userEvent.setup(); render( <LoginForm onSubmit={() => new Promise((resolve) => setTimeout(resolve, 100))} /> ); await user.type(screen.getByLabelText(/email/i), 'test@example.com'); await user.type(screen.getByLabelText(/password/i), 'password123'); await user.click(screen.getByRole('button', { name: /submit/i })); expect(screen.getByRole('button', { name: /submitting/i })).toBeDisabled(); }); }); ``` ## Component Testing (Vue) ### Basic Component Test ```ts import { mount } from '@vue/test-utils'; import UserCard from './UserCard.vue'; describe('UserCard', () => { const user = { id: '1', name: 'John Doe', email: 'john@example.com' }; it('renders user information', () => { const wrapper = mount(UserCard, { props: { user }, }); expect(wrapper.text()).toContain('John Doe'); expect(wrapper.text()).toContain('john@example.com'); }); it('emits select event when clicked', async () => { const wrapper = mount(UserCard, { props: { user }, }); await wrapper.find('button').trigger('click'); expect(wrapper.emitted('select')).toBeTruthy(); expect(wrapper.emitted('select')[0]).toEqual([user]); }); }); ``` ### Testing with Pinia ```ts import { mount } from '@vue/test-utils'; import { createTestingPinia } from '@pinia/testing'; import UserList from './UserList.vue'; import { useUserStore } from '@/stores/userStore'; describe('UserList', () => { it('renders users from store', () => { const wrapper = mount(UserList, { global: { plugins: [ createTestingPinia({ initialState: { user: { users: [ { id: '1', name: 'John' }, { id: '2', name: 'Jane' }, ], }, }, }), ], }, }); expect(wrapper.text()).toContain('John'); expect(wrapper.text()).toContain('Jane'); }); it('calls fetchUsers on mount', () => { mount(UserList, { global: { plugins: [createTestingPinia()], }, }); const store = useUserStore(); expect(store.fetchUsers).toHaveBeenCalled(); }); }); ``` ## Hook Testing ```tsx import { renderHook, act, waitFor } from '@testing-library/react'; import { useCounter } from './useCounter'; describe('useCounter', () => { it('initializes with default value', () => { const { result } = renderHook(() => useCounter()); expect(result.current.count).toBe(0); }); it('initializes with custom value', () => { const { result } = renderHook(() => useCounter({ initial: 10 })); expect(result.current.count).toBe(10); }); it('increments counter', () => { const { result } = renderHook(() => useCounter()); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); it('respects max limit', () => { const { result } = renderHook(() => useCounter({ initial: 9, max: 10 })); act(() => { result.current.increment(); result.current.increment(); }); expect(result.current.count).toBe(10); }); }); ``` ## Mocking ### API Mocking ```tsx // __mocks__/api.ts export const api = { getUsers: vi.fn(), createUser: vi.fn(), updateUser: vi.fn(), deleteUser: vi.fn(), }; // In test vi.mock('@/api'); beforeEach(() => { vi.clearAllMocks(); }); it('fetches users', async () => { api.getUsers.mockResolvedValue([{ id: '1', name: 'John' }]); render(<UserList />); await waitFor(() => { expect(api.getUsers).toHaveBeenCalledTimes(1); }); }); ``` ### Module Mocking ```tsx // Mock entire module vi.mock('@/utils/date', () => ({ formatDate: vi.fn(() => '2024-01-01'), })); // Mock specific export vi.mock('@/config', async () => { const actual = await vi.importActual('@/config'); return { ...actual, API_URL: 'http://test-api.com', }; }); ``` ### Timer Mocking ```tsx describe('Debounced search', () => { beforeEach(() => { vi.useFakeTimers(); });
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen