> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibetunnel.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

# Testing Guide

## Quick Commands

```bash theme={null}
# Run all tests
./scripts/test-all.sh

# Platform-specific
cd mac && xcodebuild test
cd ios && ./scripts/test-with-coverage.sh
cd web && pnpm test

# With coverage
cd web && pnpm test:coverage
```

## Test Structure

```
tests/
├── unit/           # Unit tests
├── integration/    # Integration tests
├── e2e/           # End-to-end tests
└── fixtures/      # Test data
```

## Unit Testing

### Swift (XCTest)

```swift theme={null}
// mac/VibeTunnelTests/ServerManagerTests.swift
import XCTest
@testable import VibeTunnel

class ServerManagerTests: XCTestCase {
    func testServerStart() async throws {
        let manager = ServerManager()
        
        try await manager.start()
        
        XCTAssertTrue(manager.isRunning)
        XCTAssertEqual(manager.port, "4020")
    }
    
    func testPortValidation() {
        XCTAssertThrowsError(try validatePort("abc"))
        XCTAssertNoThrow(try validatePort("8080"))
    }
}
```

### TypeScript (Vitest)

```typescript theme={null}
// web/tests/session-manager.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { SessionManager } from '../src/server/services/session-manager';

describe('SessionManager', () => {
  let manager: SessionManager;
  
  beforeEach(() => {
    manager = new SessionManager();
  });
  
  it('creates session', async () => {
    const session = await manager.create({
      shell: '/bin/bash',
      cols: 80,
      rows: 24
    });
    
    expect(session.id).toBeDefined();
    expect(session.status).toBe('running');
  });
});
```

## Integration Testing

### API Testing

```typescript theme={null}
// web/tests/integration/api.test.ts
import request from 'supertest';
import { app } from '../../src/server/app';

describe('API Integration', () => {
  it('creates session via API', async () => {
    const response = await request(app)
      .post('/api/sessions')
      .send({ shell: '/bin/bash' })
      .expect(201);
    
    expect(response.body).toHaveProperty('id');
    expect(response.body.status).toBe('running');
  });
});
```

### WebSocket Testing

```typescript theme={null}
// web/tests/integration/websocket.test.ts
import { WebSocket } from 'ws';
// Helpers live in `web/src/shared/ws-v3.ts`.
import { encodeWsV3Frame, encodeWsV3SubscribePayload, WsV3MessageType, WsV3SubscribeFlags } from './ws-v3';

describe('WebSocket Integration', () => {
  it('connects to session', async () => {
    const ws = new WebSocket('ws://localhost:4020/ws?token=JWT_TOKEN');
    
    await new Promise((resolve) => {
      ws.on('open', resolve);
    });

    ws.send(
      encodeWsV3Frame({
        type: WsV3MessageType.SUBSCRIBE,
        sessionId: 'test',
        payload: encodeWsV3SubscribePayload({ flags: WsV3SubscribeFlags.Stdout }),
      })
    );
    
    const message = await new Promise((resolve) => {
      ws.on('message', resolve);
    });
    
    expect(message.toString()).toContain('test');
  });
});
```

## E2E Testing

### Playwright Setup

```typescript theme={null}
// web/playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests/e2e',
  use: {
    baseURL: 'http://localhost:4020',
    trace: 'on-first-retry',
  },
  webServer: {
    command: 'pnpm dev',
    port: 4020,
    reuseExistingServer: !process.env.CI,
  },
});
```

### E2E Tests

```typescript theme={null}
// web/tests/e2e/terminal.test.ts
import { test, expect } from '@playwright/test';

test('complete terminal workflow', async ({ page }) => {
  // Navigate to app
  await page.goto('/');
  
  // Create new terminal
  await page.click('button:has-text("New Terminal")');
  
  // Wait for terminal to load
  const terminal = page.locator('.terminal');
  await expect(terminal).toBeVisible();
  
  // Type command
  await page.keyboard.type('echo "Hello, VibeTunnel"');
  await page.keyboard.press('Enter');
  
  // Verify output
  await expect(terminal).toContainText('Hello, VibeTunnel');
  
  // Close session
  await page.click('button[aria-label="Close terminal"]');
  await expect(terminal).not.toBeVisible();
});
```

## Performance Testing

### Load Testing

```javascript theme={null}
// tests/performance/load.js
import { check } from 'k6';
import ws from 'k6/ws';

export default function() {
  const url = 'ws://localhost:4020/ws?token=JWT_TOKEN';
  
  ws.connect(url, {}, function(socket) {
    socket.on('open', () => {
      // WebSocket v3 is binary-framed; send `SUBSCRIBE` + `INPUT_TEXT` frames.
      // (Encode using the layout in `docs/websocket.md`.)
      //
      // socket.sendBinary(<Uint8Array>);
    });
    
    socket.on('message', (data) => {
      check(data, {
        'received response': (d) => d.length > 0,
      });
    });
  });
}

export const options = {
  vus: 100,        // 100 virtual users
  duration: '30s', // 30 second test
};
```

### Benchmark Suite

```typescript theme={null}
// tests/performance/benchmark.ts
import { bench, describe } from 'vitest';

describe('Buffer encoding performance', () => {
  bench('encode 1KB', () => {
    encodeBuffer('x'.repeat(1024));
  });
  
  bench('encode 10KB', () => {
    encodeBuffer('x'.repeat(10240));
  });
});
```

## Test Coverage

### Coverage Requirements

| Component | Target | Current |
| --------- | ------ | ------- |
| Server    | 80%    | 85%     |
| Client    | 70%    | 72%     |
| Mac App   | 60%    | 65%     |
| iOS App   | 75%    | 78%     |

### Generate Reports

```bash theme={null}
# Web coverage
cd web && pnpm test:coverage

# iOS coverage
cd ios && ./scripts/test-with-coverage.sh

# View HTML report
open coverage/index.html
```

## Testing External Devices

### iPad/iPhone Testing

```bash theme={null}
# 1. Start dev server on all interfaces
cd web && pnpm dev --host 0.0.0.0

# 2. Get Mac IP
ifconfig | grep inet

# 3. Access from device
# http://192.168.1.100:4021
```

### Cross-Browser Testing

```typescript theme={null}
// playwright.config.ts
projects: [
  { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
  { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
  { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  { name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
]
```

## Mocking & Fixtures

### Mock PTY

```typescript theme={null}
// tests/mocks/pty.ts
export class MockPTY {
  write(data: string) {
    this.emit('data', `mock: ${data}`);
  }
  
  resize(cols: number, rows: number) {
    this.cols = cols;
    this.rows = rows;
  }
}
```

### Test Fixtures

```typescript theme={null}
// tests/fixtures/sessions.ts
export const mockSession = {
  id: 'test-session-123',
  name: 'Test Session',
  status: 'running',
  created: new Date(),
  pid: 12345,
};
```

## CI/CD Testing

### GitHub Actions

```yaml theme={null}
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 22
          
      - name: Install dependencies
        run: |
          cd web && pnpm install
          
      - name: Run tests
        run: ./scripts/test-all.sh
        
      - name: Upload coverage
        uses: codecov/codecov-action@v3
```

## Debugging Tests

### Debug Swift Tests

```bash theme={null}
# Run with verbose output
xcodebuild test -verbose

# Debug specific test
xcodebuild test -only-testing:VibeTunnelTests/ServerManagerTests/testServerStart
```

### Debug TypeScript Tests

```bash theme={null}
# Run with inspector
node --inspect-brk ./node_modules/.bin/vitest

# Run single test file
pnpm test session-manager.test.ts

# Watch mode
pnpm test --watch
```

## Best Practices

1. **Test naming**: Use descriptive names like `shouldCreateSessionWithCustomShell`
2. **Isolation**: Each test should be independent
3. **Cleanup**: Always cleanup resources (sessions, files, connections)
4. **Assertions**: Test both success and error cases
5. **Speed**: Keep unit tests under 100ms each
6. **Flakiness**: Retry flaky tests, investigate root cause

## Common Issues

| Issue           | Solution                      |
| --------------- | ----------------------------- |
| Tests timeout   | Increase timeout, check async |
| Port conflicts  | Use random ports in tests     |
| Flaky WebSocket | Add connection retry logic    |
| Coverage gaps   | Add tests for error paths     |

## See Also

* [Development Guide](development.md)
* [CI/CD Setup](../reference/release-process.md#cicd-pipeline)
* [Troubleshooting](../reference/troubleshooting.md)
