iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0

Day 17 結尾,我寫下今天應該補 browser-level test。

因為 ClarifyBuild 從 Day 15 到 Day 17,已經不只是單純資料模型了。

這幾天新增或改過的東西,已經開始靠近真實使用者會碰到的流程:

  • Specification Setup 從文字格式改成 row-based editor
  • Draft 可以匯出成 JSON
  • Draft 可以從 JSON 匯入
  • 匯入後 generated output 必須清空
  • Spec 和 Prompt 要從目前 draft 重新產生

所以最理想的 Day 18,看起來應該是一條 browser happy path test:

打開 App
↓
輸入 Idea
↓
一路走到 Prompt Preview
↓
匯出 Draft
↓
匯入 Draft
↓
確認回到 Specification Setup
↓
重新產生 Spec 和 Prompt

這條測試如果存在,會很安心。

因為它不是只測某個函式,而是模擬使用者真的在瀏覽器裡按按鈕、填表單、選檔案。

但今天打開專案後,我沒有直接裝 Playwright。

不是因為 browser test 不重要。

而是因為我需要先確認:

今天最小、最合理、能驗證 Day 17 風險的下一步,到底是不是引入一整套 browser test dependency?

答案是:還不是。


先看目前專案的測試形狀

ClarifyBuild 目前的 package.json 很小:

npm run dev  -> python3 -m http.server 5173
npm test     -> node --test

沒有 Vite。

沒有 jsdom。

沒有 Playwright。

也沒有任何 npm dependency。

這不是缺點。

Day 14 開始,ClarifyBuild 一直刻意維持 static frontend-first architecture。它可以用一個簡單 static server 跑起來,也可以之後放到 GitHub Pages。

目前測試也是一樣:用 Node 內建的 test runner,測 domain、engine、generator、storage。

Day 15 的 MVP happy path test,測的是從 Idea 到 AI Coding Prompt 的核心資料流:

Idea
↓
Candidate confirmation
↓
Scope
↓
Specification Setup
↓
Project Specification
↓
AI Coding Prompt

Day 16 補 storage lifecycle。

Day 17 補 draft import/export validation。

也就是說,Idea 到 Prompt 的單向流有測試,匯出匯入的資料邊界也有測試。但沒有任何一條測試走過「產生輸出 → 匯出 → 匯入 → 重新產生」這一整圈,更沒有碰到瀏覽器 UI。

如果今天直接裝 Playwright,當然可以往 browser-level test 走。

但這會讓 Day 18 的切片變大:

  • 新增 dependency
  • 設定 browser runtime
  • 決定測試 server 怎麼啟動
  • 處理下載檔案
  • 處理 file input 匯入

這些都值得做。

只是它們不是 Day 18 最小的一步。

今天真正想守住的,是 Day 17 留下的資料邊界:

匯入 Draft 之後,Project Specification 和 AI Coding Prompt 不能從檔案裡復活,必須從目前 draft 重新產生。

如果這條規則壞掉,就算 UI 看起來還能按,產品邏輯也已經出問題。

所以我今天沒有先做 browser automation。

我先做的是:

把按鈕背後的 App flow 抽成可測的核心流程。


為什麼不是只繼續測 storage?

Day 17 已經在 test/draftStorage.test.js 補了幾個測試。

例如:

匯出 draft
↓
匯入 draft
↓
generatedSpec = ""
generatedPrompt = ""

這可以確認 storage boundary 沒有把 generated output 當成來源資料。

但 Day 18 要再往上一層。

因為使用者不是直接呼叫 createDraftExport() 或 importDraft()。

使用者會按 App 上的按鈕:

  • 產生 Project Specification
  • 產生 AI Coding Prompt
  • 匯出 Draft
  • 匯入 Draft

這些動作原本都寫在 src/main.js 的 actions 裡。

所以如果只測 storage,有一個盲點:

storage 是對的,不代表 App action 串起來之後仍然是對的。

例如:

  • generateSpec 有沒有真的把 stage 改成 Spec Preview?
  • generatePrompt 有沒有從目前 generated Spec 產生 Prompt?
  • 從 Prompt Preview 匯出時,draft 檔裡的 stage 有沒有回到 Specification Setup?
  • 匯入後,App 拿到的是不是一個清空 generated output 的 state?
  • 匯入後能不能重新產生 Spec 和 Prompt?

其中第三、第四題,Day 17 的 storage 測試已經守過。第一、第二、第五題,到今天為止沒有任何測試碰過。

第五題尤其明顯。Day 17 測試用的那份 draft,三個 needsReview 都是 true,本來就沒辦法重新產生 Spec。所以「匯出再匯入之後,draft 還完整到能重新產生輸出」,一直沒有被驗證過。

所以 Day 18 的做法,是新增一個很小的檔案:

src/state/appFlow.js

它集中放四個操作:

generateSpecFromState(state)
generatePromptFromState(state)
createExportedDraftFromState(state, exportedAt)
importDraftState(raw)

這四個函式沒有新增產品邏輯。

真正從 main.js 抽出來的是前兩個:判斷 setup 有沒有完成、呼叫 generator、切換 stage,原本都寫在 action 裡。

現在 generateSpec 只剩 generateSpecFromState(state)、錯誤提示和 commit();generatePrompt 也一樣。

後兩個目前只是薄薄一層轉接,邏輯還在 storage。我還是把它們放進同一個檔案,是希望四個 action 都走同一個入口,測試也能從同一個入口走完整圈。

main.js 還是負責瀏覽器相關事情,例如 Blob、下載連結、file input、toast、render、localStorage commit。

但 Spec / Prompt generation 和 Draft import/export 的狀態入口,現在可以被測試直接使用。


今天的測試怎麼走

