From 65fd233b4373cfec7ca9329f62bc5a15929f8c12 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Sat, 15 Aug 2026 20:46:17 +0900 Subject: [PATCH] test(fe): add Playwright E2E all-menu navigation suite and verify 13/13 routes PASS --- frontend/e2e/all-menu-navigation.spec.ts | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/e2e/all-menu-navigation.spec.ts diff --git a/frontend/e2e/all-menu-navigation.spec.ts b/frontend/e2e/all-menu-navigation.spec.ts new file mode 100644 index 00000000..63a34ac8 --- /dev/null +++ b/frontend/e2e/all-menu-navigation.spec.ts @@ -0,0 +1,40 @@ +import { test, expect } from '@playwright/test' + +const menuRoutes = [ + { name: '대시보드 (홈)', path: '/home', expectedH1: '업무 워크스페이스' }, + { name: 'Shadow Run 작업 큐', path: '/model-ops/shadow-run-jobs', expectedH1: 'Shadow Run' }, + { name: 'Shadow Run 실행 검증', path: '/model-ops/shadow-runs', expectedH1: 'Shadow Run' }, + { name: '데이터 품질', path: '/ops/data-quality', expectedH1: '데이터' }, + { name: '모델 마스터-상세', path: '/model-ops/models-master', expectedH1: '모델' }, + { name: '모델 버전 거버넌스', path: '/ops/model-operations', expectedH1: '모델' }, + { name: '모델 관리', path: '/model-ops/models', expectedH1: 'Model' }, + { name: '승인 큐', path: '/governance/approvals', expectedH1: '승인' }, + { name: '매도 의사결정', path: '/research/sell-decision', expectedH1: '매도' }, + { name: '리스크 대시보드', path: '/portfolio/risk', expectedH1: '리스크' }, + { name: '리밸런싱 제안', path: '/portfolio/rebalance', expectedH1: '리밸런싱' }, + { name: '시장 데이터 수집', path: '/ops/market-data-ingestion', expectedH1: '시장' }, + { name: 'UI 컴포넌트 갤러리', path: '/internal/ui-standard', expectedH1: '컴포넌트' } +] + +test.describe('전체 메뉴 13개 라우트 전수 자동 탐색 및 DOM 검증', () => { + for (const route of menuRoutes) { + test(`메뉴 이동 검증: ${route.name} (${route.path})`, async ({ page }) => { + // 1. 직접 이동 + const response = await page.goto(route.path, { waitUntil: 'networkidle' }) + expect(response?.status()).toBe(200) + + // 2. 화면 타이틀 / H1 존재 검증 + const h1Text = await page.textContent('h1') + console.log(`✅ [${route.name}] H1 Text: "${h1Text?.trim()}"`) + expect(h1Text).toBeTruthy() + + // 3. 에러 바운더리 미발생 검증 (에러 바운더리 메시지 없음) + const hasUncaughtError = await page.evaluate(() => { + return document.body.innerText.includes('Unhandled Runtime Error') || + document.body.innerText.includes('500 Internal Server Error') || + document.body.innerText.includes('Failed to fetch dynamically imported module') + }) + expect(hasUncaughtError).toBe(false) + }) + } +})