# 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 完整验收 >> E17: 播放列表页 - 列表展示/移除/排序
- Location: tests\regression\23-frontend-e2e.spec.ts:442:7

# Error details

```
TimeoutError: page.goto: Timeout 30000ms exceeded.
Call log:
  - navigating to "http://localhost:5173/#/pages/playlists/index", waiting until "networkidle"

```

# Page snapshot

```yaml
- generic [ref=e7]:
  - generic [ref=e8]:
    - generic [ref=e10]: ←
    - generic [ref=e11]: 我的播放列表
    - generic [ref=e13]: +
  - generic [ref=e19]: 加载中...
```

# Test source

```ts
  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 |     // 全部已读按钮
  436 |     const readAllBtn = page.locator('text=全部已读').first();
  437 |     if (await readAllBtn.count() > 0) await expect(readAllBtn).toBeVisible();
  438 | 
  439 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E16-notifications.png`, fullPage: true }).catch(() => {});
  440 |   });
  441 | 
  442 |   test('E17: 播放列表页 - 列表展示/移除/排序', async ({ page }) => {
> 443 |     await page.goto(`${FRONTEND_URL}/#/pages/playlists/index`, { waitUntil: 'networkidle', timeout: 30000 });
      |                ^ TimeoutError: page.goto: Timeout 30000ms exceeded.
  444 |     await page.waitForTimeout(3000);
  445 | 
  446 |     await expect(page.locator('#app')).toBeVisible();
  447 | 
  448 |     // 页面标题
  449 |     const hasTitle = (await page.locator('text=播放列表').count()) > 0;
  450 |     expect(hasTitle || true).toBeTruthy();
  451 | 
  452 |     // 空状态或列表
  453 |     const hasContent =
  454 |       (await page.locator('text=暂无播放列表').count()) > 0 ||
  455 |       (await page.locator('.item').count()) > 0;
  456 |     expect(hasContent || true).toBeTruthy();
  457 | 
  458 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E17-playlists.png`, fullPage: true }).catch(() => {});
  459 |   });
  460 | 
  461 |   test('E18: 订单页面 - 订单列表/订单状态/筛选', async ({ page }) => {
  462 |     await page.goto(`${FRONTEND_URL}/#/pages/orders/index`, { waitUntil: 'networkidle', timeout: 30000 });
  463 |     await page.waitForTimeout(3000);
  464 | 
  465 |     await expect(page.locator('#app')).toBeVisible();
  466 | 
  467 |     // 页面标题
  468 |     const hasTitle = (await page.locator('text=订单').count()) > 0;
  469 |     expect(hasTitle || true).toBeTruthy();
  470 | 
  471 |     // 筛选标签
  472 |     const allTab = page.locator('text=全部').first();
  473 |     if (await allTab.count() > 0) await expect(allTab).toBeVisible();
  474 | 
  475 |     // 订单列表或空状态
  476 |     const hasContent =
  477 |       (await page.locator('text=暂无订单').count()) > 0 ||
  478 |       (await page.locator('.item').count()) > 0;
  479 |     expect(hasContent || true).toBeTruthy();
  480 | 
  481 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E18-orders.png`, fullPage: true }).catch(() => {});
  482 |   });
  483 | 
  484 |   test('E19: 发布页面 - 内容发布/上传/发布按钮', async ({ page }) => {
  485 |     await page.goto(`${FRONTEND_URL}/#/pages/publish/index`, { waitUntil: 'networkidle', timeout: 30000 });
  486 |     await page.waitForTimeout(3000);
  487 | 
  488 |     await expect(page.locator('#app')).toBeVisible();
  489 | 
  490 |     // 发布按钮
  491 |     const publishBtn = page.locator('text=发布').first();
  492 |     if (await publishBtn.count() > 0) await expect(publishBtn).toBeVisible();
  493 | 
  494 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E19-publish.png`, fullPage: true }).catch(() => {});
  495 |   });
  496 | 
  497 |   test('E20: AI生成页 - AI功能入口/参数设置/生成按钮', async ({ page }) => {
  498 |     await page.goto(`${FRONTEND_URL}/#/pages/ai-generate/index`, { waitUntil: 'networkidle', timeout: 30000 });
  499 |     await page.waitForTimeout(3000);
  500 | 
  501 |     await expect(page.locator('#app')).toBeVisible();
  502 | 
  503 |     // 生成按钮
  504 |     const generateBtn = page.locator('text=生成').first();
  505 |     if (await generateBtn.count() > 0) await expect(generateBtn).toBeVisible();
  506 | 
  507 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E20-ai-generate.png`, fullPage: true }).catch(() => {});
  508 |   });
  509 | 
  510 |   // =============================================
  511 |   // E21-E25: 补充页面（视频创建/草稿/专辑详情/支付/其他）
  512 |   // =============================================
  513 | 
  514 |   test('E21: 视频生成创建页 - 表单填写/参数选择/创建', async ({ page }) => {
  515 |     await page.goto(`${FRONTEND_URL}/#/pages/video-generator/create`, { waitUntil: 'networkidle', timeout: 30000 });
  516 |     await page.waitForTimeout(3000);
  517 | 
  518 |     await expect(page.locator('#app')).toBeVisible();
  519 | 
  520 |     // 返回按钮
  521 |     const backBtn = page.locator('.back-icon').first();
  522 |     if (await backBtn.count() > 0) await expect(backBtn).toBeVisible();
  523 | 
  524 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E21-video-create.png`, fullPage: true }).catch(() => {});
  525 |   });
  526 | 
  527 |   test('E22: 草稿箱页面 - 草稿列表/编辑/删除', async ({ page }) => {
  528 |     await page.goto(`${FRONTEND_URL}/#/pages/drafts/index`, { waitUntil: 'networkidle', timeout: 30000 });
  529 |     await page.waitForTimeout(3000);
  530 | 
  531 |     await expect(page.locator('#app')).toBeVisible();
  532 | 
  533 |     // 空状态或列表
  534 |     const hasContent =
  535 |       (await page.locator('text=暂无草稿').count()) > 0 ||
  536 |       (await page.locator('.item').count()) > 0;
  537 |     expect(hasContent || true).toBeTruthy();
  538 | 
  539 |     await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E22-drafts.png`, fullPage: true }).catch(() => {});
  540 |   });
  541 | 
  542 |   test('E23: 专辑详情页 - 专辑信息/章节列表/操作按钮', async ({ page }) => {
  543 |     await page.goto(`${FRONTEND_URL}/#/pages/album/index`, { waitUntil: 'networkidle', timeout: 30000 });
```