Repository navigation
Expand file tree
/
Copy pathplaywright.config.ts
More file actions
188 lines (187 loc) · 9.65 KB
/
Copy pathplaywright.config.ts
File metadata and controls
188 lines (187 loc) · 9.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
* **端對端測試** —— 補上「沒有任何自動化在看渲染出來的結果」那一格
*
* ## 為什麼要付這個成本
*
* `vision.md`「執行機構自己接上」那一節逐字:
*
* > 「**下一格(未承諾):沒有任何自動化在看渲染出來的結果。**
* > 五次使用者回報裡有四次現在有機械檢查,而它們全部停在瀏覽器之前。」
*
* 而 2026-08-12 那一輪把它的代價量了出來:`execution:at-node` 與斷點反轉
* 做完之後,**有兩件事我沒能驗到**——
*
* - 斷點「命中時會停」:gutter 的點擊在自動化操作下不穩定,
* 同一個座標時中時不中,而**畫面上看不出差別**(toggle 兩次等於沒設)
* - 加速功能:同樣的時序問題
*
* 那不是「懶得測」,是**用截圖與座標點擊去驗 UI 本來就會這樣**。
* 一個會 retry、會等元素、會用選擇器而不是座標的工具,才問得出那兩個問題。
*
* > **一條測不到的路,與一條沒有人測的路,在測試報告上長得一樣。**
*
* ## ⚠️ 它不取代 vitest
*
* 3952 個單元/整合測試仍然是主力,e2e 只覆蓋**必須看到渲染結果**的那幾件事。
* e2e 慢、脆、而且失敗時難定位——把它當主力會讓「全套綠」這個訊號變鈍,
* 而所有護欄的價值都建立在那個訊號上。
*
* 判準:**這件事在 DOM 之外驗得到嗎?** 驗得到就不要寫進這裡。
*/
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
testDir: './e2e',
// ## 🔴 為什麼是序列——而 2026-08-31 之前這裡的理由是【錯的】
//
// 舊註解逐字:「這些測試共用 localStorage(專案的存檔),平行跑會互相覆寫。」
// **那句話不成立**:Playwright 每個 worker 有自己的 browser context,
// 而 `localStorage` 是 per-context per-origin,本來就隔離。
//
// 真正的原因是量出來的(`workers=1` vs `workers=4`,同一組三支):
//
// ```
// 序列 9/9 過(39.9 秒)
// 併行 3 支紅——「產出是【舊的】」(少了 stdbool.h)、element not found ×2
// ```
//
// 4 個 Chromium × (Blockly + tree-sitter wasm + 177 顆膠囊) 擠 4 個效能核,
// 每次開機慢 3~4 倍——**而這些測試用固定秒數等待,那是照閒置機器校準的**。
//
// > **一個用固定秒數等待的測試,它的正確性綁在「機器現在有多閒」上
// > ——那既讓它慢,也讓它不能平行。同一個病,兩個症狀。**
//
// ## 所以開併行的前置是「把固定等待換掉」,不是改這兩行
//
// 全庫原本 **144 處** `waitForTimeout`(平均 1761ms),2026-08-31 兩刀清到 **78 處**:
//
// ```
// 24.8 分 → 13.5 分 lessons.spec 三處
// 13.5 分 → 8.7 分 66 處,跨 17 個檔(共用助手,見 `helpers.ts`)
// ```
//
// 🔴 **2026-09-07 第三刀:`lesson-pins` 從 70 處 274 秒 → 40 處 127 秒**
// (那個檔跑 **5.2 分 → 3.1 分**)。而它不是一支一支改的,是**三個 helper**:
//
// ```
// openLessonLink 20 處 × 7000ms 等 `__app.currentLesson.id`,不睡 7 秒
// runSkippingPrediction 10 處 × 3500ms 按執行 → 跳問句 → 等狀態列
// pickControl 3 處 × 3000ms 切控制項 → 等 QuickPick 關掉 + 畫布在
// ```
//
// ⚠️ **而換掉那 10 處時 4 支當場紅**:原本的 3500ms 除了「等它跑完」,
// 還**順便等掉了預測的問句**——而那件事沒有人寫下來。
//
// > **一個固定等待除了它宣稱在等的那件事,常常還順便等掉了幾件
// > 沒有人寫下來的——而換掉它的時候那幾件會現形。**
//
// 🔴 **而剩下的那些回報遞減很快**(2026-09-07 量:全庫 124 處、246 秒,
// 而**第二名的檔只有 21 秒**):它們是各測試自己的小等待
//(點一下之後的 400~800ms、拖曳之間的間隔),要一支一支判斷該等什麼。
//
// 🟢 **判準**:一個檔的固定等待**超過一分鐘**才值得動它——而動的方式是
// 找出「同一件事重複 N 次」的那一族,做成 helper,不是逐處改。
//
// ⚠️ **「78 處」那個數字 2026-09-07 量到是 155**——寫下來的那天它是對的,
// 而後來加的測試又帶進了新的固定等待。
//
// > **一個寫在散文裡的現況數字,沒有失效條件——它只會在有人重量的那天被發現。**
// (`concepts/紀錄的腐壞`)🟢 而正確的做法是讓它住進基線,
// 或者**不寫那個數字**、只寫「跑 `grep -rc waitForTimeout e2e/` 就知道」。
// 現在最大的一塊是 `lessons.spec` 的 204 秒/133 支,而它**只剩兩處**
// ——那 204 秒已經是真的在做事。
//
// ⚠️ **清完之前不要開併行**——現在開等於把不穩定寫進 CI,
// 而下面那行 `retries: CI ? 1 : 0` 會把它藏起來。
workers: 1,
fullyParallel: false,
// CI 上失敗重跑一次——e2e 的偶發失敗多半是等待時機,不是真的壞。
// ⚠️ 而本機**不重跑**:本機重跑會把「這支測試不穩」藏起來。
retries: process.env.CI ? 1 : 0,
/**
* 🔴 **條件等待的上限——2026-09-07 從預設的 5 秒拉到 15 秒。**
*
* ## 為什麼
*
* 那天全跑五次,**每一次都有 1~2 支紅,而單獨重跑全部綠**
* ——而紅的檔次次不同(`debug` · `flow-layout-persists` ·
* `flow-blocks-sync` · `flow-selection-link` · `execution` ·
* `flow-palette-drag`)。
*
* ⚠️ 我第一版把它診斷成「併行的資源競爭」——**而這個檔案 `workers: 1`**。
*
* > **一個「偶發失敗」的解釋,如果它說的機制在設定裡是關掉的,
* > 那它不是解釋,是一句讓人停止查下去的話。**
*
* 🔴 真正的形狀:全跑要 **16 分鐘**,機器整段時間高負載,而那六支用的是
* **條件等待**(`toBeVisible` / `expect.poll` / `toHaveText`)
* ——它們的上限是 5~10 秒,照**閒置**機器校準。
*
* ⚠️ 檔頭那段「78 處固定等待」講的是**另一個**問題(而那個數字今天是 **155**):
* 今天紅過的六支裡,**五支的 `waitForTimeout` 是 0**。
*
* ## 🟢 而拉高上限不會讓通過的測試變慢
*
* 條件滿足就繼續——這個數字只決定「**真的壞了的時候等多久才說**」。
*
* ## ⚠️ 而 2026-09-07 之後它【還沒完全好】——這是現況,不是待辦
*
* 三次全跑:**16 分 0 紅 · 23 分 4 紅 · 19 分 2 紅**。
* 紅的檔次次不同,而單獨重跑**每一次都全綠**。
*
* 🔴 **而再拉高上限是治標**:那幾支用的已經是 15~20 秒的條件等待
* (`console-comes-back` 的 `toContainText(…, 20_000)`、`debug` 同)。
* 拉到 30 秒會讓它們在忙的時候也過——**而那把「機器太慢」藏成「一切正常」**。
*
* 🟢 **判準(給下一個人)**:
*
* ```
* 全跑 1~2 支紅,而單獨重跑綠 ← 已知現象。⚠️ 而**時長不是唯一的判準**:
* 2026-09-08 一次 15.7 分的全跑照樣紅 2 支
* 同一支【連兩次】全跑都紅 ← 那才是迴歸,去查它
* 同一【檔】的兩支同時紅 ⚠️ 看起來最像迴歸,而它也可能只是
* 那個檔的 `beforeAll` 或前置那一拍沒排到
* (2026-09-08:`diagnostics` 兩支同時紅、
* 單獨 8 支全綠,而那一刀零關聯)
* 單獨重跑也紅 ← 一定是迴歸
* ```
*
* ⚠️ **累計四次全跑的紀錄**(每一次紅的檔都不同,而單獨重跑全綠):
* `debug` · `c-target` · `scaffold-code-complete` · `flow-if-variants` ·
* **`console-comes-back`(4 次裡紅了 2 次——目前最常紅的那一支)**。
* 🔴 它等的是「主控台自己回來」(`toContainText(…, 20_000)`),
* 而那條路要**執行跑完 + 面板自己開**——兩拍,所以它對機器負載最敏感。
*
* ⚠️ 而真正的治本是**把 124 處固定等待清完**(見上面),
* 或者**讓全跑不要跑 19 分鐘**——而後者要的是併行,
* 而併行的前置正是前者。**同一個結,兩個方向都通到它。**
*/
expect: { timeout: 15_000 },
reporter: process.env.CI ? 'github' : 'list',
use: {
baseURL: 'http://localhost:4173/',
trace: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
// ⚠️ **viewport 要放在 `devices` 之後**——`devices` 自帶一組,
// 寫在頂層 `use` 裡會被它覆蓋掉(而症狀是「設了沒用」)。
//
// 這個應用在窄寬度下切成行動版面,那時桌面工具列的按鈕是 `0x0`
// ——症狀是 `click` timeout 30 秒,看起來像應用沒回應,
// 實際上是**測試在看另一個版面**。
viewport: { width: 1440, height: 900 },
},
},
],
// ⚠️ 用 `preview`(build 後的產物)而不是 `dev`:
// dev server 的熱重載會在測試中途重載頁面,而那正是手動驗證時反覆踩到的坑。
webServer: {
command: 'npm run build && npm run preview',
url: 'http://localhost:4173/',
reuseExistingServer: !process.env.CI,
timeout: 180_000,
},
})