This document provides information about the testing infrastructure and how to run tests for Compose Booster.
- Testing Framework
- Running Tests
- Test Structure
- Writing Tests
- Mocking Electron APIs
- Coverage Reports
- Future: E2E Testing
Compose Booster uses Vitest as the testing framework with the following configuration:
- Test Environment: jsdom (for DOM testing)
- Global Test Utilities: Available globally (describe, it, expect, etc.)
- Coverage Provider: v8
- Setup File:
tests/setup.ts(global mocks and configuration)
Run tests in watch mode, which automatically re-runs tests when files change:
npm testExecute all tests once and exit:
npm run test:runOpen the Vitest UI for an interactive testing experience:
npm run test:uiThis opens a web interface at http://localhost:51204/__vitest__/ where you can:
- View test results in a graphical interface
- Filter tests by file or test name
- See detailed error messages and stack traces
- View code coverage
Generate a code coverage report:
npm run test:coverageCoverage reports are generated in multiple formats:
- Text: Printed to console
- HTML: Opens in browser at
coverage/index.html - JSON: Machine-readable format at
coverage/coverage-final.json
Tests are organized in the tests/ directory:
tests/
├── setup.ts # Global test setup and mocks
├── services/
│ └── apiService.test.ts # API service logic tests
├── utils/
│ └── costCalculation.test.ts # Cost calculation tests
└── types/
└── validation.test.ts # Type structure validation tests
As of the latest implementation, we have 53 passing unit tests covering:
-
API Service (18 tests)
- Prompt building with variable substitution (
${content},${tone},${date}) - Template validation (requiring
${content}) - API key validation
- Mock mode functionality
- Prompt building with variable substitution (
-
Cost Calculation (18 tests)
- Cost tier classification (Low/Medium/High)
- Edge cases for threshold boundaries
- Cost-per-million formatting
-
Type Validation (17 tests)
- Model structure validation
- Prompt structure validation (Record vs Array)
- Tone structure validation
- HotCombo structure validation
- Config structure integrity
import { describe, it, expect } from 'vitest';
import { apiService } from '../../src/main/services/apiService';
describe('FeatureName', () => {
it('should do something specific', () => {
// Arrange
const input = 'test input';
// Act
const result = functionUnderTest(input);
// Assert
expect(result).toBe('expected output');
});
});it('should handle async operations', async () => {
apiService.setMockMode(true);
const result = await apiService.testApiKey('sk-test-key');
expect(result.valid).toBe(true);
});it('should handle errors gracefully', () => {
expect(() => {
dangerousFunction();
}).toThrow('Expected error message');
});Since Electron APIs are not available in the test environment, we provide mocks in tests/setup.ts:
// Mock window.electronAPI for renderer tests
global.window = {
electronAPI: {
getConfig: vi.fn(),
setConfig: vi.fn(),
testApiKey: vi.fn(),
processEmail: vi.fn(),
getAvailableModels: vi.fn(),
onConfigUpdated: vi.fn(),
},
} as any;import { vi } from 'vitest';
it('should call Electron API', async () => {
// Setup mock return value
const mockConfig = { apiKey: 'test-key' };
window.electronAPI.getConfig = vi.fn().mockResolvedValue({
success: true,
data: mockConfig,
});
// Test code that calls the API
const result = await window.electronAPI.getConfig();
// Verify
expect(result.data).toEqual(mockConfig);
expect(window.electronAPI.getConfig).toHaveBeenCalled();
});Coverage reports show:
- Statements: Percentage of code statements executed
- Branches: Percentage of conditional branches taken
- Functions: Percentage of functions called
- Lines: Percentage of code lines executed
Currently, no minimum coverage thresholds are enforced. Focus on:
- Critical Business Logic: API service, config management, prompt building
- Edge Cases: Error handling, boundary conditions
- Data Transformations: Type conversions, validation
After running npm run test:coverage, open coverage/index.html in a browser to see:
- File-by-file coverage breakdown
- Line-by-line coverage highlighting (green = covered, red = uncovered)
- Branch coverage details
// ✅ Good
it('should replace ${content} variable with email content', () => {});
// ❌ Bad
it('works', () => {});// ✅ Good
it('should replace ${content} variable', () => {});
it('should replace ${tone} variable', () => {});
// ❌ Bad
it('should replace all variables', () => {
// Tests ${content}, ${tone}, and ${date} all at once
});it('should calculate cost tier correctly', () => {
// Arrange
const inputCost = 0.000003;
// Act
const tier = calculateCostTier(inputCost);
// Assert
expect(tier).toBe('Low');
});it('should handle empty API key', async () => {
const result = await apiService.testApiKey('');
expect(result.valid).toBe(false);
expect(result.error).toBe('API key is empty');
});
it('should handle whitespace-only API key', async () => {
const result = await apiService.testApiKey(' ');
expect(result.valid).toBe(false);
});beforeEach(() => {
apiService.setMockMode(true); // Avoid hitting real API
});Status: TODO - Planned for post-launch
We plan to implement end-to-end testing using Playwright for Electron.
-
Main Window Workflow
- Paste email → Select model/prompt/tone → Process → Verify output
- Hot combo buttons (Ctrl+1/2/3)
- Keyboard shortcuts (Ctrl+Enter, Ctrl+K, etc.)
- Clipboard operations
-
Settings Window
- Open/close settings
- Modify models/prompts/tones
- Verify changes persist
- Verify main window updates after settings change
-
Error Scenarios
- Invalid API key handling
- Network errors
- Empty input validation
- Rate limiting
-
Cross-Platform Testing
- Windows-specific behaviors
- macOS-specific behaviors (Cmd vs Ctrl)
- Window management on multi-monitor setups
npm install --save-dev @playwright/test playwrighttests/
├── e2e/
│ ├── main-window.spec.ts
│ ├── settings-window.spec.ts
│ ├── hot-combos.spec.ts
│ ├── keyboard-shortcuts.spec.ts
│ └── error-handling.spec.ts
└── fixtures/
├── test-emails.ts
└── mock-responses.ts
Ensure vitest.config.ts has the correct path aliases:
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});Check that tests/setup.ts is specified in vitest.config.ts:
export default defineConfig({
test: {
setupFiles: ['./tests/setup.ts'],
},
});Add include patterns to coverage configuration:
coverage: {
include: ['src/**/*.ts'],
exclude: ['src/**/*.test.ts', 'src/**/types.ts'],
}When adding new features:
- Write tests first (TDD approach recommended)
- Aim for >80% coverage of new code
- Test edge cases and error conditions
- Update this document if adding new test patterns or utilities