@zmice/zc 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -9
- package/dist/cli/__tests__/platform.test.js +169 -2
- package/dist/cli/__tests__/platform.test.js.map +1 -1
- package/dist/cli/__tests__/surface.test.js +52 -0
- package/dist/cli/__tests__/surface.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.d.ts +2 -0
- package/dist/cli/__tests__/team.test.d.ts.map +1 -0
- package/dist/cli/__tests__/team.test.js +29 -0
- package/dist/cli/__tests__/team.test.js.map +1 -0
- package/dist/cli/__tests__/upstream.test.js +4 -0
- package/dist/cli/__tests__/upstream.test.js.map +1 -1
- package/dist/cli/platform.d.ts +11 -3
- package/dist/cli/platform.d.ts.map +1 -1
- package/dist/cli/platform.js +186 -49
- package/dist/cli/platform.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +114 -4
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/upstream.d.ts +1 -0
- package/dist/cli/upstream.d.ts.map +1 -1
- package/dist/cli/upstream.js +84 -5
- package/dist/cli/upstream.js.map +1 -1
- package/dist/node_modules/@zmice/platform-core/dist/index.d.ts +37 -3
- package/dist/node_modules/@zmice/platform-core/dist/index.d.ts.map +1 -1
- package/dist/node_modules/@zmice/platform-core/dist/index.js +68 -0
- package/dist/node_modules/@zmice/platform-core/dist/index.js.map +1 -1
- package/dist/node_modules/@zmice/platform-core/dist/index.test.js +44 -1
- package/dist/node_modules/@zmice/platform-core/dist/index.test.js.map +1 -1
- package/dist/runtime/__tests__/worktree-manager.test.js +63 -1
- package/dist/runtime/__tests__/worktree-manager.test.js.map +1 -1
- package/dist/runtime/worktree-manager.d.ts +26 -1
- package/dist/runtime/worktree-manager.d.ts.map +1 -1
- package/dist/runtime/worktree-manager.js +126 -12
- package/dist/runtime/worktree-manager.js.map +1 -1
- package/dist/team/__tests__/orchestrator.test.js +40 -0
- package/dist/team/__tests__/orchestrator.test.js.map +1 -1
- package/dist/team/__tests__/planner.test.d.ts +2 -0
- package/dist/team/__tests__/planner.test.d.ts.map +1 -0
- package/dist/team/__tests__/planner.test.js +43 -0
- package/dist/team/__tests__/planner.test.js.map +1 -0
- package/dist/team/__tests__/task-queue.test.js +18 -0
- package/dist/team/__tests__/task-queue.test.js.map +1 -1
- package/dist/team/orchestrator.d.ts +2 -1
- package/dist/team/orchestrator.d.ts.map +1 -1
- package/dist/team/orchestrator.js +29 -10
- package/dist/team/orchestrator.js.map +1 -1
- package/dist/team/planner.d.ts +27 -0
- package/dist/team/planner.d.ts.map +1 -0
- package/dist/team/planner.js +120 -0
- package/dist/team/planner.js.map +1 -0
- package/dist/team/task-queue.d.ts +3 -0
- package/dist/team/task-queue.d.ts.map +1 -1
- package/dist/team/task-queue.js +11 -2
- package/dist/team/task-queue.js.map +1 -1
- package/dist/utils/qwen-extension-cli.d.ts.map +1 -1
- package/dist/utils/qwen-extension-cli.js +23 -0
- package/dist/utils/qwen-extension-cli.js.map +1 -1
- package/dist/utils/qwen-extension-cli.test.js +40 -0
- package/dist/utils/qwen-extension-cli.test.js.map +1 -1
- package/package.json +3 -3
- package/vendor/node_modules/@zmice/platform-core/dist/index.d.ts +37 -3
- package/vendor/node_modules/@zmice/platform-core/dist/index.d.ts.map +1 -1
- package/vendor/node_modules/@zmice/platform-core/dist/index.js +68 -0
- package/vendor/node_modules/@zmice/platform-core/dist/index.js.map +1 -1
- package/vendor/node_modules/@zmice/platform-core/dist/index.test.js +44 -1
- package/vendor/node_modules/@zmice/platform-core/dist/index.test.js.map +1 -1
- package/vendor/packages/platform-claude/dist/index.d.ts.map +1 -1
- package/vendor/packages/platform-claude/dist/index.js +12 -70
- package/vendor/packages/platform-claude/dist/index.js.map +1 -1
- package/vendor/packages/platform-codex/dist/generate.d.ts +1 -1
- package/vendor/packages/platform-codex/dist/generate.d.ts.map +1 -1
- package/vendor/packages/platform-codex/dist/generate.js +1 -1
- package/vendor/packages/platform-codex/dist/generate.js.map +1 -1
- package/vendor/packages/platform-codex/dist/index.d.ts +16 -1
- package/vendor/packages/platform-codex/dist/index.d.ts.map +1 -1
- package/vendor/packages/platform-codex/dist/index.js +268 -67
- package/vendor/packages/platform-codex/dist/index.js.map +1 -1
- package/vendor/packages/platform-codex/dist/index.test.js +102 -7
- package/vendor/packages/platform-codex/dist/index.test.js.map +1 -1
- package/vendor/packages/platform-opencode/dist/index.d.ts.map +1 -1
- package/vendor/packages/platform-opencode/dist/index.js +15 -81
- package/vendor/packages/platform-opencode/dist/index.js.map +1 -1
- package/vendor/packages/platform-qwen/dist/index.d.ts.map +1 -1
- package/vendor/packages/platform-qwen/dist/index.js +28 -84
- package/vendor/packages/platform-qwen/dist/index.js.map +1 -1
- package/vendor/packages/toolkit/src/content/agents/architect/body.md +8 -0
- package/vendor/packages/toolkit/src/content/agents/code-reviewer/body.md +10 -0
- package/vendor/packages/toolkit/src/content/agents/product-owner/body.md +8 -0
- package/vendor/packages/toolkit/src/content/commands/plan-review/body.md +3 -1
- package/vendor/packages/toolkit/src/content/commands/start/body.md +51 -2
- package/vendor/packages/toolkit/src/content/commands/start/meta.yaml +2 -2
- package/vendor/packages/toolkit/src/content/skills/branch-finish-and-cleanup/body.md +17 -0
- package/vendor/packages/toolkit/src/content/skills/browser-qa-testing/body.md +77 -520
- package/vendor/packages/toolkit/src/content/skills/ci-cd-and-automation/body.md +56 -387
- package/vendor/packages/toolkit/src/content/skills/code-review-and-quality/body.md +10 -0
- package/vendor/packages/toolkit/src/content/skills/code-simplification/body.md +55 -301
- package/vendor/packages/toolkit/src/content/skills/context-engineering/body.md +10 -0
- package/vendor/packages/toolkit/src/content/skills/continuous-learning/body.md +66 -331
- package/vendor/packages/toolkit/src/content/skills/multi-perspective-review/body.md +30 -1
- package/vendor/packages/toolkit/src/content/skills/parallel-agent-dispatch/body.md +79 -317
- package/vendor/packages/toolkit/src/content/skills/performance-optimization/body.md +60 -330
- package/vendor/packages/toolkit/src/content/skills/planning-and-task-breakdown/body.md +35 -0
- package/vendor/packages/toolkit/src/content/skills/sdd-tdd-workflow/body.md +66 -342
- package/vendor/packages/toolkit/src/content/skills/sprint-retrospective/body.md +66 -303
- package/vendor/packages/toolkit/src/content/skills/team-orchestration/body.md +81 -327
- package/vendor/packages/toolkit/src/content/skills/test-driven-development/body.md +50 -346
- package/vendor/packages/toolkit/src/content/skills/using-agent-skills/body.md +26 -2
- package/vendor/references/upstreams.yaml +5 -0
- package/dist/cli/setup.d.ts +0 -3
- package/dist/cli/setup.d.ts.map +0 -1
- package/dist/cli/setup.js +0 -41
- package/dist/cli/setup.js.map +0 -1
|
@@ -1,554 +1,111 @@
|
|
|
1
1
|
# Browser QA Testing
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 角色定位
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
用真实浏览器验证用户是否真的能完成关键路径。单元测试和 API 测试不能证明页面可用、交互可达、网络错误可见或视觉没有严重错位;浏览器 QA 用运行中的应用和可复查证据补上这层门禁。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
本文只给执行骨架。项目已有 Playwright/Cypress/Puppeteer 时优先沿用;没有既有选择时默认 Playwright。
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 何时使用
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- After backend API changes that affect frontend behavior
|
|
17
|
-
- When a bug report describes browser-specific behavior you can't reproduce with unit tests
|
|
11
|
+
- 前端页面、组件、导航、表单或状态流改动后。
|
|
12
|
+
- 后端 API 改动会影响前端体验时。
|
|
13
|
+
- 发布前需要最终用户路径证据时。
|
|
14
|
+
- bug 报告来自浏览器行为、视觉错位或交互失败时。
|
|
15
|
+
- 单元/API 测试通过,但仍需要确认真实页面没有空白、控制台错误或不可操作状态时。
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
不适用:纯后端逻辑、纯文档、无需浏览器渲染的配置改动。
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
- 浏览器 QA 的输出必须可作为证据使用,失败时要留下截图、trace、控制台或网络记录
|
|
23
|
-
- 对复杂改动谨慎推进,先锁定一条可复现路径,再扩大覆盖面
|
|
24
|
-
- 视觉、交互、网络、可访问性检查都应服务当前目标,而不是机械堆清单
|
|
19
|
+
## 快速路径
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
1. 明确 1-3 条失败就不能放行的关键路径。
|
|
22
|
+
2. 启动应用或确认目标 URL 可访问。
|
|
23
|
+
3. 准备稳定测试数据,避免依赖生产数据。
|
|
24
|
+
4. 驱动真实浏览器执行关键路径。
|
|
25
|
+
5. 同时检查 DOM 可见结果、控制台错误、网络失败和基本响应式。
|
|
26
|
+
6. 失败时保留截图、trace、控制台或网络证据。
|
|
27
|
+
7. 修复后重跑原始失败路径,证明不再复发。
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
## 工具选择
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
## Tool Selection
|
|
37
|
-
|
|
38
|
-
### Playwright (Recommended)
|
|
39
|
-
|
|
40
|
-
Best overall choice for AI-driven QA workflows:
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
Strengths:
|
|
44
|
-
- Multi-browser (Chromium, Firefox, WebKit) with one API
|
|
45
|
-
- Auto-wait for elements (no manual sleep/waitFor)
|
|
46
|
-
- Built-in screenshot, video, and trace capture
|
|
47
|
-
- Network interception and mocking
|
|
48
|
-
- Native accessibility testing via @axe-core/playwright
|
|
49
|
-
|
|
50
|
-
Install: npm init playwright@latest
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Cypress
|
|
54
|
-
|
|
55
|
-
Better for teams already invested in the Cypress ecosystem:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
Strengths:
|
|
59
|
-
- Excellent interactive test runner with time-travel debugging
|
|
60
|
-
- Rich plugin ecosystem
|
|
61
|
-
- Good for component testing in isolation
|
|
62
|
-
|
|
63
|
-
Limitations:
|
|
64
|
-
- Chromium-family only (no Safari/WebKit)
|
|
65
|
-
- Cannot test multiple browser tabs or origins in one test
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Puppeteer
|
|
69
|
-
|
|
70
|
-
Use only when you need raw Chrome DevTools Protocol access:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
Strengths:
|
|
74
|
-
- Direct CDP access for advanced scenarios (performance profiling, coverage)
|
|
75
|
-
- Lightweight if you only need Chrome
|
|
76
|
-
|
|
77
|
-
Limitations:
|
|
78
|
-
- Chrome/Chromium only
|
|
79
|
-
- No built-in test runner — you must bring your own
|
|
80
|
-
- More boilerplate than Playwright for common tasks
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Decision rule:** Default to Playwright unless the project already uses Cypress. Use Puppeteer only for Chrome-specific DevTools scenarios.
|
|
84
|
-
|
|
85
|
-
### Browser Configuration
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// playwright.config.ts — recommended baseline
|
|
89
|
-
import { defineConfig, devices } from '@playwright/test';
|
|
90
|
-
|
|
91
|
-
export default defineConfig({
|
|
92
|
-
testDir: './e2e',
|
|
93
|
-
timeout: 30_000,
|
|
94
|
-
retries: process.env.CI ? 2 : 0,
|
|
95
|
-
use: {
|
|
96
|
-
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
97
|
-
screenshot: 'only-on-failure',
|
|
98
|
-
video: 'retain-on-failure',
|
|
99
|
-
trace: 'retain-on-failure',
|
|
100
|
-
},
|
|
101
|
-
projects: [
|
|
102
|
-
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
103
|
-
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
104
|
-
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
105
|
-
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
|
|
106
|
-
{ name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
|
|
107
|
-
],
|
|
108
|
-
webServer: {
|
|
109
|
-
command: 'npm run dev',
|
|
110
|
-
port: 3000,
|
|
111
|
-
reuseExistingServer: !process.env.CI,
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## QA Testing Workflow
|
|
117
|
-
|
|
118
|
-
### Step 1: Environment Preparation
|
|
119
|
-
|
|
120
|
-
Before any browser test can run, the application must be running and data must be in a known state.
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
Pre-flight checklist:
|
|
124
|
-
□ Application builds without errors
|
|
125
|
-
□ Dev server or preview server is running and responding
|
|
126
|
-
□ Test database is seeded with known data (or API mocks are configured)
|
|
127
|
-
□ Environment variables are set (API URLs, feature flags)
|
|
128
|
-
□ No port conflicts on the target URL
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
**Seed data strategy:** Either use a test database with fixtures, or intercept network requests and return mock responses. Never rely on production data for QA tests.
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
// Example: API mocking with Playwright
|
|
135
|
-
test.beforeEach(async ({ page }) => {
|
|
136
|
-
await page.route('**/api/tasks', route =>
|
|
137
|
-
route.fulfill({
|
|
138
|
-
status: 200,
|
|
139
|
-
contentType: 'application/json',
|
|
140
|
-
body: JSON.stringify([
|
|
141
|
-
{ id: 1, title: 'Test task', done: false },
|
|
142
|
-
{ id: 2, title: 'Completed task', done: true },
|
|
143
|
-
]),
|
|
144
|
-
})
|
|
145
|
-
);
|
|
146
|
-
});
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Step 2: Core User Flow Testing
|
|
150
|
-
|
|
151
|
-
Test the critical paths a real user follows. Prioritize by business impact:
|
|
152
|
-
|
|
153
|
-
```
|
|
154
|
-
Priority 1 (Must pass — blocks release):
|
|
155
|
-
- Authentication (login, logout, session persistence)
|
|
156
|
-
- Primary CRUD operations (create, read, update, delete core entities)
|
|
157
|
-
- Payment/checkout flows (if applicable)
|
|
158
|
-
- Navigation between main sections
|
|
159
|
-
|
|
160
|
-
Priority 2 (Should pass — file bugs if broken):
|
|
161
|
-
- Search and filtering
|
|
162
|
-
- Settings and preferences
|
|
163
|
-
- Notification flows
|
|
164
|
-
- Export/import operations
|
|
165
|
-
|
|
166
|
-
Priority 3 (Nice to have):
|
|
167
|
-
- Edge case interactions
|
|
168
|
-
- Rarely used features
|
|
169
|
-
- Admin-only flows
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
先把 Priority 1 做成稳定、可重复、可留证据的路径;在它们还不稳定时,不要把精力优先花在长尾流程上。
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
// Example: Complete login → dashboard → action flow
|
|
176
|
-
test('user can log in and create a task', async ({ page }) => {
|
|
177
|
-
await page.goto('/login');
|
|
178
|
-
|
|
179
|
-
await page.getByLabel('Email').fill('user@test.com');
|
|
180
|
-
await page.getByLabel('Password').fill('testpass123');
|
|
181
|
-
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
182
|
-
|
|
183
|
-
// Verify redirect to dashboard
|
|
184
|
-
await expect(page).toHaveURL('/dashboard');
|
|
185
|
-
await expect(page.getByRole('heading', { name: 'My Tasks' })).toBeVisible();
|
|
186
|
-
|
|
187
|
-
// Create a new task
|
|
188
|
-
await page.getByRole('button', { name: 'New Task' }).click();
|
|
189
|
-
await page.getByLabel('Task title').fill('Write QA tests');
|
|
190
|
-
await page.getByRole('button', { name: 'Save' }).click();
|
|
191
|
-
|
|
192
|
-
// Verify task appears in list
|
|
193
|
-
await expect(page.getByText('Write QA tests')).toBeVisible();
|
|
194
|
-
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### Step 3: Interaction Testing
|
|
198
|
-
|
|
199
|
-
Test form submissions, navigation, and state changes that go beyond happy-path flows:
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
// Form validation testing
|
|
203
|
-
test('form shows validation errors for invalid input', async ({ page }) => {
|
|
204
|
-
await page.goto('/register');
|
|
205
|
-
|
|
206
|
-
// Submit empty form
|
|
207
|
-
await page.getByRole('button', { name: 'Create account' }).click();
|
|
208
|
-
|
|
209
|
-
// Check validation messages appear
|
|
210
|
-
await expect(page.getByText('Email is required')).toBeVisible();
|
|
211
|
-
await expect(page.getByText('Password must be at least 8 characters')).toBeVisible();
|
|
212
|
-
|
|
213
|
-
// Check invalid email
|
|
214
|
-
await page.getByLabel('Email').fill('not-an-email');
|
|
215
|
-
await page.getByRole('button', { name: 'Create account' }).click();
|
|
216
|
-
await expect(page.getByText('Enter a valid email address')).toBeVisible();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Navigation and back-button behavior
|
|
220
|
-
test('browser back button returns to previous page', async ({ page }) => {
|
|
221
|
-
await page.goto('/tasks');
|
|
222
|
-
await page.getByText('Task 1').click();
|
|
223
|
-
await expect(page).toHaveURL(/\/tasks\/\d+/);
|
|
224
|
-
|
|
225
|
-
await page.goBack();
|
|
226
|
-
await expect(page).toHaveURL('/tasks');
|
|
227
|
-
});
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### Step 4: Visual Verification
|
|
231
|
-
|
|
232
|
-
Use screenshots to detect visual regressions:
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
// Full-page screenshot comparison
|
|
236
|
-
test('dashboard matches visual baseline', async ({ page }) => {
|
|
237
|
-
await page.goto('/dashboard');
|
|
238
|
-
await page.waitForLoadState('networkidle');
|
|
239
|
-
|
|
240
|
-
await expect(page).toHaveScreenshot('dashboard.png', {
|
|
241
|
-
maxDiffPixelRatio: 0.01, // Allow 1% pixel difference
|
|
242
|
-
fullPage: true,
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Component-level screenshot
|
|
247
|
-
test('task card renders correctly', async ({ page }) => {
|
|
248
|
-
await page.goto('/tasks');
|
|
249
|
-
const card = page.getByTestId('task-card').first();
|
|
250
|
-
|
|
251
|
-
await expect(card).toHaveScreenshot('task-card.png');
|
|
252
|
-
});
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**Update baselines** when intentional visual changes are made: `npx playwright test --update-snapshots`
|
|
256
|
-
|
|
257
|
-
### Step 4.5: Failure Regression Evidence
|
|
258
|
-
|
|
259
|
-
当测试失败时,至少保留一类可以复盘的证据:
|
|
260
|
-
|
|
261
|
-
- 截图,证明页面状态与预期不符
|
|
262
|
-
- trace 或录像,证明交互链路在哪里中断
|
|
263
|
-
- console error / network failure,证明是前端异常、接口失败还是环境问题
|
|
264
|
-
|
|
265
|
-
没有证据的“浏览器测过了”不构成可审查的结论。
|
|
266
|
-
|
|
267
|
-
### Step 5: Accessibility Audit
|
|
268
|
-
|
|
269
|
-
Integrate axe-core for automated WCAG compliance checking:
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
import AxeBuilder from '@axe-core/playwright';
|
|
273
|
-
|
|
274
|
-
test('home page has no accessibility violations', async ({ page }) => {
|
|
275
|
-
await page.goto('/');
|
|
276
|
-
|
|
277
|
-
const results = await new AxeBuilder({ page })
|
|
278
|
-
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
279
|
-
.analyze();
|
|
280
|
-
|
|
281
|
-
expect(results.violations).toEqual([]);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// Targeted audit on specific component
|
|
285
|
-
test('modal dialog meets accessibility standards', async ({ page }) => {
|
|
286
|
-
await page.goto('/tasks');
|
|
287
|
-
await page.getByRole('button', { name: 'New Task' }).click();
|
|
288
|
-
|
|
289
|
-
const modal = page.getByRole('dialog');
|
|
290
|
-
const results = await new AxeBuilder({ page })
|
|
291
|
-
.include(await modal.elementHandle())
|
|
292
|
-
.analyze();
|
|
293
|
-
|
|
294
|
-
expect(results.violations).toEqual([]);
|
|
295
|
-
});
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
**Manual keyboard checks to include:**
|
|
299
|
-
|
|
300
|
-
```
|
|
301
|
-
□ Tab order follows visual order
|
|
302
|
-
□ Focus is visible on all interactive elements
|
|
303
|
-
□ Escape closes modals and dropdowns
|
|
304
|
-
□ Enter/Space activates buttons and links
|
|
305
|
-
□ Arrow keys work in menus and listboxes
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Step 6: Console and Network Monitoring
|
|
309
|
-
|
|
310
|
-
Enforce a zero-error policy in the browser console and verify network behavior:
|
|
311
|
-
|
|
312
|
-
```typescript
|
|
313
|
-
// Collect console errors during test
|
|
314
|
-
test('page loads without console errors', async ({ page }) => {
|
|
315
|
-
const errors: string[] = [];
|
|
316
|
-
page.on('console', msg => {
|
|
317
|
-
if (msg.type() === 'error') errors.push(msg.text());
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
await page.goto('/dashboard');
|
|
321
|
-
await page.waitForLoadState('networkidle');
|
|
322
|
-
|
|
323
|
-
// Filter out known third-party noise if needed
|
|
324
|
-
const realErrors = errors.filter(e => !e.includes('third-party-sdk'));
|
|
325
|
-
expect(realErrors).toHaveLength(0);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Verify no failed network requests
|
|
329
|
-
test('all API requests succeed', async ({ page }) => {
|
|
330
|
-
const failedRequests: string[] = [];
|
|
331
|
-
page.on('response', response => {
|
|
332
|
-
if (response.status() >= 400) {
|
|
333
|
-
failedRequests.push(`${response.status()} ${response.url()}`);
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
await page.goto('/dashboard');
|
|
338
|
-
await page.waitForLoadState('networkidle');
|
|
339
|
-
|
|
340
|
-
expect(failedRequests).toHaveLength(0);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
// Verify specific API calls are made
|
|
344
|
-
test('dashboard fetches tasks on load', async ({ page }) => {
|
|
345
|
-
const apiCalls: string[] = [];
|
|
346
|
-
page.on('request', req => {
|
|
347
|
-
if (req.url().includes('/api/')) apiCalls.push(req.url());
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
await page.goto('/dashboard');
|
|
351
|
-
await page.waitForLoadState('networkidle');
|
|
352
|
-
|
|
353
|
-
expect(apiCalls.some(url => url.includes('/api/tasks'))).toBe(true);
|
|
354
|
-
});
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Step 7: Regression Test Generation
|
|
358
|
-
|
|
359
|
-
When a bug is found during QA, immediately convert it into an automated test:
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
// Bug found: clicking "Delete" on last task causes blank screen
|
|
363
|
-
// → Convert to regression test:
|
|
364
|
-
|
|
365
|
-
test('deleting the last task shows empty state instead of blank screen', async ({ page }) => {
|
|
366
|
-
// Setup: single task
|
|
367
|
-
await page.route('**/api/tasks', route =>
|
|
368
|
-
route.fulfill({ body: JSON.stringify([{ id: 1, title: 'Only task', done: false }]) })
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
await page.goto('/tasks');
|
|
372
|
-
await page.getByRole('button', { name: 'Delete' }).click();
|
|
373
|
-
await page.getByRole('button', { name: 'Confirm' }).click();
|
|
374
|
-
|
|
375
|
-
// Should show empty state, not blank screen
|
|
376
|
-
await expect(page.getByText('No tasks yet')).toBeVisible();
|
|
377
|
-
await expect(page.getByRole('button', { name: 'Create Task' })).toBeVisible();
|
|
378
|
-
});
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
## Common Test Scenario Templates
|
|
382
|
-
|
|
383
|
-
### Login/Registration
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
test.describe('Authentication', () => {
|
|
387
|
-
test('successful login redirects to dashboard', async ({ page }) => { /* ... */ });
|
|
388
|
-
test('wrong password shows error message', async ({ page }) => { /* ... */ });
|
|
389
|
-
test('session persists across page reload', async ({ page }) => { /* ... */ });
|
|
390
|
-
test('logout clears session and redirects to login', async ({ page }) => { /* ... */ });
|
|
391
|
-
test('registration with valid data creates account', async ({ page }) => { /* ... */ });
|
|
392
|
-
test('registration with duplicate email shows error', async ({ page }) => { /* ... */ });
|
|
393
|
-
});
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### CRUD Operations
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
test.describe('Task CRUD', () => {
|
|
400
|
-
test('create: new task appears in list', async ({ page }) => { /* ... */ });
|
|
401
|
-
test('read: task detail page shows all fields', async ({ page }) => { /* ... */ });
|
|
402
|
-
test('update: edited task saves and reflects changes', async ({ page }) => { /* ... */ });
|
|
403
|
-
test('delete: removed task disappears with confirmation', async ({ page }) => { /* ... */ });
|
|
404
|
-
test('bulk: select-all and batch delete works', async ({ page }) => { /* ... */ });
|
|
405
|
-
});
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
### Responsive Layout
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
const viewports = [
|
|
412
|
-
{ name: 'mobile', width: 375, height: 812 },
|
|
413
|
-
{ name: 'tablet', width: 768, height: 1024 },
|
|
414
|
-
{ name: 'desktop', width: 1440, height: 900 },
|
|
415
|
-
];
|
|
416
|
-
|
|
417
|
-
for (const vp of viewports) {
|
|
418
|
-
test(`dashboard layout is correct at ${vp.name}`, async ({ page }) => {
|
|
419
|
-
await page.setViewportSize({ width: vp.width, height: vp.height });
|
|
420
|
-
await page.goto('/dashboard');
|
|
421
|
-
await expect(page).toHaveScreenshot(`dashboard-${vp.name}.png`);
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### Error State Display
|
|
427
|
-
|
|
428
|
-
```typescript
|
|
429
|
-
test.describe('Error handling', () => {
|
|
430
|
-
test('API failure shows error message with retry button', async ({ page }) => {
|
|
431
|
-
await page.route('**/api/tasks', route => route.fulfill({ status: 500 }));
|
|
432
|
-
await page.goto('/tasks');
|
|
433
|
-
|
|
434
|
-
await expect(page.getByText('Something went wrong')).toBeVisible();
|
|
435
|
-
await expect(page.getByRole('button', { name: 'Retry' })).toBeVisible();
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
test('404 page shows helpful navigation', async ({ page }) => {
|
|
439
|
-
await page.goto('/nonexistent-page');
|
|
440
|
-
await expect(page.getByText('Page not found')).toBeVisible();
|
|
441
|
-
await expect(page.getByRole('link', { name: 'Go home' })).toBeVisible();
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
## QA Report Format
|
|
31
|
+
| 工具 | 默认判断 |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Playwright | 默认推荐,适合跨浏览器、trace、截图、自动等待和 CI |
|
|
34
|
+
| Cypress | 项目已经使用 Cypress 时沿用 |
|
|
35
|
+
| Puppeteer | 只在需要 Chrome DevTools Protocol 细节时使用 |
|
|
36
|
+
| Browser plugin / in-app browser | 适合快速人工式走查和本地交互验证 |
|
|
447
37
|
|
|
448
|
-
|
|
38
|
+
不要为了浏览器 QA 引入第二套 E2E 框架,除非现有工具无法覆盖目标。
|
|
449
39
|
|
|
450
|
-
|
|
451
|
-
## QA Report — [Feature/Page Name] — [Date]
|
|
40
|
+
## 关键路径选择
|
|
452
41
|
|
|
453
|
-
|
|
454
|
-
- URL: http://localhost:3000
|
|
455
|
-
- Browser: Chromium 120, Firefox 121, WebKit 17.4
|
|
456
|
-
- Viewport: 1440×900 (desktop), 375×812 (mobile)
|
|
42
|
+
优先级:
|
|
457
43
|
|
|
458
|
-
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
- ⚠️ Flaky: 1
|
|
44
|
+
- P0:应用能打开,核心页面不是空白。
|
|
45
|
+
- P1:主要业务动作能完成,并看到可验证结果。
|
|
46
|
+
- P2:错误路径有明确反馈,不是静默失败。
|
|
47
|
+
- P3:关键移动端/桌面布局没有阻塞性重叠。
|
|
463
48
|
|
|
464
|
-
|
|
465
|
-
| Test | Browser | Error | Screenshot |
|
|
466
|
-
|------|---------|-------|------------|
|
|
467
|
-
| Login form validation | WebKit | "Email required" message not visible |  |
|
|
468
|
-
| Task delete on mobile | Mobile Chrome | Confirm button outside viewport |  |
|
|
49
|
+
bug 修复场景必须先覆盖:
|
|
469
50
|
|
|
470
|
-
|
|
471
|
-
-
|
|
472
|
-
1. Missing alt text on user avatar (img element) — Critical
|
|
473
|
-
2. Color contrast ratio 3.8:1 on secondary text — Moderate
|
|
474
|
-
3. Missing aria-label on icon-only button — Moderate
|
|
51
|
+
- 原始失败如何复现。
|
|
52
|
+
- 修复后如何证明失败不再发生。
|
|
475
53
|
|
|
476
|
-
|
|
477
|
-
- None (clean)
|
|
54
|
+
## 验收记录格式
|
|
478
55
|
|
|
479
|
-
|
|
480
|
-
- None (all requests 2xx)
|
|
56
|
+
浏览器 QA 的结果必须能被复查:
|
|
481
57
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
58
|
+
```text
|
|
59
|
+
Browser QA transcript:
|
|
60
|
+
- Target:
|
|
61
|
+
- Build/server:
|
|
62
|
+
- Paths tested:
|
|
63
|
+
- Data setup:
|
|
64
|
+
- Evidence:
|
|
65
|
+
- Console/network:
|
|
66
|
+
- Screenshots/traces:
|
|
67
|
+
- Result:
|
|
68
|
+
- Follow-up:
|
|
487
69
|
```
|
|
488
70
|
|
|
489
|
-
|
|
71
|
+
没有证据的“我点过了”不算完成。
|
|
490
72
|
|
|
491
|
-
|
|
73
|
+
## 最小检查清单
|
|
492
74
|
|
|
493
|
-
|
|
75
|
+
- 页面加载成功,没有白屏或阻塞性错误。
|
|
76
|
+
- 关键按钮、输入框、菜单、弹窗可访问。
|
|
77
|
+
- 主流程成功结果可见。
|
|
78
|
+
- 失败路径有用户可理解的错误反馈。
|
|
79
|
+
- 控制台没有与本次改动相关的新错误。
|
|
80
|
+
- 网络请求状态和错误处理符合预期。
|
|
81
|
+
- 关键视口下文本和控件不重叠。
|
|
494
82
|
|
|
495
|
-
|
|
496
|
-
Build component → Run QA → Fix issues → Re-run QA → Approve
|
|
497
|
-
```
|
|
83
|
+
## 与自动化测试的关系
|
|
498
84
|
|
|
499
|
-
|
|
85
|
+
- 已有稳定 E2E:优先运行并补充必要断言。
|
|
86
|
+
- 没有 E2E:先做最小浏览器走查,再决定是否把路径沉淀为 Playwright 测试。
|
|
87
|
+
- 一次性验证:可以只保留 transcript 和截图。
|
|
88
|
+
- 高风险回归:应补自动化测试,避免下次靠人工记忆。
|
|
500
89
|
|
|
501
|
-
|
|
90
|
+
## 常见失败处理
|
|
502
91
|
|
|
503
|
-
|
|
92
|
+
- 白屏:先看控制台、构建输出和入口资源 404。
|
|
93
|
+
- 点击无效:检查元素是否被遮挡、disabled、事件未绑定或异步状态未完成。
|
|
94
|
+
- 表单失败:检查校验、提交 payload、接口响应和错误反馈。
|
|
95
|
+
- 布局错位:截 desktop/mobile 两个视口,定位固定宽度、溢出和层级遮挡。
|
|
96
|
+
- 间歇失败:避免 `sleep`,使用明确的可见性、URL、网络或状态等待条件。
|
|
504
97
|
|
|
505
|
-
|
|
506
|
-
Unit tests (TDD) → Function-level correctness
|
|
507
|
-
Integration tests → Module interaction
|
|
508
|
-
Browser QA tests → User-facing experience (this skill)
|
|
509
|
-
```
|
|
98
|
+
## 边界
|
|
510
99
|
|
|
511
|
-
|
|
100
|
+
- 不把浏览器 QA 当成完整可访问性审计;需要深挖时转专项工具。
|
|
101
|
+
- 不把视觉主观偏好当成阻塞问题,除非它影响可用性或验收标准。
|
|
102
|
+
- 不连接生产数据做破坏性动作。
|
|
103
|
+
- 不在没有目标路径的情况下机械铺满长尾场景。
|
|
512
104
|
|
|
513
|
-
|
|
105
|
+
## 推荐输出
|
|
514
106
|
|
|
107
|
+
```text
|
|
108
|
+
Recommendation: <放行 / 修复后重测 / 补自动化测试> because <浏览器证据、失败影响和替代方案 trade-off>。
|
|
515
109
|
```
|
|
516
|
-
Verification evidence:
|
|
517
|
-
- 24/24 browser tests passing
|
|
518
|
-
- Zero console errors
|
|
519
|
-
- Zero accessibility violations
|
|
520
|
-
- Screenshots attached for visual confirmation
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
## Common Rationalizations
|
|
524
|
-
|
|
525
|
-
| Rationalization | Reality |
|
|
526
|
-
|---|---|
|
|
527
|
-
| "Unit tests are enough" | Unit tests can't catch a broken layout, a missing button, or a form that submits empty data. |
|
|
528
|
-
| "Manual QA is faster" | Manual QA is faster once. Automated QA is faster every time after that — and it doesn't forget edge cases. |
|
|
529
|
-
| "E2E tests are too slow" | A focused suite of 20-30 critical-path tests runs in under 2 minutes. That's faster than debugging a production incident. |
|
|
530
|
-
| "We'll add browser tests later" | Later never comes. The cost of writing tests increases as the app grows. Start with the critical paths now. |
|
|
531
|
-
| "It works on my machine" | Browser tests run in consistent environments. They catch the cross-browser and viewport issues you won't see on your dev setup. |
|
|
532
|
-
|
|
533
|
-
## Red Flags
|
|
534
|
-
|
|
535
|
-
- No browser tests exist for user-facing features
|
|
536
|
-
- QA is only done manually before releases (no automation)
|
|
537
|
-
- Tests use arbitrary `sleep()` / `waitForTimeout()` instead of proper element assertions
|
|
538
|
-
- Screenshot baselines haven't been updated in months (everything is ignored)
|
|
539
|
-
- Accessibility audits have never been run
|
|
540
|
-
- Console errors are ignored ("it's just a warning")
|
|
541
|
-
- Tests only run in one browser (skipping Firefox/WebKit/mobile)
|
|
542
|
-
- No test data seeding — tests depend on production data or shared environments
|
|
543
|
-
|
|
544
|
-
## Verification
|
|
545
|
-
|
|
546
|
-
After QA testing is complete:
|
|
547
110
|
|
|
548
|
-
|
|
549
|
-
- [ ] Zero unhandled console errors in the browser
|
|
550
|
-
- [ ] Accessibility audit shows zero critical violations
|
|
551
|
-
- [ ] Visual screenshots are captured and reviewed
|
|
552
|
-
- [ ] Failed tests have filed bugs with reproduction steps
|
|
553
|
-
- [ ] Regression tests are written for any bugs discovered
|
|
554
|
-
- [ ] QA report is generated with pass/fail summary and evidence
|
|
111
|
+
推荐必须说明证据来自哪条路径,以及未覆盖风险是什么。
|