ワンクリックで
revealui-testing-patterns
Testing patterns and best practices for RevealUI with Vitest
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Testing patterns and best practices for RevealUI with Vitest
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
RevealUI coding conventions for any code task — writing, editing, reviewing, creating, fixing, refactoring, changing, adding, or updating TypeScript, React, CSS, or config files. Covers TypeScript strict mode, ES Modules, Biome formatting, Tailwind v4 syntax, conventional commits, monorepo workspace protocol, feature gating, parameterization, and unused declaration policy.
RevealUI database conventions for any task involving database, schema, query, migration, Drizzle ORM, Neon, PostgreSQL, pgvector, vectors, embeddings, or data modeling. Covers the single Neon-primary database and its pgvector tables.
Systematic debugging workflow for RevealUI. Use when encountering any bug, test failure, unexpected behavior, error, or broken functionality. Prevents shotgun debugging.
RevealUI deployment guide — Vercel configuration, GitHub Actions deploy workflow, secret management, domain aliasing, and troubleshooting. Invoke when working on deploy.yml, vercel.json, deployment secrets, domain configuration, or debugging deploy failures.
Code review checklist for RevealUI. Use when reviewing code, completing a feature, checking quality, or before committing. Invoke explicitly with $revealui-review.
RevealUI safety guardrails for any code task — editing, writing, creating, fixing, refactoring, changing, adding, updating, or removing files. Protects credentials, enforces import boundaries, ensures code quality, and verifies work before completion.
| title | RevealUI Testing Patterns |
| visibility | internal |
| status | verified |
| audience | agent |
| name | revealui-testing-patterns |
| description | Testing patterns and best practices for RevealUI with Vitest |
| version | 0.1.0 |
| author | RevealUI Team |
| tags | ["testing","vitest","quality"] |
| compatibility | ["claude-code","universal"] |
| allowedTools | ["Read","Write","Edit","Bash"] |
Best practices for testing RevealUI applications with Vitest, addressing cyclic dependencies and test infrastructure issues.
Challenge: Test infrastructure exists but has blockers
packages/
└── package-name/
├── src/
│ ├── index.ts
│ └── __tests__/
│ ├── unit.test.ts
│ └── integration.test.ts
└── vitest.config.ts
apps/
└── admin/
└── src/
├── __tests__/
│ ├── auth/
│ ├── integration/
│ └── setup.ts
└── vitest.config.ts
// __tests__/setup.ts
import { beforeAll, afterAll, beforeEach } from 'vitest'
import { initializeTestDatabase } from './helpers/database'
beforeAll(async () => {
// One-time setup
await initializeTestDatabase()
}, 30000) // Increased timeout for parallel execution
afterAll(async () => {
// Cleanup
})
beforeEach(() => {
// Reset state between tests
})
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import { Button } from './Button'
describe('Button', () => {
it('renders with text', () => {
render(<Button>Click me</Button>)
expect(screen.getByText('Click me')).toBeInTheDocument()
})
it('calls onClick when clicked', async () => {
const handleClick = vi.fn()
render(<Button onClick={handleClick}>Click</Button>)
await userEvent.click(screen.getByText('Click'))
expect(handleClick).toHaveBeenCalledOnce()
})
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click</Button>)
expect(screen.getByRole('button')).toBeDisabled()
})
})
import { describe, it, expect } from 'vitest'
import { formatDate, validateEmail } from './utils'
describe('formatDate', () => {
it('formats date correctly', () => {
const date = new Date('2024-01-01')
expect(formatDate(date)).toBe('January 1, 2024')
})
it('handles invalid dates', () => {
expect(() => formatDate(null)).toThrow('Invalid date')
})
})
describe('validateEmail', () => {
it('validates correct emails', () => {
expect(validateEmail('user@example.com')).toBe(true)
})
it('rejects invalid emails', () => {
expect(validateEmail('invalid')).toBe(false)
expect(validateEmail('user@')).toBe(false)
})
})
import { describe, it, expect, beforeEach } from 'vitest'
import { createMockRequest, createMockResponse } from './helpers'
describe('POST /api/posts', () => {
beforeEach(async () => {
await clearDatabase()
})
it('creates a new post', async () => {
const req = createMockRequest({
method: 'POST',
body: {
title: 'Test Post',
content: 'Test content',
},
})
const res = createMockResponse()
await handler(req, res)
expect(res.status).toHaveBeenCalledWith(201)
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
id: expect.any(String),
title: 'Test Post',
})
)
})
it('requires authentication', async () => {
const req = createMockRequest({
method: 'POST',
headers: {}, // No auth header
})
const res = createMockResponse()
await handler(req, res)
expect(res.status).toHaveBeenCalledWith(401)
})
})
import { describe, it, expect, beforeEach } from 'vitest'
import { getRevealUI } from '@revealui/core'
import { buildConfig } from '@revealui/core'
describe('Posts Collection', () => {
let revealui: Awaited<ReturnType<typeof getRevealUI>>
beforeEach(async () => {
const config = await buildConfig({ /* ...test config... */ })
revealui = await getRevealUI({ config })
await revealui.delete({ collection: 'posts', where: {} })
})
it('creates a post', async () => {
const post = await revealui.create({
collection: 'posts',
data: {
title: 'Test Post',
_status: 'published',
},
})
expect(post).toMatchObject({
id: expect.any(String),
title: 'Test Post',
_status: 'published',
})
})
it('finds posts by status', async () => {
await revealui.create({
collection: 'posts',
data: { title: 'Published', _status: 'published' },
})
await revealui.create({
collection: 'posts',
data: { title: 'Draft', _status: 'draft' },
})
const { docs } = await revealui.find({
collection: 'posts',
where: { _status: { equals: 'published' } },
})
expect(docs).toHaveLength(1)
expect(docs[0].title).toBe('Published')
})
})
import { vi, beforeEach } from 'vitest'
// Mock fetch
global.fetch = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
})
it('fetches user data', async () => {
(fetch as any).mockResolvedValueOnce({
ok: true,
json: async () => ({ id: '1', name: 'John' }),
})
const user = await fetchUser('1')
expect(fetch).toHaveBeenCalledWith('/api/users/1')
expect(user).toEqual({ id: '1', name: 'John' })
})
// Mock the entire module
vi.mock('@revealui/auth', () => ({
getCurrentUser: vi.fn(),
checkPermission: vi.fn(),
}))
import { getCurrentUser } from '@revealui/auth'
it('requires authentication', async () => {
(getCurrentUser as any).mockResolvedValue(null)
const result = await protectedAction()
expect(result.error).toBe('Unauthorized')
})
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/__tests__/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'**/*.config.{js,ts}',
'**/__tests__/**',
],
},
// Per-worker isolation for parallel tests
pool: 'threads',
poolOptions: {
threads: {
singleThread: false,
},
},
},
})
// ❌ BAD: Circular dependency
// fileA.ts
import { funcB } from './fileB'
export const funcA = () => funcB()
// fileB.ts
import { funcA } from './fileA'
export const funcB = () => funcA()
// ✅ GOOD: Break the cycle
// fileA.ts
export const funcA = (callback: () => void) => callback()
// fileB.ts
export const funcB = () => console.log('B')
// usage.ts
import { funcA } from './fileA'
import { funcB } from './fileB'
funcA(funcB)
// ✅ Increase timeout for slow operations
it('initializes database', async () => {
await initializeDatabase()
}, 30000) // 30 second timeout
// ✅ Use done callback for complex async
it('handles multiple async operations', (done) => {
Promise.all([
operation1(),
operation2(),
operation3(),
]).then(() => {
expect(true).toBe(true)
done()
})
})
// Create separate database for each test worker
import { afterAll, beforeAll } from 'vitest'
let db: Database
beforeAll(async () => {
const workerId = process.env.VITEST_WORKER_ID || '1'
db = await createTestDatabase(`test-db-${workerId}`)
})
afterAll(async () => {
await db.destroy()
})
Target: 80%+ coverage Current: Infrastructure in development
Run coverage:
pnpm test:coverage
// ❌ BAD: Testing implementation details
it('sets state correctly', () => {
const { result } = renderHook(() => useState(0))
expect(result.current[0]).toBe(0)
})
// ✅ GOOD: Testing behavior
it('displays initial count', () => {
render(<Counter />)
expect(screen.getByText('Count: 0')).toBeInTheDocument()
})
// ❌ BAD
it('works', () => { /* ... */ })
// ✅ GOOD
it('creates a post with valid data', () => { /* ... */ })
it('returns 401 when user is not authenticated', () => { /* ... */ })
it('updates user profile', async () => {
// Arrange
const user = await createTestUser()
const updates = { name: 'New Name' }
// Act
const result = await updateUser(user.id, updates)
// Assert
expect(result.name).toBe('New Name')
})
// ✅ GOOD: Focused tests
it('validates required fields', () => {
expect(validate({})).toHaveProperty('email')
})
it('validates email format', () => {
expect(validate({ email: 'invalid' })).toHaveProperty('email')
})
# Run all tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage
pnpm test:coverage
# Specific file
pnpm test path/to/file.test.ts
# Run in a specific package
pnpm --filter @revealui/core test
// Use vi.debug to inspect mocks
it('debugs test', () => {
const mock = vi.fn()
mock('test')
console.log(mock.mock.calls) // See all calls
})
// Use --reporter=verbose for detailed output
// pnpm test --reporter=verbose
Every test written makes RevealUI more reliable! 🧪