12-5-enterprise-mini-login.md 10 KB

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

Status: ready-for-dev

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

  • [ ] 任务 1: 创建测试文件和基础设施 (AC: #7)

    • 1.1 创建 web/tests/e2e/specs/mini/enterprise-mini-login.spec.ts
    • 1.2 配置 test fixtures(adminLoginPage 用于准备数据, enterpriseMiniPage)
    • 1.3 添加测试前置条件(准备企业用户数据)
  • [ ] 任务 2: 实现基本登录成功测试 (AC: #1)

    • 2.1 编写"应该成功登录企业小程序"测试
    • 2.2 验证登录成功后页面变化
    • 2.3 验证 token 存储正确
  • [ ] 任务 3: 实现登录失败测试 (AC: #2)

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

    • 4.1 编写"手机号为空时显示错误提示"测试
    • 4.2 编写"密码为空时显示错误提示"测试
  • [ ] 任务 5: 实现 Token 持久性测试 (AC: #4)

    • 5.1 编写"页面刷新后 token 仍然有效"测试
    • 5.2 编写"使用已存储 token 继续访问"测试
  • [ ] 任务 6: 实现退出登录测试 (AC: #5)

    • 6.1 编写"成功退出登录"测试
    • 6.2 验证 token 被清除
    • 6.3 验证退出后无法访问需要认证的页面
  • [ ] 任务 7: 实现测试数据准备和清理策略 (AC: #6)

    • 7.1 添加 beforeAll 钩子准备测试用户
    • 7.2 添加 afterEach 钩子清理测试数据
    • 7.3 使用时间戳确保用户名唯一
  • [ ] 任务 8: 验证代码质量 (AC: #7)

    • 8.1 运行 pnpm typecheck 验证类型检查
    • 8.2 运行测试确保所有测试通过
    • 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 可用方法:

// 导航方法
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):

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 中学习到的测试数据准备模式:

企业用户创建模式:

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

测试清理策略:

test.afterEach(async ({ userManagementPage }) => {
  // 清理测试数据
  await userManagementPage.deleteUser(username);
});

小程序登录流程

预期登录流程:

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

Token 验证方法:

// 验证 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 类型定义

测试数据类型:

interface EmployerUserCredentials {
  username: string;  // 手机号
  password: string;
  nickname: string;
  userType: 'EMPLOYER';
  companyId: number;
}

测试超时配置

使用 TIMEOUTS 常量:

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 或测试文件中:

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

N/A - 开发尚未开始

Completion Notes List

待开发完成后填写

File List

待开发完成后填写

Change Log

  • 2026-01-13: Story 12.5 创建完成
    • 企业小程序登录测试需求
    • 7 个主要验收标准
    • 8 个任务/子任务
    • 状态:ready-for-dev