All Posts
Migrating from @testing-library/svelte to vitest-browser-svelte
sveltesveltekittestingmigrationvitest
If your SvelteKit project uses @testing-library/svelte with JSDOM,
it’s time to consider migrating to vitest-browser-svelte. Here’s how
to do it systematically.
Why Migrate?
@testing-library/svelte served us well, but:
- JSDOM doesn’t support Svelte 5’s runes properly
- Real browser testing catches bugs JSDOM misses
- The ecosystem is moving to browser-based testing
- Better debugging with Playwright’s tools
Prerequisites
You’ll need:
- Vitest 3.x or higher
- Node.js 18+
- Playwright installed
Step 1: Install Dependencies
pnpm add -D vitest-browser-svelte @vitest/browser playwright Step 2: Update Vitest Config
// vitest.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}'],
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
}); Step 3: Update Imports
The biggest change is the import path:
// Before
import { render, screen } from '@testing-library/svelte';
// After
import { render, screen } from 'vitest-browser-svelte'; Step 4: Update Assertions
Assertions become async with expect.element():
// Before
expect(screen.getByRole('button')).toHaveTextContent('Click me');
// After
await expect.element(screen.getByRole('button')).toHaveTextContent(
'Click me',
); Step 5: Update Event Handling
Events use locator methods directly:
// Before
import { fireEvent } from '@testing-library/svelte';
fireEvent.click(screen.getByRole('button'));
// After
await screen.getByRole('button').click(); Step 6: Handle userEvent Changes
If you used @testing-library/user-event:
// Before
import userEvent from '@testing-library/user-event';
await userEvent.type(screen.getByRole('textbox'), 'hello');
// After
await screen.getByRole('textbox').fill('hello'); Common Migration Patterns
Container Queries
// Before
const { container } = render(Component);
const element = container.querySelector('.my-class');
// After
const { container } = render(Component);
const element = container.locator('.my-class');
await expect.element(element).toBeVisible(); Waiting for Elements
// Before
await waitFor(() => {
expect(screen.getByText('Loaded')).toBeInTheDocument();
});
// After
await expect.element(screen.getByText('Loaded')).toBeVisible(); Component Props Updates
// Before
const { rerender } = render(Component, { props: { count: 0 } });
await rerender({ count: 1 });
// After
const { rerender } = render(Component, { props: { count: 0 } });
await rerender({ props: { count: 1 } }); Debugging Tips
Run Tests in Headed Mode
pnpm vitest --browser.headless=false Use Playwright’s UI Mode
pnpm vitest --ui Inspect Failing Tests
test('debug example', async () => {
render(Component);
await page.pause(); // Stops execution, opens inspector
}); CI Configuration
For GitHub Actions:
- name: Install Playwright
run: pnpm exec playwright install chromium
- name: Run tests
run: pnpm test Migration Checklist
- Install new dependencies
- Update vitest.config.ts
- Replace imports file by file
- Update assertions to async
- Replace fireEvent with locator methods
- Update userEvent to fill/click/etc
- Test in CI with Playwright installed
- Remove old @testing-library dependencies
Getting Stuck?
Migration can surface hidden issues in your test suite. If you need help modernizing your SvelteKit testing infrastructure - let’s talk.