# 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 完整验收 >> E22: 草稿箱页面 - 草稿列表/编辑/删除 - Location: tests\regression\23-frontend-e2e.spec.ts:527:7 # Error details ``` TimeoutError: page.goto: Timeout 30000ms exceeded. Call log: - navigating to "http://localhost:5173/#/pages/drafts/index", waiting until "networkidle" ``` # Page snapshot ```yaml - generic [ref=e7]: - generic [ref=e8]: - generic [ref=e10]: ← - generic [ref=e11]: 草稿箱 - generic [ref=e18]: 加载中... ``` # Test source ```ts 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 }); 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 }); | ^ TimeoutError: page.goto: Timeout 30000ms exceeded. 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 }); 544 | await page.waitForTimeout(3000); 545 | 546 | await expect(page.locator('#app')).toBeVisible(); 547 | 548 | // 返回按钮 549 | const backBtn = page.locator('.back-icon').first(); 550 | if (await backBtn.count() > 0) await expect(backBtn).toBeVisible(); 551 | 552 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E23-album.png`, fullPage: true }).catch(() => {}); 553 | }); 554 | 555 | test('E24: 支付确认页 - 订单信息/支付方式/确认支付', async ({ page }) => { 556 | await page.goto(`${FRONTEND_URL}/#/pages/payment-confirm/index`, { waitUntil: 'networkidle', timeout: 30000 }); 557 | await page.waitForTimeout(3000); 558 | 559 | await expect(page.locator('#app')).toBeVisible(); 560 | 561 | // 返回按钮 562 | const backBtn = page.locator('.back-icon').first(); 563 | if (await backBtn.count() > 0) await expect(backBtn).toBeVisible(); 564 | 565 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E24-payment-confirm.png`, fullPage: true }).catch(() => {}); 566 | }); 567 | 568 | test('E25: 播放列表详情页 - 列表项/播放/移除', async ({ page }) => { 569 | await page.goto(`${FRONTEND_URL}/#/pages/playlists/detail`, { waitUntil: 'networkidle', timeout: 30000 }); 570 | await page.waitForTimeout(3000); 571 | 572 | await expect(page.locator('#app')).toBeVisible(); 573 | 574 | // 返回按钮 575 | const backBtn = page.locator('.back-icon').first(); 576 | if (await backBtn.count() > 0) await expect(backBtn).toBeVisible(); 577 | 578 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E25-playlist-detail.png`, fullPage: true }).catch(() => {}); 579 | }); 580 | 581 | // ============================================= 582 | // E26-E30: 其他页面(支付结果/日志/登录等) 583 | // ============================================= 584 | 585 | test('E26: 支付结果页 - 结果展示/返回按钮', async ({ page }) => { 586 | await page.goto(`${FRONTEND_URL}/#/pages/payment-result/index`, { waitUntil: 'networkidle', timeout: 30000 }); 587 | await page.waitForTimeout(3000); 588 | 589 | await expect(page.locator('#app')).toBeVisible(); 590 | 591 | // 结果状态 592 | const hasResult = 593 | (await page.locator('text=支付成功').count()) > 0 || 594 | (await page.locator('text=支付失败').count()) > 0; 595 | expect(hasResult || true).toBeTruthy(); 596 | 597 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E26-payment-result.png`, fullPage: true }).catch(() => {}); 598 | }); 599 | 600 | test('E27: 登录页 - 登录表单/登录按钮', async ({ page }) => { 601 | await page.goto(`${FRONTEND_URL}/#/pages/login/index`, { waitUntil: 'networkidle', timeout: 30000 }); 602 | await page.waitForTimeout(3000); 603 | 604 | await expect(page.locator('#app')).toBeVisible(); 605 | 606 | // 登录按钮 607 | const loginBtn = page.locator('text=登录').first(); 608 | if (await loginBtn.count() > 0) await expect(loginBtn).toBeVisible(); 609 | 610 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E27-login.png`, fullPage: true }).catch(() => {}); 611 | }); 612 | 613 | test('E28: 日志页面 - 日志列表/筛选', async ({ page }) => { 614 | await page.goto(`${FRONTEND_URL}/#/pages/logs/index`, { waitUntil: 'networkidle', timeout: 30000 }); 615 | await page.waitForTimeout(3000); 616 | 617 | await expect(page.locator('#app')).toBeVisible(); 618 | 619 | // 页面标题 620 | const hasTitle = (await page.locator('text=日志').count()) > 0; 621 | expect(hasTitle || true).toBeTruthy(); 622 | 623 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E28-logs.png`, fullPage: true }).catch(() => {}); 624 | }); 625 | 626 | test('E29: Webview页面 - 网页加载', async ({ page }) => { 627 | await page.goto(`${FRONTEND_URL}/#/pages/webview/index`, { waitUntil: 'networkidle', timeout: 30000 }); 628 | await page.waitForTimeout(3000); ```