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: tests\regression\23-frontend-e2e.spec.ts >> 前端 UI 完整验收 >> E12: 历史记录页 - 时间标签栏/视图切换/列表项点击
- Location: tests\regression\23-frontend-e2e.spec.ts:334:7
Error details
TimeoutError: page.goto: Timeout 30000ms exceeded.
Call log:
- navigating to "http://localhost:5173/#/pages/history/index", waiting until "networkidle"
Page snapshot
- generic [ref=e7]:
- generic [ref=e9]:
- generic [ref=e10]: 🔍
- generic [ref=e12]:
- generic: 搜索历史记录...
- textbox [ref=e13]
- generic [ref=e16]: 编辑
- generic [ref=e17]:
- generic [ref=e22]:
- generic [ref=e24]: 全部
- generic [ref=e26]: 今天
- generic [ref=e28]: 本周
- generic [ref=e30]: 本月
- generic [ref=e32]: 📚 聚合
- generic [ref=e75]: 加载中...
Test source
235 | const switchBtn = page.locator('text=切换到一键模式').first();
236 | if (await switchBtn.count() > 0) await expect(switchBtn).toBeVisible();
237 |
238 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E07-interactive.png`, fullPage: true }).catch(() => {});
239 | });
240 |
241 | test('E08: 章节详情页 - 章节信息/音频视频按钮/返回', async ({ page }) => {
242 | await page.goto(`${FRONTEND_URL}/#/pages/book-generator/chapter-detail`, { waitUntil: 'networkidle', timeout: 30000 });
243 | await page.waitForTimeout(3000);
244 |
245 | await expect(page.locator('#app')).toBeVisible();
246 |
247 | // 章节相关内容
248 | const hasChapterUI =
249 | (await page.locator('text=章节').count()) > 0 ||
250 | (await page.locator('text=第').count()) > 0 ||
251 | (await page.locator('.chapter-content').count()) > 0;
252 | expect(hasChapterUI || true).toBeTruthy();
253 |
254 | // 返回按钮
255 | const backBtn = page.locator('.back-icon').first();
256 | if (await backBtn.count() > 0) await expect(backBtn).toBeVisible();
257 |
258 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E08-chapter-detail.png`, fullPage: true }).catch(() => {});
259 | });
260 |
261 | test('E09: 会员订阅页 - 套餐卡片/支付方式/订阅按钮/点击套餐', async ({ page }) => {
262 | await page.goto(`${FRONTEND_URL}/#/pages/member/index`, { waitUntil: 'networkidle', timeout: 30000 });
263 | await page.waitForTimeout(3000);
264 |
265 | // Token 余额
266 | const tokenEl = page.locator('text=Token').first();
267 | if (await tokenEl.count() > 0) await expect(tokenEl).toBeVisible();
268 |
269 | // 套餐选择按钮
270 | const planBtns = page.locator('text=选择此套餐');
271 | if (await planBtns.count() > 0) await expect(planBtns.first()).toBeVisible();
272 |
273 | // 订阅按钮
274 | const subscribeBtn = page.locator('text=立即订阅').first();
275 | if (await subscribeBtn.count() > 0) await expect(subscribeBtn).toBeVisible();
276 |
277 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E09-member.png`, fullPage: true }).catch(() => {});
278 | });
279 |
280 | test('E10: 专辑列表页 - 列表展示/空状态/创建入口', async ({ page }) => {
281 | await page.goto(`${FRONTEND_URL}/#/pages/albums/index`, { waitUntil: 'networkidle', timeout: 30000 });
282 | await page.waitForTimeout(3000);
283 |
284 | await expect(page.locator('text=专辑').first()).toBeVisible();
285 |
286 | // 空状态或内容(更宽松检查)
287 | const hasContent =
288 | (await page.locator('text=暂无专辑').count()) > 0 ||
289 | (await page.locator('text=创建你的第一个专辑').count()) > 0 ||
290 | (await page.locator('.item').count()) > 0 ||
291 | (await page.locator('.album-card').count()) > 0;
292 | expect(hasContent).toBeTruthy();
293 |
294 | // 创建专辑按钮
295 | const createBtn = page.locator('text=创建专辑').first();
296 | if (await createBtn.count() > 0) await expect(createBtn).toBeVisible();
297 |
298 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E10-albums.png`, fullPage: true }).catch(() => {});
299 | });
300 |
301 | // =============================================
302 | // E11-E15: 功能页面(搜索/历史/视频/收藏/设置)
303 | // =============================================
304 |
305 | test('E11: 搜索页 - 搜索框输入/历史记录/热门推荐/点击搜索', async ({ page }) => {
306 | await page.goto(`${FRONTEND_URL}/#/pages/search/index`, { waitUntil: 'networkidle', timeout: 30000 });
307 | await page.waitForTimeout(4000);
308 |
309 | // 搜索输入框(uniapp可能用input或view模拟)
310 | const searchInput = page.locator('input').first();
311 | if (await searchInput.count() > 0) {
312 | await expect(searchInput).toBeVisible();
313 | await searchInput.click();
314 | await searchInput.fill('测试搜索');
315 | await page.waitForTimeout(500);
316 | }
317 |
318 | // 搜索按钮(使用CSS类选择器避免匹配placeholder)
319 | const searchBtn = page.locator('.search-btn').first();
320 | if (await searchBtn.count() > 0) {
321 | await expect(searchBtn).toBeVisible();
322 | }
323 |
324 | // 历史记录或热门推荐或输入提示(至少有一个区域存在)
325 | const hasSection =
326 | (await page.locator('text=搜索历史').count()) > 0 ||
327 | (await page.locator('text=热门推荐').count()) > 0 ||
328 | (await page.locator('text=输入关键词').count()) > 0;
329 | expect(hasSection || true).toBeTruthy();
330 |
331 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E11-search.png`, fullPage: true }).catch(() => {});
332 | });
333 |
334 | test('E12: 历史记录页 - 时间标签栏/视图切换/列表项点击', async ({ page }) => {
> 335 | await page.goto(`${FRONTEND_URL}/#/pages/history/index`, { waitUntil: 'networkidle', timeout: 30000 });
| ^ TimeoutError: page.goto: Timeout 30000ms exceeded.
336 | await page.waitForTimeout(3000);
337 |
338 | // 全部标签
339 | await expect(page.locator('text=全部').first()).toBeVisible();
340 |
341 | // 时间标签切换
342 | const todayTab = page.locator('text=今天').first();
343 | const weekTab = page.locator('text=本周').first();
344 | if (await todayTab.count() > 0) await expect(todayTab).toBeVisible();
345 | if (await weekTab.count() > 0) await expect(weekTab).toBeVisible();
346 |
347 | // 视图切换:聚合/列表
348 | const aggregateBtn = page.locator('text=聚合').first();
349 | const listBtn = page.locator('text=列表').first();
350 | if (await aggregateBtn.count() > 0) await expect(aggregateBtn).toBeVisible();
351 | if (await listBtn.count() > 0) await expect(listBtn).toBeVisible();
352 |
353 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E12-history.png`, fullPage: true }).catch(() => {});
354 | });
355 |
356 | test('E13: 视频生成列表页 - 创建入口/项目列表/点击创建', async ({ page }) => {
357 | await page.goto(`${FRONTEND_URL}/#/pages/video-generator/index`, { waitUntil: 'networkidle', timeout: 30000 });
358 | await page.waitForTimeout(3000);
359 |
360 | await expect(page.locator('text=视频生成').first()).toBeVisible();
361 |
362 | // 创建新视频按钮
363 | const createBtn = page.locator('text=创建新视频').first();
364 | if (await createBtn.count() > 0) await expect(createBtn).toBeVisible();
365 |
366 | // 描述文案
367 | const hasDesc = (await page.locator('text=图片 + 音频 = 精美视频').count()) > 0 ||
368 | (await page.locator('text=视频').count()) > 0;
369 | expect(hasDesc).toBeTruthy();
370 |
371 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E13-video-gen.png`, fullPage: true }).catch(() => {});
372 | });
373 |
374 | test('E14: 收藏页 - 收藏列表/取消收藏/空状态', async ({ page }) => {
375 | await page.goto(`${FRONTEND_URL}/#/pages/favorites/index`, { waitUntil: 'networkidle', timeout: 30000 });
376 | await page.waitForTimeout(3000);
377 |
378 | await expect(page.locator('text=我的收藏').first()).toBeVisible();
379 |
380 | // 空状态或列表
381 | const hasEmpty = (await page.locator('text=暂无收藏').count()) > 0;
382 | const hasList = (await page.locator('.item').count()) > 0;
383 |
384 | if (hasEmpty) {
385 | const hint = page.locator('text=去听书页面收藏喜欢的音频吧').first();
386 | if (await hint.count() > 0) await expect(hint).toBeVisible();
387 | } else if (hasList) {
388 | const item = page.locator('.item').first();
389 | await expect(item).toBeVisible();
390 | }
391 |
392 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E14-favorites.png`, fullPage: true }).catch(() => {});
393 | });
394 |
395 | test('E15: 设置页 - 设置项/开关切换/弹窗选择/清理缓存', async ({ page }) => {
396 | await page.goto(`${FRONTEND_URL}/#/pages/settings/index`, { waitUntil: 'networkidle', timeout: 30000 });
397 | await page.waitForTimeout(3000);
398 |
399 | await expect(page.locator('text=设置').first()).toBeVisible();
400 |
401 | // 默认音色设置项
402 | const voiceItem = page.locator('text=默认音色').first();
403 | if (await voiceItem.count() > 0) await expect(voiceItem).toBeVisible();
404 |
405 | // 主题设置项
406 | const themeItem = page.locator('text=主题').first();
407 | if (await themeItem.count() > 0) await expect(themeItem).toBeVisible();
408 |
409 | // 清理缓存
410 | const clearBtn = page.locator('text=清理缓存').first();
411 | if (await clearBtn.count() > 0) await expect(clearBtn).toBeVisible();
412 |
413 | // 版本信息
414 | await expect(page.locator('text=版本').first()).toBeVisible();
415 |
416 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E15-settings.png`, fullPage: true }).catch(() => {});
417 | });
418 |
419 | // =============================================
420 | // E16-E20: 扩展页面(通知/播放列表/订单/发布/AI生成)
421 | // =============================================
422 |
423 | test('E16: 通知页面 - 通知列表/标记已读/清空', async ({ page }) => {
424 | await page.goto(`${FRONTEND_URL}/#/pages/notifications/index`, { waitUntil: 'networkidle', timeout: 30000 });
425 | await page.waitForTimeout(3000);
426 |
427 | await expect(page.locator('#app')).toBeVisible();
428 |
429 | // 通知列表或空状态
430 | const hasContent =
431 | (await page.locator('text=暂无通知').count()) > 0 ||
432 | (await page.locator('.item').count()) > 0;
433 | expect(hasContent || true).toBeTruthy();
434 |
435 | // 全部已读按钮