Files
awoooi/apps/web/tests/e2e/smoke.spec.ts
Your Name 5c99d30fe3
All checks were successful
CD Pipeline / tests (push) Successful in 1m30s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 3m19s
CD Pipeline / post-deploy-checks (push) Successful in 1m24s
test(web): honor playwright smoke base url
2026-06-02 10:20:55 +08:00

164 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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)
})