Day 09|不只看畫面:教同事檢查 Console

前言

Day 8 教會它留證,一輪跑完會交回截圖、console、network、trace 四份檔案。今天面對下一個問題:四份檔案攤在面前,你怎麼知道該看哪一份?底下的數字都是 2026-08-02 對 automationexercise.com 實跑量到的,證據在 output/evidence/20260802-automationexercise/

為什麼要分辨證據來源

留證有個陷阱:檔案愈多,愈像做完了。實際上那四份檔案回答的是四個不同的問題,而它們之間有縫。

截圖說得出症狀,說不出原因。network 說得出發了哪些請求,說不出回 200 的那一筆內容是錯的。console 說得出程式報了什麼錯,說不出畫面因此長成什麼樣。

縫的代價是漏。缺陷同時落在兩份檔案上,一份寫症狀、一份寫原因,只讀其中一份的人會判它通過。更糟的是這種漏不會留下痕跡:你手上有完整的證據包,報告寫「未發現異常」,兩件事並存,沒有任何人會發現。

所以今天要立的規矩是:先問這個症狀該由誰回答,再去開對應的那份檔案。

實驗:整站字型掉回預設,只有 console 說得出為什麼

首頁。看起來沒事,這就是問題所在。

打開首頁,console 固定 3 筆錯誤:

[ERROR] Mixed Content: The page at 'https://automationexercise.com/' was loaded over HTTPS,
        but requested an insecure stylesheet 'http://fonts.googleapis.com/css?family=Roboto:...'.
        This request has been blocked; the content must be served over HTTPS.

三筆都是同一類:HTTPS 頁面去載 HTTP 的字型 CSS,被瀏覽器擋下。network 加了 --static 之後也看得到,形式不太一樣:

[GET] http://fonts.googleapis.com/css?family=Roboto:400,300,... => [FAILED] mixed-content
[GET] http://fonts.googleapis.com/css?family=Open+Sans:400,800,... => [FAILED] mixed-content
[GET] http://fonts.googleapis.com/css?family=Abel => [FAILED] mixed-content

注意它不是 4xx,是 FAILED。只挑非 2xx 狀態碼看的人會漏掉,因為它根本沒有走到有狀態碼的那一步。

畫面上的症狀是什麼?網站的 CSS 寫著 font-family: Roboto, sans-serif,但 Roboto 的字型檔被擋了,所以整站掉回系統預設的 sans-serif。

同一件事在三種來源上長成三個樣子:

來源 它說什麼
截圖 字型跟設計稿不一樣,而且你多半不會發現
console Mixed Content,被擋掉了,連網址都給你
network [FAILED] mixed-content,沒有狀態碼

截圖只會告訴你「字看起來跟設計稿不太一樣」,甚至你根本不會注意到,console 才說得出為什麼。症狀和原因,往往不在同一份證據裡。

跑這一輪的指令長這樣:

/evidence-package 打開 automationexercise.com 首頁,記錄 console 與 network 有沒有異常
/evidence-package 進商品頁與購物車,比對 console 新增了哪幾筆、分別來自哪個網域

授權提醒:automationexercise 是共用練習站。createAccountdeleteAccountupdateAccount 這幾個端點會留下副作用,示範一律只用讀取類端點。訂閱與聯絡表單會真的送出,不要按。

四種證據來源,各自看不見什麼

