Running Tests
DevDaily uses Vitest for fast, modern testing.
All tests
Specific file
Watch mode
Coverage report
Tests run automatically in CI across Node.js versions 18, 20, and 22 on every pull request.
Test Structure
All tests live in the tests/ directory and mirror the source structure:
tests/
├── auto-snapshot.test.ts # 74 tests
├── commitlint.test.ts # 6 tests
├── context-analyzer.test.ts # 22 tests
├── copilot.test.ts # 38 tests
├── git-analyzer.test.ts # 15 tests
├── notifications.test.ts # 70 tests
├── pr-creation.test.ts # 123 tests
├── pr-prompt.test.ts # 26 tests
├── pr-template.test.ts # 25 tests
├── project-management.test.ts # 130 tests
├── standup-context.test.ts # 70 tests
├── ui.test.ts # 2 tests
└── work-journal.test.ts # 123 tests
Module Tests Description auto-snapshot74 Side-effect snapshots, hooks, config work-journal123 Persistent storage, search, aggregation project-management130 Jira, Linear, GitHub Issues integration standup-context70 Context building, formatting notifications70 Slack/Discord webhooks, output formatter pr-creation123 PR generation pipeline copilot38 AI prompt building pr-prompt26 Custom prompt file loading pr-template25 Template detection and parsing context-analyzer22 Work pattern analysis git-analyzer15 Git operations commitlint6 Conventional commit parsing ui2 UI rendering Total 724
Writing Tests
Test Conventions
Place tests in tests/ directory
Create files with .test.ts suffix
Use descriptive names
describe and it blocks should clearly explain what’s being tested
Mock external dependencies
Tests must not require network access, git repos, or API keys
Test happy path and edge cases
Cover both successful execution and error scenarios
Basic Test Example
import { describe , it , expect , vi } from 'vitest' ;
import { formatOutput } from '../src/utils/formatter.js' ;
describe ( 'formatOutput' , () => {
describe ( 'plain format' , () => {
it ( 'strips markdown header markers' , () => {
// Arrange
const input = '## Completed \n\n - Task A' ;
// Act
const result = formatOutput ( input , 'plain' );
// Assert
expect ( result . text ). not . toContain ( '##' );
expect ( result . text ). toContain ( 'Completed:' );
});
it ( 'preserves the original markdown in .raw' , () => {
const md = '## Title \n\n **Bold text**' ;
const result = formatOutput ( md , 'plain' );
expect ( result . raw ). toBe ( md );
});
});
});
Follow the Arrange-Act-Assert pattern for clear, readable tests.
Mocking
Config Mocking
Most tests mock the config module to avoid file system dependencies:
import { vi , beforeEach } from 'vitest' ;
const mockConfig = {
output: {
format: 'markdown' ,
copyToClipboard: true ,
showStats: true ,
verbose: false
},
git: {
defaultBranch: 'main' ,
excludeAuthors: [],
excludePatterns: []
},
standup: {
defaultDays: 1 ,
sections: [ 'completed' , 'in-progress' , 'blockers' ]
}
};
vi . mock ( '../src/config/index.js' , () => ({
getConfig : () => mockConfig ,
getSecrets : () => ({}),
}));
beforeEach (() => {
vi . clearAllMocks ();
});
Network Request Mocking
For webhook and API tests, stub the global fetch:
import { vi , beforeEach , afterEach } from 'vitest' ;
const fetchMock = vi . fn ();
beforeEach (() => {
vi . stubGlobal ( 'fetch' , fetchMock );
fetchMock . mockReset ();
});
afterEach (() => {
vi . unstubAllGlobals ();
});
it ( 'sends webhook to Slack' , async () => {
fetchMock . mockResolvedValueOnce ({
ok: true ,
status: 200 ,
});
await sendNotification ({ /* ... */ });
expect ( fetchMock ). toHaveBeenCalledWith (
expect . stringContaining ( 'slack.com' ),
expect . objectContaining ({
method: 'POST' ,
})
);
});
Git Operations Mocking
For tests involving git operations:
import { vi } from 'vitest' ;
import type { SimpleGit } from 'simple-git' ;
const mockGit : Partial < SimpleGit > = {
log: vi . fn (). mockResolvedValue ({
all: [
{
hash: 'abc123' ,
message: 'feat: add new feature' ,
date: '2025-01-15' ,
author_name: 'Test User' ,
},
],
}),
branch: vi . fn (). mockResolvedValue ({
current: 'feature/test' ,
all: [ 'main' , 'feature/test' ],
}),
};
vi . mock ( 'simple-git' , () => ({
simpleGit : () => mockGit ,
}));
Testing Best Practices
DO ✓
Test both success and failure paths
it ( 'returns data when API call succeeds' , async () => {
// Test happy path
});
it ( 'throws error when API call fails' , async () => {
// Test error handling
});
Use descriptive test names
// Good ✓
it ( 'strips markdown headers when format is plain' )
// Bad ✗
it ( 'works correctly' )
Mock external dependencies
// Mock file system, network, and external APIs
vi . mock ( 'fs/promises' );
vi . stubGlobal ( 'fetch' , mockFetch );
Clear mocks between tests
beforeEach (() => {
vi . clearAllMocks ();
});
DON’T ✗
Don’t make real network requests
Don’t depend on file system state
Don’t rely on specific git repository state
Don’t use real API keys or credentials
Don’t write tests that depend on execution order
Coverage Reports
Generate an HTML coverage report:
xdg-open coverage/index.html
start coverage/index.html
Coverage Configuration
Vitest is configured to generate coverage using v8:
// vitest.config.ts
export default defineConfig ({
test: {
globals: true ,
environment: 'node' ,
coverage: {
provider: 'v8' ,
reporter: [ 'text' , 'json' , 'html' ],
exclude: [
'node_modules/**' ,
'dist/**' ,
'**/*.test.ts' ,
'**/*.config.*' ,
],
},
} ,
}) ;
When adding new features, aim to maintain or improve test coverage. Every new module in src/core/ should have a corresponding test file.
Quality Checklist
Before submitting a pull request, ensure all checks pass:
TypeScript compilation
Must complete with zero errors
Linting
Must complete with zero errors
Formatting
All files must be properly formatted
Production build
Build must complete successfully
These same checks run automatically in CI on every pull request across Node.js 18, 20, and 22.
Manual Testing
For changes affecting CLI behavior, manual smoke testing is recommended:
1. Build and Link
2. Test in a Real Repository
# Navigate to any git repository
cd /path/to/your/project
# Test affected commands
devdaily standup --days 1 --no-copy
devdaily pr --debug --no-copy
devdaily week --no-copy
devdaily doctor
Use --debug to see the full prompt and context sent to Copilot CLI. Use --no-copy to prevent clipboard writes during testing.
3. Cleanup
When done, unlink the global command:
npm unlink -g devdaily-ai
Debugging Tests
Run a Single Test File
npx vitest run tests/notifications.test.ts
Run Tests Matching a Pattern
npx vitest run -t "webhook"
Enable Verbose Output
npx vitest run --reporter=verbose
Clear Vitest Cache
If tests behave unexpectedly:
npx vitest run --clearCache
Continuous Integration
Tests run automatically on every pull request via GitHub Actions:
# .github/workflows/ci.yml
strategy :
matrix :
node-version : [ 18 , 20 , 22 ]
os : [ ubuntu-latest , macos-latest ]
steps :
- run : npm run typecheck
- run : npm run lint
- run : npm run format:check
- run : npm test
- run : npm run build
All checks must pass before a PR can be merged.
Next Steps
Setup Guide Set up your development environment
Architecture Understand the project structure
Contributing Submit your first contribution