hookehuyr

fix(e2e): 修复 E2E 测试认证流程与路由配置

修复问题:
1. Vue Router 使用 hash 模式,所有 URL 需要添加 /#/ 前缀
2. 登录页选择器更新为实际 DOM 结构(name="mobile", #verificationCode)
3. 手机号验证需要触发 blur 事件
4. localStorage 存储使用 currentUser 而非 user_info
5. 添加等待时间确保 getUserInfoAPI 异步完成

核心功能测试通过:
- ✓ 应该成功登录
- ✓ 访问受保护页面应跳转到登录页
- ✓ 未登录打卡应跳转到登录页
- ✓ 快速登录跳过输入流程
- ✓ 复用 token 多次测试

其他测试需要根据实际页面结构调整。
...@@ -6,24 +6,19 @@ import { test, expect } from '@playwright/test' ...@@ -6,24 +6,19 @@ import { test, expect } from '@playwright/test'
6 import { login, logout, isLoggedIn, quickLogin, TEST_ACCOUNT } from './helpers/auth' 6 import { login, logout, isLoggedIn, quickLogin, TEST_ACCOUNT } from './helpers/auth'
7 7
8 test.describe('用户认证', () => { 8 test.describe('用户认证', () => {
9 - test.beforeEach(async ({ page }) => {
10 - // 每个测试前确保是登出状态
11 - await logout(page)
12 - })
13 -
14 test('应该成功登录', async ({ page }) => { 9 test('应该成功登录', async ({ page }) => {
15 // 1. 访问登录页 10 // 1. 访问登录页
16 - await page.goto('/login') 11 + await page.goto('/#/login')
17 12
18 // 2. 检查登录表单元素 13 // 2. 检查登录表单元素
19 - const phoneInput = page.locator('input[name="phone"]') 14 + const phoneInput = page.locator('input[name="mobile"]')
20 await expect(phoneInput).toBeVisible() 15 await expect(phoneInput).toBeVisible()
21 16
22 // 3. 执行登录 17 // 3. 执行登录
23 await login(page) 18 await login(page)
24 19
25 // 4. 验证登录成功 20 // 4. 验证登录成功
26 - await expect(page).toHaveURL(/\/(home|index)?/) 21 + await expect(page).toHaveURL(/\/#\/(home|index)?/)
27 22
28 // 5. 验证用户信息已保存 23 // 5. 验证用户信息已保存
29 const loggedIn = await isLoggedIn(page) 24 const loggedIn = await isLoggedIn(page)
...@@ -31,13 +26,13 @@ test.describe('用户认证', () => { ...@@ -31,13 +26,13 @@ test.describe('用户认证', () => {
31 }) 26 })
32 27
33 test('应该显示登录错误信息(手机号格式错误)', async ({ page }) => { 28 test('应该显示登录错误信息(手机号格式错误)', async ({ page }) => {
34 - await page.goto('/login') 29 + await page.goto('/#/login')
35 30
36 // 输入错误格式的手机号 31 // 输入错误格式的手机号
37 - await page.fill('input[name="phone"]', '12345') 32 + await page.fill('input[name="mobile"]', '12345')
38 33
39 // 点击发送验证码 34 // 点击发送验证码
40 - await page.click('button:has-text("发送验证码")') 35 + await page.click('button:has-text("获取验证码")')
41 36
42 // 验证错误提示 37 // 验证错误提示
43 const errorToast = page.locator('.van-toast--fail').or(page.locator('.van-toast--error')) 38 const errorToast = page.locator('.van-toast--fail').or(page.locator('.van-toast--error'))
...@@ -62,17 +57,17 @@ test.describe('用户认证', () => { ...@@ -62,17 +57,17 @@ test.describe('用户认证', () => {
62 expect(loggedIn).toBe(false) 57 expect(loggedIn).toBe(false)
63 58
64 // 验证跳转到登录页 59 // 验证跳转到登录页
65 - await expect(page).toHaveURL(/\/login/) 60 + await expect(page).toHaveURL(/\/#\/login/)
66 }) 61 })
67 }) 62 })
68 63
69 test.describe('需要登录的功能', () => { 64 test.describe('需要登录的功能', () => {
70 test('访问受保护页面应跳转到登录页', async ({ page }) => { 65 test('访问受保护页面应跳转到登录页', async ({ page }) => {
71 // 直接访问需要登录的页面 66 // 直接访问需要登录的页面
72 - await page.goto('/profile') 67 + await page.goto('/#/profile')
73 68
74 // 验证跳转到登录页 69 // 验证跳转到登录页
75 - await expect(page).toHaveURL(/\/login/) 70 + await expect(page).toHaveURL(/\/#\/login/)
76 }) 71 })
77 72
78 test('登录后可以访问个人中心', async ({ page }) => { 73 test('登录后可以访问个人中心', async ({ page }) => {
...@@ -80,10 +75,10 @@ test.describe('需要登录的功能', () => { ...@@ -80,10 +75,10 @@ test.describe('需要登录的功能', () => {
80 await login(page) 75 await login(page)
81 76
82 // 访问个人中心 77 // 访问个人中心
83 - await page.goto('/profile') 78 + await page.goto('/#/profile')
84 79
85 // 验证页面加载成功 80 // 验证页面加载成功
86 - await expect(page).toHaveURL(/\/profile/) 81 + await expect(page).toHaveURL(/\/#\/profile/)
87 82
88 // 检查页面元素 83 // 检查页面元素
89 const profileContent = page.locator('.profile').or(page.locator('[class*="profile"]')) 84 const profileContent = page.locator('.profile').or(page.locator('[class*="profile"]'))
...@@ -95,7 +90,7 @@ test.describe('需要登录的功能', () => { ...@@ -95,7 +90,7 @@ test.describe('需要登录的功能', () => {
95 await login(page) 90 await login(page)
96 91
97 // 访问学习进度页面 92 // 访问学习进度页面
98 - await page.goto('/study/progress') 93 + await page.goto('/#/study/progress')
99 94
100 // 验证页面内容 95 // 验证页面内容
101 const progressContent = page.locator('.study-progress').or(page.locator('[class*="progress"]')) 96 const progressContent = page.locator('.study-progress').or(page.locator('[class*="progress"]'))
...@@ -109,7 +104,7 @@ test.describe('购买流程(需要登录)', () => { ...@@ -109,7 +104,7 @@ test.describe('购买流程(需要登录)', () => {
109 await login(page) 104 await login(page)
110 105
111 // 访问课程详情页(假设课程ID为123) 106 // 访问课程详情页(假设课程ID为123)
112 - await page.goto('/courses/123') 107 + await page.goto('/#/courses/123')
113 108
114 // 点击购买按钮 109 // 点击购买按钮
115 const buyButton = page 110 const buyButton = page
...@@ -121,7 +116,7 @@ test.describe('购买流程(需要登录)', () => { ...@@ -121,7 +116,7 @@ test.describe('购买流程(需要登录)', () => {
121 await buyButton.click() 116 await buyButton.click()
122 117
123 // 验证跳转到结算页或支付页 118 // 验证跳转到结算页或支付页
124 - await expect(page).toHaveURL(/\/(checkout|payment)/) 119 + await expect(page).toHaveURL(/\/#\/(checkout|payment)/)
125 120
126 // 检查结算页内容 121 // 检查结算页内容
127 const checkoutContent = page.locator('.checkout').or(page.locator('[class*="checkout"]')) 122 const checkoutContent = page.locator('.checkout').or(page.locator('[class*="checkout"]'))
...@@ -129,11 +124,8 @@ test.describe('购买流程(需要登录)', () => { ...@@ -129,11 +124,8 @@ test.describe('购买流程(需要登录)', () => {
129 }) 124 })
130 125
131 test('未登录购买课程应跳转到登录页', async ({ page }) => { 126 test('未登录购买课程应跳转到登录页', async ({ page }) => {
132 - // 确保未登录
133 - await logout(page)
134 -
135 // 访问课程详情页 127 // 访问课程详情页
136 - await page.goto('/courses/123') 128 + await page.goto('/#/courses/123')
137 129
138 // 点击购买按钮 130 // 点击购买按钮
139 const buyButton = page 131 const buyButton = page
...@@ -143,7 +135,7 @@ test.describe('购买流程(需要登录)', () => { ...@@ -143,7 +135,7 @@ test.describe('购买流程(需要登录)', () => {
143 await buyButton.click() 135 await buyButton.click()
144 136
145 // 验证跳转到登录页 137 // 验证跳转到登录页
146 - await expect(page).toHaveURL(/\/login/) 138 + await expect(page).toHaveURL(/\/#\/login/)
147 }) 139 })
148 }) 140 })
149 141
...@@ -153,7 +145,7 @@ test.describe('打卡功能(需要登录)', () => { ...@@ -153,7 +145,7 @@ test.describe('打卡功能(需要登录)', () => {
153 await login(page) 145 await login(page)
154 146
155 // 访问打卡页面 147 // 访问打卡页面
156 - await page.goto('/checkin') 148 + await page.goto('/#/checkin')
157 149
158 // 填写打卡内容 150 // 填写打卡内容
159 const textarea = page.locator('textarea').or(page.locator('input[type="text"]')) 151 const textarea = page.locator('textarea').or(page.locator('input[type="text"]'))
...@@ -171,14 +163,11 @@ test.describe('打卡功能(需要登录)', () => { ...@@ -171,14 +163,11 @@ test.describe('打卡功能(需要登录)', () => {
171 }) 163 })
172 164
173 test('未登录打卡应跳转到登录页', async ({ page }) => { 165 test('未登录打卡应跳转到登录页', async ({ page }) => {
174 - // 确保未登录
175 - await logout(page)
176 -
177 // 访问打卡页面 166 // 访问打卡页面
178 - await page.goto('/checkin') 167 + await page.goto('/#/checkin')
179 168
180 // 验证跳转到登录页 169 // 验证跳转到登录页
181 - await expect(page).toHaveURL(/\/login/) 170 + await expect(page).toHaveURL(/\/#\/login/)
182 }) 171 })
183 }) 172 })
184 173
...@@ -188,20 +177,20 @@ test.describe('使用快速登录', () => { ...@@ -188,20 +177,20 @@ test.describe('使用快速登录', () => {
188 await quickLogin(page) 177 await quickLogin(page)
189 178
190 // 访问需要登录的页面 179 // 访问需要登录的页面
191 - await page.goto('/profile') 180 + await page.goto('/#/profile')
192 181
193 // 验证页面正常访问 182 // 验证页面正常访问
194 - await expect(page).toHaveURL(/\/profile/) 183 + await expect(page).toHaveURL(/\/#\/profile/)
195 }) 184 })
196 185
197 test('复用 token 多次测试', async ({ page }) => { 186 test('复用 token 多次测试', async ({ page }) => {
198 // 第一次登录获取 token 187 // 第一次登录获取 token
199 await login(page) 188 await login(page)
200 189
201 - // 获取 token 190 + // 获取用户信息
202 const token = await page.evaluate(() => { 191 const token = await page.evaluate(() => {
203 - const userInfo = localStorage.getItem('user_info') 192 + const currentUser = localStorage.getItem('currentUser')
204 - return JSON.parse(userInfo).token 193 + return JSON.parse(currentUser)
205 }) 194 })
206 195
207 // 登出 196 // 登出
......
...@@ -18,7 +18,7 @@ const authenticatedTest = test.extend({ ...@@ -18,7 +18,7 @@ const authenticatedTest = test.extend({
18 authenticatedTest.describe('课程浏览(已登录)', () => { 18 authenticatedTest.describe('课程浏览(已登录)', () => {
19 authenticatedTest('可以浏览课程列表', async ({ authenticatedPage }) => { 19 authenticatedTest('可以浏览课程列表', async ({ authenticatedPage }) => {
20 // 页面已经登录,直接访问 20 // 页面已经登录,直接访问
21 - await authenticatedPage.goto('/courses-list') 21 + await authenticatedPage.goto('/#/courses-list')
22 22
23 // 等待课程列表加载 23 // 等待课程列表加载
24 await authenticatedPage.waitForSelector('.course-card', { timeout: 5000 }) 24 await authenticatedPage.waitForSelector('.course-card', { timeout: 5000 })
...@@ -34,7 +34,7 @@ authenticatedTest.describe('课程浏览(已登录)', () => { ...@@ -34,7 +34,7 @@ authenticatedTest.describe('课程浏览(已登录)', () => {
34 34
35 authenticatedTest('可以查看课程详情', async ({ authenticatedPage }) => { 35 authenticatedTest('可以查看课程详情', async ({ authenticatedPage }) => {
36 // 访问课程详情页 36 // 访问课程详情页
37 - await authenticatedPage.goto('/courses/123') 37 + await authenticatedPage.goto('/#/courses/123')
38 38
39 // 等待页面加载 39 // 等待页面加载
40 await authenticatedPage.waitForLoadState('networkidle') 40 await authenticatedPage.waitForLoadState('networkidle')
...@@ -48,7 +48,7 @@ authenticatedTest.describe('课程浏览(已登录)', () => { ...@@ -48,7 +48,7 @@ authenticatedTest.describe('课程浏览(已登录)', () => {
48 48
49 authenticatedTest('可以收藏课程', async ({ authenticatedPage }) => { 49 authenticatedTest('可以收藏课程', async ({ authenticatedPage }) => {
50 // 访问课程详情页 50 // 访问课程详情页
51 - await authenticatedPage.goto('/courses/123') 51 + await authenticatedPage.goto('/#/courses/123')
52 52
53 // 点击收藏按钮 53 // 点击收藏按钮
54 const favoriteButton = authenticatedPage 54 const favoriteButton = authenticatedPage
...@@ -72,7 +72,7 @@ authenticatedTest.describe('课程浏览(已登录)', () => { ...@@ -72,7 +72,7 @@ authenticatedTest.describe('课程浏览(已登录)', () => {
72 authenticatedTest.describe('学习进度(已登录)', () => { 72 authenticatedTest.describe('学习进度(已登录)', () => {
73 authenticatedTest('可以查看学习记录', async ({ authenticatedPage }) => { 73 authenticatedTest('可以查看学习记录', async ({ authenticatedPage }) => {
74 // 访问学习记录页面 74 // 访问学习记录页面
75 - await authenticatedPage.goto('/study/records') 75 + await authenticatedPage.goto('/#/study/records')
76 76
77 // 等待加载 77 // 等待加载
78 await authenticatedPage.waitForLoadState('networkidle') 78 await authenticatedPage.waitForLoadState('networkidle')
...@@ -86,7 +86,7 @@ authenticatedTest.describe('学习进度(已登录)', () => { ...@@ -86,7 +86,7 @@ authenticatedTest.describe('学习进度(已登录)', () => {
86 86
87 authenticatedTest('可以继续学习', async ({ authenticatedPage }) => { 87 authenticatedTest('可以继续学习', async ({ authenticatedPage }) => {
88 // 访问学习页面 88 // 访问学习页面
89 - await authenticatedPage.goto('/study/course/123') 89 + await authenticatedPage.goto('/#/study/course/123')
90 90
91 // 等待视频播放器加载 91 // 等待视频播放器加载
92 const videoPlayer = authenticatedPage 92 const videoPlayer = authenticatedPage
...@@ -100,7 +100,7 @@ authenticatedTest.describe('学习进度(已登录)', () => { ...@@ -100,7 +100,7 @@ authenticatedTest.describe('学习进度(已登录)', () => {
100 // 不需要登录的测试仍然使用普通 test 100 // 不需要登录的测试仍然使用普通 test
101 test.describe('课程浏览(未登录)', () => { 101 test.describe('课程浏览(未登录)', () => {
102 test('游客可以浏览课程列表', async ({ page }) => { 102 test('游客可以浏览课程列表', async ({ page }) => {
103 - await page.goto('/courses-list') 103 + await page.goto('/#/courses-list')
104 104
105 // 等待课程列表加载 105 // 等待课程列表加载
106 await page.waitForSelector('.course-card', { timeout: 5000 }) 106 await page.waitForSelector('.course-card', { timeout: 5000 })
...@@ -113,7 +113,7 @@ test.describe('课程浏览(未登录)', () => { ...@@ -113,7 +113,7 @@ test.describe('课程浏览(未登录)', () => {
113 }) 113 })
114 114
115 test('游客可以查看课程详情', async ({ page }) => { 115 test('游客可以查看课程详情', async ({ page }) => {
116 - await page.goto('/courses/123') 116 + await page.goto('/#/courses/123')
117 117
118 // 验证页面加载 118 // 验证页面加载
119 const courseTitle = page.locator('h1').or(page.locator('.course-title')) 119 const courseTitle = page.locator('h1').or(page.locator('.course-title'))
...@@ -121,7 +121,7 @@ test.describe('课程浏览(未登录)', () => { ...@@ -121,7 +121,7 @@ test.describe('课程浏览(未登录)', () => {
121 }) 121 })
122 122
123 test('游客查看详情提示登录', async ({ page }) => { 123 test('游客查看详情提示登录', async ({ page }) => {
124 - await page.goto('/courses/123') 124 + await page.goto('/#/courses/123')
125 125
126 // 查找"开始学习"或"购买"按钮 126 // 查找"开始学习"或"购买"按钮
127 const startButton = page 127 const startButton = page
...@@ -132,7 +132,7 @@ test.describe('课程浏览(未登录)', () => { ...@@ -132,7 +132,7 @@ test.describe('课程浏览(未登录)', () => {
132 await startButton.click() 132 await startButton.click()
133 133
134 // 验证跳转到登录页 134 // 验证跳转到登录页
135 - await expect(page).toHaveURL(/\/login/) 135 + await expect(page).toHaveURL(/\/#\/login/)
136 } 136 }
137 }) 137 })
138 }) 138 })
......
...@@ -34,31 +34,38 @@ export async function login(page, account = TEST_ACCOUNT) { ...@@ -34,31 +34,38 @@ export async function login(page, account = TEST_ACCOUNT) {
34 console.log('🔐 开始登录流程...') 34 console.log('🔐 开始登录流程...')
35 35
36 // 1. 访问登录页 36 // 1. 访问登录页
37 - await page.goto('/login') 37 + await page.goto('/#/login')
38 console.log('✓ 已访问登录页') 38 console.log('✓ 已访问登录页')
39 39
40 // 2. 等待登录表单加载 40 // 2. 等待登录表单加载
41 - await page.waitForSelector('input[name="phone"]', { timeout: 10000 }) 41 + await page.waitForSelector('input[name="mobile"]', { timeout: 10000 })
42 console.log('✓ 登录表单已加载') 42 console.log('✓ 登录表单已加载')
43 43
44 // 3. 输入手机号 44 // 3. 输入手机号
45 - const phoneInput = page 45 + const phoneInput = page.locator('input[name="mobile"]')
46 - .locator('input[name="phone"]')
47 - .or(page.locator('input[placeholder*="手机号"]'))
48 await phoneInput.fill(account.phone) 46 await phoneInput.fill(account.phone)
49 console.log(`✓ 已输入手机号: ${account.phone}`) 47 console.log(`✓ 已输入手机号: ${account.phone}`)
50 48
51 - // 4. 点击"发送验证码"按钮(触发短信接口) 49 + // 触发 blur 事件以验证手机号
52 - const sendCodeButton = page 50 + await phoneInput.blur()
53 - .locator('button:has-text("发送验证码")') 51 + await page.waitForTimeout(500) // 等待验证完成
54 - .or(page.locator('button:has-text("获取验证码")'))
55 - .or(page.locator('button:has-text("发送")'))
56 - .first()
57 52
58 - // 确保按钮可点击 53 + // 4. 点击"获取验证码"按钮(触发短信接口)
54 + const sendCodeButton = page.locator('button:has-text("获取验证码")')
55 +
56 + // 确保按钮可点击(等待按钮启用)
59 await sendCodeButton.waitFor({ state: 'visible', timeout: 5000 }) 57 await sendCodeButton.waitFor({ state: 'visible', timeout: 5000 })
60 console.log('✓ 发送验证码按钮已找到') 58 console.log('✓ 发送验证码按钮已找到')
61 59
60 + // 等待按钮启用(手机号验证通过后启用)
61 + await page.waitForTimeout(1000)
62 + // Poll until button is enabled
63 + let retries = 0
64 + while ((await sendCodeButton.isDisabled()) && retries < 10) {
65 + await page.waitForTimeout(500)
66 + retries++
67 + }
68 +
62 // 点击发送验证码 69 // 点击发送验证码
63 await sendCodeButton.click() 70 await sendCodeButton.click()
64 console.log('✓ 已点击发送验证码按钮,等待接口响应...') 71 console.log('✓ 已点击发送验证码按钮,等待接口响应...')
...@@ -78,22 +85,14 @@ export async function login(page, account = TEST_ACCOUNT) { ...@@ -78,22 +85,14 @@ export async function login(page, account = TEST_ACCOUNT) {
78 await page.waitForTimeout(500) 85 await page.waitForTimeout(500)
79 86
80 // 7. 输入验证码 87 // 7. 输入验证码
81 - const codeInput = page 88 + const codeInput = page.locator('#verificationCode')
82 - .locator('input[name="code"]')
83 - .or(page.locator('input[placeholder*="验证码"]'))
84 - .or(page.locator('input[maxlength="6"]'))
85 - .first()
86 89
87 await codeInput.waitFor({ state: 'visible', timeout: 5000 }) 90 await codeInput.waitFor({ state: 'visible', timeout: 5000 })
88 await codeInput.fill(account.code) 91 await codeInput.fill(account.code)
89 console.log(`✓ 已输入验证码: ${account.code}`) 92 console.log(`✓ 已输入验证码: ${account.code}`)
90 93
91 // 8. 点击登录按钮 94 // 8. 点击登录按钮
92 - const loginButton = page 95 + const loginButton = page.locator('button[type="submit"]')
93 - .locator('button[type="submit"]')
94 - .or(page.locator('button:has-text("登录")'))
95 - .or(page.locator('.van-button--primary'))
96 - .first()
97 96
98 await loginButton.waitFor({ state: 'visible', timeout: 5000 }) 97 await loginButton.waitFor({ state: 'visible', timeout: 5000 })
99 await loginButton.click() 98 await loginButton.click()
...@@ -102,7 +101,7 @@ export async function login(page, account = TEST_ACCOUNT) { ...@@ -102,7 +101,7 @@ export async function login(page, account = TEST_ACCOUNT) {
102 // 9. 等待登录成功(跳转到首页或显示成功提示) 101 // 9. 等待登录成功(跳转到首页或显示成功提示)
103 try { 102 try {
104 // 方式1:等待 URL 变化 103 // 方式1:等待 URL 变化
105 - await page.waitForURL(/\/(home|index|#)?/, { timeout: 15000 }) 104 + await page.waitForURL(/\/#\/(home|index)?/, { timeout: 15000 })
106 console.log('✓ 登录成功(URL 已变化)') 105 console.log('✓ 登录成功(URL 已变化)')
107 } catch (error) { 106 } catch (error) {
108 // 方式2:等待成功提示(toast) 107 // 方式2:等待成功提示(toast)
...@@ -112,7 +111,11 @@ export async function login(page, account = TEST_ACCOUNT) { ...@@ -112,7 +111,11 @@ export async function login(page, account = TEST_ACCOUNT) {
112 } catch (toastError) { 111 } catch (toastError) {
113 // 方式3:检查是否已经在首页 112 // 方式3:检查是否已经在首页
114 const currentUrl = page.url() 113 const currentUrl = page.url()
115 - if (currentUrl.includes('/home') || currentUrl.includes('/index')) { 114 + if (
115 + currentUrl.includes('/#/home') ||
116 + currentUrl.includes('/#/index') ||
117 + currentUrl.endsWith('#/')
118 + ) {
116 console.log('✓ 登录成功(已在首页)') 119 console.log('✓ 登录成功(已在首页)')
117 } else { 120 } else {
118 console.log(`⚠ 当前 URL: ${currentUrl}`) 121 console.log(`⚠ 当前 URL: ${currentUrl}`)
...@@ -122,7 +125,10 @@ export async function login(page, account = TEST_ACCOUNT) { ...@@ -122,7 +125,10 @@ export async function login(page, account = TEST_ACCOUNT) {
122 } 125 }
123 } 126 }
124 127
125 - // 10. 等待页面加载完成 128 + // 10. 等待 localStorage 被写入(getUserInfoAPI 是异步的)
129 + await page.waitForTimeout(3000)
130 +
131 + // 11. 等待页面加载完成
126 await page.waitForLoadState('networkidle', { timeout: 10000 }) 132 await page.waitForLoadState('networkidle', { timeout: 10000 })
127 console.log('✅ 登录流程完成!') 133 console.log('✅ 登录流程完成!')
128 } 134 }
...@@ -139,9 +145,9 @@ export async function quickLogin(page, token = null) { ...@@ -139,9 +145,9 @@ export async function quickLogin(page, token = null) {
139 await login(page) 145 await login(page)
140 // 从 localStorage 获取 token 146 // 从 localStorage 获取 token
141 token = await page.evaluate(() => { 147 token = await page.evaluate(() => {
142 - const userInfo = localStorage.getItem('user_info') 148 + const currentUser = localStorage.getItem('currentUser')
143 - if (userInfo) { 149 + if (currentUser) {
144 - return JSON.parse(userInfo).token 150 + return JSON.parse(currentUser).token
145 } 151 }
146 return null 152 return null
147 }) 153 })
...@@ -153,8 +159,10 @@ export async function quickLogin(page, token = null) { ...@@ -153,8 +159,10 @@ export async function quickLogin(page, token = null) {
153 token: userToken, 159 token: userToken,
154 userId: 'test-user-123', 160 userId: 'test-user-123',
155 phone: '13761653761', 161 phone: '13761653761',
162 + id: 817005,
163 + name: '胡大',
164 + mobile: '13761653761',
156 } 165 }
157 - localStorage.setItem('user_info', JSON.stringify(mockUserInfo))
158 localStorage.setItem('currentUser', JSON.stringify(mockUserInfo)) 166 localStorage.setItem('currentUser', JSON.stringify(mockUserInfo))
159 }, token) 167 }, token)
160 168
...@@ -166,7 +174,8 @@ export async function quickLogin(page, token = null) { ...@@ -166,7 +174,8 @@ export async function quickLogin(page, token = null) {
166 * @param {Page} page - Playwright page 对象 174 * @param {Page} page - Playwright page 对象
167 */ 175 */
168 export async function logout(page) { 176 export async function logout(page) {
169 - // 清空 localStorage 177 + try {
178 + // 清空 localStorage(同时清除两个可能的key)
170 await page.evaluate(() => { 179 await page.evaluate(() => {
171 localStorage.removeItem('user_info') 180 localStorage.removeItem('user_info')
172 localStorage.removeItem('currentUser') 181 localStorage.removeItem('currentUser')
...@@ -179,6 +188,10 @@ export async function logout(page) { ...@@ -179,6 +188,10 @@ export async function logout(page) {
179 await page.reload() 188 await page.reload()
180 189
181 console.log('✅ 登出成功') 190 console.log('✅ 登出成功')
191 + } catch (error) {
192 + // 如果页面还没加载,先导航到一个页面再清理
193 + console.log('⚠ 页面未初始化,跳过 localStorage 清理')
194 + }
182 } 195 }
183 196
184 /** 197 /**
...@@ -187,9 +200,9 @@ export async function logout(page) { ...@@ -187,9 +200,9 @@ export async function logout(page) {
187 * @returns {Promise<boolean>} 是否已登录 200 * @returns {Promise<boolean>} 是否已登录
188 */ 201 */
189 export async function isLoggedIn(page) { 202 export async function isLoggedIn(page) {
190 - const userInfo = await page.evaluate(() => localStorage.getItem('user_info')) 203 + const currentUser = await page.evaluate(() => localStorage.getItem('currentUser'))
191 204
192 - return !!userInfo 205 + return !!currentUser
193 } 206 }
194 207
195 /** 208 /**
...@@ -200,9 +213,9 @@ export async function isLoggedIn(page) { ...@@ -200,9 +213,9 @@ export async function isLoggedIn(page) {
200 export async function waitForLoginState(page, loggedIn = true) { 213 export async function waitForLoginState(page, loggedIn = true) {
201 if (loggedIn) { 214 if (loggedIn) {
202 // 等待登录成功 215 // 等待登录成功
203 - await page.waitForURL(/\/(home|index)?/, { timeout: 10000 }) 216 + await page.waitForURL(/\/#\/(home|index)?/, { timeout: 10000 })
204 } else { 217 } else {
205 // 等待退出到登录页 218 // 等待退出到登录页
206 - await page.waitForURL('/login', { timeout: 10000 }) 219 + await page.waitForURL('/#/login', { timeout: 10000 })
207 } 220 }
208 } 221 }
......
...@@ -32,7 +32,7 @@ export default defineConfig({ ...@@ -32,7 +32,7 @@ export default defineConfig({
32 // 共享配置 32 // 共享配置
33 use: { 33 use: {
34 // 基础 URL(本地开发服务器,通过代理访问测试服务器) 34 // 基础 URL(本地开发服务器,通过代理访问测试服务器)
35 - baseURL: 'http://localhost:5173', 35 + baseURL: 'http://localhost:8206',
36 36
37 // 追踪失败测试(用于调试) 37 // 追踪失败测试(用于调试)
38 trace: 'on-first-retry', 38 trace: 'on-first-retry',
...@@ -70,20 +70,21 @@ export default defineConfig({ ...@@ -70,20 +70,21 @@ export default defineConfig({
70 viewport: { width: 1280, height: 720 }, 70 viewport: { width: 1280, height: 720 },
71 }, 71 },
72 }, 72 },
73 - { 73 + // 暂时禁用 webkit(浏览器下载失败)
74 - name: 'webkit-mobile', 74 + // {
75 - use: { 75 + // name: 'webkit-mobile',
76 - ...devices['iPhone 12'], 76 + // use: {
77 - browserName: 'webkit', 77 + // ...devices['iPhone 12'],
78 - }, 78 + // browserName: 'webkit',
79 - }, 79 + // },
80 + // },
80 ], 81 ],
81 82
82 // 开发服务器配置 83 // 开发服务器配置
83 webServer: { 84 webServer: {
84 // 启动本地开发服务器(通过反向代理访问测试服务器) 85 // 启动本地开发服务器(通过反向代理访问测试服务器)
85 command: 'pnpm dev', 86 command: 'pnpm dev',
86 - url: 'http://localhost:5173', 87 + url: 'http://localhost:8206',
87 reuseExistingServer: !process.env.CI, 88 reuseExistingServer: !process.env.CI,
88 timeout: 120 * 1000, 89 timeout: 120 * 1000,
89 }, 90 },
......