// Generate comprehensive tests following three-layer pyramid - unit (Vitest), integration (real database), E2E (Playwright). Use when user needs "add tests", "improve coverage", "test [feature]", "write E2E tests", "generate test factory", or mentions testing auth, forms, database, AI features, visualizations, real-time collaboration, performance, accessibility, or import/export. Provides templates and patterns for 80%+ coverage including AI mocking, chart testing, WebSocket testing, and Page Object Models. Do NOT use when building new features (use worldcrafter-feature-builder which includes tests), database-only changes (use worldcrafter-database-setup), or simple routes (use worldcrafter-route-creator).
[HINT] 下载包含 SKILL.md 和所有相关文件的完整技能目录
| name | worldcrafter-test-generator |
| description | Generate comprehensive tests following three-layer pyramid - unit (Vitest), integration (real database), E2E (Playwright). Use when user needs "add tests", "improve coverage", "test [feature]", "write E2E tests", "generate test factory", or mentions testing auth, forms, database, AI features, visualizations, real-time collaboration, performance, accessibility, or import/export. Provides templates and patterns for 80%+ coverage including AI mocking, chart testing, WebSocket testing, and Page Object Models. Do NOT use when building new features (use worldcrafter-feature-builder which includes tests), database-only changes (use worldcrafter-database-setup), or simple routes (use worldcrafter-route-creator). |
Version: 2.0.0 Last Updated: 2025-01-09
This skill provides tools and templates for generating comprehensive tests following WorldCrafter's three-layer testing pyramid: unit tests (Vitest), integration tests (real database), and E2E tests (Playwright).
Related Skills:
worldcrafter-feature-builder - Feature-builder already includes basic tests, use test-generator for additional coverageworldcrafter-database-setup - Use to create test database schema before integration testsworldcrafter-auth-guard - Test auth flows and protected routesExample Use Cases:
Use this skill when:
1. Unit Tests (60-70% of tests) - Vitest + React Testing Library
2. Integration Tests (20-30% of tests) - Vitest + Real Test Database
3. E2E Tests (10-20% of tests) - Playwright
Coverage Goal: 80%+ overall (enforced by Vitest)
For Components:
For Server Actions:
For User Flows:
Automated Generation:
For component tests:
python .claude/skills/worldcrafter-test-generator/scripts/generate_tests.py component <ComponentName>
For integration tests:
python .claude/skills/worldcrafter-test-generator/scripts/generate_tests.py integration <feature-name>
For E2E tests:
python .claude/skills/worldcrafter-test-generator/scripts/generate_e2e.py <feature-name>
For test factories:
python .claude/skills/worldcrafter-test-generator/scripts/generate_factory.py <ModelName>
Manual Approach:
Copy templates from assets/templates/ and customize.
Component Testing Pattern:
import { describe, it, expect, vi } from 'vitest'
import { renderWithProviders, screen, userEvent } from '@/test/utils/render'
import YourComponent from '../YourComponent'
describe('YourComponent', () => {
it('renders correctly', () => {
renderWithProviders(<YourComponent />)
expect(screen.getByRole('heading')).toBeInTheDocument()
})
it('handles user interaction', async () => {
const user = userEvent.setup()
renderWithProviders(<YourComponent />)
await user.click(screen.getByRole('button'))
expect(screen.getByText(/success/i)).toBeInTheDocument()
})
})
Utility Testing Pattern:
import { describe, it, expect } from 'vitest'
import { yourUtility } from '../utils'
describe('yourUtility', () => {
it('handles valid input', () => {
expect(yourUtility('input')).toBe('expected')
})
it('handles edge cases', () => {
expect(yourUtility('')).toBe('default')
})
})
Reference: references/testing-patterns.md for more examples
Server Action Testing Pattern:
import { describe, it, expect, afterAll } from 'vitest'
import { prisma } from '@/lib/prisma'
import { submitFeature } from '../actions'
describe('Feature Integration Tests', () => {
const createdIds: string[] = []
afterAll(async () => {
// Clean up test data
await prisma.feature.deleteMany({
where: { id: { in: createdIds } }
})
})
it('creates record in database', async () => {
const result = await submitFeature({
title: 'Test Feature'
})
expect(result.success).toBe(true)
createdIds.push(result.data!.id)
// Verify in database
const dbRecord = await prisma.feature.findUnique({
where: { id: result.data!.id }
})
expect(dbRecord).toBeTruthy()
})
})
Key Points:
.env.test)afterAllReference: references/testing-patterns.md for integration test patterns
E2E Testing Pattern (with Page Object Model):
Create Page Object:
// e2e/pages/feature.page.ts
import { Page, Locator } from '@playwright/test'
export class FeaturePage {
readonly page: Page
readonly titleInput: Locator
readonly submitButton: Locator
constructor(page: Page) {
this.page = page
this.titleInput = page.locator('input[name="title"]')
this.submitButton = page.locator('button[type="submit"]')
}
async goto() {
await this.page.goto('/feature')
}
async submitForm(title: string) {
await this.titleInput.fill(title)
await this.submitButton.click()
}
}
Write E2E test:
// e2e/feature.spec.ts
import { test, expect } from '@playwright/test'
import { FeaturePage } from './pages/feature.page'
test('user can submit feature', async ({ page }) => {
const featurePage = new FeaturePage(page)
await featurePage.goto()
await featurePage.submitForm('Test Feature')
await expect(page.locator('text=Success')).toBeVisible()
})
Reference: references/testing-patterns.md for E2E patterns
Factory Pattern:
// src/test/factories/user.ts
import { faker } from '@faker-js/faker'
export function createMockUser(overrides = {}) {
return {
id: faker.string.uuid(),
email: faker.internet.email(),
name: faker.person.fullName(),
createdAt: new Date(),
updatedAt: new Date(),
...overrides
}
}
Usage:
const user = createMockUser({ email: 'test@example.com' })
Generate factory:
python .claude/skills/worldcrafter-test-generator/scripts/generate_factory.py User
Mocking Patterns:
Reference references/mocking-guide.md for:
Example: Mock Server Action
import { vi } from 'vitest'
vi.mock('../actions', () => ({
submitFeature: vi.fn().mockResolvedValue({
success: true,
data: { id: '1', title: 'Test' }
})
}))
# Run unit tests (watch mode)
npm test
# Run with coverage
npm run test:coverage
# Run E2E tests
npm run test:e2e
# Run all tests
npm run test:all
Coverage Report:
coverage/index.htmlgetByRole - Preferred (accessibility)
screen.getByRole('button', { name: /submit/i })
getByLabelText - Forms
screen.getByLabelText(/email/i)
getByText - Static content
screen.getByText(/welcome/i)
getByTestId - Last resort
screen.getByTestId('custom-element')
import { waitFor } from '@testing-library/react'
// Wait for element
await waitFor(() => {
expect(screen.getByText(/loaded/i)).toBeInTheDocument()
})
// Wait for element to disappear
await waitForElementToBeRemoved(() => screen.queryByText(/loading/i))
import { userEvent } from '@testing-library/user-event'
const user = userEvent.setup()
await user.type(input, 'Hello')
await user.click(button)
await user.selectOptions(select, 'option1')
Describe blocks:
describe('FeatureComponent', () => {
describe('rendering', () => {
it('renders heading', () => { /* ... */ })
})
describe('user interactions', () => {
it('handles click', () => { /* ... */ })
})
})
beforeEach(() => {
// Run before each test
})
afterEach(() => {
// Run after each test
})
beforeAll(() => {
// Run once before all tests
})
afterAll(() => {
// Run once after all tests
})
test('validates required fields', async () => {
const user = userEvent.setup()
renderWithProviders(<Form />)
await user.click(screen.getByRole('button', { name: /submit/i }))
expect(screen.getByText(/required/i)).toBeInTheDocument()
})
test('submits valid data', async () => {
const user = userEvent.setup()
const onSubmit = vi.fn()
renderWithProviders(<Form onSubmit={onSubmit} />)
await user.type(screen.getByLabelText(/name/i), 'John')
await user.click(screen.getByRole('button', { name: /submit/i }))
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({ name: 'John' })
})
})
test('redirects when not authenticated', async ({ page }) => {
await page.goto('/protected')
await expect(page).toHaveURL('/login')
})
test('shows protected content when authenticated', async ({ page }) => {
// Login first
await loginAs('user@example.com', 'password')
await page.goto('/protected')
await expect(page.locator('h1')).toContainText('Protected')
})
test('enforces RLS policies', async () => {
// Create user's record
const userRecord = await prisma.post.create({
data: { authorId: 'user1', title: 'User 1 Post' }
})
// Try to access as different user (should fail)
// This requires mocking auth context
const result = await getPost(userRecord.id, 'user2')
expect(result).toBeNull() // RLS blocked access
})
WorldCrafter integrates AI for story generation, character creation, and world-building. See references/testing-patterns.md for comprehensive patterns:
vi.mock('ai')Example:
vi.mock('ai', () => ({
generateText: vi.fn()
}))
it('generates character from AI', async () => {
const { generateText } = await import('ai')
vi.mocked(generateText).mockResolvedValue({
text: JSON.stringify({ name: 'Aria', class: 'Mage' }),
usage: { totalTokens: 100 }
})
const character = await generateCharacter({ archetype: 'mage' })
expect(character.name).toBe('Aria')
})
See references/testing-patterns.md → "AI Feature Testing Patterns" for full examples.
WorldCrafter uses canvas/SVG for timelines, relationship graphs, and world maps. See comprehensive patterns for:
Example:
test('timeline renders events', async ({ page }) => {
await page.goto('/world/timeline')
const canvas = page.locator('canvas[data-timeline]')
await expect(canvas).toBeVisible()
const events = page.locator('svg circle.event-marker')
await expect(events).toHaveCount(5)
})
See references/testing-patterns.md → "Visualization Testing Patterns" for full examples.
WorldCrafter supports multi-user editing with WebSockets. See patterns for:
vi.fn(() => mockWs)Example:
let mockWs: any
beforeEach(() => {
mockWs = {
send: vi.fn(),
addEventListener: vi.fn()
}
global.WebSocket = vi.fn(() => mockWs) as any
})
it('sends presence updates', () => {
const collab = createCollaborationClient({ worldId: '123' })
collab.updatePresence({ viewing: 'char-456' })
expect(mockWs.send).toHaveBeenCalledWith(
expect.stringContaining('"type":"presence"')
)
})
See references/testing-patterns.md → "Real-time Collaboration Testing Patterns" for full examples.
Ensure WorldCrafter meets performance targets:
performance.now()Example:
test('world list loads fast', async ({ page }) => {
const start = Date.now()
await page.goto('/worlds')
await page.waitForSelector('[data-testid="world-list"]')
expect(Date.now() - start).toBeLessThan(200)
})
See references/testing-patterns.md → "Performance Testing Patterns" for full examples.
Ensure WCAG 2.1 AA compliance:
@axe-core/playwrightExample:
import AxeBuilder from '@axe-core/playwright'
test('page is accessible', async ({ page }) => {
await page.goto('/worlds')
const results = await new AxeBuilder({ page }).analyze()
expect(results.violations).toEqual([])
})
See references/testing-patterns.md → "Accessibility Testing Patterns" for full examples.
WorldCrafter supports CSV, Markdown, and JSON import/export:
Example:
test('downloads CSV', async ({ page }) => {
await page.goto('/characters')
const [download] = await Promise.all([
page.waitForEvent('download'),
page.click('button:has-text("Export CSV")')
])
expect(download.suggestedFilename()).toMatch(/\.csv$/)
})
See references/testing-patterns.md → "Import/Export Testing Patterns" for full examples.
# Generate component test
python .claude/skills/worldcrafter-test-generator/scripts/generate_tests.py component Button
# Generate integration test
python .claude/skills/worldcrafter-test-generator/scripts/generate_tests.py integration user-profile
# Generate E2E test
python .claude/skills/worldcrafter-test-generator/scripts/generate_e2e.py checkout-flow
# Generate test factory
python .claude/skills/worldcrafter-test-generator/scripts/generate_factory.py User
vi.clearAllMocks()test.setTimeout(60000)npm run test:coverage.env.test is configuredafterAll hooksreferences/testing-patterns.md - Comprehensive testing patternsreferences/mocking-guide.md - How to mock dependenciesreferences/assertion-guide.md - Common assertions and utilitiesreferences/related-skills.md - How this skill works with other WorldCrafter skillsassets/templates/ - Complete test templatesThis skill is typically used to ADD test coverage to existing code or to supplement tests generated by other skills.
Adding Tests to Existing Features:
Complete Feature Development (Database-First):
Testing Authentication Features:
Improving Coverage:
npm run test:coverage to identify gapsClaude will orchestrate test-generator with other skills when:
Example orchestration:
User: "Add a comments feature and make sure it has comprehensive test coverage"
Claude's workflow:
1. worldcrafter-database-setup: Create Comment model
2. worldcrafter-feature-builder: Create comment form and Server Actions (includes basic tests)
3. worldcrafter-test-generator (this skill): Add edge case tests, RLS policy tests, E2E tests for comment threading
Choose this skill when:
Choose worldcrafter-feature-builder instead when:
Use this skill AFTER feature-builder when:
For each feature:
Core Testing:
AI Features (if applicable):
Visualizations (if applicable):
Real-time Collaboration (if applicable):
Performance:
Accessibility:
Import/Export (if applicable):
Final Verification:
npm run test:allComplete test coverage includes: