Files
proxycast/docs/test/e2e-tests.md
T
2026-01-30 01:31:09 +08:00

6.4 KiB

ProxyCast E2E 测试指南

端到端测试验证完整用户流程

概述

E2E 测试模拟真实用户操作,验证从前端到后端的完整流程。ProxyCast 使用 Tauri 框架,E2E 测试需要覆盖:

  • 桌面应用启动和初始化
  • 用户界面交互
  • API 代理完整流程
  • 凭证管理流程

测试框架

Tauri E2E 测试

使用 tauri-driver 进行自动化测试:

# 安装依赖
cargo install tauri-driver

# 运行 E2E 测试
npm run test:e2e

测试配置

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests/e2e',
  timeout: 30000,
  use: {
    baseURL: 'tauri://localhost',
  },
});

测试场景

1. 应用启动流程

import { test, expect } from '@playwright/test';

test.describe('应用启动', () => {
  test('应用正常启动并显示主界面', async ({ page }) => {
    // 等待应用加载
    await page.waitForSelector('[data-testid="main-layout"]');
    
    // 验证核心组件存在
    await expect(page.locator('[data-testid="sidebar"]')).toBeVisible();
    await expect(page.locator('[data-testid="content-area"]')).toBeVisible();
  });

  test('首次启动显示欢迎引导', async ({ page }) => {
    // 清除本地存储模拟首次启动
    await page.evaluate(() => localStorage.clear());
    await page.reload();
    
    await expect(page.locator('[data-testid="welcome-modal"]')).toBeVisible();
  });
});

2. 凭证管理流程

test.describe('凭证管理', () => {
  test('添加 Kiro 凭证', async ({ page }) => {
    // 打开凭证管理
    await page.click('[data-testid="credentials-tab"]');
    await page.click('[data-testid="add-credential-btn"]');
    
    // 选择 Provider
    await page.click('[data-testid="provider-kiro"]');
    
    // 上传凭证文件
    const fileInput = page.locator('input[type="file"]');
    await fileInput.setInputFiles('./tests/fixtures/test-credential.json');
    
    // 验证凭证添加成功
    await expect(page.locator('[data-testid="credential-item"]')).toBeVisible();
    await expect(page.locator('text=test@example.com')).toBeVisible();
  });

  test('删除凭证', async ({ page }) => {
    // 假设已有凭证
    await page.click('[data-testid="credentials-tab"]');
    
    // 删除凭证
    await page.click('[data-testid="credential-menu"]');
    await page.click('[data-testid="delete-credential"]');
    await page.click('[data-testid="confirm-delete"]');
    
    // 验证凭证已删除
    await expect(page.locator('[data-testid="credential-item"]')).not.toBeVisible();
  });
});

3. API 代理流程

test.describe('API 代理', () => {
  test('启动代理服务器', async ({ page }) => {
    await page.click('[data-testid="server-tab"]');
    await page.click('[data-testid="start-server-btn"]');
    
    // 等待服务器启动
    await expect(page.locator('text=服务器运行中')).toBeVisible();
    await expect(page.locator('[data-testid="server-port"]')).toContainText('8080');
  });

  test('代理请求成功', async ({ page, request }) => {
    // 启动服务器
    await page.click('[data-testid="start-server-btn"]');
    await page.waitForSelector('text=服务器运行中');
    
    // 发送测试请求
    const response = await request.post('http://localhost:8080/v1/chat/completions', {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer test-key',
      },
      data: {
        model: 'gpt-4',
        messages: [{ role: 'user', content: 'Hello' }],
      },
    });
    
    expect(response.ok()).toBeTruthy();
  });
});

4. Agent 对话流程

test.describe('Agent 对话', () => {
  test('发送消息并接收响应', async ({ page }) => {
    await page.click('[data-testid="agent-tab"]');
    
    // 输入消息
    await page.fill('[data-testid="message-input"]', '你好,请介绍一下自己');
    await page.click('[data-testid="send-btn"]');
    
    // 等待响应
    await expect(page.locator('[data-testid="assistant-message"]')).toBeVisible({
      timeout: 30000,
    });
  });

  test('流式响应正确显示', async ({ page }) => {
    await page.click('[data-testid="agent-tab"]');
    await page.fill('[data-testid="message-input"]', '写一首短诗');
    await page.click('[data-testid="send-btn"]');
    
    // 验证流式显示(内容逐渐增加)
    const messageEl = page.locator('[data-testid="assistant-message"]');
    
    let prevLength = 0;
    for (let i = 0; i < 5; i++) {
      await page.waitForTimeout(500);
      const text = await messageEl.textContent();
      expect(text?.length).toBeGreaterThan(prevLength);
      prevLength = text?.length || 0;
    }
  });
});

测试数据管理

Fixtures

tests/
├── fixtures/
│   ├── test-credential.json    # 测试凭证
│   ├── mock-responses/         # Mock API 响应
│   │   ├── chat-completion.json
│   │   └── streaming-response.txt
│   └── test-config.json        # 测试配置
└── e2e/
    └── *.spec.ts

Mock 服务

// tests/mocks/api-server.ts
import { setupServer } from 'msw/node';
import { rest } from 'msw';

export const mockServer = setupServer(
  rest.post('*/v1/chat/completions', (req, res, ctx) => {
    return res(
      ctx.json({
        id: 'test-id',
        choices: [{
          message: { role: 'assistant', content: 'Mock response' },
        }],
      })
    );
  })
);

运行 E2E 测试

# 构建应用
npm run build

# 运行 E2E 测试
npm run test:e2e

# 运行特定测试
npm run test:e2e -- --grep "凭证管理"

# 生成测试报告
npm run test:e2e -- --reporter=html

CI/CD 集成

# .github/workflows/e2e.yml
name: E2E Tests

on: [push, pull_request]

jobs:
  e2e:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      
      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build app
        run: npm run build
      
      - name: Run E2E tests
        run: npm run test:e2e

下一步