Day 18 新增的測試叫:

app flow exports an importable MVP draft and requires regenerated outputs after import

它不開瀏覽器,也不碰 DOM。它呼叫的,是 App action 用的同一組 helper。

測試不從 Idea 開始走,Idea 到 Prompt 那一段是 Day 15 的測試在守。今天的測試先直接組出一份 Specification Setup 都填完的 MVP draft:

idea = 我想做一個讓研究生整理論文的網站
target user = 研究生
problem = 論文資料散落在不同地方
goal = 快速找到需要的論文
platform = Web
must have feature = 搜尋論文
project constraint = 必須可以部署為靜態網站
tech stack = ai_delegated

再補上 Specification Setup 需要的資料:

  • Target Project User Flow
  • Must Have Feature Specification
  • Project Constraints
  • Tech Stack decision

接著測試跑第一段:

Specification Setup
↓
generateSpecFromState
↓
Spec Preview

它確認:

  • stage 變成 spec_preview
  • generated Spec 裡有 Must Have Feature Specifications
  • generated Spec 裡有「搜尋論文」

再跑第二段:

Spec Preview
↓
generatePromptFromState
↓
Prompt Preview

它確認:

  • stage 變成 prompt_preview
  • generated Prompt 裡有 Development Instructions
  • generated Prompt 裡有嵌進 Project Specification

到這裡,這份 state 的形狀,和使用者走到 Prompt Preview 時一樣。差別在於它是程式直接組出來的,不是從畫面一步一步點出來的。

接著進入 Day 17 最重要的邊界:

Prompt Preview
↓
createExportedDraftFromState
↓
clarifybuild.draft JSON

測試確認三件事:

第一,匯出的 stage 不是 prompt_preview,而是:

specification_setup

因為 Prompt Preview 是 generated output 畫面。

匯出 draft 存的是來源資料和 Builder lifecycle,不是輸出畫面的快照。

第二,匯出的 JSON 裡沒有:

generatedSpec
generatedPrompt

第三,就算我故意在匯入檔案裡塞回這兩個欄位:

generatedSpec: "# stale generated spec"
generatedPrompt: "stale generated prompt"

匯入後也不採用。

匯入後應該是:

stage = specification_setup
generatedSpec = ""
generatedPrompt = ""

最後測試再做一次 regenerate:

imported draft
↓
generateSpecFromState
↓
generatePromptFromState

確認新的 Spec 和 Prompt 是從目前 draft 重新產生的,而且和匯出前逐字相同。

這就是 Day 18 想守的完整資料流:

MVP source data
↓
generated output
↓
export draft
↓
import draft
↓
generated output is gone
↓
regenerate output from source data

這不是 browser test,但它比單純 unit test 多守一層

今天這個測試不是 browser-level test。它不開瀏覽器,不碰 DOM,沒有點按鈕、選檔案或下載檔案。

它守的是 generate → export → import → regenerate 這串 App flow 狀態轉換。

而且 main.js 現在也呼叫同一組 helper。測試跑的,就是 App 實際用的那組函式,沒有另外寫一條平行路徑。

但這只在 main.js 繼續呼叫它們的時候成立。有沒有呼叫、呼叫之後有沒有多做別的事,今天的測試看不到。這一段還是要等 browser 層的測試。

我覺得這是今天最合適的折衷:

還沒有 browser automation,但先把 browser action 背後的狀態流程測起來。


測試結果

目前所有測試都通過:原本 19 個,加上今天的 1 個,現在是 20 個。

main.js 今天被改過,但沒有任何自動化測試會執行它。我只做了語法檢查,也確認本機的 static server 能提供首頁。這兩件事都不能證明按鈕按下去還照原本運作。


今天故意沒做的事

Day 18 最容易誤會的地方是:

這是不是已經完成 browser-level happy path test?

還沒有。沒有 Playwright,沒有 jsdom,沒有真實 DOM,也沒有任何一次真的點按鈕、選檔案或下載檔案。Day 17 結尾問的那件事:使用者能不能在瀏覽器裡一路走過 MVP、匯出、匯入、再重新產生,今天沒有答案。

今天有答案的是一個比較小的問題:匯出、匯入、重新產生這一整圈,核心狀態流程是通的,而且有測試守著。至於改了來源資料就清掉 generated output,那段寫在 main.js 的 action 裡,今天沒有被測到。


Day 18 小結

Day 18 原本看起來應該補 browser-level happy path test。

但真正檢查 repo 狀態後,我沒有直接引入 browser test dependency。

我選了一個更小的切片:

把瀏覽器 action 背後的狀態流程抽出來,測 MVP draft 從生成、匯出、匯入,到重新產生輸出這一整圈。

這一步沒有改 ClarifyBuild 的產品邏輯。

它也沒有把 generated Spec 或 Prompt 放進 draft。

它只是讓這條規則變得更可驗證:

Draft 可以保存來源資料和 Builder 狀態;generated output 匯入後必須消失,並從目前 draft 重新產生。

明天 Day 19,我還是先不補 browser automation。

Day 17 已經留下一個更直接的缺口:現在匯入一份合法 draft,會直接取代目前的 draft,而且被取代的舊 draft 回不來。

匯出、匯入、重新產生這一圈,今天總算有測試從頭走到尾,接下來才適合在匯入前加一道確認:使用者選好檔案後,先看到提示,再決定要不要取代。

Day 18 完成。

明天見。


上一篇
Day 17|Draft 可以匯出了,但 Spec 和 Prompt 不能跟著放進去
下一篇
Day 19|匯入的 Draft,在按下確定之前不能先進 App
系列文
AI 寫不好,可能是我沒說清楚:30 天打造 ClarifyBuild,讓 Vibe Coding 從需求開始 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言