src/__tests__/
├── vitest/ # Integration tests (ESM)
│ ├── docs/ # Documentation tests
│ └── setup.ts # Vitest setup
└── vscode/ # VS Code tests (CommonJS)
├── runTest.cts # VS Code test runner
└── suite/ # VS Code test suite
├── index.cts # Test suite entry point
└── *.test.cts # Test files
pnpm test:vitest # Run integration tests
pnpm test:vscode # Run VS Code tests
pnpm test:all # Run all tests
The project uses a mixed module system approach:
-
Main Project (ESM)
- Root
package.json
:"type": "module"
- Modern ES modules for better tree-shaking
- Used by Vitest tests and React components
- Root
-
VS Code Extension (CommonJS)
- VS Code API requires CommonJS
- Extension code built with webpack (CommonJS output)
- Test files use
.cts
extension - Single
package.json
in VS Code test directory
- Uses ESM (
.ts
files) - Configuration in
vitest.config.ts
:export default defineConfig({ test: { testTimeout: 2000, // 2s for tests hookTimeout: 2000, // 2s for hooks teardownTimeout: 200, // 200ms for cleanup setupFiles: ['src/__tests__/vitest/setup.ts'], environment: 'node' } });
- Test setup in
setup.ts
:- Starts MCP server
- Sets up test workspace
- Handles cleanup
- No webpack involvement
- Native ESM support
- Uses CommonJS (
.cts
files) - Dedicated
package.json
with"type": "commonjs"
- Configuration in
src/__tests__/vscode/tsconfig.json
:{ "compilerOptions": { "module": "commonjs", "outDir": "../../../out/vscode-tests", "types": ["mocha", "node"] } }
- Test runner in
runTest.cts
:- Uses
@vscode/test-electron
- Handles extension loading
- Sets up test workspace
- Uses
- Direct TypeScript compilation
- Fixed test port (54321)
// Setup connection
const eventSource = new EventSource(`${serverUrl}/sse`);
// Handle connection open
eventSource.onopen = () => {
console.log('SSE connection established');
};
// Handle messages
eventSource.onmessage = (event) => {
const message = JSON.parse(event.data);
// Handle message
};
// Handle errors
eventSource.onerror = (error) => {
console.error('SSE error:', error);
};
// Cleanup
eventSource.close();
// Get extension
const ext = vscode.extensions.getExtension('id');
await ext.activate();
// Test WebView
const panel = ext.exports.getWebviewPanel();
await panel.webview.postMessage({ type: 'TEST' });
// Wait for response
await new Promise<void>((resolve) => {
panel.webview.onDidReceiveMessage(msg => {
if (msg.type === 'RESPONSE') resolve();
});
});
- VS Code tests: Use relative paths (../../utils/logging)
- Vitest tests: Can use path aliases
- Keep imports consistent within each test suite
- No path aliases in VS Code tests (makes debugging easier)
- Keep tests close to implementation
- Use appropriate module system per domain
- Share utilities within module boundaries
- Maintain clear separation of concerns
-
Server Cleanup
- Close SSE connections first
- Wait for server to close
- Close HTTP server
- Clear test instances
-
Resource Management
- Dispose WebView panels
- Clean test workspaces
- Reset global state
// Enable test logging
setLogCallback((msg, type) => {
console.log(`[${new Date().toISOString()}] ${msg}`);
});
// Log test steps
logStep('Test step description');
logSuccess('Test succeeded');
-
Initial Connection
- Timeout: 200ms
- Retry attempts: 3
- Backoff: 20ms
-
Message Exchange
- Timeout: 2000ms per message
- Keep-alive interval: 20s
- Ping/pong mechanism
-
Reconnection
- Max retries: 3
- Backoff: Exponential (20ms, 200ms, 2000ms)
- State preservation
-
Cleanup
- Grace period: 200ms
- Force close: After 2000ms
- Resource cleanup timeout: 20ms
-
Connection Errors
- Network issues
- Invalid origin
- Server unavailable
-
Message Errors
- Invalid format
- Missing fields
- Protocol violations
-
State Errors
- Connection lost
- Server timeout
- Client timeout
-
Connection Testing
it('should handle connection lifecycle', async () => { // Setup const eventSource = new EventSource(url); // Test phases await testConnection(eventSource); await testMessageExchange(eventSource); await testReconnection(eventSource); // Cleanup await gracefulClose(eventSource); });
-
Error Testing
it('should handle errors gracefully', async () => { const eventSource = new EventSource(url); // Test error scenarios await testNetworkError(eventSource); await testInvalidMessage(eventSource); await testTimeout(eventSource); // Verify cleanup await verifyCleanup(eventSource); });
All timeouts must follow these rules:
-
Valid Values
- Must be powers of 10 multiplied by 2: [2, 20, 200, 2000, 20000] ms
- No timeout may exceed 20 seconds (20000ms)
- Default timeouts:
- Test timeout: 2000ms (2s)
- Hook timeout: 2000ms (2s)
- Teardown timeout: 200ms
- Connection timeout: 200ms
- Cleanup wait: 20ms
-
Configuration Locations
- Vitest: Global timeouts in
vitest.config.ts
- VS Code: Per-test timeouts in test files
- Server operations: Constants in
src/constants.ts
- Vitest: Global timeouts in
-
Best Practices
- Use smallest sufficient timeout value
- Document timeout choices in comments
- Use constants from
TIMEOUTS
object - Prefer event-based waiting over fixed timeouts
-
Port Conflicts
- VS Code tests use fixed port 54321
- Other tests use random ports 54321-54421
- Solution: Check
lsof -i :54321
and kill processes
-
SSE Connection Issues
- Verify origin matches test environment
- Check connection cleanup
- Use test utilities for connections
- Verify server state management
-
State Management
- Monitor connection states
- Track message sequences
- Verify cleanup completion
- Check resource disposal