| name | test-writer |
| description | Generate comprehensive Vitest tests for code examples in JavaScript concept documentation pages, following project conventions and referencing source lines |
Skill: Test Writer for Concept Pages
Use this skill to generate comprehensive Vitest tests for all code examples in a concept documentation page. Tests verify that code examples in the documentation are accurate and work as described.
When to Use
- After writing a new concept page
- When adding new code examples to existing pages
- When updating existing code examples
- To verify documentation accuracy through automated tests
- Before publishing to ensure all examples work correctly
Test Writing Methodology
Follow these four phases to create comprehensive tests for a concept page.
Phase 1: Code Example Extraction
Scan the concept page for all code examples and categorize them:
| Category | Characteristics | Action |
|---|
| Testable | Has console.log with output comments, returns values | Write tests |
| DOM-specific | Uses document, window, DOM APIs, event handlers | Write DOM tests (separate file) |
| Error examples | Intentionally throws errors, demonstrates failures | Write tests with toThrow |
| Conceptual | ASCII diagrams, pseudo-code, incomplete snippets | Skip (document why) |
| Browser-only | Uses browser APIs not available in jsdom | Skip or mock |
Phase 2: Determine Test File Structure
tests/
├── fundamentals/ # Concepts 1-6
├── functions-execution/ # Concepts 7-8
├── web-platform/ # Concepts 9-10
├── object-oriented/ # Concepts 11-15
├── functional-programming/ # Concepts 16-19
├── async-javascript/ # Concepts 20-22
├── advanced-topics/ # Concepts 23-31
└── beyond/ # Extended concepts
└── {subcategory}/
File naming:
- Standard tests:
{concept-name}.test.js
- DOM tests:
{concept-name}.dom.test.js
Phase 3: Convert Examples to Tests
For each testable code example:
- Identify the expected output (from
console.log comments or documented behavior)
- Convert to
expect assertions
- Add source line reference in comments
- Group related tests in
describe blocks matching documentation sections
Phase 4: Handle Special Cases
| Case | Solution |
|---|
| Browser-only APIs | Use jsdom environment or skip with note |
| Timing-dependent code | Use vi.useFakeTimers() or test the logic, not timing |
| Side effects | Capture output or test mutations |
| Intentional errors | Use expect(() => {...}).toThrow() |
| Async code | Use async/await with proper assertions |
Project Test Conventions
Import Pattern
import { describe, it, expect } from 'vitest'
For DOM tests or tests needing mocks:
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
DOM Test File Header
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
Describe Block Organization
Match the structure of the documentation:
describe('Concept Name', () => {
describe('Section from Documentation', () => {
describe('Subsection if needed', () => {
it('should [specific behavior]', () => {
})
})
})
})
Test Naming Convention
- Start with "should"
- Be descriptive and specific
- Match the documented behavior
it('should return "object" for typeof null', () => {})
it('should throw TypeError when accessing property of undefined', () => {})
it('should resolve promises in order they were created', () => {})
it('test typeof', () => {})
it('works correctly', () => {})
it('null test', () => {})
Source Line References
Always reference the documentation source:
describe('Section Name', () => {
it('should return correct type strings', () => {
})
})
Test Patterns Reference
Pattern 1: Basic Value Assertion
Documentation:
console.log(typeof "hello")
console.log(typeof 42)
Test:
it('should return correct type for primitives', () => {
expect(typeof "hello").toBe("string")
expect(typeof 42).toBe("number")
})
Pattern 2: Multiple Related Assertions
Documentation:
let a = "hello"
let b = "hello"
console.log(a === b)
let obj1 = { x: 1 }
let obj2 = { x: 1 }
console.log(obj1 === obj2)
Test:
it('should compare primitives by value', () => {
let a = "hello"
let b = "hello"
expect(a === b).toBe(true)
})
it('should compare objects by reference', () => {
let obj1 = { x: 1 }
let obj2 = { x: 1 }
expect(obj1 === obj2).toBe(false)
})
Pattern 3: Function Return Values
Documentation:
function greet(name) {
return "Hello, " + name + "!"
}
console.log(greet("Alice"))
Test:
it('should return greeting with name', () => {
function greet(name) {
return "Hello, " + name + "!"
}
expect(greet("Alice")).toBe("Hello, Alice!")
})
Pattern 4: Error Testing
Documentation:
const obj = null
console.log(obj.property)
Test:
it('should throw TypeError when accessing property of null', () => {
const obj = null
expect(() => {
obj.property
}).toThrow(TypeError)
})
Pattern 5: Specific Error Messages
Documentation:
function divide(a, b) {
if (b === 0) throw new Error("Cannot divide by zero")
return a / b
}
Test:
it('should throw error when dividing by zero', () => {
function divide(a, b) {
if (b === 0) throw new Error("Cannot divide by zero")
return a / b
}
expect(() => divide(10, 0)).toThrow("Cannot divide by zero")
expect(divide(10, 2)).toBe(5)
})
Pattern 6: Async/Await Testing
Documentation:
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
Test:
it('should fetch user data asynchronously', async () => {
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ id: 1, name: 'Alice' })
})
)
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`)
return response.json()
}
const user = await fetchUser(1)
expect(user).toEqual({ id: 1, name: 'Alice' })
})
Pattern 7: Promise Testing
Documentation:
const promise = new Promise((resolve) => {
resolve("done")
})
promise.then(result => console.log(result))
Test:
it('should resolve with correct value', async () => {
const promise = new Promise((resolve) => {
resolve("done")
})
await expect(promise).resolves.toBe("done")
})
Pattern 8: Promise Rejection
Documentation:
const promise = new Promise((resolve, reject) => {
reject(new Error("Something went wrong"))
})
Test:
it('should reject with error', async () => {
const promise = new Promise((resolve, reject) => {
reject(new Error("Something went wrong"))
})
await expect(promise).rejects.toThrow("Something went wrong")
})
Pattern 9: Floating Point Comparison
Documentation:
console.log(0.1 + 0.2)
console.log(0.1 + 0.2 === 0.3)
Test:
it('should demonstrate floating point imprecision', () => {
expect(0.1 + 0.2).not.toBe(0.3)
expect(0.1 + 0.2).toBeCloseTo(0.3)
expect(0.1 + 0.2 === 0.3).toBe(false)
})
Pattern 10: Array Method Testing
Documentation:
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)
console.log(doubled)
Test:
it('should double all numbers in array', () => {
const numbers = [1, 2, 3, 4, 5]
const doubled = numbers.map(n => n * 2)
expect(doubled).toEqual([2, 4, 6, 8, 10])
expect(numbers).toEqual([1, 2, 3, 4, 5])
})
Pattern 11: Object Mutation Testing
Documentation:
const obj = { a: 1 }
obj.b = 2
console.log(obj)
Test:
it('should allow adding properties to objects', () => {
const obj = { a: 1 }
obj.b = 2
expect(obj).toEqual({ a: 1, b: 2 })
})
Pattern 12: Closure Testing
Documentation:
function counter() {
let count = 0
return function() {
count++
return count
}
}
const increment = counter()
console.log(increment())
console.log(increment())
console.log(increment())
Test:
it('should maintain state across calls via closure', () => {
function counter() {
let count = 0
return function() {
count++
return count
}
}
const increment = counter()
expect(increment()).toBe(1)
expect(increment()).toBe(2)
expect(increment()).toBe(3)
})
it('should create independent counters', () => {
function counter() {
let count = 0
return function() {
count++
return count
}
}
const counter1 = counter()
const counter2 = counter()
expect(counter1()).toBe(1)
expect(counter1()).toBe(2)
expect(counter2()).toBe(1)
})
Pattern 13: DOM Event Testing
Documentation:
const button = document.getElementById('myButton')
button.addEventListener('click', function(event) {
console.log('Button clicked!')
console.log(event.type)
})
Test (in .dom.test.js file):
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
describe('DOM Event Handlers', () => {
let button
beforeEach(() => {
button = document.createElement('button')
button.id = 'myButton'
document.body.appendChild(button)
})
afterEach(() => {
document.body.innerHTML = ''
})
it('should fire click event handler', () => {
const output = []
button.addEventListener('click', function(event) {
output.push('Button clicked!')
output.push(event.type)
})
button.click()
expect(output).toEqual(['Button clicked!', 'click'])
})
})
Pattern 14: DOM Manipulation Testing
Documentation:
const div = document.createElement('div')
div.textContent = 'Hello'
div.classList.add('greeting')
document.body.appendChild(div)
Test:
it('should create element with text and class', () => {
const div = document.createElement('div')
div.textContent = 'Hello'
div.classList.add('greeting')
document.body.appendChild(div)
const element = document.querySelector('.greeting')
expect(element).not.toBeNull()
expect(element.textContent).toBe('Hello')
expect(element.classList.contains('greeting')).toBe(true)
})
Pattern 15: Timer Testing
Documentation:
console.log('First')
setTimeout(() => console.log('Second'), 0)
console.log('Third')
Test:
it('should execute setTimeout callback after synchronous code', async () => {
const output = []
output.push('First')
setTimeout(() => output.push('Second'), 0)
output.push('Third')
await new Promise(resolve => setTimeout(resolve, 10))
expect(output).toEqual(['First', 'Third', 'Second'])
})
Pattern 16: Strict Mode Behavior
Documentation:
"use strict"
x = 10
Test:
it('should throw ReferenceError in strict mode for undeclared variables', () => {
expect(() => {
"use strict"
eval('undeclaredVar = 10')
}).toThrow()
})
Complete Test File Template
import { describe, it, expect } from 'vitest'
describe('[Concept Name]', () => {
describe('[First Section]', () => {
it('should [expected behavior]', () => {
expect(result).toBe(expected)
})
it('should [another expected behavior]', () => {
expect(result).toEqual(expected)
})
})
describe('[Second Section]', () => {
it('should [behavior]', () => {
})
})
describe('Edge Cases', () => {
it('should handle [edge case]', () => {
})
})
describe('Common Mistakes', () => {
it('should demonstrate the incorrect behavior', () => {
})
it('should demonstrate the correct behavior', () => {
})
})
})
Complete DOM Test File Template
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
describe('[Concept Name] - DOM', () => {
let container
beforeEach(() => {
container = document.createElement('div')
container.id = 'test-container'
document.body.appendChild(container)
})
afterEach(() => {
document.body.innerHTML = ''
vi.restoreAllMocks()
})
describe('[Section Name]', () => {
it('should [expected DOM behavior]', () => {
const element = document.createElement('div')
container.appendChild(element)
element.textContent = 'Hello'
expect(element.textContent).toBe('Hello')
})
})
describe('Event Handling', () => {
it('should handle click events', () => {
const button = document.createElement('button')
container.appendChild(button)
let clicked = false
button.addEventListener('click', () => {
clicked = true
})
button.click()
expect(clicked).toBe(true)
})
})
})
Running Tests
npm test
npm test -- tests/fundamentals/primitive-types/
npm test -- tests/fundamentals/primitive-types/primitive-types.test.js
npm test -- tests/fundamentals/primitive-types/primitive-types.dom.test.js
npm run test:watch
npm run test:coverage
npm test -- --reporter=verbose
Quality Checklist
Completeness
Correctness
Convention
Verification
Test Report Template
Use this template to document test coverage for a concept page.
# Test Coverage Report: [Concept Name]
**Concept Page:** `/docs/concepts/[slug].mdx`
**Test File:** `/tests/{category}/{concept}/{concept}.test.js`
**DOM Test File:** `/tests/{category}/{concept}/{concept}.dom.test.js` (if applicable)
**Date:** YYYY-MM-DD
**Author:** [Name/Claude]
## Summary
| Metric | Count |
|--------|-------|
| Total Code Examples in Doc | XX |
| Testable Examples | XX |
| Tests Written | XX |
| DOM Tests Written | XX |
| Skipped (with reason) | XX |
## Tests by Section
| Section | Line Range | Examples | Tests | Status |
|---------|------------|----------|-------|--------|
| [Section 1] | XX-YY | X | X | ✅ |
| [Section 2] | XX-YY | X | X | ✅ |
| [Section 3] | XX-YY | X | X | ⚠️ (1 skipped) |
## Skipped Examples
| Line | Example Description | Reason |
|------|---------------------|--------|
| XX | ASCII diagram of call stack | Conceptual, not executable |
| YY | Browser fetch example | Requires network, mocked instead |
## Test Execution
```bash
npm test -- tests/{category}/{concept}/
Result: ✅ XX passing | ❌ X failing | ⏭️ X skipped
Notes
[Any special considerations, mock requirements, or issues encountered]
---
## Common Issues and Solutions
### Issue: Test passes but shouldn't
**Problem:** Test expectations don't match documentation output
**Solution:** Double-check the expected value matches the `console.log` comment exactly
```javascript
// Documentation says: console.log(result) // [1, 2, 3]
// Make sure test uses:
expect(result).toEqual([1, 2, 3]) // NOT toBe for arrays
Issue: Async test times out
Problem: Async test never resolves
Solution: Ensure all promises are awaited and async function is marked
it('should fetch data', () => {
const data = fetchData()
expect(data).toBeDefined()
})
it('should fetch data', async () => {
const data = await fetchData()
expect(data).toBeDefined()
})
Issue: DOM test fails with "document is not defined"
Problem: Missing jsdom environment
Solution: Add environment directive at top of file
Issue: Test isolation problems
Problem: Tests affect each other
Solution: Use beforeEach/afterEach for cleanup
afterEach(() => {
document.body.innerHTML = ''
vi.restoreAllMocks()
})
Summary
When writing tests for a concept page:
- Extract all code examples from the documentation
- Categorize as testable, DOM, error, or conceptual
- Create test file in correct location with proper naming
- Convert each example to test using appropriate pattern
- Reference source lines in comments for traceability
- Run tests to verify all pass
- Document coverage using the report template
Remember: Tests serve two purposes:
- Verify documentation is accurate
- Catch regressions if code examples are updated
Every testable code example in the documentation should have a corresponding test. If an example can't be tested, document why.