# Story 12.5: 企业小程序登录测试

Status: in-progress

<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->

## Story

作为测试开发者，
我想要编写企业小程序登录的 E2E 测试，
以便验证企业用户可以通过小程序成功登录，并解锁小程序端的测试能力。

## Acceptance Criteria

### AC1: 基本登录成功测试
**Given** 企业小程序 Page Object (Story 12.4) 已完成
**When** 编写企业用户登录成功的测试用例
**Then** 测试应验证以下功能：
- 导航到企业小程序 H5 页面 (`/mini`)
- 使用在 Story 12.2 中创建的企业用户凭据登录
- 填写手机号（用户名）和密码
- 点击登录按钮
- 验证登录成功（页面跳转或用户信息显示）
- 验证 token 正确存储在 localStorage 或 sessionStorage

### AC2: 登录失败测试
**Given** 企业小程序登录页面已加载
**When** 使用错误的凭据尝试登录
**Then** 测试应验证以下场景：
- 使用不存在的用户名登录，显示错误提示
- 使用错误的密码登录，显示错误提示
- 验证错误提示内容正确（如"用户名或密码错误"）
- 验证未存储 token

### AC3: 表单验证测试
**Given** 企业小程序登录表单已显示
**When** 在不同表单验证场景下尝试提交
**Then** 测试应验证以下场景：
- 手机号为空时提交，显示错误提示
- 密码为空时提交，显示错误提示
- 验证表单验证错误提示清晰可见

### AC4: Token 持久性测试
**Given** 企业用户已成功登录
**When** 刷新页面或重新打开页面
**Then** 测试应验证以下功能：
- 验证 token 在页面刷新后仍然有效
- 验证使用已存储 token 可以继续访问需要认证的页面
- 验证用户信息仍然正确显示

### AC5: 退出登录测试
**Given** 企业用户已成功登录
**When** 执行退出登录操作
**Then** 测试应验证以下功能：
- 点击退出登录按钮
- 验证 token 被清除
- 验证返回登录页面
- 验证退出后无法访问需要认证的页面

### AC6: 测试数据准备和清理
**Given** 测试运行环境中需要企业用户数据
**When** 执行测试
**Then** 测试应遵循以下策略：
- 在测试前准备企业用户数据（使用 Story 12.2 的模式）
- 每个测试后清理测试数据（避免数据冲突）
- 使用时间戳确保用户名唯一

### AC7: 代码质量标准
**Given** 遵循项目测试规范
**When** 编写测试代码
**Then** 代码应符合以下标准：
- 使用 TIMEOUTS 常量定义超时
- 使用 data-testid 选择器（优先级高于文本选择器）
- 测试文件命名：`enterprise-mini-login.spec.ts`
- 完整的测试描述和注释
- TypeScript 类型安全
- 通过 `pnpm typecheck` 类型检查

## Tasks / Subtasks

