[How to] Set up a React app with Vite

Let's start a new SPA project using Vite, React, Typescript, Vitest and react-testing-library



Part 1: Vite + React + Typescript

  1. Initialize the project using pnpm
pnpm create vite
  1. Follow the prompts, select react and typescript template
  2. Start a dev server
pnpm run dev

Part 2: Vitest + react-testing-library(i.e. RTL)

  1. Install vitest, RTL and other necessary packages using pnpm
pnpm add -D vitest @testing-library/react @testing-library/user-event @testing-library/dom jsdom @testing-library/jest-dom @types/testing-library__jest-dom
  1. Edit vite.config.ts file to be like this
/// <reference types="vitest" />

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
	plugins: [react()],
	test: {
		// now we can call test(), expect() in test files without needing to `import` them
		globals: true,

		// required environment to run RTL
		environment: 'jsdom',

		// a setup file where we will add more settings
		setupFiles: './src/setupTest.ts',

		// you might want to disable it, if you don't
		// have tests that rely on CSS since parsing CSS is slow
		css: true,
	},
})
  1. Create ./src/setupTest.ts file
import { afterEach, expect } from 'vitest'
import { cleanup } from '@testing-library/react'
import matchers from '@testing-library/jest-dom/matchers'

// enable `expect(...).toBeInTheDocument()`, etc.
expect.extend(matchers)

// unmount all components which are rendered by `render(...)`
afterEach(() => {
	cleanup()
})
  1. The following is an example of a test file (*.test.tsx)
// `./src/Button.test.tsx`

import { Button } from './Button'
import { myFunction as mockMyFunction } from './myFunction'
import { render, screen } from '@testing-library/react'
import { vi, beforeEach } from 'vitest'
import userEvent from '@testing-library/user-event'
	
// how to mock a module using vitest
// assume that this module is used inside <Button/> comp
vi.mock('./myFunction', () => {
	return { myFunction: vi.fn(() => 'mock text') }
})

// other options are resetAllMocks(), restoreAllMocks()
beforeEach(() => {
	vi.clearAllMocks()
})

test('The first test', async () => {
	// how to mock a function using vitest
	const mockOnClick = vi.fn()
	
	render(<Button num={5} onClick={mockOnClick} />)
	expect(screen.getByText(/Something/)).toBeInTheDocument()
	await userEvent.click(screen.getByText(/Something/))
	
	// inspect the mocked module
	expect(mockMyFunction).toBeCalledTimes(1)
	expect(mockMyFunction).toBeCalledWith(5)
	// inspect the mocked function
	expect(mockOnClick).toBeCalledTimes(1)
	expect(mockOnClick).toBeCalledWith('mock text')
})
  1. Run all tests
pnpm vitest
  1. Optionally, add a new field in scripts (package.json file) and run the test with pnpm run test instead.
"scripts": {
	// ...
	"test": "vitest"
},