Przeglądaj źródła

docs: 创建故事010.007 - 租户后台统一广告管理UI交互E2E测试

- 创建E2E测试文件和Page Object模式
- 覆盖登录、导航、CRUD、表单验证、分页、搜索等场景
- 包含响应式布局测试和图片选择器测试
- 更新E2E测试规范文档

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 2 tygodni temu
rodzic
commit
d609156ae1
1 zmienionych plików z 392 dodań i 0 usunięć
  1. 392 0
      docs/stories/010.007.story.md

+ 392 - 0
docs/stories/010.007.story.md

@@ -0,0 +1,392 @@
+# Story 010.007: 租户后台统一广告管理UI交互E2E测试
+
+## Status
+Approved
+
+## Story
+
+**As a** 超级管理员,
+**I want** 通过E2E测试验证租户后台统一广告管理的完整UI交互流程,
+**so that** 确保租户后台的广告管理功能在实际浏览器环境中能够正常工作,包括登录、导航、CRUD操作、表单验证等所有交互场景。
+
+## Acceptance Criteria
+
+1. 创建UI交互E2E测试文件:`web/tests/e2e/specs/tenant-advertisement-ui.spec.ts`
+2. 测试登录流程:超级管理员登录租户后台
+3. 测试导航:验证广告管理菜单项可点击,页面正确跳转
+4. 测试广告列表:验证广告列表正确显示,包含正确数据
+5. 测试创建广告:打开创建表单,填写字段,提交,验证创建成功
+6. 测试编辑广告:点击编辑按钮,修改数据,保存,验证更新成功
+7. 测试删除广告:点击删除按钮,确认删除,验证数据删除
+8. 测试广告类型管理:验证类型列表、创建、编辑、删除
+9. 测试分页功能:验证翻页功能正常工作
+10. 测试搜索功能:验证按标题/代码搜索功能正常
+11. 测试表单验证:验证必填字段、格式验证、错误提示
+12. 测试图片选择器:验证图片选择器集成正常工作
+13. 测试响应式布局:验证页面在不同屏幕尺寸下正常显示
+14. 更新E2E测试规范文档,添加UI交互测试示例
+
+## Tasks / Subtasks
+
+- [ ] **任务1: 创建测试基础结构** (AC: 1, 2)
+  - [ ] 在 `web/tests/e2e/specs/` 目录创建 `tenant-advertisement-ui.spec.ts`
+  - [ ] 创建 Page Object:`web/tests/e2e/pages/tenant/tenant-login.page.ts`
+  - [ ] 创建 Page Object:`web/tests/e2e/pages/tenant/tenant-advertisement.page.ts`
+  - [ ] 创建测试 fixtures:`web/tests/e2e/fixtures/test-advertisements.json`
+  - [ ] 设置测试工具函数
+
+- [ ] **任务2: 实现登录流程测试** (AC: 2)
+  - [ ] 创建租户后台登录 Page Object
+  - [ ] 测试超级管理员登录(username=admin, password=admin123, tenantId=1)
+  - [ ] 验证登录成功后跳转到租户后台首页
+  - [ ] 验证登录失败场景(错误密码)
+
+- [ ] **任务3: 实现导航测试** (AC: 3)
+  - [ ] 验证广告管理菜单项存在且可点击
+  - [ ] 验证广告类型管理菜单项存在且可点击
+  - [ ] 验证点击菜单项后正确跳转到对应页面
+
+- [ ] **任务4: 实现广告列表测试** (AC: 4)
+  - [ ] 验证广告列表页面正确显示
+  - [ ] 验证广告列表数据正确渲染(标题、类型、状态、排序等)
+  - [ ] 验证列表操作按钮(编辑、删除)存在
+
+- [ ] **任务5: 实现创建广告测试** (AC: 5, 11, 12)
+  - [ ] 测试点击"新建"按钮打开创建对话框
+  - [ ] 测试填写表单字段(标题、类型、代码、URL、排序等)
+  - [ ] 测试图片选择器交互
+  - [ ] 测试表单验证(必填字段、格式验证)
+  - [ ] 测试提交创建成功
+  - [ ] 验证创建成功后列表中显示新广告
+
+- [ ] **任务6: 实现编辑广告测试** (AC: 6)
+  - [ ] 测试点击"编辑"按钮打开编辑对话框
+  - [ ] 测试修改广告数据
+  - [ ] 测试保存更新
+  - [ ] 验证更新成功后列表中数据已更新
+
+- [ ] **任务7: 实现删除广告测试** (AC: 7)
+  - [ ] 测试点击"删除"按钮
+  - [ ] 测试确认删除对话框
+  - [ ] 验证删除成功后列表中数据已移除
+
+- [ ] **任务8: 实现广告类型管理测试** (AC: 8)
+  - [ ] 测试广告类型列表页面显示
+  - [ ] 测试创建广告类型
+  - [ ] 测试编辑广告类型
+  - [ ] 测试删除广告类型
+
+- [ ] **任务9: 实现分页功能测试** (AC: 9)
+  - [ ] 测试分页组件显示
+  - [ ] 测试点击下一页/上一页
+  - [ ] 测试跳转到指定页码
+  - [ ] 验证分页数据正确加载
+
+- [ ] **任务10: 实现搜索功能测试** (AC: 10)
+  - [ ] 测试搜索输入框
+  - [ ] 测试按标题搜索
+  - [ ] 测试按代码搜索
+  - [ ] 验证搜索结果正确过滤
+
+- [ ] **任务11: 实现表单验证测试** (AC: 11)
+  - [ ] 测试必填字段验证(标题、类型等)
+  - [ ] 测试格式验证(URL格式、排序必须是数字等)
+  - [ ] 测试长度限制验证
+  - [ ] 验证错误提示正确显示
+
+- [ ] **任务12: 实现图片选择器测试** (AC: 12)
+  - [ ] 测试图片选择器按钮可点击
+  - [ ] 测试图片选择对话框打开
+  - [ ] 测试选择图片后确认
+  - [ ] 验证图片预览正确显示
+
+- [ ] **任务13: 实现响应式布局测试** (AC: 13)
+  - [ ] 测试桌面视图(Desktop Chrome)
+  - [ ] 测试移动端视图(Mobile Chrome/iPhone)
+  - [ ] 验证页面布局在不同尺寸下正常显示
+
+- [ ] **任务14: 更新E2E测试规范文档** (AC: 14)
+  - [ ] 在 `docs/architecture/e2e-testing-standards.md` 中添加UI交互测试示例
+  - [ ] 添加Page Object模式示例
+  - [ ] 添加租户后台测试规范
+
+## Dev Notes
+
+### 前一故事关键要点(来自 010.006)
+
+**测试成果**:
+- 创建了 `web/tests/e2e/unified-advertisement-api.spec.ts` API兼容性测试
+- 50个E2E测试通过,5个跳过
+- 添加了JWT认证逻辑到E2E测试
+- 创建了E2E测试规范文档
+
+**测试基础**:
+- **Playwright配置**: `web/tests/e2e/playwright.config.ts`(testDir: '.')
+- **测试命令**: `cd web && pnpm test:e2e:chromium`
+- **认证方式**: 通过 `/api/v1/auth/login?tenantId=1` 登录获取JWT token
+- **测试用户**: 超级管理员(username=admin, password=admin123, tenantId=1)
+
+### 租户后台结构
+
+**路由配置** [Source: web/src/client/tenant/routes.tsx]:
+```typescript
+// 广告管理路由
+{
+  path: 'unified-advertisements',
+  element: <UnifiedAdvertisementManagement />,
+}
+
+// 广告类型管理路由
+{
+  path: 'unified-advertisement-types',
+  element: <UnifiedAdvertisementTypeManagement />,
+}
+```
+
+**菜单配置** [Source: web/src/client/tenant/menu.tsx]:
+- 菜单项使用 Megaphone 图标
+- 菜单路径:`/tenant/unified-advertisements` 和 `/tenant/unified-advertisement-types`
+
+**统一广告管理UI包**:
+- 组件:`@d8d/unified-advertisement-management-ui`
+- 包含:广告列表、创建表单、编辑表单、删除确认对话框
+- 图片选择器:使用 `@d8d/file-management-ui-mt` 的文件选择器
+
+### API端点
+
+**管理员API(需要JWT认证)**:
+```typescript
+// 广告管理
+GET    /api/v1/admin/unified-advertisements           // 列表
+POST   /api/v1/admin/unified-advertisements           // 创建
+PUT    /api/v1/admin/unified-advertisements/:id       // 更新
+DELETE /api/v1/admin/unified-advertisements/:id       // 删除
+
+// 广告类型管理
+GET    /api/v1/admin/unified-advertisement-types      // 列表
+POST   /api/v1/admin/unified-advertisement-types      // 创建
+PUT    /api/v1/admin/unified-advertisement-types/:id  // 更新
+DELETE /api/v1/admin/unified-advertisement-types/:id  // 删除
+```
+
+### E2E测试框架配置
+
+**Playwright配置** [Source: web/tests/e2e/playwright.config.ts]:
+```typescript
+export default defineConfig({
+  testDir: '.',  // 当前目录,扫描.spec.ts文件
+  fullyParallel: true,
+  forbidOnly: !!process.env.CI,
+  retries: process.env.CI ? 2 : 0,
+  workers: process.env.CI ? 1 : undefined,
+  use: {
+    baseURL: process.env.E2E_BASE_URL || 'http://localhost:8080',
+    trace: 'on-first-retry',
+    screenshot: 'only-on-failure',
+    video: 'retain-on-failure',
+  },
+  projects: [
+    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
+    { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
+  ],
+});
+```
+
+### Page Object模式
+
+**登录页面对象示例** [Source: docs/architecture/e2e-testing-standards.md]:
+```typescript
+// pages/tenant/tenant-login.page.ts
+import { Page, expect } from '@playwright/test';
+
+export class TenantLoginPage {
+  readonly page: Page;
+  readonly usernameInput = this.page.locator('input[name="username"]');
+  readonly passwordInput = this.page.locator('input[name="password"]');
+  readonly submitButton = this.page.locator('button[type="submit"]');
+
+  constructor(page: Page) {
+    this.page = page;
+  }
+
+  async goto() {
+    await this.page.goto('/tenant/login');
+  }
+
+  async login(username: string, password: string) {
+    await this.usernameInput.fill(username);
+    await this.passwordInput.fill(password);
+    await this.submitButton.click();
+  }
+}
+```
+
+**广告管理页面对象示例**:
+```typescript
+// pages/tenant/tenant-advertisement.page.ts
+import { Page, expect } from '@playwright/test';
+
+export class TenantAdvertisementPage {
+  readonly page: Page;
+  readonly createButton = this.page.locator('[data-testid="create-advertisement-button"]');
+  readonly searchInput = this.page.locator('input[placeholder*="搜索"]');
+  readonly table = this.page.locator('[data-testid="advertisement-table"]');
+
+  constructor(page: Page) {
+    this.page = page;
+  }
+
+  async goto() {
+    await this.page.goto('/tenant/unified-advertisements');
+  }
+
+  async clickCreate() {
+    await this.createButton.click();
+  }
+
+  async search(keyword: string) {
+    await this.searchInput.fill(keyword);
+    await this.page.waitForTimeout(300); // 等待搜索防抖
+  }
+}
+```
+
+### 测试选择器规范
+
+**使用 data-testid** [Source: docs/architecture/ui-package-standards.md]:
+- 关键交互元素必须添加 `data-testid` 属性
+- 命名约定:`{action}-{element}-{purpose}`
+- 示例:
+  - `data-testid="create-advertisement-button"`
+  - `data-testid="edit-advertisement-button-1"`
+  - `data-testid="delete-confirm-dialog-title"`
+
+### 测试数据准备
+
+**测试用户数据** [Source: docs/architecture/e2e-testing-standards.md]:
+```sql
+-- 测试租户
+INSERT INTO tenant_mt (id, name, code, status, created_at, updated_at)
+VALUES (1, '测试租户', 'test-tenant', 1, NOW(), NOW());
+
+-- 测试超级管理员 (密码: admin123)
+INSERT INTO users_mt (id, tenant_id, username, password, registration_source, is_disabled, is_deleted, created_at, updated_at)
+VALUES (1, 1, 'admin', '$2b$10$x3t2kofPmACnk6y6lfL6ouU836LBEuZE9BinQ3ZzA4Xd04izyY42K', 'web', 0, 0, NOW(), NOW());
+```
+
+**测试广告数据**:
+```typescript
+// fixtures/test-advertisements.json
+{
+  "testAdvertisement": {
+    "title": "测试广告",
+    "typeId": 1,
+    "code": "TEST_AD",
+    "url": "https://example.com",
+    "sort": 1,
+    "status": 1,
+    "actionType": 1
+  },
+  "testAdvertisementType": {
+    "name": "测试类型",
+    "code": "TEST_TYPE",
+    "description": "测试广告类型",
+    "status": 1,
+    "sortOrder": 1
+  }
+}
+```
+
+### 统一广告管理UI组件结构
+
+**组件来源** [Source: packages/unified-advertisement-management-ui/src/components/]:
+- `UnifiedAdvertisementManagement.tsx` - 主管理组件
+- `AdvertisementList.tsx` - 广告列表
+- `AdvertisementForm.tsx` - 广告表单(创建/编辑)
+- `AdvertisementTypeManagement.tsx` - 广告类型管理
+- `AdvertisementTypeForm.tsx` - 广告类型表单
+
+**表单字段**:
+- 广告表单:title(必填)、typeId(必填)、code、url、imageFileId、sort、status、actionType
+- 广告类型表单:name(必填)、code(必填)、description、status、sortOrder
+
+### 响应式布局测试
+
+**设备配置** [Source: docs/architecture/e2e-testing-standards.md]:
+- Desktop Chrome (1920x1080)
+- Pixel 5 (移动端,393x851)
+- iPhone 12 (移动端,390x844)
+
+### 测试执行命令
+
+```bash
+# 运行所有E2E测试
+cd web && pnpm test:e2e:chromium
+
+# 运行特定测试文件
+pnpm test:e2e tenant-advertisement-ui
+
+# 调试模式
+pnpm test:e2e:debug
+
+# 查看测试列表
+pnpm exec playwright test --config=tests/e2e/playwright.config.ts --list
+```
+
+### 关键注意事项
+
+1. **使用page对象**: 本故事的重点是使用 `page` 对象进行真正的浏览器UI交互测试,而不是像故事010.006那样使用 `request` 对象
+2. **Page Object模式**: 必须使用Page Object模式封装页面交互,提高测试可维护性
+3. **test-id选择器**: 优先使用 `data-testid` 而不是文本选择器,避免国际化导致的测试不稳定
+4. **数据清理**: 每个测试后应清理创建的测试数据,避免影响其他测试
+5. **异步等待**: 使用 `waitForSelector` 或 `waitForTimeout` 确保元素加载完成
+6. **测试隔离**: 每个测试应该独立运行,不依赖其他测试的状态
+
+### Testing
+
+**测试文件位置**:
+- 主测试文件: `web/tests/e2e/specs/tenant-advertisement-ui.spec.ts`
+- Page Objects: `web/tests/e2e/pages/tenant/`
+- 测试Fixtures: `web/tests/e2e/fixtures/`
+
+**测试框架**:
+- Playwright (chromium) [Source: docs/architecture/tech-stack.md]
+- 支持 Desktop 和 Mobile 视口测试
+
+**测试标准**:
+- 关键用户流程 100% 覆盖
+- 主要用户流程 80% 覆盖
+- 使用 Page Object 模式
+- 使用 data-testid 选择器
+
+**测试覆盖范围**:
+- 登录和导航流程
+- 广告CRUD操作(创建、读取、更新、删除)
+- 广告类型CRUD操作
+- 表单验证和错误处理
+- 分页和搜索功能
+- 图片选择器集成
+- 响应式布局验证
+
+## Change Log
+
+| Date | Version | Description | Author |
+|------|---------|-------------|--------|
+| 2026-01-03 | 1.0 | 初始故事创建 | Bob (Scrum Master) |
+
+## Dev Agent Record
+
+### Agent Model Used
+_待开发代理填写_
+
+### Debug Log References
+_待开发代理填写_
+
+### Completion Notes List
+_待开发代理填写_
+
+### File List
+_待开发代理填写_
+
+## QA Results
+_QA代理待填写_