來源 指令 擅長抓什麼 抓不到什麼
截圖 playwright-cli screenshot --full-page --filename <路徑> 版面、文案、看得見的症狀 為什麼會這樣
Trace playwright-cli tracing-starttracing-stop 事後逐步回放,四種訊號都在裡面 要有人去點開對的那一格
Network playwright-cli network --raw(看靜態資源要加 --static 狀態碼、請求數、哪個請求沒成功 回 200 但內容是錯的
Video playwright-cli video-start <檔名>video-chaptervideo-stop 動態過程、卡頓、閃現的畫面 精確數值

Network 預設會濾掉圖片字型這類靜態資源。這是今天最關鍵的開關:不加 --static,上一節那個缺陷你一個字都看不到。

Video 實測可用,video-stop 會回傳檔案路徑,短流程約 537 KB 的 .webm。Day 8 講過只在失敗時留影片與 trace,這一輪只驗了能錄、能停、能插章節,保留策略還沒實測。

Trace 這一輪也只是帶過。它把四種訊號都包在一份檔案裡,代價是要有人去點開對的那一格,而這一天沒有示範怎麼點。

還有第五種來源,它不在任何一份檔案裡:耗時。這個站的首頁載入量到 9.05 秒,共 125 個請求。時間要自己量,而且量到了也只是一個數字,是網路慢、伺服器慢、還是 125 個請求太多,今天沒有分辨。

console 有兩種訊息,數錯的那一種比較嚴重

console 收得到兩類訊息,但它們前綴不一樣。實測:

playwright-cli eval '() => { setTimeout(() => { throw new Error("UNCAUGHT_TEST_ERROR"); }, 0); console.error("CONSOLE_ERROR_TEST"); }'
playwright-cli console --raw

輸出:

Total messages: 2 (Errors: 2, Warnings: 0)

[ERROR] CONSOLE_ERROR_TEST @ :0
Error: UNCAUGHT_TEST_ERROR
    at eval (eval at <anonymous> ...)

console.error() 印出來的有 [ERROR] 前綴,未捕捉的例外沒有。

所以這一行是錯的:

grep -c '^\[ERROR\]' console.log   # 數到 1

摘要行說 Errors: 2。少掉的那一筆,正是未捕捉的 JS 例外,通常是比較嚴重的那一種。

你的過濾器決定了你看得到什麼。用行首前綴數錯誤,會系統性地漏掉未捕捉的例外。要嘛讀摘要行,要嘛連沒有前綴的堆疊一起看。

這個坑我自己踩過:今天前面幾輪的「console error 數」都是用那條 grep 數的。

噪音要跟缺陷分開

購物車頁的 console 有 4 筆錯誤,其中 3 筆是站方自己的 Mixed Content,第 4 筆長這樣:

[ERROR] Attestation check for Shared Storage on https://www.googleadservices.com failed.

那是第三方廣告腳本的問題,跟受測產品無關。每一輪都要重新判斷一次同一筆噪音,是很貴的浪費。這種東西該進一份已知噪音清單(output/known-false-positives.yaml),下一輪直接略過。

判斷的方法很土但有效:看網域。 請求打去 googleadservices.combacktrace.io 這種第三方,跟受測產品無關;打去自己網域的才要追。

這份清單今天只說了該有,格式與比對方式要等第四週處理誤報與去重時才寫得完整。

小結

看到一個症狀,先問它該由哪一種來源回答,再去開那份檔案;開之前確認過濾器沒有先幫你篩掉東西(network--static、你自己那條 grep 的行首前綴);讀到的錯誤先分掉第三方網域的噪音,剩下的才是候選。症狀與原因分屬兩份檔案時,兩份都要附進證據包,缺一份,下一個人就得再跑一次。

                    一個症狀
                        │
        ┌───────────────┼───────────────┬───────────────┐
        ▼               ▼               ▼               ▼
      截圖           console         network          video
    看得見什麼      為什麼會這樣     發了哪些請求      動態過程
        │               │               │               │
        └───────────────┴───────┬───────┴───────────────┘
                                ▼
                    先檢查過濾器有沒有騙你
                (--static?行首前綴?只挑非 2xx?)
                                │
                                ▼
                        分掉第三方網域的噪音
                                │
                                ▼
                    症狀 + 原因,湊成一筆才算數

症狀和原因常常不在同一份證據裡,截圖看到字型不對,只有 console 說得出為什麼。預設的過濾器與你自己寫的過濾器也都會騙你:network 不加 --static 看不到字型請求,grep '^\[ERROR\]' 漏掉未捕捉的例外。而噪音要先分掉再談缺陷,第三方網域的錯誤每一輪都會再出現一次,不進清單就每一輪都要重判。

下一步

今天是被動讀已經留下的證據:東西已經在檔案裡,差別只在你開哪一份。

明天換一種姿勢。挑一個 UI 動作,主動追它到底做了什麼,然後面對一種今天四種來源全部乾淨、卻確實有缺陷的情況:什麼都沒發生

---

參考資料

  1. Playwright — Videos - 錄影的開關與檔案落地
  2. Playwright — Network - 攔截、觀察、mock 請求
  3. Playwright — Trace Viewer - Console/Network/DOM 各自在哪一格
  4. Playwright API — ConsoleMessage - console 訊息的型別與來源
  5. Playwright API — page.on('pageerror') - 未捕捉的例外,跟 console 是兩條事件,本篇第三節的依據
  6. MDN — Mixed content - 為什麼 HTTPS 頁面載 HTTP 資源會被擋
  7. Chrome DevTools — Console - 給讀者對照手動除錯的經驗
  8. Automation Exercise - 本篇的受測站
  9. 本篇證據 output/evidence/20260802-automationexercise/ - 截圖、console、network 的原始檔