Skip to main content

Test stack

Vitest

Fast unit test runner built on Vite. Configured in vitest.config.ts with a jsdom environment.

jsdom

Simulates a browser DOM in Node.js so React components can render and interact without a real browser.

@testing-library/react

Renders React components and provides queries to find elements the way a user would.

@testing-library/jest-dom

Adds custom matchers like toBeInTheDocument() and toHaveTextContent() to Vitest assertions.

Running tests

npm run test
This runs vitest, which watches for changes by default. To run tests once without watching:
npm run test -- --run

Where to put test files

Test files use the .test.ts or .test.tsx extension. You can place them in either location:
LocationWhen to use
Next to the source fileUnit tests for a specific component or utility
src/test/Shared test helpers, setup files, and integration tests
Vitest discovers all files matching **/*.test.ts and **/*.test.tsx automatically.
The existing src/basic.test.ts is a good reference for how tests are structured in this project.

Writing a component test

Here is a complete example testing a React component with @testing-library/react:
// src/components/DolarCard.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import DolarCard from './DolarCard.jsx';

describe('DolarCard', () => {
  beforeEach(() => {
    vi.stubGlobal(
      'fetch',
      vi.fn(() =>
        Promise.resolve({
          json: () => Promise.resolve({ value: '1050.50' }),
        })
      )
    );
  });

  it('renders the dolar value after loading', async () => {
    render(<DolarCard />);

    const value = await screen.findByText('1050.50');
    expect(value).toBeInTheDocument();
  });

  it('shows a loading state before data arrives', () => {
    render(<DolarCard />);

    expect(screen.getByText('Cargando...')).toBeInTheDocument();
  });
});

Key patterns

  • Use render() from @testing-library/react to mount a component.
  • Use screen.findBy* for elements that appear asynchronously (after a fetch resolves).
  • Use screen.getBy* for elements that are present in the initial render.
  • Mock fetch with vi.stubGlobal to control API responses without hitting a real network.
  • Use vi.fn() and beforeEach to reset mocks between tests.

Vitest configuration

The test environment is configured in vitest.config.ts. Because the project uses Astro, it wraps Vite config using getViteConfig from astro/config rather than Vitest’s own defineConfig:
vitest.config.ts
/// <reference types="vitest" />
import { getViteConfig } from 'astro/config';

export default getViteConfig({
  test: {
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
    globals: true,
  },
} as any);
The setup file at src/test/setup.ts imports @testing-library/jest-dom to make its matchers available in every test:
src/test/setup.ts
import '@testing-library/jest-dom';
Always run npm run build before committing, even when all tests pass. The build runs astro check for TypeScript validation, which can catch type errors that tests do not cover.

Build docs developers (and LLMs) love