# Frontend Testing Plan ## Overview This document outlines a comprehensive testing strategy for VibeTunnel's web frontend components. Currently, only one frontend test exists (`buffer-subscription-service.test.ts`), leaving the UI components untested. ## Testing Philosophy ### What to Test - **User interactions**: Click handlers, form submissions, keyboard shortcuts - **Component state management**: State transitions, property updates - **Event handling**: Custom events, DOM events, WebSocket messages - **Error scenarios**: Network failures, invalid inputs, edge cases - **Accessibility**: ARIA attributes, keyboard navigation ### What NOT to Test - LitElement framework internals - Third-party library behavior (xterm.js, Monaco editor) - CSS styling (unless it affects functionality) - Browser API implementations ## Component Test Categories ### 1. Core Terminal Components #### terminal.ts **Test scenarios:** - Terminal initialization with different configurations - Input handling (keyboard events, paste operations) - WebSocket connection lifecycle - Buffer updates and rendering - Resize handling - Error states (disconnection, invalid data) **Example test structure:** ```typescript describe('VibeTerminal', () => { let element: VibeTerminal; let mockWebSocket: MockWebSocket; beforeEach(async () => { mockWebSocket = new MockWebSocket(); element = await fixture(html``); }); it('should initialize terminal with correct dimensions', async () => { await element.updateComplete; expect(element.terminal).toBeDefined(); expect(element.terminal.cols).toBe(80); expect(element.terminal.rows).toBe(24); }); it('should handle keyboard input', async () => { const inputSpy = vi.fn(); element.addEventListener('terminal-input', inputSpy); await element.updateComplete; element.terminal.write('test'); expect(inputSpy).toHaveBeenCalledWith( expect.objectContaining({ detail: { data: 'test' } }) ); }); }); ``` #### vibe-terminal-buffer.ts **Test scenarios:** - Buffer rendering from different data formats - Cursor position updates - Selection handling - Performance with large buffers ### 2. Session Management Components #### session-list.ts **Test scenarios:** - Loading sessions from API - Real-time updates via SSE - Session filtering and sorting - Empty state handling - Error handling #### session-card.ts **Test scenarios:** - Session status display - Action buttons (connect, disconnect, delete) - Preview rendering - Hover/focus states #### session-create-form.ts **Test scenarios:** - Form validation - API submission - Loading states - Error handling - Success navigation ### 3. Authentication Components #### auth-login.ts **Test scenarios:** - Form submission - Password validation - Error message display - Redirect after successful login - Remember me functionality ### 4. Utility Components #### notification-status.ts **Test scenarios:** - Permission request flow - Status display updates - Settings persistence - Browser API mocking #### file-browser.ts **Test scenarios:** - Directory navigation - File selection - Path validation - Upload handling ## Testing Utilities ### Enhanced Test Helpers ```typescript // test/utils/component-helpers.ts export async function renderComponent( template: TemplateResult, options?: { viewport?: { width: number; height: number } } ): Promise { const element = await fixture(template); if (options?.viewport) { Object.defineProperty(window, 'innerWidth', { value: options.viewport.width }); Object.defineProperty(window, 'innerHeight', { value: options.viewport.height }); } return element; } export function mockFetch(responses: Map) { return vi.fn((url: string) => { const response = responses.get(url); return Promise.resolve({ ok: true, json: async () => response, text: async () => JSON.stringify(response) }); }); } ``` ### WebSocket Test Utilities ```typescript // test/utils/websocket-mock.ts export class MockWebSocket extends EventTarget { readyState = WebSocket.CONNECTING; url: string; constructor(url: string) { super(); this.url = url; setTimeout(() => this.open(), 0); } open() { this.readyState = WebSocket.OPEN; this.dispatchEvent(new Event('open')); } send(data: string | ArrayBuffer) { this.dispatchEvent(new MessageEvent('message', { data })); } close() { this.readyState = WebSocket.CLOSED; this.dispatchEvent(new Event('close')); } } ``` ## Test Organization ``` web/src/client/ ├── components/ │ ├── __tests__/ │ │ ├── terminal.test.ts │ │ ├── session-list.test.ts │ │ ├── session-card.test.ts │ │ └── ... │ ├── terminal.ts │ ├── session-list.ts │ └── ... └── services/ └── __tests__/ └── buffer-subscription-service.test.ts ``` ## CI Integration Frontend tests will run in the same CI pipeline as backend tests: 1. **Same test command**: `pnpm run test:coverage` 2. **Same job**: `build-and-test` in `.github/workflows/node.yml` 3. **Unified coverage**: Frontend and backend coverage combined 4. **Same thresholds**: 80% coverage requirement applies ### CI Considerations - Tests use `happy-dom` environment (already configured) - No need for real browser testing initially - Coverage reports aggregate automatically - Failing tests block PR merges ## Implementation Phases ### Phase 1: Core Components (Week 1) - [ ] terminal.ts - Basic functionality - [ ] session-list.ts - Data loading and display - [ ] session-card.ts - User interactions - [ ] Test utilities enhancement ### Phase 2: Session Management (Week 2) - [ ] session-create-form.ts - Form handling - [ ] session-view.ts - Complete session lifecycle - [ ] Error scenarios across components - [ ] WebSocket interaction tests ### Phase 3: Secondary Components (Week 3) - [ ] auth-login.ts - Authentication flow - [ ] notification-status.ts - Browser API mocking - [ ] file-browser.ts - File operations - [ ] Integration tests for component interactions ### Phase 4: Polish and Coverage (Week 4) - [ ] Achieve 80% coverage target - [ ] Performance tests for large datasets - [ ] Accessibility test suite - [ ] Documentation and examples ## Success Metrics - **Coverage**: Achieve and maintain 80% code coverage - **Test Speed**: All unit tests complete in < 10 seconds - **Reliability**: Zero flaky tests - **Maintainability**: Clear test names and structure - **Documentation**: Every complex test has explanatory comments ## Example Component Test Here's a complete example for the session-card component: ```typescript import { describe, it, expect, vi, beforeEach } from 'vitest'; import { fixture, html, oneEvent } from '@open-wc/testing'; import { SessionCard } from '../session-card'; import type { Session } from '../../types'; describe('SessionCard', () => { let element: SessionCard; const mockSession: Session = { id: 'test-123', name: 'Test Session', status: 'active', createdAt: '2024-01-01T00:00:00Z', cols: 80, rows: 24 }; beforeEach(async () => { element = await fixture(html` `); }); it('displays session information', () => { const nameEl = element.shadowRoot!.querySelector('.session-name'); expect(nameEl?.textContent).toBe('Test Session'); const statusEl = element.shadowRoot!.querySelector('.session-status'); expect(statusEl?.classList.contains('active')).toBe(true); }); it('emits connect event when clicked', async () => { const listener = oneEvent(element, 'session-connect'); const card = element.shadowRoot!.querySelector('.session-card') as HTMLElement; card.click(); const event = await listener; expect(event.detail.sessionId).toBe('test-123'); }); it('handles delete action', async () => { // Mock the fetch API global.fetch = vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({}) }) ); const deleteBtn = element.shadowRoot!.querySelector('.delete-btn') as HTMLElement; deleteBtn.click(); // Confirm dialog would appear here - mock it element.confirmDelete(); expect(fetch).toHaveBeenCalledWith('/api/sessions/test-123', { method: 'DELETE' }); }); it('shows error state on delete failure', async () => { global.fetch = vi.fn(() => Promise.reject(new Error('Network error')) ); await element.deleteSession(); expect(element.error).toBe('Failed to delete session'); const errorEl = element.shadowRoot!.querySelector('.error-message'); expect(errorEl).toBeTruthy(); }); }); ``` ## Next Steps 1. **Review and approve** this testing plan 2. **Set up** enhanced testing utilities 3. **Begin Phase 1** implementation 4. **Track progress** via GitHub issues/PRs 5. **Iterate** based on learnings