vitest-browser-react

repository·main·Indexed 43 days ago

https://github.com/vitest-community/vitest-browser-react

A lightweight wrapper for rendering React components and testing hooks in Vitest Browser Mode. Version 2.3.0 integrates Vitest's native locators and CDP-based interactions to reduce flakiness, providing a testing experience inspired by Testing Library but optimized for the Vitest browser runner. Includes utilities like render, renderHook, and cleanup, and supports injection into the Vitest page object.

Tokens
3.1K
Snippets
11
Records
16
Agent score
53%

What's inside vitest-browser-react

  1. How vitest-browser-react differs from @testing-library/react

    main

    While inspired by @testing-library/react, vitest-browser-react is optimized for Vitest Browser Mode:

    1. Integrated Locators: It uses Vitest's built-in locators. This allows Vitest to continue querying an element during an assertion. If an element exists but is in an invalid state (e.g., not yet visible), the assertion will retry until the condition is met, reducing flakiness.
    2. CDP-based Interactions: Instead of faking events (like user-event often does), it uses the Chrome DevTools Protocol (CDP) for interactions like .click(), which more closely mimics real browser behavior.
    3. Simplified API: It streamlines the API to avoid the confusion between DOM/render libraries and user-event libraries that can occur when using standard testing-library in Browser Mode.
    // Comparison: Integrated Locators vs findBy
    
    // vitest-browser-react (Integrated)
    await expect.element(page.getByRole('button')).toBeVisible()
    
    // @testing-library/react (Standard)
    const button = await screen.findByRole('button')
    expect(button).toBeVisible()
  2. Inject render into the Vitest page object

    main

    You can automatically inject the render method onto the Vitest page object by importing vitest-browser-react in your Vitest setup file. This allows you to call page.render() directly in your tests.

    // vitest.config.ts
    import { defineConfig } from 'vitest/config'
    import react from '@vitejs/plugin-react'
    
    export default defineConfig({
      plugins: [react()],
      test: {
        setupFiles: ['./setup-file.ts'],
        browser: {
          // ... your config
        },
      },
    })
    
    // ./setup-file.ts
    // Add this import at the top to enable page.render() and TypeScript types
    import 'vitest-browser-react'

    Usage in test:

    import { page } from 'vitest/browser'
    
    test('counter button increments the count', async () => {
      const screen = await page.render(<Component count={1} />)
    
      await screen.cleanup()
    })
  3. Render React components in Vitest Browser Mode

    main

    Use vitest-browser-react to render React components within Vitest Browser Mode. This library integrates Vitest's locators API and retry-ability mechanism directly into the render result. This allows you to interact with elements (e.g., via .click()) without manually waiting for elements to appear or verifying their existence first.

    Requirements:

    • vitest version 4.0.0 or higher.
    • @vitejs/plugin-react is recommended for features like automatic React imports.
    import { render } from 'vitest-browser-react'
    import { expect, test } from 'vitest'
    
    test('counter button increments the count', async () => {
      const screen = await render(<Component count={1} />)
    
      await screen.getByRole('button', { name: 'Increment' }).click()
    
      await expect.element(screen.getByText('Count is 2')).toBeVisible()
    })
  4. Extend the Vitest Browser Page with React utilities

    main

    The vitest-browser-react package extends the Vitest page object from vitest/browser. This allows you to access React-specific testing utilities directly from the page instance, making your test code more concise and integrated with the Vitest browser API.

    import { page } from 'vitest/browser'
    
    // After importing vitest-browser-react, page is extended:
    await page.render(<MyComponent />)
    await page.renderHook(() => useMyHook())
  5. Configure React Strict Mode

    main

    By default, vitest-browser-react performs component cleanup before tests begin. To customize behavior or enable React Strict Mode, import configure from vitest-browser-react/pure.

    import { configure } from 'vitest-browser-react/pure'
    
    configure({
      // enabled by default in this library, but can be toggled
      reactStrictMode: true,
    })
  6. Test React hooks with renderHook

    main

    Use the renderHook helper to test standalone React hooks. The returned result object contains the current value of the hook, and act is provided to wrap updates that trigger state changes.

    import { renderHook } from 'vitest-browser-react'
    import { expect, test } from 'vitest'
    
    test('should increment counter', async () => {
      const { result, act } = await renderHook(() => useCounter())
    
      await act(() => {
        result.current.increment()
      })
    
      expect(result.current.count).toBe(1)
    })
  7. Render a React component with `render()`

    main

    Use the render function to mount a React component into the DOM for testing. It returns a RenderResult object containing the container, a locator for finding elements, and utilities for debugging and lifecycle management.

    RenderOptions

    When calling render, you can provide an options object:

    • container: An optional HTMLElement to render into. If not provided, a new div is appended to the baseElement.
    • baseElement: An optional HTMLElement to act as the parent for the container. Defaults to document.body.
    • wrapper: A React component used to wrap the component under test (e.g., for providing Context).
    • createRootOptions: Options passed directly to React's createRoot API.
    import { render } from 'vitest-browser-react'
    
    const { container, unmount, rerender, debug } = await render(<MyComponent />, {
      wrapper: MyProvider,
      createRootOptions: { /* ... */ }
    })
    
    // Use debug to inspect the DOM
    debug(container)
    
    // Re-render with new props
    await rerender(<MyComponent someProp="new" />)
    
    // Cleanup
    await unmount()
  8. Clean up the DOM with cleanup()

    main
    The cleanup function unmounts React trees and cleans up the DOM after tests to prevent side effects between test cases. In vitest-browser-react, cleanup is automatically registered with Vitest's internal component cleanup symbol and is also called in a beforeEach block to ensure a fresh environment for every test.
  9. Render React components with render()

    main

    Use render to render a React component into the browser environment provided by Vitest. This function is an extension of the Vitest page object, allowing you to integrate React component testing directly into the Vitest browser runner workflow. It is exported from vitest-browser-react and is designed to work seamlessly with Vitest's browser mode.

    import { render } from 'vitest-browser-react'
    
    render(<MyComponent />)
  10. Use `renderHook` to test React Hooks

    main

    The renderHook function allows you to test standalone React hooks by wrapping them in a test component. It provides a stable way to access the hook's return value and trigger updates.

    RenderHookResult

    The returned object contains:

    • result.current: A stable reference to the latest value returned by the hook.
    • rerender(props?): Triggers a re-render of the hook with new props.
    • unmount(): Unmounts the test component.
    • act(callback): A helper to apply pending React updates before making assertions.
    import { renderHook } from 'vitest-browser-react'
    
    const { result, rerender, unmount } = await renderHook(
      (props) => useMyHook(props.initialValue),
      { initialProps: { initialValue: 10 } }
    )
    
    // Access the current value
    console.log(result.current)
    
    // Trigger a re-render with new props
    await rerender({ initialValue: 20 })
    
    // Cleanup
    await unmount()