1
0
Files
nex/openspec/specs/e2e-testing/spec.md
lanyuanxiaoyao 5e7267db07 fix(e2e): 修复 10 个被 skip 的 E2E 测试
- 将 playwright.config.ts 的 mkdtemp 替换为固定路径,解决主进程/worker 临时目录不一致问题
- 交换后端 WAL 与迁移执行顺序,确保 sql.js 能读取到完整 schema
- 修复 models.spec.ts 断言使用 exact:true 避免统一模型 ID 列干扰
- 移除全部 10 个 test.skip,26 个 E2E 测试全部通过
2026-04-22 14:32:12 +08:00

226 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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 使用固定临时目录隔离所有文件,确保主进程与 worker 进程使用同一路径,测试结束后自动清理。
#### Scenario: 临时目录创建
- **WHEN** Playwright 配置加载
- **THEN** SHALL 使用固定路径 `path.join(os.tmpdir(), 'nex-e2e')` 作为临时目录
- **THEN** SHALL 先执行 `fs.rmSync(tempDir, { recursive: true, force: true })` 清理残留
- **THEN** SHALL 执行 `fs.mkdirSync` 创建 `log/` 子目录
- **THEN** 临时目录 SHALL 包含 `test.db`(数据库)和 `log/`(日志)子路径
#### Scenario: 临时目录清理
- **WHEN** Playwright 所有测试完成
- **THEN** `globalTeardown` SHALL 使用固定路径 `path.join(os.tmpdir(), 'nex-e2e')` 定位临时目录
- **THEN** SHALL 使用 `fs.rmSync(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 用户流程,断言 SHALL 精确匹配以避免多列文本干扰。
#### Scenario: 前置数据准备
- **WHEN** 模型测试开始前
- **THEN** SHALL 通过 API seed 一个供应商(不通过 UI
#### Scenario: 创建模型并验证
- **WHEN** 用户展开供应商行、填写模型表单并提交
- **THEN** 对话框 SHALL 关闭
- **THEN** 新模型 SHALL 出现在展开行的模型表格中
- **THEN** 模型表格 SHALL 显示统一模型 ID`provider_id/model_name` 格式)
- **THEN** 验证断言 SHALL 使用 `{ exact: true }` 精确匹配模型名称文本,避免匹配到"统一模型 ID"列
#### Scenario: 编辑模型并验证
- **WHEN** 用户点击模型编辑按钮、修改并提交
- **THEN** 对话框 SHALL 关闭
- **THEN** 模型表格 SHALL 显示更新后的数据
- **THEN** 验证断言 SHALL 使用 `{ exact: true }` 精确匹配模型名称文本
#### Scenario: 删除模型并验证
- **WHEN** 用户点击模型删除按钮并确认
- **THEN** 该模型 SHALL 从模型表格中消失
- **THEN** 验证断言 SHALL 使用 `{ exact: true }` 精确匹配模型名称文本
### Requirement: 统计页面 E2E 测试
E2E 测试 SHALL 验证统计页面的数据展示和筛选功能。所有测试用例 SHALL NOT 被跳过。
#### Scenario: 统计数据准备
- **WHEN** 统计测试开始前
- **THEN** SHALL 通过 API seed 供应商和模型
- **THEN** SHALL 通过 sql.js seed 多日历史统计数据(覆盖不同供应商、不同模型、不同日期)
#### Scenario: 统计概览验证
- **WHEN** 加载统计页面
- **THEN** 统计摘要卡片 SHALL 显示正确的总请求量
- **THEN** 统计摘要卡片 SHALL 显示正确的活跃模型数和活跃供应商数
- **THEN** 统计表格 SHALL 显示 seed 的数据行
- **THEN** 页面 SHALL 渲染趋势图表区域(标题包含"请求趋势"
#### Scenario: 统计筛选验证
- **WHEN** 页面加载完成
- **THEN** 页面 SHALL 显示筛选栏(供应商下拉选择和模型名称输入框)
- **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 逻辑中使用,不影响生产代码