You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
5.7 KiB
TypeScript
119 lines
5.7 KiB
TypeScript
// 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");
|
|
});
|
|
|
|
test("호버 강조: 카드에 올리면 self+이웃 lit, 나머지 mute, 관련 라인 on", async ({ page }) => {
|
|
const report = page.locator(".fcard.task", { hasText: "분기 리포트 초안 마무리" });
|
|
const brief = page.locator(".fcard", { hasText: "아침 브리핑 확인" }); // m-brief → t-report
|
|
const unrelated = page.locator(".fcard.task", { hasText: "온보딩 와이어프레임 피드백 정리" });
|
|
|
|
await report.hover();
|
|
await expect(report).toHaveClass(/lit/);
|
|
await expect(brief).toHaveClass(/lit/); // 이웃(들어오는 링크)
|
|
await expect(unrelated).toHaveClass(/mute/); // 비이웃
|
|
// t-report 와 연결된 라인은 .on
|
|
await expect(page.locator(".jx-link.on")).toHaveCount(2); // m-brief>t-report, t-report>k-strat
|
|
await expect(page.locator(".jx-link.off").first()).toBeVisible();
|
|
|
|
// 호버 해제 시 강조 초기화
|
|
await page.locator(".jx-label").hover();
|
|
await expect(report).not.toHaveClass(/lit/);
|
|
await expect(page.locator(".jx-link.on")).toHaveCount(0);
|
|
});
|
|
|
|
test("done 토글: 체크 시 카드 done 클래스 + 카운트 감소", async ({ page }) => {
|
|
const card = page.locator(".fcard.task", { hasText: "사용자 인터뷰 5건 정리" });
|
|
await expect(card).not.toHaveClass(/done/);
|
|
// 시작 카운트 3/4 (welcome 만 done) — 컬럼 헤더 카운트 배지(.jx-chead .n)
|
|
const count = page.locator(".jx-chead .n", { hasText: "/4" });
|
|
await expect(count).toHaveText("3/4");
|
|
|
|
await card.getByRole("button", { name: "사용자 인터뷰 5건 정리" }).click();
|
|
await expect(card).toHaveClass(/done/);
|
|
await expect(count).toHaveText("2/4");
|
|
|
|
// 다시 토글 → 복원
|
|
await card.getByRole("button", { name: "사용자 인터뷰 5건 정리" }).click();
|
|
await expect(card).not.toHaveClass(/done/);
|
|
await expect(count).toHaveText("3/4");
|
|
});
|
|
|
|
test("하단 작업 테이블: 5행 + 상태 칩 + 별 2개 on", async ({ page }) => {
|
|
const rows = page.locator(".jtable tbody tr");
|
|
await expect(rows).toHaveCount(5);
|
|
await expect(page.locator(".jtable .chip.prog").first()).toBeVisible();
|
|
await expect(page.locator(".jtable .chip.review")).toBeVisible();
|
|
await expect(page.locator(".jtable .chip.done")).toBeVisible();
|
|
await expect(page.locator(".jtable .chip.sched")).toBeVisible();
|
|
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();
|
|
});
|
|
|
|
test("작업 상태가 라이브 데이터를 반영한다(테이블 ↔ 흐름 일치)", async ({ page }) => {
|
|
// t-welcome 은 done(seed) → 흐름카드/테이블 동기화
|
|
const tableWelcome = page.locator(".jtable tbody tr", { hasText: "신규 입사자 환영" });
|
|
await expect(tableWelcome.locator(".chip.done")).toBeVisible();
|
|
});
|