فهرست منبع

docs(story-12.4): 添加企业用户 Mini 页面对象模式文档

为 Story 12.4 添加 Mini 应用的企业用户页面对象模式实现文档,包含页面选择器、表单操作和验证方法。

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 5 روز پیش
والد
کامیت
c3f0eb334c
1فایلهای تغییر یافته به همراه338 افزوده شده و 0 حذف شده
  1. 338 0
      _bmad-output/implementation-artifacts/12-4-enterprise-mini-page-object.md

+ 338 - 0
_bmad-output/implementation-artifacts/12-4-enterprise-mini-page-object.md

@@ -0,0 +1,338 @@
+# Story 12.4: 企业小程序 Page Object
+
+Status: ready-for-dev
+
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
+
+## Story
+
+作为测试开发者,
+我想要创建企业小程序的 Page Object,
+以便组织企业小程序相关的页面元素和操作。
+
+## Acceptance Criteria
+
+### AC1: Page Object 基础结构
+**Given** Playwright E2E 测试框架已配置
+**When** 创建企业小程序 Page Object
+**Then** 测试应满足以下要求:
+- 创建 `web/tests/e2e/pages/mini/enterprise-mini.page.ts` 文件
+- 定义 `EnterpriseMiniPage` 类,继承 Playwright 的 Page 对象模式
+- 实现基础选择器定义(登录表单、主页元素等)
+- 所有选择器使用 `data-testid` 属性(优先级高于文本选择器)
+- 遵循项目 Page Object 设计模式
+
+### AC2: 小程序页面导航
+**Given** 企业小程序 Page Object 已创建
+**When** 实现页面导航方法
+**Then** 测试应满足以下要求:
+- 实现 `goto()` 方法导航到企业小程序 H5 页面 (`/mini`)
+- 实现 `expectToBeVisible()` 方法验证页面可见性
+- 验证页面正确加载(检查关键元素存在)
+- 处理页面加载超时情况
+
+### AC3: 登录功能封装
+**Given** 企业小程序 Page Object 已创建
+**When** 实现登录相关方法
+**Then** 测试应满足以下要求:
+- 实现 `login(username, password)` 方法执行登录操作
+- 实现填写用户名方法 `fillUsername(username)`
+- 实现填写密码方法 `fillPassword(password)`
+- 实现点击登录按钮方法 `clickLoginButton()`
+- 实现验证登录成功方法 `expectLoginSuccess()`
+- 处理登录失败场景(错误提示显示)
+
+### AC4: Token 存储和管理
+**Given** 小程序登录后需要存储 token 用于后续操作
+**When** 实现 token 管理方法
+**Then** 测试应满足以下要求:
+- 实现 `getToken()` 方法获取当前存储的 token
+- 实现 `setToken(token)` 方法设置 token(用于测试前置条件)
+- 使用 localStorage 或 sessionStorage 存储 token
+- 验证 token 在页面刷新后仍然有效
+
+### AC5: 主页元素选择器
+**Given** 企业小程序登录后进入主页
+**When** 定义主页元素选择器
+**Then** 测试应满足以下要求:
+- 定义订单列表选择器(如适用)
+- 定义导航菜单选择器
+- 定义用户信息显示选择器
+- 所有选择器使用 `data-testid` 属性
+- 提供清晰的类型定义
+
+### AC6: 代码质量标准
+**Given** 遵循项目测试规范
+**When** 编写 Page Object 代码
+**Then** 代码应符合以下标准:
+- TypeScript 类型安全,无 `any` 类型
+- 所有公共方法有完整的 JSDoc 注释
+- 使用 TIMEOUTS 常量定义超时
+- 遵循项目命名约定(类名 PascalCase,方法名 camelCase)
+- 通过 `pnpm typecheck` 类型检查
+
+## Tasks / Subtasks
+
+- [ ] 任务 1: 创建 Page Object 基础结构 (AC: #1, #6)
+  - [ ] 1.1 创建 `web/tests/e2e/pages/mini/` 目录(如不存在)
+  - [ ] 1.2 创建 `enterprise-mini.page.ts` 文件
+  - [ ] 1.3 定义 `EnterpriseMiniPage` 类
+  - [ ] 1.4 定义基础选择器(使用 `data-testid`)
+
+- [ ] 任务 2: 实现页面导航功能 (AC: #2)
+  - [ ] 2.1 实现 `goto()` 方法(导航到 `/mini`)
+  - [ ] 2.2 实现 `expectToBeVisible()` 方法
+  - [ ] 2.3 添加页面加载验证逻辑
+
+- [ ] 任务 3: 实现登录功能封装 (AC: #3)
+  - [ ] 3.1 实现 `fillUsername()` 方法
+  - [ ] 3.2 实现 `fillPassword()` 方法
+  - [ ] 3.3 实现 `clickLoginButton()` 方法
+  - [ ] 3.4 实现 `login()` 完整登录方法
+  - [ ] 3.5 实现 `expectLoginSuccess()` 验证方法
+
+- [ ] 任务 4: 实现 Token 管理 (AC: #4)
+  - [ ] 4.1 实现 `getToken()` 方法
+  - [ ] 4.2 实现 `setToken()` 方法
+  - [ ] 4.3 使用 localStorage 存储 token
+
+- [ ] 任务 5: 定义主页元素选择器 (AC: #5)
+  - [ ] 5.1 定义订单列表选择器
+  - [ ] 5.2 定义导航菜单选择器
+  - [ ] 5.3 定义用户信息选择器
+
+- [ ] 任务 6: 代码质量验证 (AC: #6)
+  - [ ] 6.1 运行 `pnpm typecheck` 验证类型检查
+  - [ ] 6.2 添加完整的 JSDoc 注释
+  - [ ] 6.3 验证选择器使用 data-testid
+
+- [ ] 任务 7: 创建测试 fixture (AC: #6)
+  - [ ] 7.1 在 `playwright.config.ts` 中添加 `enterpriseMiniPage` fixture
+  - [ ] 7.2 验证 fixture 正确初始化
+
+## Dev Notes
+
+### Epic 12 背景和依赖
+
+**Epic 12 目标:** 为用户管理和小程序登录编写 E2E 测试,解锁小程序端的测试能力
+
+**小程序技术要点(来自 Epic 12 文档):**
+- 企业小程序 H5 URL: `http://localhost:8080/mini`
+- 人才小程序 H5 URL: `http://localhost:8080/talent-mini`
+- 登录后存储 token 进行后续操作
+- 使用 Playwright 测试 H5 页面
+- 小程序只读,无写操作
+
+**Epic 12 Story 依赖关系:**
+```
+Story 12.1: 用户管理 Page Object ✅ (已完成)
+Story 12.2: 后台创建企业用户测试 ✅ (已完成)
+Story 12.3: 后台创建人才用户测试 🔄 (进行中)
+Story 12.4: 企业小程序 Page Object ← 当前 Story
+Story 12.5: 企业小程序登录测试
+Story 12.6: 人才小程序 Page Object
+Story 12.7: 人才小程序登录测试
+Story 12.8: 用户权限验证测试
+```
+
+### Story 12.1 关键经验
+
+从已完成的 Story 12.1 中学习到的 Page Object 模式:
+
+**UserManagementPage 设计模式:**
+```typescript
+export class UserManagementPage {
+  readonly page: Page;
+
+  // 选择器定义(使用 data-testid)
+  private readonly selectors = {
+    addButton: '[data-testid="add-user-button"]',
+    usernameInput: '[data-testid="username-input"]',
+    // ...
+  };
+
+  constructor(page: Page) {
+    this.page = page;
+  }
+
+  // 导航方法
+  async goto(): Promise<void> {
+    await this.page.goto('/admin/users');
+  }
+
+  // 可见性验证
+  async expectToBeVisible(): Promise<void> {
+    await expect(this.page.locator(this.selectors.pageContainer)).toBeVisible();
+  }
+}
+```
+
+### 小程序登录流程
+
+**预期登录流程:**
+1. 导航到 `/mini` 页面
+2. 填写用户名(在测试中创建的企业用户)
+3. 填写密码
+4. 点击登录按钮
+5. 验证登录成功(跳转到主页或显示用户信息)
+6. 存储 token 用于后续请求
+
+**测试用户创建(参考 Story 12.2):**
+```typescript
+// Story 12.2 创建的企业用户
+const employerUserData = {
+  username: `test_employer_${Date.now()}`,
+  password: 'password123',
+  nickname: '测试企业用户',
+  userType: UserType.EMPLOYER,
+  companyId: 1,  // 使用测试公司
+};
+```
+
+### Token 管理策略
+
+**存储位置:** localStorage 或 sessionStorage(根据实际小程序实现)
+
+**Token 操作方法:**
+```typescript
+// 获取 token
+async getToken(): Promise<string | null> {
+  return await this.page.evaluate(() => {
+    return localStorage.getItem('token') || sessionStorage.getItem('token');
+  });
+}
+
+// 设置 token(用于测试前置条件)
+async setToken(token: string): Promise<void> {
+  await this.page.evaluate((t) => {
+    localStorage.setItem('token', t);
+  }, token);
+}
+```
+
+### Playwright Fixture 集成
+
+**在 `playwright.config.ts` 或测试文件中添加 fixture:**
+```typescript
+import { test as base } from '@playwright/test';
+import { EnterpriseMiniPage } from './pages/mini/enterprise-mini.page';
+
+type EnterpriseMiniFixtures = {
+  enterpriseMiniPage: EnterpriseMiniPage;
+};
+
+export const test = base.extend<EnterpriseMiniFixtures>({
+  enterpriseMiniPage: async ({ page }, use) => {
+    const miniPage = new EnterpriseMiniPage(page);
+    await use(miniPage);
+  },
+});
+```
+
+### 项目结构
+
+**新建文件:**
+- `web/tests/e2e/pages/mini/enterprise-mini.page.ts`
+
+**相关参考文件:**
+- `web/tests/e2e/pages/admin/user-management.page.ts` (Story 12.1)
+- `web/tests/e2e/utils/timeouts.ts` (TIMEOUTS 常量)
+
+### 小程序只读特性
+
+根据 Epic 12 文档:
+- 小程序只读,无写操作
+- 这意味着 Page Object 不需要包含创建、编辑、删除等操作方法
+- 主要关注登录、查看、验证等只读操作
+
+### 选择器策略
+
+**优先级(遵循项目标准):**
+1. `data-testid` 属性(最高优先级)
+2. ARIA 属性 + role
+3. 文本内容(最低优先级,避免使用)
+
+**示例:**
+```typescript
+private readonly selectors = {
+  // ✅ 优先:data-testid
+  usernameInput: '[data-testid="mini-username-input"]',
+  passwordInput: '[data-testid="mini-password-input"]',
+  loginButton: '[data-testid="mini-login-button"]',
+
+  // ⚠️ 备选:ARIA + role(如 data-testid 不可用)
+  // usernameInput: '[aria-label="用户名"]',
+
+  // ❌ 避免:纯文本选择器
+  // usernameInput: 'text=用户名',
+};
+```
+
+### TypeScript 类型定义
+
+**Page Object 类类型:**
+```typescript
+export class EnterpriseMiniPage {
+  readonly page: Page;
+
+  constructor(page: Page) {
+    this.page = page;
+  }
+
+  // 所有方法应有明确的返回类型
+  async goto(): Promise<void> { }
+  async login(username: string, password: string): Promise<void> { }
+  async expectLoginSuccess(): Promise<void> { }
+}
+```
+
+### 测试超时配置
+
+**使用 TIMEOUTS 常量:**
+```typescript
+import { TIMEOUTS } from '../utils/timeouts';
+
+await expect(this.page.locator(this.selectors.loginButton)).toBeVisible({
+  timeout: TIMEOUTS.default,
+});
+```
+
+### 参考文档
+
+**架构文档:**
+- `_bmad-output/planning-artifacts/architecture.md`
+- `docs/standards/e2e-radix-testing.md` (Radix UI 测试标准)
+
+**相关 Story 文档:**
+- `12-1-user-page-object.md` (用户管理 Page Object)
+- `12-2-create-employer-user.md` (后台创建企业用户测试)
+
+## Dev Agent Record
+
+### Agent Model Used
+
+Claude (d8d-model)
+
+### Debug Log References
+
+_N/A - Story 尚未开始开发_
+
+### Completion Notes List
+
+_N/A - Story 尚未开始开发_
+
+### File List
+
+**预计新建的文件:**
+- `web/tests/e2e/pages/mini/enterprise-mini.page.ts` - 企业小程序 Page Object 文件
+
+**预计修改的文件:**
+- `web/tests/e2e/playwright.config.ts` - 添加 `enterpriseMiniPage` fixture
+
+## Change Log
+
+- 2026-01-13: Story 12.4 创建完成
+  - 企业小程序 Page Object 基础结构设计
+  - 登录功能封装需求
+  - Token 管理策略
+  - 状态:ready-for-dev