Testing with Vitest

This guide will help you setup Vitest and React Testing Library for your project. Note that this guide intended for projects that use Vite as a bundler, if you are using other frameworks/bundlers, it is recommended to use Jest instead.

Installation

Install vitest and react testing library:

yarn add --dev vitest jsdom @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event

If you want to run tests from your IDE, install one of the extensions.

Configuration

Add vitest configuration to your Vite config file:

import { defineConfig } from 'vite';

export default defineConfig({
  // ... rest of your config
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './vitest.setup.mjs',
  },
});

Then create vitest.setup.mjs file in your project root and add the following code to it:

import '@testing-library/jest-dom/vitest';

import { vi } from 'vitest';

const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);
window.HTMLElement.prototype.scrollIntoView = () => {};

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: vi.fn().mockImplementation((query) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: vi.fn(),
    removeListener: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn(),
  })),
});

class ResizeObserver {
  observe() {}
  unobserve() {}
  disconnect() {}
}

window.ResizeObserver = ResizeObserver;

The code above mocks window.matchMedia and ResizeObserver APIs that are not available in jsdom environment but are required by some Mantine components.

Optionally you can add vitest scripts to your package.json:

{
  "scripts": {
    "vitest": "vitest run",
    "vitest:watch": "vitest"
  }
}

Custom render

All Mantine components require MantineProvider to be present in the component tree. To add MantineProvider to the component tree in your tests, create a custom render function:

// ./test-utils/render.tsx
import { render as testingLibraryRender } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
// Import your theme object
import { theme } from '../src/theme';

export function render(ui: React.ReactNode) {
  return testingLibraryRender(<>{ui}</>, {
    wrapper: ({ children }: { children: React.ReactNode }) => (
      <MantineProvider theme={theme}>{children}</MantineProvider>
    ),
  });
}

It is usually more convenient to export all @testing-library/* functions that you are planning to use from ./testing-utils/index.ts file:

import userEvent from '@testing-library/user-event';

export * from '@testing-library/react';
export { render } from './render';
export { userEvent };

Then you should import all testing utilities from ./testing-utils instead of @testing-library/react:

import { render, screen } from '../test-utils';
import { Welcome } from './Welcome';

describe('Welcome component', () => {
  it('has correct Next.js theming section link', () => {
    render(<Welcome />);
    expect(screen.getByText('this guide')).toHaveAttribute(
      'href',
      'https://mantine.dev/guides/next/'
    );
  });
});

Example of a full setup

You can find an example with a full Vitest setup in mantine-vite-template.