iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Vibe Coding

Vibe Mode 開啟:30 天用 AI 打造網頁,邊做邊學 JavaScript系列 第 18 篇

# Day 18 - 前端 E2E 測試:AI 協助撰寫 Playwright 自動化測試腳步

  • 分享至 

  • xImage
  •  

「沒有測試的 UI,就像沒有安全繩的高空彈跳。」
階段三(前端開發與 UI/UX 實作)今天來到最終章!前面幾天我們從 Layout、Form 表單、API 串接一路寫到 Recharts 圖表與 Framer Motion 動畫,前端功能已經相當豐富。但是,我們要如何確保未來的每一次改動,不會悄悄搞砸這些精心設計的介面?今天我們讓 AI 協助我們引入現代 E2E 測試神器 —— Playwright!

今日目標

在我們的 「AI 智慧個人財務追蹤器 (Smart Finance Tracker)」 中:

使用 Playwright 模擬真實使用者行為:登入 ➔ 填寫交易表單 ➔ 提交 ➔ 驗證清單與圖表是否即時更新。

讓 AI 生成具備強健性(Robustness)的測試腳本,避免因為 UI 動畫或 CSS 類名變動導致測試脆裂(Flaky Tests)。

掌握在 Vibe Coding 下,如何引導 AI 處理 Playwright 的非同步等待(Async Waiting)與 Mock API 網路攔截。

Human vs. AI:提示詞與協同開發

E2E 測試最忌諱「寫死選擇器 (Hardcoded Selectors)」(如 .btn-primary 或 div > div > button),因為只要樣式一改,測試就全毀。我們要引導 AI 使用符合可存取性標準(Accessibility Guidelines)的定位器(getByRole, getByLabel)。

給 AI 的指令(Prompt)

我們正在為財務追蹤器編寫 Playwright E2E 測試腳本(tests/add-transaction.spec.ts)。

請幫我撰寫一個完整的使用者流程測試:
1. 造訪 `/dashboard` 頁面。
2. 填寫「新增交易表單」:
   - 金額欄位輸入 "250"
   - 類別選擇 "餐饮"
   - 備註欄位輸入 "午餐火鍋"
3. 點擊「新增交易」按鈕。
4. 斷言(Assert)頁面上的交易列表中出現 "午餐火鍋",且金額為 "$250"。
5. 請務必使用 Playwright 推薦的 `getByRole` 或 `getByLabel` 定位器,並考慮到 Framer Motion 動畫延遲,確保等待機制正確。

AI 產出的結果

AI 非常迅速的給出了一份完整的 Playwright Spec :

// tests/add-transaction.spec.ts
import { test, expect } from '@playwright/test';

test('使用者應該能夠成功新增一筆交易', async ({ page }) => {
  await page.goto('/dashboard');

  // 填寫表單
  await page.getByLabel('金額').fill('250');
  await page.getByLabel('類別').selectOption('餐飲');
  await page.getByLabel('備註').fill('午餐火鍋');

  // 點擊提交
  await page.getByRole('button', { name: '新增交易' }).click();

  // 斷言
  const newItem = page.locator('.transaction-item').first();
  await expect(newItem).toContainText('午餐火鍋');
  await expect(newItem).toContainText('$250');
});

翻車與除錯過程 (Debug Experience)

執行 npx playwright test 後,控制台跳出黃紅交織的 Timeout 警告,測試直接宣告失敗:

TimeoutError: page.waitForSelector: Timeout 5000ms exceeded.

AI 哪裡出問題了?

  1. 選擇器打臉 (Selector Mismatch):
    AI 假設表單有 元素包裹 getByLabel('金額'),但我們在 Day 14 採用 shadcn/ui (Radix UI) 搭配 Tailwind 時,Placeholder 才是主要的標示,getByLabel 根本抓不到 DOM。

  2. 忽略了 Framer Motion 的動畫等待與 API 延遲:
    點擊「新增交易」後,useMutation 需要時間發送 POST 請求,且 Framer Motion 有 0.3s 的進場動畫。AI 產出的程式碼在點擊後立刻去檢查 .transaction-item,導致在動畫尚未播完、DOM 尚未繪製時斷言失敗。

  3. 脆弱的 CSS Class 依賴:
    AI 使用了 .transaction-item 這個特定的 Class 名稱,但我們根本沒有定義這個 Class(我們使用的是 Tailwind 原子化 Class,如 p-4 bg-slate-800)。

