Testing

Best Practices for Testing React Components using Vitest and Jest

Before writing tests, ensure your project has the necessary dependencies:

Terminal window
npm install --save-dev vitest jsdom @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event

Configuration

Create the vite.config.ts file to configure Vitest.

import { defineConfig } from "vitest/config"
export default defineConfig({
// ...
test: {
globals: true,
environment: "jsdom",
setupFiles: "./setup-test.ts",
},
})

Setting globals: true will automatically import the Vitest globals and removes the need to import expect, test, describe, etc.

Setup Test File

Create the setup-test.ts file to configure the testing environment and mock unimplemented APIs.

Here's a common example for Cerberus projects:

setup-test.ts
import "@testing-library/jest-dom/vitest"
import { JSDOM } from "jsdom"
import ResizeObserver from "resize-observer-polyfill"
import { vi } from "vitest"
import "vitest-axe/extend-expect"
const { window } = new JSDOM()
// ResizeObserver mock
vi.stubGlobal("ResizeObserver", ResizeObserver)
window["ResizeObserver"] = ResizeObserver
// IntersectionObserver mock
const IntersectionObserverMock = vi.fn(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
takeRecords: vi.fn(),
unobserve: vi.fn(),
}))
vi.stubGlobal("IntersectionObserver", IntersectionObserverMock)
window["IntersectionObserver"] = IntersectionObserverMock
// Scroll Methods mock
window.Element.prototype.scrollTo = () => {}
window.Element.prototype.scrollIntoView = () => {}
// requestAnimationFrame mock
window.requestAnimationFrame = (cb) => setTimeout(cb, 1000 / 60)
// URL object mock
window.URL.createObjectURL = () => "https://i.pravatar.cc/300"
window.URL.revokeObjectURL = () => {}
// navigator mock
Object.defineProperty(window, "navigator", {
value: {
clipboard: {
writeText: vi.fn(),
},
},
})
// Override globalThis
Object.assign(global, { window, document: window.document })

Custom Render

First, you need to create a custom render function to wrap your component in the CerberusProvider.

test-utils/render.tsx
import { render as rtlRender } from "@testing-library/react"
import {
CerberusProvider,
defineIcons,
makeSystemConfig,
NotificationCenter,
} from '@cerberus/react'
const config = makeSystemConfig({
icons: defineIcons({
// Define your icons here
// Example: "home": <svg>...</svg>
}),
})
export function render(ui: ReactNode) {
return rtlRender(<>{ui}</>, {
wrapper: (props: PropsWithChildren) => (
<CerberusProvider config={config}>
{props.children}
<NotificationCenter />
</CerberusProvider>
),
})
}

Testing Components

Now, you can use the render function to test your components.

testing/render.tsx
import { Button } from "@cerberus/react"
import { render } from "./testing/render"
test("renders a button", () => {
render(<Button>Click me</Button>)
expect(screen.getByText("Click me")).toBeInTheDocument()
})