login.spec.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { TIMEOUTS } from '../../utils/timeouts';
  2. import { test, expect } from '../../utils/test-setup';
  3. import testUsers from '../../fixtures/test-users.json' with { type: 'json' };
  4. test.describe.serial('登录页面 E2E 测试', () => {
  5. test.beforeEach(async ({ adminLoginPage }) => {
  6. await adminLoginPage.goto();
  7. });
  8. test('登录页面加载', async ({ adminLoginPage }) => {
  9. await adminLoginPage.expectToBeVisible();
  10. await expect(adminLoginPage.pageTitle).toHaveText('管理后台登录');
  11. await expect(adminLoginPage.welcomeText).toBeVisible();
  12. });
  13. test('成功登录', async ({ adminLoginPage, dashboardPage }) => {
  14. // 使用有效凭据登录
  15. await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
  16. // 验证跳转到仪表盘
  17. await dashboardPage.expectToBeVisible();
  18. await expect(dashboardPage.pageTitle).toHaveText('仪表盘');
  19. // 验证成功 toast 显示
  20. await expect(adminLoginPage.successToast).toBeVisible();
  21. await expect(adminLoginPage.successToast).toContainText('登录成功');
  22. });
  23. test('登录失败 - 错误密码', async ({ adminLoginPage }) => {
  24. // 使用错误密码尝试登录
  25. await adminLoginPage.login(testUsers.admin.username, 'wrongpassword');
  26. // 验证错误消息显示
  27. await expect(adminLoginPage.errorToast).toBeVisible();
  28. await expect(adminLoginPage.errorToast).toContainText('用户名或密码错误');
  29. // 验证仍然在登录页面
  30. await adminLoginPage.expectToBeVisible();
  31. });
  32. test('登录失败 - 不存在的用户', async ({ adminLoginPage }) => {
  33. // 使用不存在的用户尝试登录
  34. await adminLoginPage.login('nonexistentuser', 'anypassword');
  35. // 验证错误消息显示
  36. await expect(adminLoginPage.errorToast).toBeVisible();
  37. await expect(adminLoginPage.errorToast).toContainText('用户名或密码错误');
  38. // 验证仍然在登录页面
  39. await adminLoginPage.expectToBeVisible();
  40. });
  41. test('表单验证 - 空用户名', async ({ adminLoginPage }) => {
  42. // 不填写用户名直接提交
  43. await adminLoginPage.passwordInput.fill('password');
  44. await adminLoginPage.submitButton.click();
  45. // 验证验证错误显示
  46. await expect(adminLoginPage.usernameError).toBeVisible();
  47. await expect(adminLoginPage.usernameError).toContainText('请输入用户名');
  48. });
  49. test('表单验证 - 空密码', async ({ adminLoginPage }) => {
  50. // 不填写密码直接提交
  51. await adminLoginPage.usernameInput.fill('admin');
  52. await adminLoginPage.submitButton.click();
  53. // 验证验证错误显示
  54. await expect(adminLoginPage.passwordError).toBeVisible();
  55. await expect(adminLoginPage.passwordError).toContainText('请输入密码');
  56. });
  57. test('密码可见性切换', async ({ adminLoginPage }) => {
  58. // 初始状态密码应该被隐藏
  59. await expect(adminLoginPage.passwordInput).toHaveAttribute('type', 'password');
  60. // 点击显示密码按钮
  61. await adminLoginPage.togglePasswordButton.click();
  62. // 验证密码可见
  63. await expect(adminLoginPage.passwordInput).toHaveAttribute('type', 'text');
  64. // 再次点击隐藏密码
  65. await adminLoginPage.togglePasswordButton.click();
  66. // 验证密码隐藏
  67. await expect(adminLoginPage.passwordInput).toHaveAttribute('type', 'password');
  68. });
  69. test('测试账号信息显示', async ({ adminLoginPage }) => {
  70. // 验证测试账号信息存在
  71. await expect(adminLoginPage.testAccountInfo).toBeVisible();
  72. await expect(adminLoginPage.testAccountInfo).toContainText('admin');
  73. await expect(adminLoginPage.testAccountInfo).toContainText('admin123');
  74. });
  75. test('使用测试账号登录', async ({ adminLoginPage, dashboardPage }) => {
  76. // 使用测试账号信息登录
  77. await adminLoginPage.usernameInput.fill('admin');
  78. await adminLoginPage.passwordInput.fill('admin123');
  79. await adminLoginPage.submitButton.click();
  80. // 验证登录成功
  81. await dashboardPage.expectToBeVisible();
  82. await expect(dashboardPage.pageTitle).toHaveText('仪表盘');
  83. });
  84. test('登录页面样式和布局', async ({ adminLoginPage, page }) => {
  85. // 验证背景渐变
  86. await expect(adminLoginPage.backgroundElement).toHaveClass(/bg-gradient/);
  87. // 验证卡片阴影
  88. await expect(adminLoginPage.loginCard).toHaveClass(/shadow/);
  89. // 验证响应式设计
  90. await page.setViewportSize({ width: 375, height: 667 });
  91. await adminLoginPage.expectToBeVisible();
  92. // 移动端布局验证
  93. await expect(adminLoginPage.pageTitle).toBeVisible();
  94. await expect(adminLoginPage.usernameInput).toBeVisible();
  95. await expect(adminLoginPage.passwordInput).toBeVisible();
  96. });
  97. test('登录后刷新保持登录状态', async ({ adminLoginPage, dashboardPage, page }) => {
  98. // 先登录
  99. await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
  100. await dashboardPage.expectToBeVisible();
  101. // 刷新页面
  102. await page.reload();
  103. // 验证仍然保持登录状态
  104. await dashboardPage.expectToBeVisible();
  105. await expect(dashboardPage.pageTitle).toHaveText('仪表盘');
  106. });
  107. test('登出后重定向到登录页', async ({ adminLoginPage, dashboardPage, page }) => {
  108. // 先登录
  109. await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
  110. await dashboardPage.expectToBeVisible();
  111. // 执行登出
  112. await dashboardPage.logout();
  113. // 验证重定向到登录页
  114. await adminLoginPage.expectToBeVisible();
  115. await expect(adminLoginPage.pageTitle).toHaveText('管理后台登录');
  116. // 验证不能直接访问受保护页面
  117. await page.goto('/admin/dashboard');
  118. await adminLoginPage.expectToBeVisible(); // 应该重定向回登录页
  119. });
  120. test('多标签页登录状态同步', async ({ adminLoginPage, dashboardPage, context }) => {
  121. // 在第一个标签页登录
  122. await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
  123. await dashboardPage.expectToBeVisible();
  124. // 打开第二个标签页
  125. const newPage = await context.newPage();
  126. await newPage.goto('/admin/dashboard');
  127. // 验证第二个标签页也自动登录
  128. const newDashboardPage = dashboardPage.clone(newPage);
  129. await newDashboardPage.expectToBeVisible();
  130. // 在第一个标签页登出
  131. await dashboardPage.logout();
  132. await adminLoginPage.expectToBeVisible();
  133. // 验证第二个标签页也登出
  134. await newPage.reload();
  135. const newLoginPage = adminLoginPage.clone(newPage);
  136. await newLoginPage.expectToBeVisible();
  137. });
  138. test('登录加载状态显示', async ({ adminLoginPage }) => {
  139. // 填写登录信息
  140. await adminLoginPage.usernameInput.fill('admin');
  141. await adminLoginPage.passwordInput.fill('admin123');
  142. // 提交表单
  143. await adminLoginPage.submitButton.click();
  144. // 验证登录成功(简化测试,移除对加载指示器的依赖)
  145. await adminLoginPage.page.waitForLoadState('networkidle');
  146. });
  147. test('浏览器返回按钮行为', async ({ adminLoginPage, dashboardPage, page }) => {
  148. // 先登录
  149. await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
  150. await dashboardPage.expectToBeVisible();
  151. // 点击浏览器返回按钮
  152. await page.goBack();
  153. // 验证不会返回到登录页(应该停留在仪表盘或重定向)
  154. try {
  155. await adminLoginPage.expectToBeVisible({ timeout: TIMEOUTS.VERY_LONG });
  156. // 如果看到登录页,再次前进
  157. await page.goForward();
  158. await dashboardPage.expectToBeVisible();
  159. } catch {
  160. // 如果没看到登录页,说明行为正确
  161. await dashboardPage.expectToBeVisible();
  162. }
  163. });
  164. });