修正與引導(Human Decision)

我複製了 Playwright 的 Error Trace 與我們的 UI HTML 結構給 AI,要求改進:

修正 Prompt:

「測試失敗了。理由如下:

表單輸入框沒有獨立 label,請改用 getByPlaceholder 來定位欄位。

我們元件沒有 .transaction-item 類名,請改用 getByText('午餐火鍋') 來斷言。

點擊按鈕後,後端 API Response 與 Framer Motion 進場動畫需要時間,請加上 await page.waitForResponse() 確保 API 回傳成功後才進行 UI 斷言。」

最終版本

經過調整後,我們獲得了一份 E2E 測試腳本:


// tests/add-transaction.spec.ts
import { test, expect } from '@playwright/test';

test.describe('交易管理流程', () => {
  test('使用者能夠順利新增交易並在清單中即時看到結果', async ({ page }) => {
    // 1.造訪儀表板
    await page.goto('/dashboard');

    // 2. 填寫表單(使用 Placeholders 精準定位)
    await page.getByPlaceholder('請輸入金額').fill('250');
    await page.getByRole('combobox', { name: /類別/i }).click();
    await page.getByRole('option', { name: '餐飲' }).click();
    await page.getByPlaceholder('例如:午餐、買書').fill('午餐火鍋');

    // 3. 攔截 POST API 請求與點擊提交
    const responsePromise = page.waitForResponse(
      (response) => response.url().includes('/api/transactions') && response.status() === 200
    );

    await page.getByRole('button', { name: '新增交易' }).click();

    // 確保後端資料處理完畢
    await responsePromise;

    // 4. 斷言:等待 Framer Motion 動畫完成並確認文字出現在畫面上
    const cardText = page.getByText('午餐火鍋');
    await expect(cardText).toBeVisible({ timeout: 5000 });

    const amountText = page.getByText('-$250');
    await expect(amountText).toBeVisible();
  });
});

JavaScript / 前端筆記

Auto-waiting 與 Web First Assertions:
Playwright 相比舊時代的 Selenium / Cypress,最大優勢在於內建的 Auto-waiting 策略。但在處理 AI 生成的程式碼時,網路請求(Network Latency)與 UI 動畫依然是造成 Flaky Test(時好時壞測試)的兩大元兇。善用 waitForResponse 能讓測試大幅穩定。

Accessibility-first 選擇器哲學:
編寫測試的最佳實踐是「像真實使用者一樣看螢幕」。使用者不會去看 .transaction-item 或 #input-3,使用者看的是按鈕上的文字、Placeholder 與 Option 標籤。讓 AI 採用 getByRole 和 getByText 撰寫測試,同時也能倒逼你改善 UI 的無障礙設計(a11y)。


小結

今天我們成功建立了第一個端到端(E2E)自動化測試!這標誌著我們階段三(前端開發與 UI/UX 實作)的完美落幕。我們從零打造了具備設計系統、響應式排版、複雜表單、動態 API 串接、Recharts 圖表、Framer Motion 微互動,以及 Playwright 品質把關的全功能前端!

明日 Day 19,我們將開啟全新的階段四:進階功能與 AI 功能整合!第一站:串接 Stripe 支付與 Resend Email 服務,讓我們的追蹤器具備商業化 SaaS 的能力!敬請期待!


上一篇
# Day 17 - 視覺與互動微調:運用 AI 開發 CSS 動畫與微互動(Framer Motion)
系列文
Vibe Mode 開啟:30 天用 AI 打造網頁,邊做邊學 JavaScript 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言