Explorar o código

docs(e2e): 创建 Story 11.7 - Channel 管理 Page Object(可选)

- 创建完整的 Story 文档,包含 9 个验收标准和 8 个任务
- 详细分析 Channel 实体结构和 ChannelManagement UI 组件
- 提供完整的 Page Object 类设计参考
- 说明可选性原因:Channel 是订单的可选字段,优先级较低
- 更新 sprint-status.yaml 标记 Story 11.7 为 ready-for-dev

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname hai 5 días
pai
achega
a78c41412b

+ 611 - 0
_bmad-output/implementation-artifacts/11-7-channel-page-object.md

@@ -0,0 +1,611 @@
+# Story 11.7: Channel 管理 Page Object(可选)
+
+Status: ready-for-dev
+
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
+
+## Story
+
+作为测试开发者,
+我想要创建渠道管理的 Page Object(可选),
+以便为渠道管理功能的 E2E 测试封装页面元素和操作方法(如需要)。
+
+## Acceptance Criteria
+
+1. **AC1: 创建 ChannelManagementPage 文件(可选)**
+   - 文件路径: `web/tests/e2e/pages/admin/channel-management.page.ts`
+   - 使用 TypeScript 编写
+   - 导入 Playwright 的 Page、Locator、Response 等类型
+   - 定义完整的类结构和方法签名
+
+2. **AC2: 定义页面选择器**
+   - 定义页面级选择器:pageTitle, createChannelButton, searchInput, searchButton, channelTable
+   - 定义对话框选择器:createDialogTitle, editDialogTitle
+   - 定义表单字段选择器:channelNameInput, channelTypeInput, contactPersonInput, contactPhoneInput, descriptionInput
+   - 定义按钮选择器:createSubmitButton, updateSubmitButton, cancelButton, confirmDeleteButton
+   - 所有选择器使用 data-testid 定位(遵循测试标准)
+
+3. **AC3: 实现导航和基础验证方法**
+   - 实现 `goto()` 方法:导航到渠道管理页面
+   - 实现 `expectToBeVisible()` 方法:验证页面关键元素可见
+   - 等待页面标题和表格数据加载完成
+
+4. **AC4: 实现对话框操作方法**
+   - 实现 `openCreateDialog()` 方法:打开创建渠道对话框
+   - 实现 `openEditDialog(channelName)` 方法:打开编辑渠道对话框
+   - 实现 `openDeleteDialog(channelName)` 方法:打开删除确认对话框
+   - 实现 `cancelDialog()` 方法:取消对话框
+   - 实现 `waitForDialogClosed()` 方法:等待对话框关闭
+
+5. **AC5: 实现表单操作方法**
+   - 实现 `fillChannelForm(data)` 方法:填写渠道表单
+   - 实现 `submitForm()` 方法:提交表单并捕获网络响应
+   - 表单数据接口包含:channelName, channelType?, contactPerson?, contactPhone?, description?
+   - 支持创建和编辑两种表单模式
+
+6. **AC6: 实现 CRUD 操作方法**
+   - 实现 `createChannel(data)` 方法:创建渠道(完整流程)
+   - 实现 `editChannel(channelName, data)` 方法:编辑渠道(完整流程)
+   - 实现 `deleteChannel(channelName)` 方法:删除渠道(使用 API 直接删除)
+   - 删除成功后刷新页面确保列表更新
+
+7. **AC7: 实现搜索和验证方法**
+   - 实现 `searchByName(name)` 方法:按渠道名称搜索
+   - 实现 `channelExists(channelName)` 方法:验证渠道是否存在(使用精确匹配)
+   - 搜索后验证结果
+
+8. **AC8: 定义数据接口和类型**
+   - 定义 ChannelData 接口(包含所有渠道字段)
+   - 定义 NetworkResponse 接口(捕获网络响应)
+   - 定义 FormSubmitResult 接口(表单提交结果)
+   - 所有接口使用 JSDoc 注释
+
+9. **AC9: 代码质量标准**
+   - TypeScript 类型检查通过(无类型错误)
+   - 所有公共方法有完整的 JSDoc 注释
+   - 代码结构与 PlatformManagementPage 和 CompanyManagementPage 保持一致
+
+## Tasks / Subtasks
+
+- [ ] 任务 1: 创建文件和基础结构 (AC: 1, 8, 9)
+  - [ ] 创建文件 `web/tests/e2e/pages/admin/channel-management.page.ts`
+  - [ ] 导入 Playwright 类型和依赖
+  - [ ] 定义类结构:`export class ChannelManagementPage`
+  - [ ] 定义接口:ChannelData, NetworkResponse, FormSubmitResult
+
+- [ ] 任务 2: 定义页面选择器 (AC: 2, 9)
+  - [ ] 定义页面级选择器(pageTitle, createChannelButton, searchInput, searchButton, channelTable)
+  - [ ] 定义对话框选择器(createDialogTitle, editDialogTitle)
+  - [ ] 定义表单字段选择器(使用 data-testid)
+  - [ ] 定义按钮选择器(createSubmitButton, updateSubmitButton, cancelButton, confirmDeleteButton)
+
+- [ ] 任务 3: 实现导航和基础验证方法 (AC: 3, 9)
+  - [ ] 实现 `goto()` 方法
+  - [ ] 实现 `expectToBeVisible()` 方法
+  - [ ] 添加 JSDoc 注释
+
+- [ ] 任务 4: 实现对话框操作方法 (AC: 4, 9)
+  - [ ] 实现 `openCreateDialog()` 方法
+  - [ ] 实现 `openEditDialog(channelName)` 方法
+  - [ ] 实现 `openDeleteDialog(channelName)` 方法
+  - [ ] 实现 `cancelDialog()` 和 `waitForDialogClosed()` 方法
+  - [ ] 添加 JSDoc 注释
+
+- [ ] 任务 5: 实现表单操作方法 (AC: 5, 9)
+  - [ ] 实现 `fillChannelForm(data)` 方法
+  - [ ] 实现 `submitForm()` 方法(包含网络响应捕获)
+  - [ ] 处理 Toast 消息验证
+  - [ ] 添加 JSDoc 注释
+
+- [ ] 任务 6: 实现 CRUD 操作方法 (AC: 6, 9)
+  - [ ] 实现 `createChannel(data)` 方法
+  - [ ] 实现 `editChannel(channelName, data)` 方法
+  - [ ] 实现 `deleteChannel(channelName)` 方法(API 直接删除)
+  - [ ] 添加 JSDoc 注释
+
+- [ ] 任务 7: 实现搜索和验证方法 (AC: 7, 9)
+  - [ ] 实现 `searchByName(name)` 方法
+  - [ ] 实现 `channelExists(channelName)` 方法
+  - [ ] 添加 JSDoc 注释
+
+- [ ] 任务 8: TypeScript 类型检查和代码质量验证 (AC: 9)
+  - [ ] 运行 `pnpm typecheck` 检查类型
+  - [ ] 确保无类型错误
+  - [ ] 验证代码风格符合项目标准
+
+## Dev Notes
+
+### Epic 11 背景和目标
+
+**Epic 11: 基础配置管理测试 (Epic F)**
+
+为平台、公司、渠道配置管理编写 E2E 测试,为后续用户管理和跨端测试提供必要的测试数据。
+
+**实体关系链:**
+```
+Platform (平台)
+  ↓ 1:N
+Company (公司) - 必须 platformId
+  ↓ 1:N
+Order (订单) - 必须 companyId
+  ↓
+Channel (渠道) - 订单的可选条件
+```
+
+**Story 11.7 可选性的原因:**
+
+1. **Channel 是订单的可选字段**
+   - 在订单创建表单中,`channelId` 是可选的(`optional()`)
+   - 订单可以不选择渠道直接创建
+   - 不像 Platform 和 Company,它们是创建订单的必要条件
+
+2. **优先级较低**
+   - Story 11.1-11.6 已经覆盖了 Platform 和 Company 的完整测试
+   - 这些是阻塞后续 Epic(用户管理、跨端测试)的核心功能
+   - Channel 管理测试可以延后实现
+
+3. **实现时机**
+   - 如果订单管理测试(Epic 10)发现需要渠道选择功能验证,再实现此 Story
+   - 或者当有额外时间时补充渠道管理测试
+   - 当前 Epic 11 的核心目标(Platform + Company)已经完成
+
+**Channel 实体关键特点:**
+- 渠道名称全局唯一(唯一索引:`idx_channel_name`)
+- 包含渠道类型(channelType)- 区分不同渠道来源
+- 联系人信息(contactPerson, contactPhone)
+- 有状态字段(status: 1-启用,0-禁用)
+- 删除已禁用的渠道才能删除
+- 独立实体,不依赖其他表
+
+### 架构模式和约束
+
+**参考 Story 11.1 (Platform) 和 Story 11.4 (Company) 的 Page Object 模式:**
+
+1. **选择器策略:**
+   - 优先使用 `data-testid` 定位元素
+   - 使用 role + name 组合作为备用(如按钮)
+   - 避免使用不稳定的 CSS 类选择器
+
+2. **网络响应捕获:**
+   - 使用 `waitForResponse()` 捕获特定 API 响应
+   - 监听 `createChannel`、`updateChannel`、`getAllChannels` 等 API
+   - 使用 `Promise.race()` 添加超时保护
+
+3. **Toast 消息验证:**
+   - Toast 使用 Sonner UI 库:`[data-sonner-toast][data-type="error"]` 和 `[data-type="success"]`
+   - Toast 可能出现得很快,需要主动等待
+   - 同时验证 API 响应作为主要验证方式
+
+4. **删除操作策略:**
+   - 使用 API 直接删除,绕过 UI 的不可靠性
+   - 使用 `page.evaluate()` 在浏览器上下文中执行 fetch
+   - 删除成功后刷新页面确保列表更新
+
+### Channel 实体结构
+
+**Channel Entity (来自 channel.entity.ts):**
+```typescript
+{
+  id: number;              // 主键ID (channel_id)
+  channelName: string;     // 渠道名称(必填,最大100字符,全局唯一)
+  channelType: string;     // 渠道类型(必填,最大50字符,默认空字符串)
+  contactPerson: string;   // 联系人(必填,最大50字符,默认空字符串)
+  contactPhone: string;    // 联系电话(必填,最大20字符,默认空字符串)
+  description?: string;    // 描述(可选,text类型)
+  status: number;          // 状态:1-正常,0-禁用(默认1)
+  createTime: Date;        // 创建时间
+  updateTime: Date;        // 更新时间
+}
+```
+
+**唯一约束:**
+- 渠道名称全局唯一:`idx_channel_name (channelName)`
+
+**与 Company/Platform 的差异:**
+- Channel 没有 parentId 或外键关系
+- Channel 是独立的顶级实体
+- 渠道名称是全局唯一,而非在某个父级下唯一
+
+### ChannelManagement UI 组件分析
+
+**页面路径:** 待确认(可能是 `/admin/channels`)
+
+**UI 组件结构(来自 ChannelManagement.tsx):**
+
+1. **页面结构:**
+   - 页面标题:"渠道管理"
+   - 搜索栏:搜索输入框 + 搜索按钮 + 创建渠道按钮
+   - 渠道列表表格(7列):渠道ID、渠道名称、渠道类型、联系人、联系电话、创建时间、操作
+   - 分页组件(DataTablePagination)
+
+2. **创建/编辑对话框:**
+   - 对话框标题:"创建渠道" / "编辑渠道"
+   - 表单字段:
+     - channelName: Input(必填)
+     - channelType: Input(可选)
+     - contactPerson: Input(可选)
+     - contactPhone: Input(可选)
+     - description: Input(可选)
+   - 提交按钮:"创建" / "更新"
+   - 取消按钮:"取消"
+
+3. **删除确认对话框:**
+   - 标题:"确认删除"
+   - 描述:"确定要删除这个渠道吗?此操作不可撤销。"
+   - 确认按钮:"确认删除"(红色 destructive 样式)
+   - 取消按钮:"取消"
+
+4. **data-testid 属性(关键):**
+   ```
+   搜索相关:
+   - search-input (搜索输入框)
+   - search-button (搜索按钮)
+   - create-channel-button (创建渠道按钮)
+
+   表单字段(创建):
+   - create-channel-modal-title (对话框标题)
+
+   表单字段(编辑):
+   - (编辑表单使用 react-hook-form,未显式设置 data-testid)
+
+   删除对话框:
+   - delete-confirm-dialog-title (删除确认对话框标题)
+
+   表格操作按钮:
+   - edit-channel-{id} (编辑按钮,动态ID)
+   - delete-channel-{id} (删除按钮,动态ID)
+   ```
+
+**注意事项:**
+- ChannelManagement UI 组件中的 data-testid 设置不够完整
+- 编辑表单的字段缺少显式的 data-testid
+- 可能需要通过 role + name 组合来定位元素
+
+### API 端点参考
+
+**渠道管理 API 端点:**
+```typescript
+// 获取所有渠道
+GET /api/v1/channel/getAllChannels?skip=0&take=10
+
+// 搜索渠道
+GET /api/v1/channel/searchChannels?name=xxx&skip=0&take=10
+
+// 创建渠道
+POST /api/v1/channel/createChannel
+Body: { channelName, channelType?, contactPerson?, contactPhone?, description? }
+
+// 更新渠道
+POST /api/v1/channel/updateChannel
+Body: { id, channelName?, channelType?, contactPerson?, contactPhone?, description? }
+
+// 删除渠道
+POST /api/v1/channel/deleteChannel
+Body: { id: number }
+```
+
+### Page Object 设计参考
+
+**ChannelManagementPage 类结构:**
+```typescript
+import { Page, Locator } from '@playwright/test';
+
+/**
+ * 渠道数据接口
+ */
+export interface ChannelData {
+  /** 渠道名称(必填) */
+  channelName: string;
+  /** 渠道类型(可选) */
+  channelType?: string;
+  /** 联系人(可选) */
+  contactPerson?: string;
+  /** 联系电话(可选) */
+  contactPhone?: string;
+  /** 描述(可选) */
+  description?: string;
+}
+
+/**
+ * 网络响应数据接口
+ */
+export interface NetworkResponse {
+  url: string;
+  method: string;
+  status: number;
+  ok: boolean;
+  responseHeaders: Record<string, string>;
+  responseBody: unknown;
+}
+
+/**
+ * 表单提交结果接口
+ */
+export interface FormSubmitResult {
+  success: boolean;
+  hasError: boolean;
+  hasSuccess: boolean;
+  errorMessage?: string;
+  successMessage?: string;
+  responses?: NetworkResponse[];
+}
+
+/**
+ * 渠道管理 Page Object
+ *
+ * 用于渠道管理功能的 E2E 测试
+ * 页面路径: /admin/channels(待确认)
+ */
+export class ChannelManagementPage {
+  readonly page: Page;
+
+  // ===== 页面级选择器 =====
+  readonly pageTitle: Locator;
+  readonly createChannelButton: Locator;
+  readonly searchInput: Locator;
+  readonly searchButton: Locator;
+  readonly channelTable: Locator;
+
+  // ===== 对话框选择器 =====
+  readonly createDialogTitle: Locator;
+  readonly editDialogTitle: Locator;
+
+  // ===== 表单字段选择器 =====
+  readonly channelNameInput: Locator;
+  readonly channelTypeInput: Locator;
+  readonly contactPersonInput: Locator;
+  readonly contactPhoneInput: Locator;
+  readonly descriptionInput: Locator;
+
+  // ===== 按钮选择器 =====
+  readonly createSubmitButton: Locator;
+  readonly updateSubmitButton: Locator;
+  readonly cancelButton: Locator;
+  readonly confirmDeleteButton: Locator;
+
+  constructor(page: Page) {
+    this.page = page;
+    // 初始化所有选择器
+  }
+
+  // ===== 导航和基础验证 =====
+  async goto(): Promise<void>
+  async expectToBeVisible(): Promise<void>
+
+  // ===== 对话框操作 =====
+  async openCreateDialog(): Promise<void>
+  async openEditDialog(channelName: string): Promise<void>
+  async openDeleteDialog(channelName: string): Promise<void>
+  async cancelDialog(): Promise<void>
+  async waitForDialogClosed(): Promise<void>
+
+  // ===== 表单操作 =====
+  async fillChannelForm(data: ChannelData): Promise<void>
+  async submitForm(): Promise<FormSubmitResult>
+
+  // ===== CRUD 操作 =====
+  async createChannel(data: ChannelData): Promise<FormSubmitResult>
+  async editChannel(channelName: string, data: ChannelData): Promise<FormSubmitResult>
+  async deleteChannel(channelName: string): Promise<boolean>
+
+  // ===== 搜索和验证 =====
+  async searchByName(name: string): Promise<boolean>
+  async channelExists(channelName: string): Promise<boolean>
+}
+```
+
+### 与 PlatformManagementPage 和 CompanyManagementPage 的对比
+
+**相同部分:**
+- 整体结构和模式完全一致
+- 选择器命名规则一致
+- 对话框操作逻辑一致
+- 表单提交流程一致
+- Toast 验证方式一致
+- API 删除策略一致
+
+**差异部分:**
+
+| 特性 | Platform | Company | Channel |
+|------|----------|---------|---------|
+| **外键依赖** | 无 | 需要 platformId | 无 |
+| **名称唯一性** | 全局 | 同平台下唯一 | 全局 |
+| **表格列数** | 7列 | 7列 | 7列 |
+| **状态显示** | 无 | 有(状态徽章) | 有(状态字段) |
+| **必填字段** | platformName | companyName | channelName |
+| **可选字段** | 联系人信息 | 地址 + platformId | channelType, description |
+
+**Channel 特有的特点:**
+- 有 channelType 字段区分不同渠道类型(如"小程序"、"网站"等)
+- 联系人字段都是必填的(但有默认值空字符串)
+- 与其他配置实体无关联,独立存在
+
+### 测试数据唯一性策略
+
+**使用时间戳确保唯一性:**
+```typescript
+const timestamp = Date.now();
+const uniqueId = `channel_${timestamp}`;
+const channelName = `测试渠道_${uniqueId}`;
+```
+
+**测试数据清理:**
+- 每个测试用例结束后清理自己创建的渠道数据
+- 使用 `deleteChannel()` 方法删除测试数据
+- 验证删除成功后再结束测试
+
+### 依赖关系
+
+**Epic 11 内部依赖:**
+- Story 11.1: ✅ 已完成(PlatformManagementPage 作为参考)
+- Story 11.2: ✅ 已完成(平台创建测试参考)
+- Story 11.3: ✅ 已完成(平台列表测试参考)
+- Story 11.4: ✅ 已完成(CompanyManagementPage 作为参考)
+- Story 11.5: Backlog(创建测试公司)
+- Story 11.6: ✅ 已完成(公司列表测试)
+- Story 11.7: Channel 管理 Page Object(当前,可选)
+
+**外部依赖:**
+- Epic 1, 2: `@d8d/e2e-test-utils` 包(已存在)
+- `web/tests/e2e/utils/test-setup.ts`: 需要添加 channelManagementPage fixture
+
+**测试夹具配置(需要在 test-setup.ts 中添加):**
+```typescript
+export const test = test.extend<{
+  adminLoginPage: AdminLoginPage;
+  channelManagementPage: ChannelManagementPage;
+}>({
+  adminLoginPage: async ({ page }, use) => {
+    await use(new AdminLoginPage(page));
+  },
+  channelManagementPage: async ({ page }, use) => {
+    await use(new ChannelManagementPage(page));
+  },
+});
+```
+
+### 测试标准和规范
+
+遵循项目测试标准:
+- `docs/standards/testing-standards.md`
+- `docs/standards/web-ui-testing-standards.md`
+
+**关键测试原则:**
+1. 测试独立性:每个测试用例独立运行
+2. 数据清理:每个测试结束后清理自己创建的数据
+3. 清晰断言:使用 expect() 明确断言预期结果
+4. 等待策略:使用 Playwright 的 auto-waiting
+
+### 前序 Story (11.1-11.6) 关键经验
+
+从 Story 11.1-11.6 中学到的关键经验:
+
+1. **Toast 检测不可靠:**
+   - Toast 消息有时出现得很快
+   - 已改用 API 响应验证作为主要验证方式
+
+2. **表格列顺序:**
+   - Channel 表格列顺序:渠道ID(0), 渠道名称(1), 渠道类型(2), 联系人(3), 联系电话(4), 创建时间(5), 操作(6)
+   - 使用 `nth(1)` 检查渠道名称列
+
+3. **测试数据要求:**
+   - 后端 Zod schema 要求字段类型正确
+   - 空字符串在某些字段中是有效值(有默认值)
+   - 测试不填的可选字段应该设为 undefined
+
+4. **页面刷新:**
+   - 创建/删除数据后需要刷新页面或重新导航
+   - 使用 `page.reload()` 或 `goto()` 刷新
+
+5. **选择器优先级:**
+   - 优先使用 data-testid(最稳定)
+   - 其次使用 role + name(较稳定)
+   - 避免使用文本选择器(可能变化)
+
+6. **编辑表单 data-testid 缺失:**
+   - 从 CompanyManagementPage 的经验,编辑表单可能缺少 data-testid
+   - 需要使用 `getByRole('form')` + `getByLabel()` 组合定位
+
+### 已知问题和注意事项
+
+1. **页面路由待确认:**
+   - 渠道管理页面的实际路由需要确认
+   - 可能是 `/admin/channels` 或其他
+   - 需要通过实际访问验证
+
+2. **data-testid 不完整:**
+   - ChannelManagement UI 组件的 data-testid 设置不完整
+   - 编辑表单字段缺少显式的 data-testid
+   - 需要使用 role + label 组合定位
+
+3. **渠道名称全局唯一:**
+   - 渠道名称全局唯一,不是在某个父级下唯一
+   - 测试使用时间戳确保唯一性
+
+4. **删除限制:**
+   - 只有禁用状态的渠道才能删除
+   - 测试删除功能需要先禁用渠道
+   - 或者使用 API 直接删除
+
+5. **状态显示:**
+   - 状态字段存在,但表格中未显示状态列
+   - 测试中不需要验证状态显示
+
+### 开发顺序建议
+
+1. 创建文件和基础结构
+2. 定义所有选择器(注意编辑表单可能需要使用 role + label)
+3. 实现导航和基础验证方法
+4. 实现对话框操作方法
+5. 实现表单填写方法
+6. 实现表单提交方法(网络响应捕获)
+7. 实现 CRUD 操作方法
+8. 实现搜索和验证方法
+9. TypeScript 类型检查
+10. 在 test-setup.ts 中添加 fixture
+
+### 实现决策点
+
+**由于此 Story 是可选的,实现前需要确认:**
+
+1. **是否需要渠道管理测试?**
+   - 订单管理测试(Epic 10)是否需要验证渠道选择功能?
+   - 如果订单创建流程中渠道选择是关键步骤,则需要实现
+
+2. **优先级评估:**
+   - Epic 11 的核心目标(Platform + Company)已完成
+   - Channel 管理测试优先级低于其他 Epic
+   - 可以在有空余时间时实现
+
+3. **实现范围:**
+   - 如果实现,建议实现完整的 CRUD 测试
+   - 如果不实现,可以在后续有需要时补充
+
+### 可选性说明
+
+**标记为"可选"的原因总结:**
+
+1. **业务优先级低** - Channel 是订单的可选字段,不影响核心业务流程
+2. **依赖关系弱** - 不阻塞其他 Epic 的开发
+3. **时间资源有限** - Epic 11 的核心目标(Platform + Company)已实现
+4. **可延后实现** - 当发现确实需要渠道管理测试时,再实现此 Story
+
+**何时应该实现此 Story:**
+- 订单管理测试(Epic 10)中发现需要验证渠道选择功能
+- 产品团队明确要求渠道管理测试覆盖
+- 有额外开发时间资源
+
+### References
+
+- [Epic 11 基础配置管理测试](../planning-artifacts/epics.md#epic-11-基础配置管理测试-epic-f)
+- [Story 11.1 Platform Page Object](./11-1-platform-page-object.story.md)
+- [Story 11.4 Company Page Object](./11-4-company-page-object.story.md)
+- [Channel Entity](../../allin-packages/channel-module/src/entities/channel.entity.ts)
+- [Channel Schema](../../allin-packages/channel-module/src/schemas/channel.schema.ts)
+- [ChannelManagement UI](../../allin-packages/channel-management-ui/src/components/ChannelManagement.tsx)
+- [PlatformManagementPage](../../web/tests/e2e/pages/admin/platform-management.page.ts) - 参考模式
+- [CompanyManagementPage](../../web/tests/e2e/pages/admin/company-management.page.ts) - 参考模式
+- [测试标准](../../docs/standards/testing-standards.md)
+- [Web UI 测试标准](../../docs/standards/web-ui-testing-standards.md)
+- [Architecture Decision - TypeScript + Playwright 陷阱](../planning-artifacts/architecture.md#typescript--playwright-常见陷阱)
+
+### Project Structure Notes
+
+**测试文件存放路径:**
+```
+web/tests/e2e/
+├── pages/admin/
+│   ├── platform-management.page.ts   # 平台管理 Page Object(已完成)
+│   ├── company-management.page.ts    # 公司管理 Page Object(已完成)
+│   └── channel-management.page.ts    # 渠道管理 Page Object(当前,可选)
+├── specs/admin/
+│   ├── platform-create.spec.ts       # 平台创建测试(已完成)
+│   ├── platform-list.spec.ts         # 平台列表测试(已完成)
+│   ├── company-create.spec.ts        # 公司创建测试(待实现)
+│   ├── company-list.spec.ts          # 公司列表测试(已完成)
+│   └── channel-*.spec.ts             # 渠道管理测试(如需要,后续实现)
+└── utils/
+    └── test-setup.ts                  # 测试夹具配置(需要添加 channelManagementPage)
+```
+
+

+ 1 - 1
_bmad-output/implementation-artifacts/sprint-status.yaml

@@ -176,7 +176,7 @@ development_status:
   11-4-company-page-object: done  # Company 管理 Page Object(重点) - 代码审查完成,所有 HIGH 和 MEDIUM 问题已修复
   11-5-company-create-test: backlog        # 创建测试公司(需要先有平台)
   11-6-company-list-test: done         # 验证公司列表显示 ✅ 14 个测试全部通过,代码审查完成,所有 HIGH 和 MEDIUM 问题已修复 (2026-01-12)
-  11-7-channel-page-object: backlog        # Channel 管理 Page Object(可选)
+  11-7-channel-page-object: ready-for-dev        # Channel 管理 Page Object(可选)
   11-8-channel-create-test: backlog        # 创建测试渠道(可选)
   11-9-config-validation-test: backlog     # 验证订单可以选择平台和公司
   epic-11-retrospective: optional