# 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

```yaml
- 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

```ts
  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 |     // 全部已读按钮
```