1
0

feat: 完成前端重构,采用 Ant Design 5 和完整测试体系

- 采用 Ant Design 5 作为 UI 组件库,替换自定义组件
- 集成 React Router v7 提供路由导航
- 使用 TanStack Query v5 管理数据获取和缓存
- 建立 Vitest + React Testing Library 测试体系
- 添加 Playwright E2E 测试覆盖
- 使用 MSW mock API 响应
- 配置 TypeScript strict 模式
- 采用 SCSS Modules 组织样式
- 更新 OpenSpec 规格以反映前端架构变更
- 归档 frontend-refactor 变更记录
This commit is contained in:
2026-04-16 11:21:48 +08:00
parent c17903dcbc
commit 9359ca7f62
61 changed files with 4588 additions and 1095 deletions

View File

@@ -8,98 +8,96 @@ TBD - 提供供应商、模型配置和用量统计的前端管理界面
### Requirement: 提供供应商管理页面
前端 SHALL 提供用于管理供应商配置的网页
前端 SHALL 使用 Ant Design 组件提供供应商管理页面
#### Scenario: 显示供应商列表
- **WHEN** 加载供应商管理页面
- **THEN** 前端 SHALL 显示所有已配置供应商的列表
- **THEN** 每个供应商 SHALL 显示 id, name, base_url 和 enabled 状态
- **THEN** API Key SHALL 被掩码
- **THEN** 前端 SHALL 使用 Ant Design Table 显示所有已配置供应商
- **THEN** 每个供应商 SHALL 显示 namebase_url 和 enabled 状态(使用 Tag 组件)
- **THEN** API Key SHALL 被脱敏显示(掩码处理)
- **THEN** 表格 SHALL 支持展开行以显示关联模型
#### Scenario: 添加新供应商
- **WHEN** 用户点击"添加供应商"按钮
- **THEN** 前端 SHALL 显示输入供应商详情的表单
- **THEN** 表单 SHALL 包含 id, name, api_key, base_url 字段
- **THEN** 前端 SHALL 使用 Ant Design Modal + Form 显示输入表单
- **THEN** 表单 SHALL 包含 idnameapi_keybase_url 字段,带校验规则
- **WHEN** 用户提交包含有效数据的表单
- **THEN** 前端 SHALL `/api/providers` 发送 POST 请求
- **THEN** 前端 SHALL 刷新供应商列表
- **THEN** 前端 SHALL 通过 useMutation 调用创建 API
- **THEN** 成功后 SHALL 关闭 Modal 并刷新供应商列表
- **THEN** 失败 SHALL 使用 message.error() 提示
#### Scenario: 编辑现有供应商
- **WHEN** 用户点击供应商的"编辑"按钮
- **THEN** 前端 SHALL 显示预填充供应商当前数据的表单
- **THEN** 前端 SHALL 使用 Ant Design Modal + Form 显示预填充数据的表单
- **WHEN** 用户提交包含更新数据的表单
- **THEN** 前端 SHALL `/api/providers/:id` 发送 PUT 请求
- **THEN** 前端 SHALL 刷新供应商列表
- **THEN** 前端 SHALL 通过 useMutation 调用更新 API
- **THEN** 成功后 SHALL 关闭 Modal 并刷新供应商列表
#### Scenario: 删除供应商
- **WHEN** 用户点击供应商的"删除"按钮
- **THEN** 前端 SHALL 提示确认
- **THEN** 前端 SHALL 使用 Ant Design Popconfirm 弹出确认
- **WHEN** 用户确认删除
- **THEN** 前端 SHALL `/api/providers/:id` 发送 DELETE 请求
- **THEN** 前端 SHALL 刷新供应商列表
- **THEN** 前端 SHALL 通过 useMutation 调用删除 API
- **THEN** 成功后 SHALL 刷新供应商列表
### Requirement: 提供模型管理界面
前端 SHALL 在供应商页面中提供管理模型配置的界面
前端 SHALL 在供应商页面展开行中提供模型管理
#### Scenario: 显示供应商的模型
- **WHEN** 选择或展开供应商
- **WHEN** 展开供应商
- **THEN** 前端 SHALL 显示该供应商的模型列表
- **THEN** 每个模型 SHALL 显示 model_name 和 enabled 状态
#### Scenario: 为供应商添加模型
- **WHEN** 用户点击供应商的"添加模型"
- **THEN** 前端 SHALL 显示输入 model_name 的表单
- **WHEN** 用户在展开行中点击"添加模型"
- **THEN** 前端 SHALL 显示 Ant Design Modal + Form
- **THEN** provider_id SHALL 自动关联当前供应商
- **WHEN** 用户提交表单
- **THEN** 前端 SHALL `/api/models` 发送 POST 请求,携带 provider_id
- **THEN** 前端 SHALL 刷新模型列表
- **THEN** 前端 SHALL 通过 useMutation 调用创建 API
- **THEN** 成功后 SHALL 刷新模型列表
#### Scenario: 编辑模型
- **WHEN** 用户点击模型的"编辑"
- **THEN** 前端 SHALL 显示编辑 model_name 的表单
- **THEN** 前端 SHALL 显示编辑表单
- **WHEN** 用户提交表单
- **THEN** 前端 SHALL `/api/models/:id` 发送 PUT 请求
- **THEN** 前端 SHALL 刷新模型列表
- **THEN** 前端 SHALL 通过 useMutation 调用更新 API
- **THEN** 成功后 SHALL 刷新模型列表
#### Scenario: 删除模型
- **WHEN** 用户点击模型的"删除"
- **THEN** 前端 SHALL 提示确认
- **THEN** 前端 SHALL 使用 Popconfirm 弹出确认
- **WHEN** 用户确认删除
- **THEN** 前端 SHALL `/api/models/:id` 发送 DELETE 请求
- **THEN** 前端 SHALL 刷新模型列表
- **THEN** 前端 SHALL 通过 useMutation 调用删除 API
- **THEN** 成功后 SHALL 刷新模型列表
### Requirement: 提供统计查看页面
前端 SHALL 提供查看用量统计的页面。
前端 SHALL 使用 Ant Design 组件提供统计查看页面。
#### Scenario: 显示统计概览
- **WHEN** 加载统计页面
- **THEN** 前端 SHALL 显示所有供应商和模型的统计
- **THEN** 前端 SHALL 按供应商和模型分组显示请求计数
- **THEN** 前端 SHALL 使用 Ant Design Table 显示统计数据
- **THEN** 统计数据 SHALL 按供应商和模型显示请求计数
#### Scenario: 按供应商过滤统计
- **WHEN** 用户从下拉菜单选择供应商
- **THEN** 前端 SHALL 过滤统计,仅显示该供应商的数据
#### Scenario: 按模型过滤统计
- **WHEN** 用户从下拉菜单选择模型
- **THEN** 前端 SHALL 过滤统计,仅显示该模型的数据
- **WHEN** 用户从 Ant Design Select 选择供应商
- **THEN** 前端 SHALL 自动查询并过滤统计
#### Scenario: 按日期范围过滤统计
- **WHEN** 用户选择开始和结束日期
- **THEN** 前端 SHALL 过滤统计,仅显示该范围内的数据
- **WHEN** 用户使用 Ant Design DatePicker.RangePicker 选择日期范围
- **THEN** 前端 SHALL 自动查询并过滤统计
### Requirement: 优雅处理 API 错误
@@ -108,8 +106,8 @@ TBD - 提供供应商、模型配置和用量统计的前端管理界面
#### Scenario: API 请求失败
- **WHEN** API 请求失败网络错误、4xx、5xx
- **THEN** 前端 SHALL 向用户显示错误消息
- **THEN** 错误消息 SHALL 具有描述性和可操作性
- **THEN** 前端 SHALL 使用 Ant Design 的 message.error() 显示全局错误提示
- **THEN** 错误消息 SHALL 具有描述性
#### Scenario: 验证错误
@@ -119,64 +117,98 @@ TBD - 提供供应商、模型配置和用量统计的前端管理界面
### Requirement: 提供响应式布局
前端 SHALL 提供适应不同屏幕尺寸的响应式布局。
前端 SHALL 使用 Ant Design Layout 提供顶部导航布局。
#### Scenario: 桌面布局
- **WHEN** 在桌面屏幕上查看前端
- **THEN** 布局 SHALL 使用多列设计以高效利用空间
- **THEN** 布局 SHALL 使用 Ant Design Layout.Header + Menuhorizontal 模式)
- **THEN** 导航菜单 SHALL 在顶部水平排列
#### Scenario: 移动布局
#### Scenario: 页面内容区域
- **WHEN** 在移动屏幕上查看前端
- **THEN** 布局 SHALL 适应为单列设计
- **THEN** 所有功能 SHALL 保持可访问
- **WHEN** 显示页面内容
- **THEN** 内容区域 SHALL 有合理的最大宽度和内边距
- **THEN** 页面之间 SHALL 通过 React Router Outlet 渲染
### Requirement: 使用无组件库的最小 UI
前端 SHALL 使用自定义组件,不使用外部 UI 库。
前端 SHALL 使用 Ant Design 5 作为 UI 组件库。
#### Scenario: 自定义组件
#### Scenario: Ant Design 组件使用
- **WHEN** 实现前端
- **THEN** 它 SHALL 使用自定义 HTML/CSS 组件
- **THEN** 它 SHALL NOT 使用外部 UI 库,如 Ant Design、Material-UI 或 shadcn/ui
- **THEN** 它 SHALL 使用 Ant Design 5 组件Table、Form、Modal、Menu、Tag、Popconfirm、DatePicker、Button、Select 等)
- **THEN** 它 SHALL 使用 @ant-design/icons 提供图标
- **THEN** 图标 SHALL 优先使用图标库中已有的图标
#### Scenario: SCSS 样式
#### Scenario: Ant Design 默认主题
- **WHEN** 配置 Ant Design 主题
- **THEN** 前端 SHALL 使用 Ant Design 默认主题,不进行自定义主题色配置
- **THEN** 前端 SHALL NOT 支持暗色模式切换
### Requirement: SCSS 样式
前端样式 SHALL 全部使用 SCSS禁止使用纯 CSS 文件。
#### Scenario: 样式文件规范
- **WHEN** 编写样式
- **THEN** 前端 SHALL 使用 SCSS 进行样式设计
- **THEN** 样式 SHALL 有组织且可维护
- **THEN** 前端 SHALL 使用 SCSS*.scss 文件)
- **THEN** 前端 SHALL NOT 使用纯 CSS 文件(*.css
- **THEN** 前端 SHALL NOT 使用 CSS-in-JS 方案
#### Scenario: SCSS Modules 使用
- **WHEN** 编写组件级样式
- **THEN** 前端 SHALL 使用 SCSS Modules*.module.scss
- **THEN** 全局样式仅保留 index.scss 做 reset 和 CSS variables
### Requirement: 提供导航
前端 SHALL 在不同页面间提供导航。
前端 SHALL 使用 React Router v7 提供导航。
#### Scenario: 导航到供应商页面
#### Scenario: 路由配置
- **WHEN** 用户点击导航中的"供应商"
- **THEN** 前端 SHALL 导航到供应商管理页面
- **WHEN** 应用启动
- **THEN** 前端 SHALL 使用 React Router v7 Library 模式BrowserRouter
- **THEN** `/providers` 路径 SHALL 显示供应商管理页面
- **THEN** `/stats` 路径 SHALL 显示用量统计页面
- **THEN** `/` 路径 SHALL 重定向到 `/providers`
- **THEN** 不存在的路径 SHALL 显示 404 页面
#### Scenario: 导航到统计页面
#### Scenario: 导航菜单
- **WHEN** 用户点击导航中的"统计"
- **THEN** 前端 SHALL 导航到统计查看页面
- **WHEN** 用户点击导航中的"供应商管理"
- **THEN** 前端 SHALL 导航到 `/providers` 并高亮当前菜单项
- **WHEN** 用户点击导航中的"用量统计"
- **THEN** 前端 SHALL 导航到 `/stats` 并高亮当前菜单项
#### Scenario: URL 同步
- **WHEN** 用户在供应商页面刷新浏览器
- **THEN** 前端 SHALL 保持在供应商页面URL 持久化)
- **WHEN** 用户使用浏览器后退按钮
- **THEN** 前端 SHALL 正确导航到上一个页面
### Requirement: 使用 React 和 TypeScript
前端 SHALL 使用 React 和 TypeScript 实现。
前端 SHALL 使用 React 和 TypeScript 实现,遵循 strict 模式
#### Scenario: TypeScript 使用
#### Scenario: TypeScript strict 模式
- **WHEN** 编写前端代码
- **THEN** 它 SHALL 使用 TypeScript 提供类型安全
- **THEN** 所有组件和函数 SHALL 具有适当的类型定义
- **THEN** TypeScript 配置 SHALL 开启 strict: true
- **THEN** TypeScript 配置 SHALL 开启 noUncheckedIndexedAccess
- **THEN** 所有代码 SHALL NOT 使用 any 类型
- **THEN** tsconfig SHALL 合并为单文件(不使用 project references
#### Scenario: React 组件
#### Scenario: React 函数组件
- **WHEN** 实现 UI
- **THEN** 它 SHALL 使用 React 函数组件
- **THEN** 它 SHALL 使用 React hooks 进行状态管理
- **THEN** 它 SHALL 使用自定义 Hooks 封装业务逻辑
### Requirement: 使用 Vite 构建
@@ -194,15 +226,46 @@ TBD - 提供供应商、模型配置和用量统计的前端管理界面
### Requirement: 与后端 API 通信
前端 SHALL 使用 fetch 或类似方法与后端 API 通信。
前端 SHALL 使用 TanStack Query v5 和统一 API 客户端与后端通信。
#### Scenario: API 基础 URL 配置
- **WHEN** 前端发起 API 请求
- **THEN** SHALL 使用配置的后端 API 基础 URL默认http://localhost:9826
- **THEN** 开发环境 SHALL 通过 Vite proxy 转发 /api 请求到后端
- **THEN** 生产环境 SHALL 使用环境变量 VITE_API_BASE 配置基础 URL
- **THEN** 前端 SHALL NOT 硬编码 API 基础 URL
#### Scenario: API 客户端封装
#### Scenario: 统一 API 客户端
- **WHEN** 进行 API 调用
- **THEN** 它们 SHALL 封装在专用的 API 客户端模块中
- **THEN** 错误处理 SHALL 集中化
- **THEN** 所有调用 SHALL 通过 api/client.ts 的 request<T>() 方法
- **THEN** 错误处理 SHALL 统一抛出 ApiError包含 status 和 message
- **THEN** 开发环境 SHALL 使用 Vite proxy 转发 API 请求
#### Scenario: 字段名转换
- **WHEN** 接收后端 API 响应
- **THEN** API 层 SHALL 将 snake_case 字段转换为 camelCase
- **WHEN** 发送请求到后端 API
- **THEN** API 层 SHALL 将 camelCase 字段转换为 snake_case
- **THEN** hooks 和组件 SHALL 仅使用 camelCase 字段
#### Scenario: TanStack Query 数据管理
- **WHEN** 页面加载数据
- **THEN** 前端 SHALL 使用 TanStack Query 的 useQuery hook
- **THEN** 前端 SHALL 自动缓存请求结果
- **THEN** 前端 SHALL 自动处理加载和错误状态
#### Scenario: TanStack Query 写操作
- **WHEN** 用户执行创建、更新或删除操作
- **THEN** 前端 SHALL 使用 TanStack Query 的 useMutation hook
- **THEN** 操作成功后 SHALL 自动失效相关查询缓存
- **THEN** 操作失败 SHALL 使用 Ant Design message.error() 显示错误提示
#### Scenario: 错误提示
- **WHEN** API 请求失败网络错误、4xx、5xx
- **THEN** 前端 SHALL 使用 Ant Design 的 message.error() 显示全局错误提示
- **THEN** 错误消息 SHALL 具有描述性

View File

@@ -0,0 +1,188 @@
# 前端测试体系
## Purpose
建立前端测试体系,覆盖单元测试、组件测试和 E2E 测试,确保前端代码质量和功能正确性。
## Requirements
### Requirement: 建立前端单元测试体系
前端 SHALL 使用 Vitest 建立单元测试体系,覆盖 API 层和自定义 Hooks。
#### Scenario: API 客户端单测
- **WHEN** 运行 api/client.ts 的单元测试
- **THEN** SHALL 覆盖 request<T>() 的正常响应解析
- **THEN** SHALL 覆盖 HTTP 错误状态码4xx、5xx的 ApiError 抛出
- **THEN** SHALL 覆盖网络错误的错误处理
- **THEN** SHALL 覆盖 snake_case → camelCase 响应字段转换
- **THEN** SHALL 覆盖 camelCase → snake_case 请求字段转换
#### Scenario: API 模块单测
- **WHEN** 运行 api/providers.ts、api/models.ts、api/stats.ts 的单元测试
- **THEN** SHALL 覆盖所有 CRUD 函数的正确调用
- **THEN** SHALL 覆盖参数传递和返回值类型
#### Scenario: 自定义 Hooks 单测
- **WHEN** 运行 hooks/ 目录下的单元测试
- **THEN** SHALL 使用 @tanstack/react-query 的 renderHook 测试工具
- **THEN** SHALL 覆盖 useQuery 数据获取(成功和失败)
- **THEN** SHALL 覆盖 useMutation 写操作后的缓存失效
- **THEN** SHALL 使用独立的测试 QueryClient关闭 retry 和缓存)
### Requirement: 建立前端组件测试体系
前端 SHALL 使用 React Testing Library 建立组件测试体系。
#### Scenario: ProviderTable 组件测试
- **WHEN** 运行 ProviderTable 组件测试
- **THEN** SHALL 验证供应商列表正确渲染
- **THEN** SHALL 验证点击"添加"按钮弹出 Modal
- **THEN** SHALL 验证点击"编辑"按钮弹出预填充的 Modal
- **THEN** SHALL 验证删除操作触发 Popconfirm 确认
#### Scenario: ProviderForm 组件测试
- **WHEN** 运行 ProviderForm 组件测试
- **THEN** SHALL 验证表单校验规则必填字段、URL 格式)
- **THEN** SHALL 验证提交成功后调用 onSave 回调
- **THEN** SHALL 验证编辑模式下字段预填充
#### Scenario: StatsTable 组件测试
- **WHEN** 运行 StatsTable 组件测试
- **THEN** SHALL 验证筛选条件交互(供应商选择、日期范围)
- **THEN** SHALL 验证统计数据正确展示
### Requirement: 建立 E2E 测试体系
前端 SHALL 使用 Playwright 建立端到端测试体系。
#### Scenario: 供应商管理 E2E 测试
- **WHEN** 运行供应商管理的 E2E 测试
- **THEN** SHALL 测试完整的供应商创建流程
- **THEN** SHALL 测试供应商编辑流程
- **THEN** SHALL 测试供应商删除流程(含确认弹窗)
- **THEN** SHALL 测试供应商展开后的模型管理
#### Scenario: 统计查询 E2E 测试
- **WHEN** 运行统计查询的 E2E 测试
- **THEN** SHALL 测试页面加载和默认数据展示
- **THEN** SHALL 测试按供应商筛选
- **THEN** SHALL 测试按日期范围筛选
### Requirement: 使用 MSW 进行 API Mock
前端测试 SHALL 使用 MSW (Mock Service Worker) 模拟后端 API 响应。
#### Scenario: 测试环境 MSW 配置
- **WHEN** 初始化测试环境
- **THEN** SHALL 配置 MSW server 处理所有 /api/* 请求
- **THEN** SHALL 在 setup.ts 中全局启动和清理 MSW server
#### Scenario: Mock 响应定义
- **WHEN** 编写需要 API 交互的测试
- **THEN** SHALL 使用 MSW handler 定义期望的 API 响应
- **THEN** SHALL 支持成功和失败两种响应场景
- **THEN** SHALL 在每个测试用例后重置 handler
### Requirement: 测试文件组织
前端测试文件 SHALL 按层级组织在 src/__tests__/ 目录下。
#### Scenario: 目录结构
- **WHEN** 组织测试文件
- **THEN** src/__tests__/setup.ts SHALL 包含全局测试配置
- **THEN** src/__tests__/api/ SHALL 包含 API 层单测
- **THEN** src/__tests__/hooks/ SHALL 包含 Hooks 单测
- **THEN** src/__tests__/components/ SHALL 包含组件测试
- **THEN** e2e/ 目录项目根目录下SHALL 包含 Playwright E2E 测试
### Requirement: Vitest 配置
前端 SHALL 配置 Vitest 作为测试运行器。
#### Scenario: 测试环境配置
- **WHEN** 运行 vitest
- **THEN** SHALL 使用 jsdom 作为测试环境
- **THEN** SHALL 配置 setupFiles 指向 src/__tests__/setup.ts
- **THEN** SHALL 配置路径别名 @/ 与 tsconfig 一致
- **THEN** SHALL 配置 @testing-library/jest-dom 匹配器
#### Scenario: 覆盖率配置
- **WHEN** 运行覆盖率报告
- **THEN** SHALL 使用 @vitest/coverage-v8 提供者
- **THEN** SHALL 覆盖 src/ 下的所有源文件
### Requirement: 建立前端单元测试覆盖
前端代码 SHALL 建立单元测试覆盖,纳入整体测试覆盖率统计。
#### Scenario: API 层测试覆盖
- **WHEN** 运行前端 API 层的单元测试
- **THEN** SHALL 覆盖 api/client.ts 的请求封装和字段转换逻辑
- **THEN** SHALL 覆盖 api/providers.ts、api/models.ts、api/stats.ts 的所有函数
- **THEN** SHALL 使用 MSW mock API 响应
#### Scenario: Hooks 测试覆盖
- **WHEN** 运行前端 Hooks 的单元测试
- **THEN** SHALL 覆盖 useProviders、useModels、useStats 的查询和变更逻辑
- **THEN** SHALL 验证缓存失效和自动刷新行为
### Requirement: 建立前端组件测试覆盖
前端 SHALL 使用 React Testing Library 建立组件测试覆盖。
#### Scenario: 页面组件测试覆盖
- **WHEN** 运行前端组件测试
- **THEN** SHALL 覆盖 ProviderTable 的列表渲染和交互操作
- **THEN** SHALL 覆盖 ProviderForm 的表单校验和提交
- **THEN** SHALL 覆盖 ModelForm 的表单校验和提交
- **THEN** SHALL 覆盖 StatsTable 的筛选和数据展示
### Requirement: 建立前端 E2E 测试覆盖
前端 SHALL 使用 Playwright 建立 E2E 测试覆盖。
#### Scenario: 供应商管理 E2E 覆盖
- **WHEN** 运行 E2E 测试
- **THEN** SHALL 覆盖供应商创建、编辑、删除的完整用户流程
- **THEN** SHALL 覆盖模型创建、编辑、删除的完整用户流程
#### Scenario: 统计查询 E2E 覆盖
- **WHEN** 运行 E2E 测试
- **THEN** SHALL 覆盖统计页面的加载和筛选查询流程
- **THEN** SHALL 覆盖页面间的导航切换
### Requirement: 前端测试集成到构建流程
前端测试 SHALL 集成到项目的构建和验证流程中。
#### Scenario: 运行前端测试命令
- **WHEN** 在 frontend/ 目录执行测试命令
- **THEN** SHALL 运行所有 Vitest 单元测试和组件测试
- **THEN** SHALL 显示测试结果
- **THEN** SHALL 在测试失败时返回非零退出码
#### Scenario: 运行前端 E2E 测试命令
- **WHEN** 在 frontend/ 目录执行 E2E 测试命令
- **THEN** SHALL 启动 Playwright 运行 E2E 测试
- **THEN** SHALL 在测试失败时返回非零退出码

View File

@@ -104,3 +104,65 @@
- **THEN** SHALL 运行测试并生成覆盖率报告
- **THEN** SHALL 检查覆盖率是否达标
- **THEN** SHALL 在覆盖率不足时返回非零退出码
### Requirement: 建立前端单元测试覆盖
前端代码 SHALL 建立单元测试覆盖,纳入整体测试覆盖率统计。
#### Scenario: API 层测试覆盖
- **WHEN** 运行前端 API 层的单元测试
- **THEN** SHALL 覆盖 api/client.ts 的请求封装和字段转换逻辑
- **THEN** SHALL 覆盖 api/providers.ts、api/models.ts、api/stats.ts 的所有函数
- **THEN** SHALL 使用 MSW mock API 响应
#### Scenario: Hooks 测试覆盖
- **WHEN** 运行前端 Hooks 的单元测试
- **THEN** SHALL 覆盖 useProviders、useModels、useStats 的查询和变更逻辑
- **THEN** SHALL 验证缓存失效和自动刷新行为
### Requirement: 建立前端组件测试覆盖
前端 SHALL 使用 React Testing Library 建立组件测试覆盖。
#### Scenario: 页面组件测试覆盖
- **WHEN** 运行前端组件测试
- **THEN** SHALL 覆盖 ProviderTable 的列表渲染和交互操作
- **THEN** SHALL 覆盖 ProviderForm 的表单校验和提交
- **THEN** SHALL 覆盖 ModelForm 的表单校验和提交
- **THEN** SHALL 覆盖 StatsTable 的筛选和数据展示
### Requirement: 建立前端 E2E 测试覆盖
前端 SHALL 使用 Playwright 建立 E2E 测试覆盖。
#### Scenario: 供应商管理 E2E 覆盖
- **WHEN** 运行 E2E 测试
- **THEN** SHALL 覆盖供应商创建、编辑、删除的完整用户流程
- **THEN** SHALL 覆盖模型创建、编辑、删除的完整用户流程
#### Scenario: 统计查询 E2E 覆盖
- **WHEN** 运行 E2E 测试
- **THEN** SHALL 覆盖统计页面的加载和筛选查询流程
- **THEN** SHALL 覆盖页面间的导航切换
### Requirement: 前端测试集成到构建流程
前端测试 SHALL 集成到项目的构建和验证流程中。
#### Scenario: 运行前端测试命令
- **WHEN** 在 frontend/ 目录执行测试命令
- **THEN** SHALL 运行所有 Vitest 单元测试和组件测试
- **THEN** SHALL 显示测试结果
- **THEN** SHALL 在测试失败时返回非零退出码
#### Scenario: 运行前端 E2E 测试命令
- **WHEN** 在 frontend/ 目录执行 E2E 测试命令
- **THEN** SHALL 启动 Playwright 运行 E2E 测试
- **THEN** SHALL 在测试失败时返回非零退出码