# Complete Test Setup with Vitest in prompts.chat: Configuration and Examples

> Learn to set up Vitest for prompts.chat. Run fast, isolated tests with jsdom and Testing Library for efficient development and reliable code.

- Repository: [Fatih Kadir Akın/prompts.chat](https://github.com/f/prompts.chat)
- Tags: how-to-guide
- Published: 2026-04-02

---

**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`](https://github.com/f/prompts.chat/blob/main/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.

```typescript
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 testing
- **`setupFiles: ["./vitest.setup.ts"]`** – Points to the global setup file that runs before any test
- **`include: ["src/**/*.{test,spec}.{ts,tsx}"]`** – Automatically discovers all test files within the `src` directory
- **`coverage.provider: "v8"`** – Uses the native V8 coverage engine for accurate reporting

## Global Test Environment and Mocks

The **[`vitest.setup.ts`](https://github.com/f/prompts.chat/blob/main/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:

```typescript
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:

```typescript
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/navigation`** and **`next/headers`** – Mocked to prevent errors when components use routing hooks or cookie helpers
- **`next-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:

```typescript
// 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:

```bash
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`](https://github.com/f/prompts.chat/blob/main/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:

```typescript
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:

```typescript
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.ts`](https://github.com/f/prompts.chat/blob/main/vitest.config.ts)** file sets up jsdom environment, V8 coverage, and path aliases for TypeScript imports
- **Isolation**: The **[`vitest.setup.ts`](https://github.com/f/prompts.chat/blob/main/vitest.setup.ts)** file provides global mocks for Next.js navigation, Prisma, and environment variables
- **Execution**: Use `npm test` for CI, `npm run test:watch` for development, and `npm run test:coverage` for 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`](https://github.com/f/prompts.chat/blob/main/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`](https://github.com/f/prompts.chat/blob/main/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.