1
0

feat: E2E 测试集成真实后端

- Playwright 双 webServer 模式自动启动 Go 后端 + Vite 前端
- 后端使用临时 SQLite 数据库隔离,固定端口 19026
- vite.config.ts proxy target 动态读取环境变量
- 新增 sql.js 依赖用于 SQLite 统计数据 seed
- 新增 e2e/fixtures.ts 共享工具模块(API seed + SQLite seed)
- 拆分测试文件 5→7(providers/models/stats/navigation/validation)
- 删除旧文件 crud.spec.ts/sidebar.spec.ts/stats-cards.spec.ts
- E2E 测试尚有部分用例需调试修复
This commit is contained in:
2026-04-22 00:31:35 +08:00
parent 4fc5fb4764
commit 59179094ed
20 changed files with 973 additions and 540 deletions

View File

@@ -0,0 +1,216 @@
## ADDED Requirements
### Requirement: E2E 测试基础设施
前端 E2E 测试 SHALL 自动启动隔离的 Go 后端实例,提供真实 API 交互能力。
#### Scenario: 双 webServer 自动启动
- **WHEN** 执行 `bun run test:e2e`
- **THEN** Playwright SHALL 按 webServer 数组顺序先启动 Go 后端(端口 19026再启动 Vite 前端(端口 5173
- **THEN** Go 后端 SHALL 使用临时目录中的独立数据库文件和日志目录
- **THEN** Go 后端启动命令 SHALL 包含 `--server-port 19026``--database-path``--log-path``--log-level warn` 参数
- **THEN** Go 后端 cwd SHALL 指向 `backend/` 目录
- **THEN** webServer 等待超时 SHALL 为 60000ms
- **THEN** 两个 webServer SHALL 设置 `reuseExistingServer: false`
#### Scenario: 环境变量传递
- **WHEN** Playwright 配置加载
- **THEN** `playwright.config.ts` SHALL 设置 `process.env.NEX_BACKEND_PORT``'19026'`
- **THEN** `process.env.NEX_E2E_TEMP_DIR` SHALL 设置为临时目录路径
- **THEN** Vite dev server 子进程 SHALL 自动继承上述环境变量
- **THEN** E2E 测试文件 SHALL 通过 `process.env` 读取后端端口和临时目录信息
#### Scenario: Vite proxy 动态化
- **WHEN** `vite.config.ts` 配置 proxy target
- **THEN** proxy target SHALL 读取 `process.env.NEX_BACKEND_PORT`,回退到 `'9826'`
- **THEN** 日常 `bun run dev`无环境变量proxy target SHALL 为 `http://localhost:9826`
- **THEN** E2E 测试时 proxy target SHALL 为 `http://localhost:19026`
### Requirement: 临时文件隔离
E2E 测试 SHALL 使用临时目录隔离所有文件,测试结束后自动清理。
#### Scenario: 临时目录创建
- **WHEN** Playwright 配置加载
- **THEN** SHALL 使用 `fs.mkdtempSync(path.join(os.tmpdir(), 'nex-e2e-'))` 创建临时目录
- **THEN** 临时目录 SHALL 包含 `test.db`(数据库)和 `log/`(日志)子路径
#### Scenario: 临时目录清理
- **WHEN** Playwright 所有测试完成
- **THEN** `globalTeardown` SHALL 使用 `fs.rm(dir, { recursive: true, force: true })` 删除临时目录
- **THEN** 清理 SHALL 在 webServer 进程关闭之后执行
### Requirement: 统计数据 seed
E2E 测试 SHALL 能通过 sql.js 直接操作 SQLite 文件 seed 历史统计数据。
#### Scenario: SQLite 统计数据插入
- **WHEN** 统计页面测试需要历史数据
- **THEN** SHALL 使用 sql.js 打开临时数据库文件
- **THEN** SHALL 通过 `INSERT INTO usage_stats` 插入指定日期和请求量的统计数据
- **THEN** SHALL 将修改后的数据库写回文件(`db.export()``fs.writeFileSync`
- **THEN** seed 操作 SHALL 在 API 创建供应商和模型之后串行执行
- **THEN** seed 完成后 SHALL 关闭 sql.js 数据库连接
### Requirement: E2E 共享工具模块
E2E 测试 SHALL 提供共享工具模块(`e2e/fixtures.ts`)封装常用操作。
#### Scenario: 共享常量
- **WHEN** 测试文件需要后端 API 地址
- **THEN** fixtures SHALL export `API_BASE` 常量,值为 `http://localhost:${process.env.NEX_BACKEND_PORT}`
#### Scenario: API seed 工具
- **WHEN** 测试需要通过 API 准备数据
- **THEN** fixtures SHALL export `seedProvider(request, data)` 函数,通过 `request.post` 创建供应商
- **THEN** fixtures SHALL export `seedModel(request, data)` 函数,通过 `request.post` 创建模型
#### Scenario: SQLite seed 工具
- **WHEN** 测试需要 seed 统计数据
- **THEN** fixtures SHALL export `seedUsageStats(statsData)` 函数,通过 sql.js 插入 `usage_stats` 记录
### Requirement: 供应商管理 E2E 测试
E2E 测试 SHALL 验证供应商的完整 CRUD 用户流程。
#### Scenario: 创建供应商并验证
- **WHEN** 用户通过 UI 填写供应商表单并提交
- **THEN** 对话框 SHALL 关闭
- **THEN** 新供应商 SHALL 出现在表格中
- **THEN** 表格 SHALL 显示正确的 id、name、base_url、协议、enabled 状态
#### Scenario: 编辑供应商并验证
- **WHEN** 用户点击编辑按钮、修改名称并提交
- **THEN** 对话框 SHALL 关闭
- **THEN** 表格中该供应商的名称 SHALL 更新为新值
#### Scenario: 删除供应商并验证
- **WHEN** 用户点击删除按钮并确认
- **THEN** 确认框 SHALL 消失
- **THEN** 该供应商 SHALL 从表格中消失
### Requirement: 模型管理 E2E 测试
E2E 测试 SHALL 验证模型的完整 CRUD 用户流程。
#### Scenario: 前置数据准备
- **WHEN** 模型测试开始前
- **THEN** SHALL 通过 API seed 一个供应商(不通过 UI
#### Scenario: 创建模型并验证
- **WHEN** 用户展开供应商行、填写模型表单并提交
- **THEN** 对话框 SHALL 关闭
- **THEN** 新模型 SHALL 出现在展开行的模型表格中
- **THEN** 模型表格 SHALL 显示统一模型 ID`provider_id/model_name` 格式)
#### Scenario: 编辑模型并验证
- **WHEN** 用户点击模型编辑按钮、修改并提交
- **THEN** 对话框 SHALL 关闭
- **THEN** 模型表格 SHALL 显示更新后的数据
#### Scenario: 删除模型并验证
- **WHEN** 用户点击模型删除按钮并确认
- **THEN** 该模型 SHALL 从模型表格中消失
### Requirement: 统计页面 E2E 测试
E2E 测试 SHALL 验证统计页面的数据展示和筛选功能。
#### Scenario: 统计数据准备
- **WHEN** 统计测试开始前
- **THEN** SHALL 通过 API seed 供应商和模型
- **THEN** SHALL 通过 sql.js seed 多日历史统计数据(覆盖不同供应商、不同模型、不同日期)
#### Scenario: 统计概览验证
- **WHEN** 加载统计页面
- **THEN** 统计摘要卡片 SHALL 显示正确的总请求量
- **THEN** 统计摘要卡片 SHALL 显示正确的活跃模型数和活跃供应商数
- **THEN** 统计表格 SHALL 显示 seed 的数据行
#### Scenario: 统计筛选验证
- **WHEN** 用户选择供应商筛选条件
- **THEN** 统计表格 SHALL 只显示该供应商的数据
- **WHEN** 用户输入模型名称筛选条件
- **THEN** 统计表格 SHALL 只显示匹配模型的数据
### Requirement: 导航 E2E 测试
E2E 测试 SHALL 验证页面导航和侧边栏功能。
#### Scenario: 侧边栏渲染
- **WHEN** 加载任意页面
- **THEN** 侧边栏 SHALL 显示应用名称和导航菜单项
#### Scenario: 页面切换
- **WHEN** 用户点击侧边栏导航菜单项
- **THEN** 页面 SHALL 切换到对应路由并显示正确内容
- **THEN** 当前菜单项 SHALL 高亮
#### Scenario: URL 持久化
- **WHEN** 用户在统计页面刷新浏览器
- **THEN** 页面 SHALL 保持在统计页面
### Requirement: 表单验证 E2E 测试
E2E 测试 SHALL 验证表单的校验行为。
#### Scenario: 供应商表单必填校验
- **WHEN** 用户打开添加供应商对话框并直接点击保存
- **THEN** SHALL 显示所有必填字段的验证错误提示
#### Scenario: URL 格式校验
- **WHEN** 用户在 base_url 字段输入无效 URL 并提交
- **THEN** SHALL 显示 URL 格式错误提示
#### Scenario: 对话框行为
- **WHEN** 用户打开对话框后点击取消
- **THEN** 对话框 SHALL 关闭且表单重置
- **WHEN** 用户再次打开对话框
- **THEN** 表单字段 SHALL 为空
### Requirement: E2E 测试文件组织
E2E 测试 SHALL 按 7 个 spec 文件组织。
#### Scenario: 文件结构
- **WHEN** 查看 `e2e/` 目录
- **THEN** SHALL 包含以下测试文件:`providers.spec.ts``models.spec.ts``stats.spec.ts``navigation.spec.ts``validation.spec.ts`
- **THEN** SHALL 包含以下辅助文件:`fixtures.ts``global-setup.ts``global-teardown.ts`
- **THEN** SHALL NOT 包含已删除的文件:`stats-cards.spec.ts``sidebar.spec.ts``crud.spec.ts`
### Requirement: 新增依赖
E2E 测试 SHALL 引入 sql.js 作为 devDependency。
#### Scenario: sql.js 依赖
- **WHEN** 安装前端依赖
- **THEN** `package.json` 的 devDependencies SHALL 包含 `sql.js`
- **THEN** `package.json` 的 devDependencies SHALL 包含 `@types/sql.js`
- **THEN** sql.js SHALL 仅在 E2E 测试的 seed 逻辑中使用,不影响生产代码