- apps/api: FastAPI backend with Dockerfile - apps/web: Next.js frontend with Dockerfile - apps/sensor: Signal collection agent - packages: shared packages Co-Authored-By: Claude <noreply@anthropic.com>
161 lines
5.3 KiB
TypeScript
161 lines
5.3 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
/**
|
|
* Action Log 頁面測試
|
|
* ====================
|
|
* Phase 4: 行動日誌 E2E 測試
|
|
*
|
|
* 測試項目:
|
|
* 1. 頁面正確載入
|
|
* 2. i18n 標題顯示正確
|
|
* 3. 統計卡片顯示
|
|
* 4. 表格結構正確
|
|
* 5. 分頁控制項存在
|
|
*/
|
|
|
|
test.describe('Action Log 頁面測試', () => {
|
|
test.setTimeout(60000)
|
|
|
|
test('繁體中文頁面 - 基本元素驗證', async ({ page }) => {
|
|
// 1. 導覽至 Action Log 頁面
|
|
await page.goto('/zh-TW/action-logs', { waitUntil: 'domcontentloaded' })
|
|
|
|
// 等待主要內容渲染 (使用 main 作為等待目標,更可靠)
|
|
await page.waitForSelector('main', { timeout: 15000 })
|
|
await page.waitForTimeout(2000) // 等待 hydration
|
|
|
|
// 截圖: 初始頁面
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-01-zh-TW-initial.png',
|
|
fullPage: true,
|
|
})
|
|
|
|
// 2. 驗證頁面標題為繁體中文「行動日誌」(h2 或 h3)
|
|
const pageTitle = page.locator('h2, h3').filter({ hasText: '行動日誌' }).first()
|
|
await expect(pageTitle).toBeVisible({ timeout: 10000 })
|
|
|
|
// 3. 驗證副標題
|
|
const subtitle = page.locator('text=K8s 操作執行稽核軌跡')
|
|
await expect(subtitle).toBeVisible()
|
|
|
|
// 4. 驗證頁面結構 - 有 main 內容區域
|
|
const mainContent = page.locator('main')
|
|
await expect(mainContent).toBeVisible()
|
|
|
|
// 截圖: 頁面結構
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-02-page-structure.png',
|
|
})
|
|
})
|
|
|
|
test('英文頁面 - 基本元素驗證', async ({ page }) => {
|
|
// 導覽至英文 Action Log 頁面
|
|
await page.goto('/en/action-logs', { waitUntil: 'domcontentloaded' })
|
|
await page.waitForSelector('h2', { timeout: 15000 })
|
|
await page.waitForTimeout(2000)
|
|
|
|
// 截圖: 英文頁面
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-03-en-initial.png',
|
|
fullPage: true,
|
|
})
|
|
|
|
// 驗證英文標題
|
|
const pageTitle = page.locator('h2').filter({ hasText: 'Action Log' })
|
|
await expect(pageTitle).toBeVisible({ timeout: 10000 })
|
|
|
|
// 驗證英文副標題
|
|
const subtitle = page.locator('text=K8s Operation Execution Audit Trail')
|
|
await expect(subtitle).toBeVisible()
|
|
})
|
|
|
|
test('資料狀態顯示 - 顯示空狀態、表格或錯誤訊息', async ({ page }) => {
|
|
// 導覽至 Action Log 頁面
|
|
await page.goto('/zh-TW/action-logs', { waitUntil: 'domcontentloaded' })
|
|
await page.waitForSelector('main', { timeout: 15000 })
|
|
await page.waitForTimeout(3000) // 等待 API 回應
|
|
|
|
// 截圖: 當前狀態
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-04-data-state.png',
|
|
fullPage: true,
|
|
})
|
|
|
|
// 檢查各種可能的狀態
|
|
const emptyState = page.locator('text=目前沒有執行紀錄')
|
|
const table = page.locator('table')
|
|
const errorState = page.locator('text=無法取得稽核日誌')
|
|
const loadingState = page.locator('text=載入中')
|
|
|
|
// 至少一個狀態元素可見 (資料、空、錯誤或載入中)
|
|
const hasEmptyState = await emptyState.isVisible()
|
|
const hasTable = await table.isVisible()
|
|
const hasError = await errorState.isVisible()
|
|
const hasLoading = await loadingState.isVisible()
|
|
|
|
// 驗證頁面正確處理了各種狀態
|
|
expect(hasEmptyState || hasTable || hasError || hasLoading).toBeTruthy()
|
|
})
|
|
|
|
test('側邊欄導航 - 行動日誌連結可點擊', async ({ page }) => {
|
|
// 先導覽至首頁
|
|
await page.goto('/zh-TW/demo', { waitUntil: 'domcontentloaded' })
|
|
await page.waitForSelector('h1', { timeout: 15000 })
|
|
await page.waitForTimeout(2000)
|
|
|
|
// 截圖: 首頁
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-05-before-nav.png',
|
|
})
|
|
|
|
// 找到側邊欄中的「行動日誌」連結
|
|
const actionLogLink = page.locator('a').filter({ hasText: '行動日誌' })
|
|
await expect(actionLogLink).toBeVisible()
|
|
|
|
// 點擊導航到 Action Log 頁面
|
|
await actionLogLink.click()
|
|
|
|
// 等待導航完成
|
|
await page.waitForURL('**/action-logs', { timeout: 15000 })
|
|
await page.waitForSelector('h2', { timeout: 15000 })
|
|
await page.waitForTimeout(2000)
|
|
|
|
// 截圖: 導航後
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-06-after-nav.png',
|
|
fullPage: true,
|
|
})
|
|
|
|
// 驗證標題
|
|
const pageTitle = page.locator('h2').filter({ hasText: '行動日誌' })
|
|
await expect(pageTitle).toBeVisible({ timeout: 10000 })
|
|
})
|
|
|
|
test('重新整理按鈕存在且可點擊', async ({ page }) => {
|
|
await page.goto('/zh-TW/action-logs', { waitUntil: 'domcontentloaded' })
|
|
await page.waitForSelector('h2', { timeout: 15000 })
|
|
await page.waitForTimeout(2000)
|
|
|
|
// 找到重新整理按鈕
|
|
const refreshButton = page.locator('button').filter({ hasText: '重新整理' })
|
|
await expect(refreshButton).toBeVisible()
|
|
|
|
// 截圖: 點擊前
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-07-before-refresh.png',
|
|
})
|
|
|
|
// 點擊重新整理
|
|
await refreshButton.click()
|
|
await page.waitForTimeout(1000)
|
|
|
|
// 截圖: 點擊後
|
|
await page.screenshot({
|
|
path: 'test-results/screenshots/action-log-08-after-refresh.png',
|
|
})
|
|
|
|
// 按鈕仍然存在
|
|
await expect(refreshButton).toBeVisible()
|
|
})
|
|
})
|