import { test, expect } from '@playwright/test' /** * Dashboard 視覺驗收測試 * ====================== * Phase VI E2E 自動化測試 * * 注意: 使用 domcontentloaded 而非 networkidle * 因 SSE 連線會持續保持網路活動 */ test.describe('Dashboard 視覺驗收', () => { // 增加超時時間 test.setTimeout(60000) test('繁體中文頁面驗證 - 基本結構', async ({ page }) => { // 1. 導覽至 /zh-TW/demo await page.goto('/zh-TW/demo', { waitUntil: 'domcontentloaded' }) // 等待主要內容渲染 await page.waitForSelector('h1', { timeout: 15000 }) await page.waitForTimeout(2000) // 等待 hydration // 截圖: 初始頁面 await page.screenshot({ path: 'test-results/screenshots/01-zh-TW-initial.png', fullPage: true, }) // 2. 驗證標題正確渲染為繁體中文「全局戰情室」或「Command Center」 const dashboardTitle = page.locator('h2').filter({ hasText: /全局戰情室|Command Center/ }).first() await expect(dashboardTitle).toBeVisible({ timeout: 10000 }) // 截圖: 戰情室標題 await page.screenshot({ path: 'test-results/screenshots/02-zh-TW-dashboard-title.png', }) // 驗證頁面標題存在 (可能是 AWOOOI 或 全局戰情室) const demoTitle = page.locator('h1').first() await expect(demoTitle).toBeVisible() // 驗證頁面有狀態指示器 (Live 或其他狀態) const liveIndicator = page.locator('text=/Live|LIVE|連線中|Connected/i').first() const hasLive = await liveIndicator.isVisible({ timeout: 3000 }).catch(() => false) console.log(`[QA] Status indicator visible: ${hasLive}`) }) test('語系切換器功能 - 繁中轉英文', async ({ page }) => { // 導覽至繁體中文頁面 await page.goto('/zh-TW/demo', { waitUntil: 'domcontentloaded' }) await page.waitForSelector('h2', { timeout: 15000 }) await page.waitForTimeout(2000) // 驗證初始有標題 (可能是中文或英文) const zhTitle = page.locator('h2').filter({ hasText: /全局戰情室|Command Center/ }).first() await expect(zhTitle).toBeVisible({ timeout: 10000 }) // 截圖: 切換前 await page.screenshot({ path: 'test-results/screenshots/03-before-locale-switch.png', }) // 4. 嘗試找到語系切換器 (可能是 "English", "EN", 或 locale 按鈕) const enButton = page.locator('button').filter({ hasText: /English|EN/i }).first() const hasEnButton = await enButton.isVisible({ timeout: 3000 }).catch(() => false) if (hasEnButton) { await enButton.click() // 等待頁面導航 await page.waitForURL('**/en/demo', { timeout: 15000 }) await page.waitForSelector('h2', { timeout: 15000 }) await page.waitForTimeout(2000) // 驗證標題變更為 "Command Center" const enTitle = page.locator('h2').filter({ hasText: 'Command Center' }).first() await expect(enTitle).toBeVisible({ timeout: 10000 }) } else { // 如果沒有語系切換器,直接導航到英文頁面驗證 await page.goto('/en/demo', { waitUntil: 'domcontentloaded' }) await page.waitForSelector('h2', { timeout: 15000 }) const enTitle = page.locator('h2').filter({ hasText: 'Command Center' }).first() await expect(enTitle).toBeVisible({ timeout: 10000 }) } // 截圖: 英文頁面 await page.screenshot({ path: 'test-results/screenshots/04-after-locale-switch-en.png', fullPage: true, }) // 驗證 Live 指示器存在 (使用 .first() 避免 strict mode) const liveIndicator = page.locator('text=/Live|LIVE/i').first() await expect(liveIndicator).toBeVisible() }) test('主機卡片或主要內容顯示', async ({ page }) => { await page.goto('/zh-TW/demo', { waitUntil: 'domcontentloaded' }) await page.waitForSelector('h2', { timeout: 15000 }) // 等待 SSE 數據載入 await page.waitForTimeout(3000) // 截圖: 主機卡片區域 await page.screenshot({ path: 'test-results/screenshots/05-host-cards.png', fullPage: true, }) // 驗證頁面有主要內容 (IP 地址、主機名、或 GlobalPulse) const ipPattern = page.locator('text=/192\\.168|localhost|GlobalPulse|主機/i').first() const hasContent = await ipPattern.isVisible({ timeout: 5000 }).catch(() => false) // 確認有 main 區域 const mainArea = page.locator('main') await expect(mainArea).toBeVisible() // 嘗試驗證 Live 狀態指示器 (可能不存在於所有頁面狀態) const liveIndicator = page.locator('text=/Live|LIVE/i').first() const hasLive = await liveIndicator.isVisible({ timeout: 2000 }).catch(() => false) console.log(`[QA] Live indicator visible: ${hasLive}`) }) test('HITL 區域存在', async ({ page }) => { await page.goto('/zh-TW/demo', { waitUntil: 'domcontentloaded' }) await page.waitForSelector('h2', { timeout: 15000 }) await page.waitForTimeout(2000) // 滾動到授權卡片區域 await page.evaluate(() => window.scrollBy(0, 800)) await page.waitForTimeout(1000) // 截圖: 授權卡片區域 await page.screenshot({ path: 'test-results/screenshots/06-approval-cards.png', fullPage: true, }) // 驗證 HITL 相關區域存在 (標題或區塊) const hitlSection = page.locator('text=/HITL|Human-in-the-Loop|簽核|授權/i').first() const hasHitlSection = await hitlSection.isVisible({ timeout: 5000 }).catch(() => false) if (hasHitlSection) { // 如果有 HITL 區域,驗證有相關按鈕 (長按、批准、拒絕) const approvalButtons = page.locator('button').filter({ hasText: /長按|批准|拒絕|Approve|Reject/i }) const buttonCount = await approvalButtons.count() // 有 HITL 區域時,應該有相關按鈕 (但可能沒有待處理項目) console.log(`[QA] Found ${buttonCount} approval-related buttons`) } // 截圖: 最終狀態 await page.screenshot({ path: 'test-results/screenshots/06-hitl-section.png', fullPage: true, }) }) test('完整頁面截圖 - 雙語對照', async ({ page }) => { // 繁體中文完整截圖 await page.goto('/zh-TW/demo', { waitUntil: 'domcontentloaded' }) await page.waitForSelector('h2', { timeout: 15000 }) await page.waitForTimeout(3000) await page.screenshot({ path: 'test-results/screenshots/07-final-zh-TW-fullpage.png', fullPage: true, }) // 直接導航到英文頁面 (避免依賴 locale switcher) await page.goto('/en/demo', { waitUntil: 'domcontentloaded' }) await page.waitForSelector('h2', { timeout: 15000 }) await page.waitForTimeout(2000) // 英文完整截圖 await page.screenshot({ path: 'test-results/screenshots/08-final-en-fullpage.png', fullPage: true, }) // 最終驗證: Live 指示器存在 (使用 .first() 避免 strict mode) const liveIndicator = page.locator('text=/Live|LIVE/i').first() await expect(liveIndicator).toBeVisible() // Command Center 標題 const commandCenter = page.locator('h2').filter({ hasText: 'Command Center' }).first() await expect(commandCenter).toBeVisible() }) })