/** * AWOOOI Smoke Tests * ================== * 5 個關鍵頁面的 Smoke Test — 部署後快速驗證基本功能 * * 測試範圍: * 1. 首頁 (/) — 200 OK,標題包含 AWOOOI * 2. Dashboard (/dashboard) — 無 JS console 錯誤 * 3. Incidents (/incidents) — 列表容器存在 * 4. Approvals (/approvals) — 頁面可存取 * 5. Omni-Terminal (/terminal) — WebSocket 連線不報錯 * * 注意: 需登入才能查看的頁面內容以 test.skip 跳過,僅驗證頁面可存取性。 * 截圖存至 test-results/screenshots/ 供人工比對。 * * @author 首席架構師 (Claude Code) * @version 1.0.0 * @date 2026-04-01 (台北時間) */ import { test, expect } from '@playwright/test' import * as fs from 'fs' import * as path from 'path' const BASE_URL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000' const SCREENSHOT_DIR = 'test-results/screenshots' // 確保截圖目錄存在 test.beforeAll(() => { if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }) } }) test.setTimeout(30000) // ============================================================================= // 1. 首頁 — 200 OK + 標題包含 AWOOOI // ============================================================================= test('首頁載入 — HTTP 200 + 標題含 AWOOOI', async ({ page }) => { const response = await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' }) // HTTP 狀態碼 200 expect(response?.status()).toBe(200) // 頁面標題含 AWOOOI (大小寫不限) const title = await page.title() expect(title.toLowerCase()).toContain('awoooi') // 截圖 await page.screenshot({ path: path.join(SCREENSHOT_DIR, '01-home.png'), fullPage: false, }) }) // ============================================================================= // 2. Dashboard — 無 JS console 錯誤 // ============================================================================= test('Dashboard 載入 — 無 JS console 錯誤', async ({ page }) => { const jsErrors: string[] = [] // 收集 JS 錯誤 (排除 Next.js HMR/chunk 載入警告) page.on('pageerror', (err) => { jsErrors.push(err.message) }) const response = await page.goto(`${BASE_URL}/dashboard`, { waitUntil: 'domcontentloaded' }) // 頁面可存取 (200 或 重導向後的頁面) expect(response?.status()).toBeLessThan(500) // 截圖 await page.screenshot({ path: path.join(SCREENSHOT_DIR, '02-dashboard.png'), fullPage: false, }) // 無嚴重 JS 錯誤 const criticalErrors = jsErrors.filter( (e) => !e.includes('ChunkLoadError') && // Next.js 開發時的 chunk 錯誤可忽略 !e.includes('Loading chunk') && !e.includes('Loading CSS chunk'), ) expect(criticalErrors).toHaveLength(0) }) // ============================================================================= // 3. Incidents — 列表容器存在 // ============================================================================= test('Incidents 頁面 — 列表容器存在', async ({ page }) => { const response = await page.goto(`${BASE_URL}/incidents`, { waitUntil: 'domcontentloaded' }) expect(response?.status()).toBeLessThan(500) // 截圖 (登入前) await page.screenshot({ path: path.join(SCREENSHOT_DIR, '03-incidents.png'), fullPage: false, }) // 頁面應存在某個容器 (未登入時會是登入頁或列表頁) // 驗證頁面 body 有內容 (非空白頁) const bodyText = await page.evaluate(() => document.body.innerText.trim()) expect(bodyText.length).toBeGreaterThan(0) // Note: 列表容器 (data-testid="incidents-list" 或同類) 需登入才可見, // 此處僅驗證頁面可存取且非空白。 // 完整列表驗證見 authenticated E2E tests。 }) // ============================================================================= // 4. Approvals — 頁面可存取 // ============================================================================= test('Approvals 頁面 — 頁面可存取', async ({ page }) => { const response = await page.goto(`${BASE_URL}/approvals`, { waitUntil: 'domcontentloaded' }) expect(response?.status()).toBeLessThan(500) await page.screenshot({ path: path.join(SCREENSHOT_DIR, '04-approvals.png'), fullPage: false, }) // 頁面有內容 const bodyText = await page.evaluate(() => document.body.innerText.trim()) expect(bodyText.length).toBeGreaterThan(0) }) // ============================================================================= // 5. Omni-Terminal — WebSocket 連線不報錯 // ============================================================================= test('Omni-Terminal — 頁面載入且無 WebSocket 連線錯誤', async ({ page }) => { const wsErrors: string[] = [] // 監聽 WebSocket 錯誤 page.on('pageerror', (err) => { if (err.message.toLowerCase().includes('websocket')) { wsErrors.push(err.message) } }) const response = await page.goto(`${BASE_URL}/terminal`, { waitUntil: 'domcontentloaded' }) expect(response?.status()).toBeLessThan(500) // 等待短暫時間讓 WS 初始化 await page.waitForTimeout(2000) await page.screenshot({ path: path.join(SCREENSHOT_DIR, '05-terminal.png'), fullPage: false, }) // 無 WebSocket 錯誤 expect(wsErrors).toHaveLength(0) })