Testing Guide
Comprehensive testing documentation for DocBits components with Vitest, Vue Test Utils, and Playwright.
Testing Strategy
DocBits components are thoroughly tested with three levels of testing:
1. Unit Tests (Low Level)
Test individual functions and component logic in isolation.
- Focus: Pure functions, utilities, component logic
- Tools: Vitest, Jest assertions
- Coverage: >95% line coverage
- Speed: Fast (milliseconds)
- Example: Testing sort direction toggle logic
it('toggles direction from asc to desc', () => {
const direction = 'asc'
const newDirection = direction === 'asc' ? 'desc' : 'asc'
expect(newDirection).toBe('desc')
})2. Component Tests (Mid Level)
Test components working together with realistic user interactions.
- Focus: Component integration, user workflows, features
- Tools: Vitest, Vue Test Utils, @vue/test-utils
- Coverage: 40+ tests per major component
- Speed: Fast (seconds)
- Example: Testing row selection with checkbox
it('selects row when checkbox clicked', async () => {
const wrapper = mount(RowSelectionCheckbox, {
props: { rowId: '1', isSelected: false }
})
await wrapper.find('input').trigger('click')
expect(wrapper.emitted('toggle')).toBeTruthy()
})3. E2E Tests (High Level)
Test complete user workflows in a browser environment.
- Focus: Full application flows, user journeys
- Tools: Playwright
- Coverage: Critical paths
- Speed: Slower (seconds to minutes)
- Example: Login → Select rows → Perform bulk action → Verify result
Test Statistics
Our components include:
- 100+ Unit Tests - Core functionality and edge cases
- 40+ Component Tests - Integration and workflows
- 30+ Test Files - Organized by component and feature
Coverage Requirements
All DocBits components maintain:
- Line Coverage: >95%
- Branch Coverage: >90%
- Function Coverage: >95%
Testing Documentation
Unit Testing
Detailed patterns and examples for unit testing:
- Props validation
- Event emission
- User interactions
- Computed properties
- Edge cases
- Running tests with coverage
Component Testing
Patterns for testing component integration:
- Component integration scenarios
- Feature flags and toggles
- Complete user workflows
- Async operations
- Accessibility testing
- Dark mode verification
Examples
Real test code from the DocBits component library:
- RowSelectionCheckbox test suite (42 tests)
- BulkActionsMenu test suite (22 tests)
- Integration test examples
- Async operation patterns
- Copy-paste ready examples
Quick Start
Setup
# Install test dependencies
npm install -D vitest @vue/test-utils jsdom
# Create vite.config.ts with test configuration
# Create test files alongside componentsRun Tests
# Run all tests
npm run test
# Run specific test file
npm run test -- src/components/Table/RowSelectionCheckbox.spec.ts
# Run tests in watch mode
npm run test:watch
# Run with coverage report
npm run test:coverageBasic Test Structure
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
describe('MyComponent', () => {
it('renders component', () => {
const wrapper = mount(MyComponent, {
props: { /* props */ }
})
expect(wrapper.exists()).toBe(true)
})
it('emits event on interaction', async () => {
const wrapper = mount(MyComponent, {
props: { /* props */ }
})
await wrapper.find('button').trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
})Best Practices
1. Test User Behavior, Not Implementation
// ✅ Good: Tests what user sees
it('shows selected arrow in primary color', () => {
const wrapper = mount(ColumnOrderBy, {
props: { column: 'name', currentSort: 'name', currentDirection: 'asc' }
})
const arrow = wrapper.find('.up-arrow.selected')
expect(arrow.exists()).toBe(true)
})
// ❌ Bad: Tests internal state
it('sets internal state.selectedDirection to asc', () => {
const wrapper = mount(ColumnOrderBy, { /* ... */ })
expect(wrapper.vm.state.selectedDirection).toBe('asc')
})2. Use Arrange-Act-Assert Pattern
it('handles row selection workflow', () => {
// Arrange: Set up initial state
const wrapper = mount(RowSelectionCheckbox, {
props: { rowId: '1', isSelected: false }
})
// Act: Perform user action
const checkbox = wrapper.find('input')
await checkbox.trigger('click')
// Assert: Verify result
expect(wrapper.emitted('toggle')).toBeTruthy()
expect(wrapper.emitted('toggle')[0]).toEqual(['1', true])
})3. Test Edge Cases
// Empty/null values
it('handles null input', () => { /* */ })
// Boundary conditions
it('handles maximum values', () => { /* */ })
// Special characters
it('handles special characters in input', () => { /* */ })
// Rapid interactions
it('handles rapid clicks', async () => {
await element.trigger('click')
await element.trigger('click')
await element.trigger('click')
expect(wrapper.emitted('click')).toHaveLength(3)
})4. Test Complete Workflows
it('completes select and delete workflow', async () => {
const wrapper = mount(DocBitsTable, { /* ... */ })
// Step 1: Select row
const checkbox = wrapper.find('input[type="checkbox"]')
await checkbox.trigger('click')
expect(wrapper.emitted('selection-change')).toBeTruthy()
// Step 2: Verify bulk menu appears
expect(wrapper.find('.bulk-actions-menu').exists()).toBe(true)
// Step 3: Trigger delete
await wrapper.find('[aria-label*="Delete"]').trigger('click')
expect(wrapper.emitted('bulk-action')).toBeTruthy()
})Debugging Tests
View Component HTML
it('renders correctly', () => {
const wrapper = mount(Component, { props: { /* ... */ } })
console.log(wrapper.html()) // Print full rendered HTML
})Check Event Emissions
it('emits events', async () => {
const wrapper = mount(Component, { props: { /* ... */ } })
await wrapper.find('button').trigger('click')
console.log(wrapper.emitted()) // Print all emitted events
})Inspect Component State
it('updates state', async () => {
const wrapper = mount(Component, { props: { /* ... */ } })
console.log(wrapper.vm.$data) // Component state before
await wrapper.find('button').trigger('click')
console.log(wrapper.vm.$data) // Component state after
})Wait for Updates
// Vue reactivity updates
await wrapper.vm.$nextTick()
// DOM updates after async
await new Promise(r => setTimeout(r, 0))
// Await prop changes
await wrapper.setProps({ loading: true })Test File Organization
src/
├── components/
│ ├── Table/
│ │ ├── ColumnOrderBy.vue
│ │ ├── ColumnOrderBy.spec.ts # Unit tests
│ │ ├── RowSelectionCheckbox.vue
│ │ ├── RowSelectionCheckbox.spec.ts
│ │ └── ...
│ └── ...
└── tests/
├── unit/
│ ├── sorting.unit.spec.ts
│ └── selection.unit.spec.ts
├── component/
│ ├── table-integration.spec.ts
│ └── workflows.spec.ts
└── e2e/
├── selection-workflow.spec.ts
└── bulk-actions.spec.tsContinuous Integration
Tests run automatically on every commit:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
- run: npm ci
- run: npm run test -- --coverage
- run: npm run lintCoverage Goals
Target coverage by component:
| Component | Line | Branch | Function |
|---|---|---|---|
| ColumnOrderBy | >98% | >95% | 100% |
| RowSelectionCheckbox | >97% | >95% | 100% |
| BulkActionsMenu | >96% | >90% | >98% |
| RowActionsMenu | >95% | >90% | >95% |
| DocBitsTable | >95% | >85% | >95% |
| UnifiedTable | >94% | >85% | >94% |
Common Testing Issues
"Element not found" errors
Cause: Component hasn't rendered yet
Solution: Wait for Vue updates
await wrapper.vm.$nextTick()Event not emitted
Cause: Wrong selector or trigger type
Solution: Verify element exists first
expect(wrapper.find('button').exists()).toBe(true)
await wrapper.find('button').trigger('click')Props not updating
Cause: Missing await on setProps
Solution: Always await prop changes
await wrapper.setProps({ disabled: true })Tests timeout
Cause: Waiting for async operation that never completes
Solution: Add reasonable timeout or mock the async operation
it('loads data', async () => {
vi.mock('@/api', () => ({
fetchData: vi.fn(() => Promise.resolve([]))
}))
// ... test code
}, { timeout: 5000 })Resources
- Unit Testing Guide - Detailed patterns and examples
- Component Testing Guide - Integration testing
- Code Examples - Real test code from DocBits
- Vitest Documentation - Official test runner docs
- Vue Test Utils - Component testing library
- Playwright - E2E testing framework