- [x] 任务 1: 创建测试文件和基础设施 (AC: #7) ✅
  - [x] 1.1 创建 `web/tests/e2e/specs/mini/enterprise-mini-login.spec.ts` ✅
  - [x] 1.2 配置 test fixtures（enterpriseMiniPage） ✅
  - [x] 1.3 添加测试前置条件 ✅

- [ ] 任务 2: 实现基本登录成功测试 (AC: #1) ⏸️ 需要预创建用户
  - [ ] 2.1 编写"应该成功登录企业小程序"测试 (SKIPPED - 需要独立测试套件)
  - [ ] 2.2 验证登录成功后页面变化 (SKIPPED - 需要独立测试套件)
  - [ ] 2.3 验证 token 存储正确 (SKIPPED - 需要独立测试套件)

- [x] 任务 3: 实现登录失败测试 (AC: #2) ✅
  - [x] 3.1 编写"使用不存在的用户名登录失败"测试 ✅
  - [x] 3.2 编写"使用错误的密码登录失败"测试 ✅
  - [x] 3.3 验证错误提示显示 ✅

- [x] 任务 4: 实现表单验证测试 (AC: #3) ✅
  - [x] 4.1 编写"手机号为空时显示错误提示"测试 ✅
  - [x] 4.2 编写"密码为空时显示错误提示"测试 ✅

- [ ] 任务 5: 实现 Token 持久性测试 (AC: #4) ⏸️ 需要预创建用户
  - [ ] 5.1 编写"页面刷新后 token 仍然有效"测试 (SKIPPED - 需要独立测试套件)
  - [ ] 5.2 编写"使用已存储 token 继续访问"测试 (SKIPPED - 需要独立测试套件)

- [ ] 任务 6: 实现退出登录测试 (AC: #5) ⏸️ 需要预创建用户
  - [ ] 6.1 编写"成功退出登录"测试 (SKIPPED - 需要独立测试套件)
  - [ ] 6.2 验证 token 被清除 (SKIPPED - 需要独立测试套件)
  - [ ] 6.3 验证退出后无法访问需要认证的页面 (SKIPPED - 需要独立测试套件)

- [ ] 任务 7: 实现测试数据准备和清理策略 (AC: #6) ⏸️ 部分完成
  - [ ] 7.1 添加 beforeAll 钩子准备测试用户 (NOT NEEDED - 使用有效手机号格式)
  - [ ] 7.2 添加 afterEach 钩子清理测试数据 (NOT NEEDED)
  - [ ] 7.3 使用时间戳确保用户名唯一 (NOT NEEDED)

- [x] 任务 8: 验证代码质量 (AC: #7) ✅
  - [x] 8.1 运行 `pnpm typecheck` 验证类型检查 ✅
  - [x] 8.2 运行测试确保所有测试通过 ✅ (6/6 测试通过)
  - [x] 8.3 验证选择器使用 data-testid ✅

## 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 12.5: 企业小程序登录测试 ← 当前 Story
Story 12.6: 人才小程序 Page Object
Story 12.7: 人才小程序登录测试
Story 12.8: 用户权限验证测试
```

### Story 12.4 关键经验

从已完成的 Story 12.4 中学习到的 Page Object 使用模式：

**EnterpriseMiniPage 可用方法：**
```typescript
// 导航方法
async goto(): Promise<void>
async expectToBeVisible(): Promise<void>

// 登录方法
async login(phone: string, password: string): Promise<void>
async fillPhone(phone: string): Promise<void>
async fillPassword(password: string): Promise<void>
async clickLoginButton(): Promise<void>

// 验证方法
async expectLoginSuccess(): Promise<void>
async expectLoginError(expectedMessage?: string): Promise<void>

// Token 管理
async getToken(): Promise<string | null>
async setToken(token: string): Promise<void>
async clearAuth(): Promise<void>
```

**选择器定义（Story 12.4 已添加 data-testid）：**
```typescript
private readonly selectors = {
  loginPage: '[data-testid="mini-login-page"]',
  phoneInput: '[data-testid="mini-phone-input"]',
  passwordInput: '[data-testid="mini-password-input"]',
  loginButton: '[data-testid="mini-login-button"]',
  pageTitle: '[data-testid="mini-page-title"]',
  userInfo: '[data-testid="mini-user-info"]',
  // ...
};
```

### Story 12.2 关键经验

从已完成的 Story 12.2 中学习到的测试数据准备模式：

**企业用户创建模式：**
```typescript
// 使用 UserManagementPage 在测试前创建企业用户
const employerUserData = {
  username: `test_employer_${Date.now()}`,
  password: 'password123',
  nickname: '测试企业用户',
  userType: UserType.EMPLOYER,
  companyId: 1,  // 使用测试公司
};
```

**测试清理策略：**
```typescript
test.afterEach(async ({ userManagementPage }) => {
  // 清理测试数据
  await userManagementPage.deleteUser(username);
});
```

### 小程序登录流程

**预期登录流程：**
1. 导航到 `/mini` 页面
2. 填写手机号（在 Story 12.2 中创建的企业用户的用户名）
3. 填写密码
4. 点击登录按钮
5. 验证登录成功（跳转到主页或显示用户信息）
6. 验证 token 存储在 localStorage 或 sessionStorage

**Token 验证方法：**
```typescript
// 验证 token 存储正确
const token = await enterpriseMiniPage.getToken();
expect(token).toBeTruthy();

// 验证 token 在页面刷新后仍然有效
await page.reload();
const tokenAfterReload = await enterpriseMiniPage.getToken();
expect(tokenAfterReload).toBe(token);
```

### 项目结构

**新建文件：**
- `web/tests/e2e/specs/mini/enterprise-mini-login.spec.ts`

**相关参考文件：**
- `web/tests/e2e/pages/mini/enterprise-mini.page.ts` (Story 12.4)
- `web/tests/e2e/pages/admin/user-management.page.ts` (Story 12.1)
- `web/tests/e2e/specs/admin/user-create-employer.spec.ts` (Story 12.2)

### 选择器策略

**优先级（遵循项目标准）：**
1. `data-testid` 属性（最高优先级）
2. ARIA 属性 + role
3. 文本内容（最低优先级，避免使用）

**Story 12.4 已添加的 data-testid：**
- `mini-login-page` - 页面容器
- `mini-phone-input` - 手机号输入框
- `mini-password-input` - 密码输入框
- `mini-login-button` - 登录按钮
- `mini-page-title` - 页面标题（Navbar 组件）
- `mini-user-info` - 用户信息显示

### TypeScript 类型定义

**测试数据类型：**
```typescript
interface EmployerUserCredentials {
  username: string;  // 手机号
  password: string;
  nickname: string;
  userType: 'EMPLOYER';
  companyId: number;
}
```

### 测试超时配置

**使用 TIMEOUTS 常量：**
```typescript
import { TIMEOUTS } from '../../utils/timeouts';

await expect(page.locator(selectors.userInfo)).toBeVisible({
  timeout: TIMEOUTS.default,
});
```

### 参考文档

**架构文档：**
- `_bmad-output/planning-artifacts/architecture.md`
- `docs/standards/e2e-radix-testing.md` (Radix UI 测试标准)

**相关 Story 文档：**
- `12-4-enterprise-mini-page-object.md` (企业小程序 Page Object)
- `12-2-create-employer-user.md` (后台创建企业用户测试)

### 小程序只读特性

根据 Epic 12 文档：
- 小程序只读，无写操作
- 登录测试主要关注认证和 token 管理
- 无需测试创建、编辑、删除等操作

### Playwright Fixture 集成

**在 `playwright.config.ts` 或测试文件中：**
```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);
  },
});
```

## Dev Agent Record

### Agent Model Used

Claude (d8d-model)

### Debug Log References

测试开发过程中的主要问题和解决方案：

1. **Taro Input 组件交互问题**
   - 问题：Playwright 的 `.fill()` 方法不支持 Taro 的自定义元素 (`<taro-input-core>`)
   - 解决：改用 `.click()` + `.type()` 组合，并使用 `Control+A` 全选已有内容

2. **Page Object 选择器严格模式冲突**
   - 问题：使用 `.or()` 选择器时，多个匹配元素导致严格模式冲突
   - 解决：简化为单一 `data-testid` 选择器

3. **登录失败测试的表单验证**
   - 问题：表单验证阻止无效手机号提交到后端
   - 解决：使用有效格式的手机号（11位，1开头）触发后端验证

4. **expect 未定义错误**
   - 问题：Page Object 文件缺少 `expect` 导入
   - 解决：从 `@playwright/test` 导入 `expect`

### Completion Notes List

**实现摘要：**
- ✅ 6 个测试通过（表单验证 + 登录失败）
- ⏸️ 8 个测试跳过（需要预创建用户：登录成功、Token 持久性、退出登录）

**已完成的验收标准：**
- AC2: 登录失败测试 ✅
- AC3: 表单验证测试 ✅
- AC7: 代码质量标准 ✅

**部分完成的验收标准：**
- AC1: 基本登录成功测试 ⏸️ (需要预创建用户)
- AC4: Token 持久性测试 ⏸️ (需要预创建用户)
- AC5: 退出登录测试 ⏸️ (需要预创建用户)
- AC6: 测试数据准备和清理 ⏸️ (部分完成)

**技术挑战：**
1. **Fixture 共享问题**：adminLoginPage 和 enterpriseMiniPage 共享同一个 `page` 实例
   - 解决方案：将需要用户创建的测试移至独立测试套件，使用专用 page context

2. **Taro 组件兼容性**：Taro Input 组件不是标准 HTML 元素
   - 解决方案：使用 `.type()` 代替 `.fill()`

**后续工作建议：**
1. 创建独立的测试套件处理需要预创建用户的测试（AC1, AC4, AC5）
2. 在独立套件中使用 beforeAll/afterAll 钩子管理测试数据
3. 确保测试套件之间不共享 page context

### File List

**新建文件：**
- `web/tests/e2e/specs/mini/enterprise-mini-login.spec.ts` - 企业小程序登录测试文件

**修改文件：**
- `web/tests/e2e/utils/test-setup.ts` - 添加 `enterpriseMiniPage` fixture
- `web/tests/e2e/pages/mini/enterprise-mini.page.ts` - 修复 Taro Input 组件交互方法

## Change Log

- 2026-01-13: Story 12.5 开发进行中
  - ✅ 创建测试文件和配置 fixtures
  - ✅ 实现表单验证测试 (AC3) - 3/3 测试通过
  - ✅ 实现登录失败测试 (AC2) - 3/3 测试通过
  - ✅ 验证代码质量 (AC7) - 类型检查通过
  - ⏸️ 跳过需要预创建用户的测试 (AC1, AC4, AC5)
  - 状态：in-progress (6/14 测试通过，8 个需要预创建用户的测试已跳过)
