Claude Code for Browser Mode Testing — Guide
The Setup
You are testing frontend components with Vitest Browser Mode, which runs tests in a real browser instead of jsdom or happy-dom. Browser Mode uses Playwright or WebDriverIO to launch an actual browser, giving you real DOM APIs, real CSS rendering, and real event handling. Claude Code can write Vitest tests, but it generates tests that assume jsdom environment and miss Browser Mode’s capabilities.
What Claude Code Gets Wrong By Default
-
Uses jsdom-specific workarounds. Claude adds
jest.fn()forwindow.matchMediaor mocksIntersectionObserver. In Browser Mode, real browser APIs are available — these mocks are unnecessary and can cause conflicts. -
Tests with
renderfrom testing-library. Claude importsrenderfrom@testing-library/react. Browser Mode uses@vitest/browser/contextfor page interaction and can use testing-library through@testing-library/vueor@testing-library/reactbut with the browser provider handling actual rendering. -
Skips visual assertions. Claude only tests functionality, not appearance. Browser Mode runs in a real browser — you can test CSS properties, computed styles, layout, and even take screenshots for visual regression.
-
Ignores browser-specific configuration. Claude sets up Vitest with default config. Browser Mode requires
browser: { enabled: true, provider: 'playwright', name: 'chromium' }invitest.config.ts.
The CLAUDE.md Configuration
# Vitest Browser Mode Testing
## Testing
- Runner: Vitest with Browser Mode
- Browser: Playwright provider (chromium/firefox/webkit)
- Real DOM: No jsdom — tests run in actual browser
- Config: vitest.config.ts with browser section
## Browser Mode Rules
- Config: browser.enabled = true, browser.provider = 'playwright'
- No jsdom mocks needed — real browser APIs available
- Use page from @vitest/browser/context for interactions
- Use locator API: page.getByRole, page.getByText
- Real CSS: test computed styles, layout, visibility
- Screenshots: page.screenshot() for visual regression
- No window/document mocks — they are real
## Conventions
- Test files: *.browser.test.ts for browser tests
- Setup: playwright installed as dev dependency
- Use locator API over direct DOM queries
- Test accessibility with real screen reader semantics
- Test responsive: resize viewport in browser tests
- Use expect(element).toBeVisible() — real visibility check
- CI: run with headless browser (default in CI)
Workflow Example
You want to test a dropdown menu component that depends on CSS positioning and keyboard navigation. Prompt Claude Code:
“Write Vitest Browser Mode tests for a DropdownMenu component. Test that the menu opens on click, positions below the trigger, handles arrow key navigation between items, and closes on Escape. Use the Playwright locator API.”
Claude Code should configure the browser test with import { page } from '@vitest/browser/context', use page.getByRole('button') to find the trigger, click it, verify the menu is visible with real DOM checks, test arrow key navigation with keyboard.press('ArrowDown'), and verify Escape closes the menu.
Common Pitfalls
-
Mixing jsdom and browser tests. Claude puts all tests in the same config. Browser Mode tests should be in separate files or use workspace config — jsdom tests and browser tests cannot share the same Vitest instance.
-
Not waiting for animations. Claude asserts immediately after triggering a transition. In a real browser, CSS animations take time. Use
waitFororexpect.pollto wait for the element to reach its final state. -
Forgetting headless mode for CI. Claude runs browser tests without configuring headless mode. In CI environments, set
browser.headless: truein vitest config to run without a visible browser window.