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: regression\23-frontend-e2e.spec.ts >> 前端 UI 完整验收 >> E01: 首页 - 搜索栏/通知铃铛/最近收听/点击搜索
- Location: regression\23-frontend-e2e.spec.ts:21:7
Error details
Error: expect(locator).toBeVisible() failed
Locator: locator('.search-bar').first()
Expected: visible
Timeout: 15000ms
Error: element(s) not found
Call log:
- Expect "toBeVisible" with timeout 15000ms
- waiting for locator('.search-bar').first()
Test source
1 | /**
2 | * L2 回归测试:前端 UI 功能验收 - 完整版
3 | *
4 | * 测试范围:
5 | * 1. 所有页面加载与渲染
6 | * 2. 所有可点击元素的交互
7 | * 3. TabBar 导航切换
8 | *
9 | * 风格:Navigate → Locate → Click/Assert → Screenshot
10 | */
11 | import { test, expect } from '@playwright/test';
12 |
13 | const FRONTEND_URL = process.env.FRONTEND_URL || 'http://localhost:5173';
14 | const SCREENSHOT_DIR = 'test-results/screenshots';
15 |
16 | test.describe('前端 UI 完整验收', () => {
17 | // =============================================
18 | // E01-E05: TabBar 主页面(首页/生成/书籍/我的/播放)
19 | // =============================================
20 |
21 | test('E01: 首页 - 搜索栏/通知铃铛/最近收听/点击搜索', async ({ page }) => {
22 | await page.goto(`${FRONTEND_URL}/#/pages/index/index`, { waitUntil: 'networkidle', timeout: 30000 });
23 | await page.waitForTimeout(4000);
24 |
25 | // 搜索栏(uniapp用text模拟placeholder)
26 | const searchBar = page.locator('.search-bar').first();
> 27 | await expect(searchBar).toBeVisible();
| ^ Error: expect(locator).toBeVisible() failed
28 |
29 | // 搜索图标
30 | const searchIcon = page.locator('.search-icon').first();
31 | if (await searchIcon.count() > 0) await expect(searchIcon).toBeVisible();
32 |
33 | // 搜索占位文字
34 | const searchPlaceholder = page.locator('.search-placeholder').first();
35 | if (await searchPlaceholder.count() > 0) await expect(searchPlaceholder).toBeVisible();
36 |
37 | // 通知铃铛
38 | const bell = page.locator('.bell-icon').first();
39 | if (await bell.count() > 0) await expect(bell).toBeVisible();
40 |
41 | // 页面主体存在(骨架屏/内容/空状态至少有一个)
42 | const hasContent =
43 | (await page.locator('.recent-section').count()) > 0 ||
44 | (await page.locator('.onboarding').count()) > 0 ||
45 | (await page.locator('.empty').count()) > 0 ||
46 | (await page.locator('.skeleton-grid').count()) > 0 ||
47 | (await page.locator('.album-grid').count()) > 0;
48 | expect(hasContent).toBeTruthy();
49 |
50 | // 点击搜索栏区域
51 | await searchBar.click();
52 | await page.waitForTimeout(1000);
53 |
54 | // 验证导航到搜索页
55 | await expect(page.locator('#app')).toBeVisible();
56 |
57 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E01-index.png`, fullPage: true }).catch(() => {});
58 | });
59 |
60 | test('E02: 生成音频页 - 文本输入/音色选择/生成按钮/点击交互', async ({ page }) => {
61 | await page.goto(`${FRONTEND_URL}/#/pages/create/index`, { waitUntil: 'networkidle', timeout: 30000 });
62 | await page.waitForTimeout(3000);
63 |
64 | // 页面标题
65 | await expect(page.locator('text=开始生成').first()).toBeVisible();
66 |
67 | // 文本输入框(uniapp textarea)
68 | const textarea = page.locator('textarea').first();
69 | if (await textarea.count() > 0) {
70 | await expect(textarea).toBeVisible();
71 | await textarea.click();
72 | await textarea.fill('测试文本');
73 | await page.waitForTimeout(300);
74 | }
75 |
76 | // AI 生成内容按钮
77 | const aiBtn = page.locator('text=AI 生成内容').first();
78 | if (await aiBtn.count() > 0) await expect(aiBtn).toBeVisible();
79 |
80 | // 音色选择区卡片
81 | const voiceSection = page.locator('text=选择音色').first();
82 | if (await voiceSection.count() > 0) await expect(voiceSection).toBeVisible();
83 |
84 | // 参数调节区卡片
85 | const paramsSection = page.locator('text=参数调节').first();
86 | if (await paramsSection.count() > 0) await expect(paramsSection).toBeVisible();
87 |
88 | // 生成音频按钮
89 | const generateBtn = page.locator('text=开始生成').last();
90 | await expect(generateBtn).toBeVisible();
91 |
92 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E02-create.png`, fullPage: true }).catch(() => {});
93 | });
94 |
95 | test('E03: 书籍生成列表页 - 空状态/创建按钮/点击创建', async ({ page }) => {
96 | await page.goto(`${FRONTEND_URL}/#/pages/book-generator/index`, { waitUntil: 'networkidle', timeout: 30000 });
97 | await page.waitForTimeout(3000);
98 |
99 | await expect(page.locator('text=书籍生成').first()).toBeVisible();
100 |
101 | // 创建新书籍按钮
102 | const createBtn = page.locator('text=创建新书籍').first();
103 | if (await createBtn.count() > 0) await expect(createBtn).toBeVisible();
104 |
105 | // 空状态或列表(更宽松的检查)
106 | const hasContent =
107 | (await page.locator('text=暂无书籍').count()) > 0 ||
108 | (await page.locator('text=创建第一本').count()) > 0 ||
109 | (await page.locator('.item').count()) > 0 ||
110 | (await page.locator('.album-card').count()) > 0 ||
111 | (await page.locator('.book-card').count()) > 0;
112 | expect(hasContent).toBeTruthy();
113 |
114 | await page.screenshot({ path: `${SCREENSHOT_DIR}/e2e-E03-book-list.png`, fullPage: true }).catch(() => {});
115 | });
116 |
117 | test('E04: 我的页面 - 用户卡片/菜单项点击/退出登录', async ({ page }) => {
118 | await page.goto(`${FRONTEND_URL}/#/pages/mine/index`, { waitUntil: 'networkidle', timeout: 30000 });
119 | await page.waitForTimeout(3000);
120 |
121 | await expect(page.locator('#app')).toBeVisible();
122 |
123 | // 点击"书籍生成"按钮,验证跳转到书籍生成页
124 | const bookGenBtn = page.locator('text=书籍生成').first();
125 | if (await bookGenBtn.count() > 0) {
126 | await bookGenBtn.click();
127 | await page.waitForTimeout(2000);