[FEAT] 導入 Playwright E2E 測試環境與登入功能測試腳本
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m36s
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m36s
This commit is contained in:
266
.agents/skills/e2e-testing/SKILL.md
Normal file
266
.agents/skills/e2e-testing/SKILL.md
Normal file
@@ -0,0 +1,266 @@
|
||||
---
|
||||
name: E2E 端到端測試規範 (E2E Testing with Playwright)
|
||||
description: 規範 Playwright 端到端測試的撰寫慣例、目錄結構、共用工具與執行方式,確保所有 E2E 測試保持一致性與可維護性。
|
||||
---
|
||||
|
||||
# E2E 端到端測試規範 (E2E Testing with Playwright)
|
||||
|
||||
本技能定義了 Star ERP 系統中端到端 (E2E) 測試的實作標準,使用 Playwright 模擬真實使用者操作瀏覽器,驗證 UI 顯示與功能流程的正確性。
|
||||
|
||||
---
|
||||
|
||||
## 1. 專案結構
|
||||
|
||||
### 1.1 目錄配置
|
||||
|
||||
```
|
||||
star-erp/
|
||||
├── playwright.config.ts # Playwright 設定檔
|
||||
├── e2e/ # E2E 測試根目錄
|
||||
│ ├── helpers/ # 共用工具函式
|
||||
│ │ └── auth.ts # 登入 helper
|
||||
│ ├── screenshots/ # 測試截圖存放
|
||||
│ ├── auth.spec.ts # 認證相關測試(登入、登出)
|
||||
│ ├── inventory.spec.ts # 庫存模組測試
|
||||
│ ├── products.spec.ts # 商品模組測試
|
||||
│ └── {module}.spec.ts # 依模組命名
|
||||
├── playwright-report/ # HTML 測試報告(自動產生,已 gitignore)
|
||||
└── test-results/ # 失敗截圖與錄影(自動產生,已 gitignore)
|
||||
```
|
||||
|
||||
### 1.2 命名規範
|
||||
|
||||
| 項目 | 規範 | 範例 |
|
||||
|---|---|---|
|
||||
| 測試檔案 | 小寫,依模組命名 `.spec.ts` | `inventory.spec.ts` |
|
||||
| 測試群組 | `test.describe('中文功能名稱')` | `test.describe('庫存查詢')` |
|
||||
| 測試案例 | 中文描述「**應**」開頭 | `test('應顯示庫存清單')` |
|
||||
| 截圖檔案 | `{module}-{scenario}.png` | `inventory-search-result.png` |
|
||||
|
||||
---
|
||||
|
||||
## 2. 設定檔 (playwright.config.ts)
|
||||
|
||||
### 2.1 核心設定
|
||||
|
||||
```typescript
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: 'html',
|
||||
use: {
|
||||
baseURL: 'http://localhost:8081', // Sail 開發伺服器
|
||||
screenshot: 'only-on-failure', // 失敗時自動截圖
|
||||
video: 'retain-on-failure', // 失敗時保留錄影
|
||||
trace: 'on-first-retry', // 重試時收集 trace
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### 2.2 重要注意事項
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `baseURL` 必須指向本機 Sail 開發伺服器(預設 `http://localhost:8081`)。
|
||||
> 確保測試前已執行 `./vendor/bin/sail up -d` 與 `./vendor/bin/sail npm run dev`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 共用工具 (Helpers)
|
||||
|
||||
### 3.1 登入 Helper
|
||||
|
||||
位置:`e2e/helpers/auth.ts`
|
||||
|
||||
```typescript
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* 共用登入函式
|
||||
* 使用測試帳號登入 ERP 系統
|
||||
*/
|
||||
export async function login(page: Page, username = 'mama', password = 'mama9453') {
|
||||
await page.goto('/');
|
||||
await page.fill('#username', username);
|
||||
await page.fill('#password', password);
|
||||
await page.getByRole('button', { name: '登入系統' }).click();
|
||||
// 等待儀表板載入完成
|
||||
await page.waitForSelector('text=系統概況', { timeout: 10000 });
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 使用方式
|
||||
|
||||
```typescript
|
||||
import { login } from './helpers/auth';
|
||||
|
||||
test('應顯示庫存清單', async ({ page }) => {
|
||||
await login(page);
|
||||
await page.goto('/inventory/stock-query');
|
||||
// ...斷言
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 測試撰寫規範
|
||||
|
||||
### 4.1 測試結構模板
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { login } from './helpers/auth';
|
||||
|
||||
test.describe('模組功能名稱', () => {
|
||||
|
||||
// 若整個 describe 都需要登入,使用 beforeEach
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await login(page);
|
||||
});
|
||||
|
||||
test('應正確顯示頁面標題與關鍵元素', async ({ page }) => {
|
||||
await page.goto('/target-page');
|
||||
|
||||
// 驗證頁面標題
|
||||
await expect(page.getByText('頁面標題')).toBeVisible();
|
||||
|
||||
// 驗證表格存在
|
||||
await expect(page.locator('table')).toBeVisible();
|
||||
});
|
||||
|
||||
test('應能執行 CRUD 操作', async ({ page }) => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 4.2 斷言 (Assertions) 慣例
|
||||
|
||||
| 場景 | 優先使用 | 避免使用 |
|
||||
|---|---|---|
|
||||
| 驗證頁面載入 | `page.getByText('關鍵文字')` | `page.waitForURL()` ※ |
|
||||
| 驗證元素存在 | `expect(locator).toBeVisible()` | `.count() > 0` |
|
||||
| 驗證表格資料 | `page.locator('table tbody tr')` | 硬編碼行數 |
|
||||
| 等待操作完成 | `expect().toBeVisible({ timeout })` | `page.waitForTimeout()` |
|
||||
|
||||
> [!NOTE]
|
||||
> ※ Star ERP 使用 Inertia.js,頁面導航不一定改變 URL(例如儀表板路由為 `/`)。
|
||||
> 因此**優先使用頁面內容驗證**,而非依賴 URL 變化。
|
||||
|
||||
### 4.3 選擇器優先順序
|
||||
|
||||
依照 Playwright 官方建議,選擇器優先順序為:
|
||||
|
||||
1. **Role** — `page.getByRole('button', { name: '登入系統' })`
|
||||
2. **Text** — `page.getByText('系統概況')`
|
||||
3. **Label** — `page.getByLabel('帳號')`
|
||||
4. **Placeholder** — `page.getByPlaceholder('請輸入...')`
|
||||
5. **Test ID** — `page.getByTestId('submit-btn')`(需在元件加 `data-testid`)
|
||||
6. **CSS** — `page.locator('#username')`(最後手段)
|
||||
|
||||
### 4.4 禁止事項
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:硬等待(不可預期的等待時間)
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// ✅ 正確:等待特定條件
|
||||
await expect(page.getByText('操作成功')).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// ❌ 禁止:在測試中寫死測試資料的 ID
|
||||
await page.goto('/products/42/edit');
|
||||
|
||||
// ✅ 正確:從頁面互動導航
|
||||
await page.locator('table tbody tr').first().getByRole('button', { name: '編輯' }).click();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 截圖與視覺回歸
|
||||
|
||||
### 5.1 手動截圖(文件用途)
|
||||
|
||||
```typescript
|
||||
// 成功截圖存於 e2e/screenshots/
|
||||
await page.screenshot({
|
||||
path: 'e2e/screenshots/inventory-list.png',
|
||||
fullPage: true,
|
||||
});
|
||||
```
|
||||
|
||||
### 5.2 視覺回歸測試(偵測 UI 變化)
|
||||
|
||||
```typescript
|
||||
test('庫存頁面 UI 應保持一致', async ({ page }) => {
|
||||
await login(page);
|
||||
await page.goto('/inventory/stock-query');
|
||||
// 比對截圖,pixel 級差異會報錯
|
||||
await expect(page).toHaveScreenshot('stock-query.png', {
|
||||
maxDiffPixelRatio: 0.01, // 容許 1% 差異(動態資料)
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> 首次執行 `toHaveScreenshot()` 會自動建立基準截圖。
|
||||
> 後續執行會與基準比對,更新基準用:`npx playwright test --update-snapshots`
|
||||
|
||||
---
|
||||
|
||||
## 6. 執行指令速查
|
||||
|
||||
```bash
|
||||
# 執行所有 E2E 測試
|
||||
npx playwright test
|
||||
|
||||
# 執行特定模組測試
|
||||
npx playwright test e2e/login.spec.ts
|
||||
|
||||
# UI 互動模式(可視化瀏覽器操作)
|
||||
npx playwright test --ui
|
||||
|
||||
# 帶頭模式(顯示瀏覽器畫面)
|
||||
npx playwright test --headed
|
||||
|
||||
# 產生 HTML 報告並開啟
|
||||
npx playwright test --reporter=html
|
||||
npx playwright show-report
|
||||
|
||||
# 更新視覺回歸基準截圖
|
||||
npx playwright test --update-snapshots
|
||||
|
||||
# 只執行特定測試案例(用 -g 篩選名稱)
|
||||
npx playwright test -g "登入"
|
||||
|
||||
# Debug 模式(逐步執行)
|
||||
npx playwright test --debug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 開發檢核清單 (Checklist)
|
||||
|
||||
### 新增頁面或功能時:
|
||||
|
||||
- [ ] 是否已為新頁面建立對應的 `.spec.ts` 測試檔?
|
||||
- [ ] 測試是否覆蓋主要的 Happy Path(正常操作流程)?
|
||||
- [ ] 測試是否覆蓋關鍵的 Error Path(錯誤處理)?
|
||||
- [ ] 共用的登入步驟是否使用 `helpers/auth.ts`?
|
||||
- [ ] 斷言是否優先使用頁面內容而非 URL?
|
||||
- [ ] 選擇器是否遵循優先順序(Role > Text > Label > CSS)?
|
||||
- [ ] 測試是否可獨立執行(不依賴其他測試的狀態)?
|
||||
|
||||
### 提交程式碼前:
|
||||
|
||||
- [ ] 全部 E2E 測試是否通過?(`npx playwright test`)
|
||||
- [ ] 是否有遺留的 `test.only` 或 `test.skip`?
|
||||
Reference in New Issue
Block a user