Refactor high-complexity React components in Dify frontend. Use when `pnpm analyze-component...
npx skills add gpolanco/skills-as-context --skill "testing-vitest"
Install specific skill from multi-skill repository
# Description
>
# SKILL.md
name: testing-vitest
description: >
Comprehensive testing patterns using Vitest and React Testing Library for modern React applications.
Trigger: Use when writing tests, setting up test infrastructure, or establishing testing best practices.
license: Apache-2.0
metadata:
author: devcontext
version: "1.0.0"
scope: [root]
auto_invoke: "Writing tests or test configuration"
allowed-tools: Read, Write, Bash
Testing with Vitest (Hybrid Skill)
🚨 CRITICAL: Reference Files are MANDATORY
This SKILL.md provides OVERVIEW only. For EXACT patterns:
| Task | MANDATORY Reading |
|---|---|
| Configuration Setup | ⚠️ reference/vitest-config.md |
| Mocking Guide | ⚠️ reference/mocking-guide.md |
| Advanced Patterns | ⚠️ reference/advanced-patterns.md |
| Coverage Strategy | ⚠️ reference/coverage-strategy.md |
⚠️ DO NOT configure Vitest or implement complex mocks without reading the corresponding reference file FIRST.
When to Use
Use this skill when:
- Writing unit tests for business logic, utilities, or hooks
- Writing component tests for React components with user interactions
- Writing integration tests for API routes or server actions
- Setting up test infrastructure (config, mocks, utilities)
- Establishing testing conventions and coverage goals
- Debugging failing tests or improving test reliability
Critical Patterns
ALWAYS
- Test User Behavior: Test what users do and see, not implementation details
- Arrange-Act-Assert: Structure tests with clear setup, action, and verification phases
- Descriptive Test Names: Use
it('should <expected behavior> when <condition>')format - Mock External Dependencies: Mock API calls, third-party services, and environment variables
- Test Edge Cases: Include happy path, error cases, and boundary conditions
- Clean Up: Use
beforeEach/afterEachto reset state and clean up side effects - Accessibility Testing: Include ARIA roles and labels in component tests
- Type Safety: Import and use proper types in test files
NEVER
- Never test implementation details: Avoid testing internal state, private methods, or class names
- Never use array indices as test identifiers: Use stable selectors (roles, labels, test-ids)
- Never skip error scenarios: Always test error states and edge cases
- Never commit
.onlyor.skip: These are for local debugging only - Never use
anyin test types: Maintain type safety in tests - Never share test state: Each test should be independent
- Never test framework internals: Don't test React internals or library behavior
DEFAULTS
- File Location:
/tests/directory mirroring/src/structure - File Naming:
<component-name>.test.ts(x)or<feature-name>.test.ts - Coverage Goals: Minimum 70% overall, 100% for critical business logic
- Test Framework: Vitest (fast, ESM-native, Vite-compatible)
- Component Testing: React Testing Library (user-centric)
- Assertions: Vitest's
expectAPI + Testing Library matchers
🚫 Critical Anti-Patterns
- DO NOT test implementation details (internal state, private methods) → test what users see and do.
- DO NOT share state between tests → ensure each test is independent and uses
beforeEach/afterEachfor cleanup. - DO NOT use
anyin test types → maintain the same level of strict typing in tests as in production code. - DO NOT commit
.onlyor.skipblocks → these are for local development only.
Decision Tree
Need to test React component? → Use @testing-library/react
Need to test hook? → Use @testing-library/react-hooks
Need to test API/server action? → Use supertest or direct function calls
Need to test pure logic/utility? → Use standard Vitest unit tests
Need to test async behavior? → Use async/await with waitFor
Need to debug test? → Use screen.debug() or test.only
Test Structure Patterns
Unit Test Structure
// File: /tests/lib/utils/format-date.test.ts
import { describe, it, expect } from "vitest";
import { formatDate } from "@/lib/utils/format-date";
describe("formatDate", () => {
// Happy path
it("should format date in ISO format by default", () => {
const date = new Date("2024-01-15");
expect(formatDate(date)).toBe("2024-01-15");
});
// Edge cases
it("should handle invalid dates gracefully", () => {
expect(formatDate(new Date("invalid"))).toBe("Invalid Date");
});
// Boundary conditions
it("should handle leap year dates correctly", () => {
const leapDate = new Date("2024-02-29");
expect(formatDate(leapDate)).toBe("2024-02-29");
});
});
Component Test Structure
// File: /tests/components/ui/button.test.tsx
import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Button } from '@/components/ui/button'
describe('Button Component', () => {
it('should render with correct text', () => {
render(<Button>Click me</Button>)
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument()
})
it('should call onClick handler when clicked', async () => {
const handleClick = vi.fn()
const user = userEvent.setup()
render(<Button onClick={handleClick}>Click me</Button>)
await user.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('should be disabled when disabled prop is true', () => {
render(<Button disabled>Click me</Button>)
expect(screen.getByRole('button')).toBeDisabled()
})
it('should apply correct variant styles', () => {
const { container } = render(<Button variant="primary">Primary</Button>)
expect(container.firstChild).toHaveClass('bg-primary')
})
})
Integration Test Structure
// File: /tests/domains/auth/login.test.ts
import { describe, it, expect, beforeEach, vi } from "vitest";
import { loginUser } from "@/domains/auth/actions/login-user";
// Mock external dependencies
vi.mock("@/lib/supabase/client", () => ({
supabase: {
auth: {
signInWithPassword: vi.fn(),
},
},
}));
describe("Login Feature", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should login with valid credentials", async () => {
const result = await loginUser({
email: "[email protected]",
password: "ValidPass123!",
});
expect(result.success).toBe(true);
});
it("should return error for invalid credentials", async () => {
const result = await loginUser({
email: "[email protected]",
password: "WrongPass",
});
expect(result.success).toBe(false);
expect(result.error).toBeDefined();
});
it("should handle network errors gracefully", async () => {
// Simulate network failure
vi.mocked(supabase.auth.signInWithPassword).mockRejectedValue(
new Error("Network error"),
);
const result = await loginUser({
email: "[email protected]",
password: "ValidPass123!",
});
expect(result.success).toBe(false);
expect(result.error).toContain("Network error");
});
});
Testing Library Queries
Query Priority (Use in Order)
// ✅ 1. ACCESSIBLE TO EVERYONE
screen.getByRole("button", { name: /submit/i });
screen.getByLabelText(/email/i);
screen.getByPlaceholderText(/search/i);
screen.getByText(/welcome/i);
// ✅ 2. SEMANTIC QUERIES
screen.getByAltText(/profile picture/i);
screen.getByTitle(/close/i);
// ⚠️ 3. TEST IDS (Last Resort)
screen.getByTestId("custom-element");
Query Variants
// ❌ DON'T: Throws error if not found
screen.getByRole("button");
// ✅ DO: Returns null if not found (for conditional checks)
screen.queryByRole("button");
// ✅ DO: For async elements
await screen.findByRole("button");
Mocking Patterns
Mock External Modules
import { vi } from "vitest";
// Mock entire module
vi.mock("@/lib/analytics", () => ({
trackEvent: vi.fn(),
}));
// Mock specific function
vi.mock("@/lib/api", async () => {
const actual = await vi.importActual("@/lib/api");
return {
...actual,
fetchUser: vi.fn(),
};
});
Mock Environment Variables
import { beforeEach, afterEach } from "vitest";
beforeEach(() => {
process.env.NEXT_PUBLIC_API_URL = "http://localhost:3000";
});
afterEach(() => {
delete process.env.NEXT_PUBLIC_API_URL;
});
Mock Next.js Router
import { vi } from "vitest";
vi.mock("next/navigation", () => ({
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
back: vi.fn(),
}),
usePathname: () => "/test-path",
}));
Commands
# Run all tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test:coverage
# Run tests for specific file
pnpm test path/to/file.test.ts
# Run tests matching pattern
pnpm test --grep "login"
# Debug tests with UI
pnpm test:ui
Actions (Setup)
Initial Setup
# Install dependencies
pnpm add -D vitest @testing-library/react @testing-library/user-event @testing-library/jest-dom jsdom
# Create vitest config
touch vitest.config.ts
# Create test setup file
mkdir -p tests/setup
touch tests/setup/test-setup.ts
Configuration Files
See reference/vitest-config.md for complete setup.
Resources
- Configuration Guide: reference/vitest-config.md
- Advanced Patterns: reference/advanced-patterns.md
- Mocking Guide: reference/mocking-guide.md
- Coverage Strategy: reference/coverage-strategy.md
Cross-References
- For React patterns → See
react-19skill - For file structure → See
structuring-projectsskill - For form testing → See
formsskill - For type safety → See
typescriptskill
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.