Test components the way users use them with Vitest and Svelte Testing Library, then cover critical flows end-to-end in a real browser with Playwright.
Why: component tests render the component and interact with it the way a user would — find by role and text, click, assert on what is visible. Testing implementation details (internal state, CSS selectors) makes tests break on every refactor; testing behavior does not. Note: the SvelteKit setup wizard (npx sv create) can install Vitest for you.
$ pnpm add -D vitest jsdom @testing-library/svelte @testing-library/user-eventimport { render, screen } from '@testing-library/svelte'
import userEvent from '@testing-library/user-event'
import { expect, test } from 'vitest'
// The Counter component from the Runes lesson,
// saved as Counter.svelte in the same folder as this test
import Counter from './Counter.svelte'
test('increments when clicked', async () => {
render(Counter, { props: { step: 2 } })
// Query like a user would: by role and visible text
const button = screen.getByRole('button', { name: /count: 0/i })
await userEvent.click(button)
// After one click with step 2, the label should read "Count: 2"
expect(screen.getByRole('button', { name: /count: 2/i })).toBeDefined()
})Why: end-to-end tests drive the real app in a real browser — routing, server, database, everything wired together. A handful of E2E tests over your critical flows catch what unit tests cannot. Note: the SvelteKit setup wizard can install Playwright for you.
$ pnpm create playwrightimport { test, expect } from '@playwright/test'
// Drives the Signup form from the Forms lesson
// (src/routes/signup/+page.svelte) — start the app first with
// pnpm dev (or npm run dev), then run the test
// page is a real browser tab that the test drives
test('user can sign up', async ({ page }) => {
await page.goto('http://localhost:5173/signup')
// Find elements the way a user would, then act on them
await page.getByPlaceholder('you@example.com').fill('ada@example.com')
await page.getByRole('button', { name: 'Sign up' }).click()
// Passes once the welcome message appears
await expect(page.getByText('Welcome, ada')).toBeVisible()
})