import { test, expect } from '../../utils/test-setup'; import { AdminLoginPage } from '../../pages/admin/login.page'; import { UserManagementPage } from '../../pages/admin/user-management.page'; import { UserType } from '@d8d/shared-types'; /** * 企业小程序登录 E2E 测试 * * 测试企业用户通过小程序登录的功能,验证: * - 登录失败场景 * - 表单验证 * - 登录成功场景(使用独立 context 创建测试用户) * - Token 持久性 * - 退出登录 * * @see {@link ../pages/mini/enterprise-mini.page.ts} EnterpriseMiniPage */ /** * 创建测试用户的辅助函数 * * 在独立的 browser context 中通过管理后台创建测试用户, * 避免与小程序登录测试共享 page 实例导致的冲突。 * * @param browser Playwright browser 实例 * @param userData 用户数据 * @returns 创建的用户名和密码 */ async function createTestUser(browser: typeof test['fixtures']['browser'], userData: { username: string; password: string; nickname?: string; phone?: string; email?: string; userType?: UserType; companyName?: string; }): Promise<{ username: string; password: string }> { // 创建独立的 browser context const adminContext = await browser.newContext(); try { // 在独立 context 中创建 page const adminPage = await adminContext.newPage(); // 创建管理后台 Page Objects const adminLoginPage = new AdminLoginPage(adminPage); const userManagementPage = new UserManagementPage(adminPage); // 登录管理后台 await adminLoginPage.goto(); await adminLoginPage.login('admin', 'admin123'); // 导航到用户管理页面 await userManagementPage.goto(); // 创建测试用户(使用 EMPLOYER 类型,企业用户) const result = await userManagementPage.createUser({ username: userData.username, password: userData.password, nickname: userData.nickname || '测试企业用户', phone: userData.phone, email: userData.email, userType: userData.userType || UserType.EMPLOYER, }, userData.companyName || '测试公司_E2E'); // 验证创建成功 expect(result.success).toBe(true); return { username: userData.username, password: userData.password }; } finally { // 清理:关闭独立 context await adminContext.close(); } } test.describe('企业小程序登录功能', () => { test.describe('表单验证测试 (AC3)', () => { test('手机号为空时应该显示错误提示', async ({ enterpriseMiniPage }) => { // 导航到登录页面 await enterpriseMiniPage.goto(); // 不填写任何信息,直接点击登录按钮 await enterpriseMiniPage.clickLoginButton(); // 验证仍然在登录页面(未跳转) const currentUrl = enterpriseMiniPage.page.url(); expect(currentUrl).toContain('/mini'); // 验证登录页面容器仍然可见 await expect(enterpriseMiniPage.loginPage).toBeVisible(); }); test('密码为空时应该显示错误提示', async ({ enterpriseMiniPage }) => { // 导航到登录页面 await enterpriseMiniPage.goto(); // 只填写手机号,不填写密码 await enterpriseMiniPage.fillPhone('13800138000'); // 尝试点击登录按钮 await enterpriseMiniPage.clickLoginButton(); // 验证仍然在登录页面(未跳转) const currentUrl = enterpriseMiniPage.page.url(); expect(currentUrl).toContain('/mini'); // 验证登录页面容器仍然可见 await expect(enterpriseMiniPage.loginPage).toBeVisible(); }); test('表单验证错误提示应该清晰可见', async ({ enterpriseMiniPage }) => { // 导航到登录页面 await enterpriseMiniPage.goto(); // 不填写任何信息,直接点击登录 await enterpriseMiniPage.clickLoginButton(); // 验证仍在登录页面 expect(enterpriseMiniPage.page.url()).toContain('/mini'); // 验证登录页面容器仍然可见 await expect(enterpriseMiniPage.loginPage).toBeVisible(); }); }); test.describe('登录失败测试 (AC2)', () => { test('使用不存在的用户名登录失败', async ({ enterpriseMiniPage }) => { // 导航到登录页面 await enterpriseMiniPage.goto(); // 使用不存在的用户名尝试登录(使用有效的手机号格式) const fakeUsername = `199${Date.now().toString().slice(-8)}`; // 11位数字,符合手机号格式 await enterpriseMiniPage.login(fakeUsername, 'password123'); // 验证显示错误提示 await enterpriseMiniPage.expectLoginError('用户名或密码错误'); // 验证未存储 token const token = await enterpriseMiniPage.getToken(); expect(token).toBeNull(); }); test('使用错误的密码登录失败', async ({ enterpriseMiniPage }) => { // 导航到登录页面 await enterpriseMiniPage.goto(); // 使用不存在的用户尝试登录(使用有效的手机号格式) await enterpriseMiniPage.login('19912345678', 'wrongpassword'); // 验证显示错误提示 await enterpriseMiniPage.expectLoginError('用户名或密码错误'); // 验证未存储 token const token = await enterpriseMiniPage.getToken(); expect(token).toBeNull(); }); test('错误提示内容应该正确', async ({ enterpriseMiniPage }) => { // 导航到登录页面 await enterpriseMiniPage.goto(); // 使用错误的凭据尝试登录(使用有效的手机号格式) await enterpriseMiniPage.login('19987654321', 'wrongpassword'); // 验证错误提示包含"用户名或密码错误"或类似内容 await enterpriseMiniPage.expectLoginError(); }); }); test.describe.serial('基本登录成功测试 (AC1)', () => { test('应该成功登录企业小程序', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户(使用独立 context) const testUsername = `mini_test_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `138${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序测试用户', userType: UserType.EMPLOYER, }); // 2. 使用该用户登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); // 3. 验证登录成功(URL 跳转到 dashboard 或显示用户信息) await enterpriseMiniPage.expectLoginSuccess(); }); test('登录成功后应该显示用户信息', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_info_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `139${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序信息测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); // 3. 验证登录成功 await enterpriseMiniPage.expectLoginSuccess(); // 4. 验证用户信息显示(检查 userInfo 元素或 dashboard 可见) const currentUrl = enterpriseMiniPage.page.url(); expect(currentUrl).toMatch(/dashboard|pages/); }); test('登录成功后 token 应该正确存储', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_token_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `137${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序 Token 测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); // 3. 验证登录成功 await enterpriseMiniPage.expectLoginSuccess(); // 4. 验证 token 被正确存储 const token = await enterpriseMiniPage.getToken(); expect(token).not.toBeNull(); expect(token?.length).toBeGreaterThan(0); }); }); test.describe.serial('Token 持久性测试 (AC4)', () => { test('页面刷新后 token 仍然有效', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_refresh_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `136${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序刷新测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); await enterpriseMiniPage.expectLoginSuccess(); // 3. 获取登录后的 token const tokenBeforeRefresh = await enterpriseMiniPage.getToken(); expect(tokenBeforeRefresh).not.toBeNull(); // 4. 刷新页面 await enterpriseMiniPage.page.reload(); // 5. 等待页面加载完成 await enterpriseMiniPage.page.waitForLoadState('domcontentloaded'); // 6. 验证 token 仍然存在 const tokenAfterRefresh = await enterpriseMiniPage.getToken(); expect(tokenAfterRefresh).toBe(tokenBeforeRefresh); }); test('使用已存储 token 可以继续访问', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_persist_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `135${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序持久化测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); await enterpriseMiniPage.expectLoginSuccess(); // 3. 获取 token const token = await enterpriseMiniPage.getToken(); expect(token).not.toBeNull(); // 4. 重新导航到登录页面(模拟关闭后重新打开) await enterpriseMiniPage.goto(); // 5. 验证由于 token 存在,页面自动跳转到 dashboard await enterpriseMiniPage.page.waitForURL( url => url.pathname.includes('/dashboard') || url.pathname.includes('/pages'), { timeout: 10000 } ).catch(() => { // 如果没有自动跳转,检查当前 URL const currentUrl = enterpriseMiniPage.page.url(); expect(currentUrl).toMatch(/dashboard|pages/); }); }); }); test.describe.serial('退出登录测试 (AC5)', () => { test('应该成功退出登录', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_logout_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `134${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序退出测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); await enterpriseMiniPage.expectLoginSuccess(); // 3. 退出登录 await enterpriseMiniPage.logout(); // 4. 验证返回到登录页面 await enterpriseMiniPage.expectLoggedOut(); // 5. 验证 URL 返回到登录页面 const currentUrl = enterpriseMiniPage.page.url(); expect(currentUrl).toContain('/mini'); }); test('退出后 token 应该被清除', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_token_clear_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `133${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序 Token 清除测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); await enterpriseMiniPage.expectLoginSuccess(); // 3. 验证 token 存在 const tokenBeforeLogout = await enterpriseMiniPage.getToken(); expect(tokenBeforeLogout).not.toBeNull(); // 4. 退出登录 await enterpriseMiniPage.logout(); await enterpriseMiniPage.expectLoggedOut(); // 5. 验证 token 已被清除 const tokenAfterLogout = await enterpriseMiniPage.getToken(); expect(tokenAfterLogout).toBeNull(); }); test('退出后无法访问需要认证的页面', async ({ enterpriseMiniPage, browser }) => { // 1. 创建测试用户 const testUsername = `mini_auth_${Date.now()}`; const testPassword = 'Test123!@#'; const testPhone = `132${Date.now().toString().slice(-8)}`; await createTestUser(browser, { username: testUsername, password: testPassword, phone: testPhone, nickname: '小程序认证测试', userType: UserType.EMPLOYER, }); // 2. 登录小程序 await enterpriseMiniPage.goto(); await enterpriseMiniPage.login(testPhone, testPassword); await enterpriseMiniPage.expectLoginSuccess(); // 3. 退出登录 await enterpriseMiniPage.logout(); await enterpriseMiniPage.expectLoggedOut(); // 4. 尝试直接访问需要认证的页面(dashboard) await enterpriseMiniPage.page.goto('/mini/dashboard'); // 5. 验证被重定向回登录页面 await enterpriseMiniPage.page.waitForLoadState('domcontentloaded'); const currentUrl = enterpriseMiniPage.page.url(); expect(currentUrl).toContain('/mini'); // 6. 验证登录页面可见 await expect(enterpriseMiniPage.loginPage).toBeVisible(); }); }); });