# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: admin\login.spec.ts >> 管理端登录流程 >> L-01 管理端登录页正常渲染
- Location: e2e\admin\login.spec.ts:14:3

# Error details

```
Error: expect(locator).toHaveText(expected) failed

Locator: locator('.login-header h2')
Expected: "智创未来"
Timeout: 10000ms
Error: element(s) not found

Call log:
  - Expect "toHaveText" with timeout 10000ms
  - waiting for locator('.login-header h2')

```

# Test source

```ts
  1   | import { test, expect } from '../fixtures/admin.fixture'
  2   | import { setupApiMocks, TENANT_CODE, MOCK_TOKEN } from '../fixtures/admin.fixture'
  3   | 
  4   | /**
  5   |  * 登录流程测试
  6   |  * 测试管理端登录页面的各项功能
  7   |  */
  8   | 
  9   | test.describe('管理端登录流程', () => {
  10  |   test.beforeEach(async ({ page }) => {
  11  |     await setupApiMocks(page)
  12  |   })
  13  | 
  14  |   test('L-01 管理端登录页正常渲染', async ({ page }) => {
  15  |     await page.goto(`/${TENANT_CODE}/login`)
  16  | 
  17  |     // 验证页面标题
> 18  |     await expect(page.locator('.login-header h2')).toHaveText('智创未来')
      |                                                    ^ Error: expect(locator).toHaveText(expected) failed
  19  | 
  20  |     // 验证表单字段可见
  21  |     await expect(page.locator('input[placeholder="请输入用户名"]')).toBeVisible()
  22  |     await expect(page.locator('input[placeholder="请输入密码"]')).toBeVisible()
  23  | 
  24  |     // 验证登录按钮可见
  25  |     await expect(page.locator('button.login-btn')).toBeVisible()
  26  |     // Ant Design 按钮文本可能有空格，使用正则匹配
  27  |     await expect(page.locator('button.login-btn')).toHaveText(/登\s*录/)
  28  |   })
  29  | 
  30  |   test('L-02 空表单提交显示校验错误', async ({ page }) => {
  31  |     await page.goto(`/${TENANT_CODE}/login`)
  32  | 
  33  |     // 开发模式会自动填充 admin/admin123，先清空字段
  34  |     const usernameInput = page.locator('input[placeholder="请输入用户名"]')
  35  |     const passwordInput = page.locator('input[type="password"]')
  36  |     await usernameInput.clear()
  37  |     await passwordInput.clear()
  38  | 
  39  |     // 点击提交按钮触发 Ant Design 表单校验（html-type="submit"）
  40  |     await page.locator('button.login-btn').click()
  41  | 
  42  |     // Ant Design Vue 表单校验失败时会显示错误提示
  43  |     await expect(
  44  |       page.locator('.ant-form-item-explain-error, .ant-form-item-explain, .ant-form-item-with-help, .has-error').first()
  45  |     ).toBeVisible({ timeout: 5000 })
  46  |   })
  47  | 
  48  |   test('L-03 错误密码登录失败', async ({ page }) => {
  49  |     await page.goto(`/${TENANT_CODE}/login`)
  50  | 
  51  |     // 填写错误的用户名和密码
  52  |     await page.locator('input[placeholder="请输入用户名"]').fill('wrong')
  53  |     await page.locator('input[type="password"]').fill('wrongpassword')
  54  | 
  55  |     // 点击登录
  56  |     await page.locator('button.login-btn').click()
  57  | 
  58  |     // 验证错误提示信息
  59  |     await expect(page.locator('.ant-message')).toBeVisible({ timeout: 5000 })
  60  |   })
  61  | 
  62  |   test('L-04 正确凭据登录成功跳转', async ({ page }) => {
  63  |     await page.goto(`/${TENANT_CODE}/login`)
  64  | 
  65  |     // 填写正确的用户名和密码
  66  |     await page.locator('input[placeholder="请输入用户名"]').fill('admin')
  67  |     await page.locator('input[type="password"]').fill('admin123')
  68  | 
  69  |     // 点击登录
  70  |     await page.locator('button.login-btn').click()
  71  | 
  72  |     // 验证跳转到管理端页面（离开登录页）
  73  |     await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15_000 })
  74  | 
  75  |     // 验证侧边栏可见（说明进入了管理端布局）
  76  |     await expect(page.locator('.custom-sider')).toBeVisible({ timeout: 10_000 })
  77  |   })
  78  | 
  79  |   test('L-05 登录后 Token 存储正确', async ({ page }) => {
  80  |     await page.goto(`/${TENANT_CODE}/login`)
  81  | 
  82  |     // 填写并提交登录
  83  |     await page.locator('input[placeholder="请输入用户名"]').fill('admin')
  84  |     await page.locator('input[type="password"]').fill('admin123')
  85  |     await page.locator('button.login-btn').click()
  86  | 
  87  |     // 等待跳转
  88  |     await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15_000 })
  89  | 
  90  |     // 验证 Cookie 中包含 token
  91  |     const cookies = await page.context().cookies()
  92  |     const tokenCookie = cookies.find((c) => c.name === 'token')
  93  |     expect(tokenCookie).toBeDefined()
  94  |     expect(tokenCookie!.value.length).toBeGreaterThan(0)
  95  |   })
  96  | 
  97  |   test('L-06 退出登录清除状态', async ({ page }) => {
  98  |     await page.goto(`/${TENANT_CODE}/login`)
  99  | 
  100 |     // 先登录
  101 |     await page.locator('input[placeholder="请输入用户名"]').fill('admin')
  102 |     await page.locator('input[type="password"]').fill('admin123')
  103 |     await page.locator('button.login-btn').click()
  104 |     await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15_000 })
  105 |     await expect(page.locator('.custom-sider')).toBeVisible({ timeout: 10_000 })
  106 | 
  107 |     // 点击用户头像区域
  108 |     await page.locator('.user-info').click()
  109 | 
  110 |     // 点击退出登录
  111 |     await page.locator('text=退出登录').click()
  112 | 
  113 |     // 验证跳转回登录页
  114 |     await page.waitForURL(/\/login/, { timeout: 10_000 })
  115 |     await expect(page.locator('.login-container')).toBeVisible()
  116 |   })
  117 | })
  118 | 
```