Troubleshooting
Common Errors & Solutions
Client-Server Mismatch Issues
Cause: Client and server expecting different data formats or field names, often hidden by heavy mocking in tests.
Example Scenarios:
- Client sends
emailbut server expectsuser_email - Form sends
FormDatabut server expects JSON - Client uses different validation rules than server
Solution: Use the Client-Server Alignment Strategy:
1// ❌ BRITTLE: Mocking hides real mismatches
2const mock_request = {
3 formData: vi.fn().mockResolvedValue({
4 get: vi.fn().mockReturnValue('[email protected]'),
5 }),
6};
7
8// ✅ ROBUST: Real FormData catches field name issues
9const form_data = new FormData();
10form_data.append('email', '[email protected]'); // Must match server expectations
11const request = new Request('http://localhost/api/register', {
12 method: 'POST',
13 body: form_data,
14});Prevention:
- Share validation logic between client and server
- Use real
FormData/Requestobjects in server tests - Add E2E tests for critical form flows
“Expected 2 arguments, but got 0”
Cause: Mock function signature doesn’t match the actual function being mocked.
Example Error:
1TypeError: Expected 2 arguments, but got 0Solution: Update your mock to accept the correct number of arguments:
1// ❌ Incorrect - no arguments expected
2const util_function = vi.fn(() => 'result');
3
4// ✅ Correct - accepts expected arguments
5const util_function = vi.fn(
6 (input: string, options: object) => 'result',
7);Debugging Steps:
- Check the actual function signature in your code
- Update the mock to match the expected parameters
- Use
vi.fn().mockImplementation()for complex logic
“lifecycle_outside_component”
Cause: Attempting to use Svelte context functions like getContext() outside of a component.
Example Error:
1Error: getContext can only be called during component initialisationSolution: Skip context-dependent tests and plan for Svelte 5 updates:
1test.skip('context dependent feature', () => {
2 // TODO: Update for Svelte 5 context handling
3 // This test requires component context
4});Alternative Approach:
1// Mock the context instead
2vi.mock('svelte', async (importOriginal) => {
3 const actual = await importOriginal();
4 return {
5 ...actual,
6 getContext: vi.fn(() => ({
7 subscribe: vi.fn(),
8 set: vi.fn(),
9 update: vi.fn(),
10 })),
11 };
12});Element Not Found Errors
Cause: Element queries failing due to timing or incorrect selectors.
Example Error:
1Error: Element not found: getByRole('button')Solution: Use proper waits and semantic queries:
1// ❌ May fail due to timing
2const button = page.getByRole('button');
3button.click();
4
5// ✅ Wait for element to exist
6await expect.element(page.getByRole('button')).toBeInTheDocument();
7await page.getByRole('button').click();Debugging Steps:
- Check if the element exists in the DOM
- Verify the selector is correct
- Add waits for dynamic content
- Use browser DevTools to inspect the actual HTML
Test Hangs or Timeouts
Cause: Tests waiting indefinitely for elements or actions that never complete.
Common Scenarios:
- Clicking submit buttons with SvelteKit form enhancement
- Waiting for elements that never appear
- Infinite loading states
Solution:
1// ❌ Can cause hangs with SvelteKit forms
2await page.getByRole('button', { name: 'Submit' }).click();
3
4// ✅ Test form state directly
5await render(Form, { props: { errors: { email: 'Required' } } });
6await expect.element(page.getByText('Required')).toBeInTheDocument();
7
8// ✅ Use timeouts for flaky elements
9await expect.element(page.getByText('Success')).toBeInTheDocument({
10 timeout: 5000,
11});Snippet Type Errors
Cause: vitest-browser-svelte has limitations with Svelte 5 snippet types.
Example Error:
1Type '() => string' is not assignable to type 'Snippet<[]>'Solution: Use alternative approaches or createRawSnippet:
1// ❌ Problematic with vitest-browser-svelte
2render(Component, {
3 children: () => 'Text content',
4});
5
6// ✅ Use createRawSnippet
7const children = createRawSnippet(() => ({
8 render: () => `<span>Text content</span>`,
9}));
10await render(Component, { children });
11
12// ✅ Or use alternative props
13await render(Component, {
14 label: 'Text content', // Instead of children
15});Brittle Tests Breaking After Library Updates
Cause: Tests checking exact implementation details instead of user value.
Example Error:
1Expected: containing "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
2Received: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1..."This happens when icon libraries update (Heroicons v1 → v2, Lucide updates) and SVG path data changes.
Solution: Test semantic classes and user experience instead:
1// ❌ BRITTLE - Breaks when icon library updates
2test('should render success icon', () => {
3 const { body } = render(StatusIcon, { status: 'success' });
4 expect(body).toContain(
5 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
6 );
7});
8
9// ✅ ROBUST - Tests user-visible styling and accessibility
10test('should indicate success to users', async () => {
11 await render(StatusIcon, { status: 'success' });
12
13 // Test what users see and experience
14 await expect
15 .element(page.getByRole('img', { name: /success/i }))
16 .toBeInTheDocument();
17 await expect
18 .element(page.getByTestId('status-icon'))
19 .toHaveClass('text-success');
20
21 // Test semantic structure (survives library updates)
22 const { body } = render(StatusIcon, { status: 'success' });
23 expect(body).toContain('text-success'); // Color users see
24 expect(body).toContain('h-4 w-4'); // Size users see
25 expect(body).toContain('<svg'); // Icon is present
26 expect(body).toContain('aria-label'); // Accessible
27});Prevention Strategy:
- Test CSS classes that control appearance (
text-success,h-4 w-4) - Test semantic HTML structure (
<svg>,role="img") - Test user interactions and accessibility
- Avoid testing exact SVG paths, internal markup, or generated class names
Common Brittle Patterns to Avoid:
1// ❌ SVG path coordinates (change with icon library updates)
2expect(body).toContain('M9 12l2 2 4-4...');
3
4// ❌ Internal component IDs (change with build tools)
5expect(body).toContain('__svelte_component_123');
6
7// ❌ Generated CSS class names (change with CSS-in-JS)
8expect(body).toContain('styles__button__abc123');Browser Environment Issues
Playwright Installation Problems
Error: browserType.launch: Executable doesn't exist
Solution:
1# Install Playwright browsers
2npx playwright install
3
4# Or install specific browser
5npx playwright install chromiumBrowser Launch Failures
Error: Browser launch failed
Common Causes & Solutions:
- Missing dependencies on Linux:
1# Install required dependencies
2npx playwright install-deps- Insufficient permissions:
1# Run with proper permissions
2sudo npx playwright install- CI/CD environment issues:
1# In GitHub Actions
2- name: Install Playwright Browsers
3 run: npx playwright install --with-depsMemory Issues
Error: Out of memory or browser crashes
Solution:
1// vite.config.ts
2import { playwright } from '@vitest/browser-playwright';
3import { defineConfig } from 'vitest/config';
4
5export default defineConfig({
6 test: {
7 browser: {
8 enabled: true,
9 name: 'chromium',
10 provider: playwright(),
11 // Reduce memory usage
12 headless: true,
13 },
14 // Limit parallel workers
15 pool: 'threads',
16 poolOptions: {
17 threads: {
18 maxThreads: 2, // Reduce from default
19 },
20 },
21 },
22});Mocking Issues
Module Not Found in Mocks
Error: Cannot resolve module in mock
Solution: Use correct import paths in mocks:
1// ❌ Incorrect path
2vi.mock('./utils', () => ({
3 // mock implementation
4}));
5
6// ✅ Correct absolute path
7vi.mock('#lib/utils.js', () => ({
8 // mock implementation
9}));Mock Not Being Applied
Issue: Mock functions not being called or real implementation running.
Solution: Ensure mocks are hoisted:
1// ✅ Mocks at top of file, before other imports
2vi.mock('#lib/api.js', () => ({
3 fetch_data: vi.fn(() => Promise.resolve({})),
4}));
5
6import { render } from 'vitest-browser-svelte';
7import Component from './component.svelte';Partial Mock Issues
Problem: Need to mock only part of a module.
Solution: Use importOriginal:
1vi.mock('#lib/utils.js', async (importOriginal) => {
2 const actual = await importOriginal();
3 return {
4 ...actual,
5 // Only mock specific functions
6 validate_email: vi.fn(() => true),
7 };
8});Component Testing Issues
Props Not Updating
Problem: Component props don’t update in tests.
Solution: Re-render with new props:
1// ❌ Props won't update
2const { rerender } = render(Component, { count: 0 });
3// count is still 0 internally
4
5// ✅ Re-render with new props
6const { rerender } = await render(Component, { count: 0 });
7rerender({ count: 1 });Event Handlers Not Firing
Problem: Event handlers in tests don’t trigger.
Debugging Steps:
- Check event handler prop names (onclick vs onClick)
- Verify event types match
- Ensure elements are interactive
1// ✅ Correct event handler props for Svelte
2await render(Button, {
3 onclick: vi.fn(), // Not onClick
4});
5
6// ✅ Ensure element is clickable
7await expect.element(page.getByRole('button')).toBeEnabled();
8await page.getByRole('button').click();CSS Classes Not Applied
Problem: CSS classes don’t appear in tests.
Solution: Check if styles are imported and applied:
1// In component
2<style>
3 .btn-primary {
4 background: blue;
5 }
6</style>
7
8// In test - check for actual class, not styles
9await expect.element(button).toHaveClass('btn-primary');
10// Don't test: computed styles in browser testsPerformance Issues
Slow Test Execution
Causes & Solutions:
- Too many browser instances:
1// Reduce workers
2export default defineConfig({
3 test: {
4 poolOptions: {
5 threads: {
6 maxThreads: 2,
7 },
8 },
9 },
10});- Heavy component rendering:
1// Mock heavy dependencies
2vi.mock('#lib/heavy-chart-component.svelte', () => ({
3 default: vi.fn().mockImplementation(() => ({
4 $$: {},
5 $set: vi.fn(),
6 $destroy: vi.fn(),
7 })),
8}));- No test parallelization:
1// Use concurrent tests where possible
2describe('Independent tests', () => {
3 test.concurrent('test 1', async () => {});
4 test.concurrent('test 2', async () => {});
5});Memory Leaks
Symptoms: Tests become slower over time, memory usage increases.
Solutions:
- Clean up after tests:
1afterEach(() => {
2 vi.clearAllMocks();
3 // Clear any global state
4});- Limit test scope:
1// Don't render entire app in every test
2await render(SpecificComponent); // ✅
3// render(App); // ❌ HeavyCI/CD Issues
Tests Pass Locally, Fail in CI
Common Causes:
- Timing differences:
1// Add longer timeouts for CI
2await expect.element(element).toBeInTheDocument({
3 timeout: process.env.CI ? 10000 : 5000,
4});- Missing browser dependencies:
1# .github/workflows/test.yml
2- name: Install dependencies
3 run: npx playwright install --with-deps- Different viewport sizes:
1// In the client project's browser config
2import { playwright } from '@vitest/browser-playwright';
3import { defineConfig } from 'vitest/config';
4
5export default defineConfig({
6 test: {
7 browser: {
8 enabled: true,
9 provider: playwright(),
10 instances: [{ browser: 'chromium', headless: true }],
11 viewport: { width: 1280, height: 720 },
12 },
13 },
14});Flaky Tests
Symptoms: Tests pass/fail randomly.
Solutions:
- Avoid hard-coded timeouts:
1// ❌ Flaky
2await page.waitForTimeout(1000);
3
4// ✅ Reliable
5await expect.element(page.getByText('Loaded')).toBeInTheDocument();- Use force clicks for overlays:
1// ❌ May fail if element is covered
2await button.click();
3
4// ✅ Reliable for covered elements
5await button.click({ force: true });- Wait for specific states:
1// ❌ Race condition
2await page.getByRole('button').click();
3await page.getByText('Success').click();
4
5// ✅ Wait for state
6await page.getByRole('button').click();
7await expect.element(page.getByText('Success')).toBeInTheDocument();
8await page.getByText('Success').click();Debugging Strategies
Visual Debugging
1// Take screenshots for debugging
2test('debug test', async () => {
3 await render(Component);
4
5 // Take screenshot
6 await page.screenshot({ path: 'debug.png' });
7
8 // Or in CI
9 if (process.env.CI) {
10 await page.screenshot({ path: 'debug.png' });
11 }
12});Console Debugging
1// View page content
2test('debug content', async () => {
3 await render(Component);
4
5 // Log current HTML
6 const html = await page.innerHTML('body');
7 console.log(html);
8
9 // Check console messages
10 page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
11});Step-by-Step Debugging
1// In the client project's browser config
2import { playwright } from '@vitest/browser-playwright';
3import { defineConfig } from 'vitest/config';
4
5export default defineConfig({
6 test: {
7 browser: {
8 enabled: true,
9 provider: playwright({ launchOptions: { slowMo: 1000 } }),
10 instances: [{ browser: 'chromium', headless: false }],
11 },
12 },
13});Quick Reference
Error Patterns
- “Expected 2 arguments” → Fix mock function signatures
- “lifecycle_outside_component” → Skip context tests or mock context
- “Element not found” → Add waits, check selectors
- Test hangs → Avoid SvelteKit form submits, add timeouts
- “Snippet type error” → Use createRawSnippet or alternative props
- Tests break after library updates → Don’t test SVG paths, test semantic classes instead
Performance Red Flags
- Tests taking >30s → Mock heavy dependencies
- Memory increasing → Clean up mocks, limit scope
- Flaky tests → Remove hard timeouts, add proper waits
Debugging Steps
- Check browser console for errors
- Take screenshots of failing tests
- Log HTML content to verify DOM state
- Slow down tests with
slowMooption - Run tests in non-headless mode for visual debugging