Complete Test Setup with Vitest in prompts.chat: Configuration and Examples
The prompts.chat repository uses Vitest with a jsdom browser environment and Testing Library to run fast, isolated tests without touching real databases or external services.
The f/prompts.chat codebase is a Next.js application that relies on Vitest for unit and component testing. Understanding the test configuration helps you contribute effectively or adapt the patterns for similar React projects. This guide covers the complete Vitest setup, global mocks, and practical testing patterns implemented in the source code.
Core Vitest Configuration
The foundation of the testing architecture lives in vitest.config.ts at the repository root. This file configures Vitest to work seamlessly with the React Vite plugin and establishes the runtime environment for all tests.
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./vitest.setup.ts"],
include: ["src/**/*.{test,spec}.{ts,tsx}"],
exclude: ["node_modules", ".next", "packages"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
exclude: [
"node_modules/",
".next/",
"packages/",
"src/**/*.d.ts",
"vitest.config.ts",
"vitest.setup.ts",
],
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
Key configuration details include:
environment: "jsdom"– Simulates a browser-like DOM for React component testingsetupFiles: ["./vitest.setup.ts"]– Points to the global setup file that runs before any testinclude: ["src/**/*.{test,spec}.{ts,tsx}"]– Automatically discovers all test files within thesrcdirectorycoverage.provider: "v8"– Uses the native V8 coverage engine for accurate reporting
Global Test Environment and Mocks
The vitest.setup.ts file executes before any test file runs, establishing a deterministic environment that prevents side effects. This setup ensures tests remain fast by mocking external dependencies that would otherwise require database connections or network requests.
Suppressing Console Output
To keep test output readable, the setup file replaces console.error and console.log with silent mocks during test execution:
import { vi, beforeAll, afterAll } from "vitest";
const originalConsoleError = console.error;
const originalConsoleLog = console.log;
beforeAll(() => {
console.error = vi.fn();
console.log = vi.fn();
});
afterAll(() => {
console.error = originalConsoleError;
console.log = originalConsoleLog;
});
Environment Variable Stubs
Critical environment variables required by Next.js Auth and Prisma are stubbed to prevent undefined errors:
process.env.NEXTAUTH_SECRET = "test-secret";
process.env.NEXTAUTH_URL = "http://localhost:3000";
process.env.DATABASE_URL = "postgresql://test:test@localhost:5432/test";
Next.js and Internationalization Mocks
Routing and localization modules are mocked to remove server dependencies:
next/navigationandnext/headers– Mocked to prevent errors when components use routing hooks or cookie helpersnext-intl– Returns the translation key itself, providing deterministic strings without loading real locale files
Database Isolation with Prisma Mocks
The Prisma client is replaced with vi.fn() stubs for all model methods. This prevents tests from querying real databases while maintaining the same interface:
// Prisma client methods are replaced with mock functions
vi.mock("@/lib/prisma", () => ({
prisma: {
user: { findUnique: vi.fn(), create: vi.fn() },
prompt: { findMany: vi.fn(), update: vi.fn() },
// ... other models
},
}));
Additionally, the application config loader (@/lib/config) returns a minimal configuration object to avoid loading production settings during tests.
Running the Test Suite
The repository provides several npm scripts for different testing workflows:
npm test # Runs all Vitest tests once in CI mode
npm run test:watch # Starts Vitest in watch mode for development
npm run test:ui # Opens the Vitest UI (HTML reporter)
npm run test:coverage # Generates a coverage report in ./coverage
Test files are automatically discovered based on the glob pattern src/**/__tests__/**/*.test.{ts,tsx} defined in the configuration.
Real-World Component Testing Example
The src/__tests__/components/copy-button.test.tsx file demonstrates comprehensive patterns for testing React components with external dependencies. The CopyButton component copies text to the clipboard and displays toast notifications while tracking analytics events.
Mocking External Dependencies
The test uses vi.mock hoisting to replace modules before importing the component:
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, act } from "@testing-library/react";
// Mocks must be declared before importing the component
vi.mock("next-intl", () => ({
useTranslations: () => (key: string) => ({
copied: "Copied!",
failedToCopy: "Failed to copy",
}[key] ?? key),
}));
vi.mock("sonner", () => ({
toast: { success: vi.fn(), error: vi.fn() },
}));
vi.mock("@/lib/analytics", () => ({
analyticsPrompt: { copy: vi.fn() },
}));
import { CopyButton } from "@/components/prompts/copy-button";
import { toast } from "sonner";
import { analyticsPrompt } from "@/lib/analytics";
Testing Clipboard Interactions
The test overrides the browser Clipboard API and simulates user interactions:
describe("CopyButton", () => {
const mockClipboard = { writeText: vi.fn() };
beforeEach(() => {
vi.clearAllMocks();
Object.assign(navigator, { clipboard: mockClipboard });
mockClipboard.writeText.mockResolvedValue(undefined);
});
it("copies content to clipboard and shows success toast", async () => {
render(<CopyButton content="Hello World" promptId="123" />);
await act(async () => {
fireEvent.click(screen.getByRole("button"));
});
// Verify clipboard API called
expect(mockClipboard.writeText).toHaveBeenCalledWith("Hello World");
// Verify toast notification
expect(toast.success).toHaveBeenCalledWith("Copied!");
// Verify analytics tracking
expect(analyticsPrompt.copy).toHaveBeenCalledWith({ promptId: "123" });
});
});
This pattern ensures the test verifies all side effects—clipboard access, UI feedback, and analytics tracking—without relying on actual browser APIs or external services.
Summary
- Configuration: The
vitest.config.tsfile sets up jsdom environment, V8 coverage, and path aliases for TypeScript imports - Isolation: The
vitest.setup.tsfile provides global mocks for Next.js navigation, Prisma, and environment variables - Execution: Use
npm testfor CI,npm run test:watchfor development, andnpm run test:coveragefor reporting - Patterns: Component tests mock external dependencies before imports, stub browser APIs like
navigator.clipboard, and verify side effects using Testing Library assertions
Frequently Asked Questions
How does prompts.chat prevent tests from hitting the real database?
The vitest.setup.ts file replaces the Prisma client with vi.fn() stubs for all model methods. This substitution occurs before any test runs, ensuring all database calls return mocked values instead of querying PostgreSQL.
Why does the test configuration use jsdom instead of node?
The environment: "jsdom" setting in vitest.config.ts provides a browser-like DOM environment necessary for React component testing. This allows Testing Library to render components, simulate clicks, and query DOM elements as if running in a real browser.
What is the purpose of the vitest.setup.ts file?
This file runs before all tests to establish global state and mocks. It suppresses console output, stubs environment variables, mocks Next.js modules like next/navigation, and replaces Prisma with mock functions, creating a deterministic test environment without side effects.
How can I run a specific test file instead of the entire suite?
While the repository scripts run all tests matching src/**/*.{test,spec}.{ts,tsx}, you can pass a file pattern directly to Vitest: npx vitest src/__tests__/components/copy-button.test.tsx. The watch mode (npm run test:watch) also provides filtering options interactively.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →