From harness-claude
Tests Svelte components using Testing Library with render, fireEvent, screen queries, and Vitest. Covers rendering, user interactions, props updates, events, forms, and async states.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Test Svelte components with Testing Library using render, fireEvent, and waitFor
Tests Svelte 5 components with Vitest and @testing-library/svelte: rendering, user events, store mocking, async tick flushing.
Provides testing patterns and examples for React components, hooks, and integrations using Vitest, React Testing Library, and Jest.
Guides Svelte 5 best practices for reactivity using $state, $derived, $effect, $props; covers event handling, styling, library integration. For writing/editing Svelte components.
Share bugs, ideas, or general feedback.
Test Svelte components with Testing Library using render, fireEvent, and waitFor
npm install -D @testing-library/svelte @testing-library/jest-dom vitest jsdom
Configure Vitest for browser environment:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte({ hot: false })],
test: { environment: 'jsdom' },
});
import { render, screen } from '@testing-library/svelte';
import Greeting from './Greeting.svelte';
it('displays the name', () => {
render(Greeting, { props: { name: 'Alice' } });
expect(screen.getByText('Hello, Alice!')).toBeInTheDocument();
});
import { render, screen, fireEvent } from '@testing-library/svelte';
import Counter from './Counter.svelte';
it('increments count on button click', async () => {
render(Counter);
const button = screen.getByRole('button', { name: 'Increment' });
expect(screen.getByText('Count: 0')).toBeInTheDocument();
await fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
import { render, screen, fireEvent } from '@testing-library/svelte';
import SearchBox from './SearchBox.svelte';
it('updates search results on input', async () => {
render(SearchBox);
const input = screen.getByPlaceholderText('Search...');
await fireEvent.input(input, { target: { value: 'hello' } });
expect(await screen.findByText('Results for: hello')).toBeInTheDocument();
});
import { render, screen } from '@testing-library/svelte';
import UserCard from './UserCard.svelte';
it('updates when props change', async () => {
const { rerender } = render(UserCard, { props: { name: 'Alice' } });
expect(screen.getByText('Alice')).toBeInTheDocument();
await rerender({ name: 'Bob' });
expect(screen.getByText('Bob')).toBeInTheDocument();
});
import { render, screen, fireEvent } from '@testing-library/svelte';
import Button from './Button.svelte';
it('dispatches click event with payload', async () => {
const { component } = render(Button);
const handler = vi.fn();
component.$on('customClick', handler);
await fireEvent.click(screen.getByRole('button'));
expect(handler).toHaveBeenCalledWith(expect.objectContaining({ detail: { action: 'confirm' } }));
});
it('shows loading then data', async () => {
render(UserList);
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(await screen.findByText('Alice')).toBeInTheDocument();
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});
import { writable } from 'svelte/store';
it('reacts to store changes', async () => {
const user = writable({ name: 'Alice' });
render(UserDisplay, { props: { user } });
expect(screen.getByText('Alice')).toBeInTheDocument();
user.set({ name: 'Bob' });
await screen.findByText('Bob');
});
@testing-library/svelte follows the same user-centric testing philosophy as the React variant. Tests interact with rendered DOM elements, not component internals.
Svelte-specific considerations:
.svelte filesfireEvent, changes may not be reflected immediately. Use await or tick() from svelte to wait for updates$on('eventName', handler) — different from React's prop-based callbacksQuery priority (same as React Testing Library):
getByRole — accessible role and namegetByLabelText — form labelsgetByText — visible textgetByTestId — last resortSvelte 5 runes: If using Svelte 5 with runes ($state, $derived), the testing patterns remain the same at the component level — Testing Library interacts with the DOM, which is the output of runes.
Trade-offs:
fireEvent in Svelte requires await for reactivity updates, which is easy to forget$on is less ergonomic than React's callback propshttps://testing-library.com/docs/svelte-testing-library/intro/