// frontend/playwright/journey.spec.ts — 여정(오늘의 흐름) E2E // 문서 §9.1 테스팅 섹션 + 코너케이스(베지어 호버 강조, done 토글, 반응형) 포함. import { test, expect } from "@playwright/test"; test.beforeEach(async ({ page }) => { await page.goto("/journey"); await expect(page.getByRole("heading", { name: "오늘의 여정" })).toBeVisible(); }); test("헤더: 날짜·날씨 eyebrow + 타이틀", async ({ page }) => { await expect(page.locator(".ph-eyebrow")).toContainText("6월 10일 화요일"); await expect(page.locator(".ph-eyebrow")).toContainText("맑음"); await expect(page.getByPlaceholder("흐름에서 작업·사람 찾기…")).toBeVisible(); }); test("흐름 패널 라벨: 4단계 · 12개 항목 · 5명 협업", async ({ page }) => { const label = page.locator(".jx-label"); await expect(label).toContainText("오늘의 흐름"); await expect(label).toContainText("4단계"); await expect(label).toContainText("12개 항목"); await expect(label).toContainText("5명 협업"); }); test("4개 컬럼 스테이지 라벨 + 시간", async ({ page }) => { const labels = page.locator(".jx-labels .cl"); await expect(labels).toHaveCount(4); await expect(labels.nth(0)).toContainText("아침 준비"); await expect(labels.nth(1)).toContainText("오전 집중"); await expect(labels.nth(2)).toContainText("오후 협업"); await expect(labels.nth(3)).toContainText("마무리 & 내일"); await expect(labels.nth(0)).toContainText("06:40"); }); test("피플 스택: 핵심 5인", async ({ page }) => { // 메일 발신자 제외, 핵심 5명만 (지/현/민/재/수) + '+3' more const avs = page.locator(".jx-stack .av"); await expect(avs).toHaveCount(5); await expect(page.locator(".jx-stack .more")).toHaveText("+3"); await expect(avs.nth(0)).toContainText("지"); await expect(avs.nth(1)).toContainText("현"); }); test("베지어 커넥터: 8개 .jx-line 경로가 렌더된다", async ({ page }) => { // DOM 측정 후 RAF/폰트 로드까지 경로 계산이 끝나길 대기 await expect.poll(async () => page.locator(".jx-svg .jx-line").count()).toBe(8); // svg 는 접근성 트리에서 숨김 + 카드 아래(z-index) await expect(page.locator(".jx-svg")).toHaveAttribute("aria-hidden", "true"); }); // phase-16+: 작업 시드 제거 → 카드 내용(작업 제목)·done 상태·라이브 동기화는 // 실제 작업이 있어야 검증 가능(노드 매핑 k1/k3 는 공개 API로 생성 불가) → 해당 테스트 제외. // 여정의 구조(노드 카드·베지어·테이블 행/별·게이지·반응형)는 아래에서 계속 검증한다. test("하단 작업 테이블: 5행 + 별 2개 on(구조)", async ({ page }) => { const rows = page.locator(".jtable tbody tr"); await expect(rows).toHaveCount(5); // TABLE_ROWS 고정 await expect(page.locator(".jtable .star.on")).toHaveCount(2); }); test("지표 게이지 2개 + 풋 문구", async ({ page }) => { await expect(page.locator(".gauges .gauge")).toHaveCount(2); await expect(page.locator(".gauges")).toContainText("딥 워크"); await expect(page.locator(".gauges")).toContainText("활동 링"); await expect(page.locator(".gauge-foot")).toContainText("스트레칭"); }); test("힌트 문구 노출", async ({ page }) => { await expect(page.locator(".jx-hint")).toContainText("마우스를 올리면"); }); test("반응형: 720px 이하에서 베지어 svg 숨김", async ({ page }) => { await expect(page.locator(".jx-svg")).toBeVisible(); await page.setViewportSize({ width: 700, height: 900 }); await expect(page.locator(".jx-svg")).toBeHidden(); await expect(page.locator(".jx-labels")).toBeHidden(); });