143 lines
5.9 KiB
Markdown
143 lines
5.9 KiB
Markdown
|
|
# 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-04 正确凭据登录成功跳转
|
|||
|
|
- Location: e2e\admin\login.spec.ts:62:3
|
|||
|
|
|
|||
|
|
# Error details
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
TimeoutError: locator.fill: Timeout 10000ms exceeded.
|
|||
|
|
Call log:
|
|||
|
|
- waiting for locator('input[placeholder="请输入用户名"]')
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
# 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('乐绘世界创想活动乐园')
|
|||
|
|
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')
|
|||
|
|
| ^ TimeoutError: locator.fill: Timeout 10000ms exceeded.
|
|||
|
|
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 |
|
|||
|
|
```
|