spin-frontend/e2e/project-flow.spec.ts
theorose49 f902fdb3ee
All checks were successful
build-and-push / build (push) Successful in 36s
test: Playwright E2E 자동화 도입 (14 스펙, 관리자/구성원 역할 커버)
- playwright.config.ts: 로컬 스택(5380/8380) 대상 headless chromium
- e2e/spin.spec.ts: 대시보드·네비·쪽지함·캘린더 일정 생성·프로젝트 생성 모달·검증, 구성원 역할(관리자 메뉴 숨김·관리 라우트 리다이렉트·프로필)
- e2e/project-flow.spec.ts: 프로젝트 상세 개요/작업자/메일 탭, 타임라인 작업 추가→상세 모달→칸반 카드
- e2e/member-domains.spec.ts: 근무·휴가 신청 검증, 인센티브 게이지, 대시보드 내 이슈
- ?as=user 로 역할 전환(api.ts 포워딩 활용), afterAll API 정리
- npm run test:e2e 스크립트, test-results/·playwright-report/ gitignore

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:14:39 +09:00

62 lines
2.9 KiB
TypeScript

import { test, expect } from "@playwright/test";
// 실제 프로젝트 상세 워크플로(개요/작업자/타임라인 칸반/메일 탭 + 작업 생성→상세 모달).
const API = "http://localhost:8380/api";
const stamp = Date.now().toString().slice(-6);
let projectId = "";
test.beforeAll(async () => {
const r = await fetch(`${API}/projects`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: `E2E흐름-${stamp}`, clientDomain: "meditech.com" }),
});
const p = await r.json();
projectId = p.id;
// 작업자 1명 추가(담당자 옵션)
await fetch(`${API}/projects/${projectId}/members`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ memberEmail: "park@special-partners.com", portion: 100, role: "작업자" }),
});
});
test.afterAll(async () => {
if (projectId) await fetch(`${API}/projects/${projectId}`, { method: "DELETE" }).catch(() => {});
});
test("프로젝트 상세 — 개요 렌더", async ({ page }) => {
await page.goto(`/projects/${projectId}`);
await expect(page.getByRole("heading", { name: new RegExp(`E2E흐름-${stamp}`) })).toBeVisible();
await expect(page.getByText("컨설팅 종류")).toBeVisible();
await expect(page.getByPlaceholder(/주의할 점/)).toBeVisible();
});
test("탭 이동 — 작업자/메일", async ({ page }) => {
await page.goto(`/projects/${projectId}`);
await page.getByRole("button", { name: "작업자", exact: true }).click();
await expect(page.getByText(/기여도\(portion\)/)).toBeVisible();
await expect(page.getByText("park@special-partners.com")).toBeVisible();
await page.getByRole("button", { name: "메일", exact: true }).click();
// 로컬은 Google Workspace 연동 미설정 → 안내 empty state가 정상
await expect(page.getByText("메일 연동이 설정되지 않았습니다")).toBeVisible();
});
test("타임라인 — 작업 추가 → 상세 모달 → 칸반 카드", async ({ page }) => {
const errs: string[] = [];
page.on("pageerror", (e) => errs.push(e.message));
await page.goto(`/projects/${projectId}`);
await page.getByRole("button", { name: "타임라인", exact: true }).click();
await page.getByRole("button", { name: "작업 추가" }).click();
const title = `E2E작업-${stamp}`;
await page.getByPlaceholder("예: 기술문서 초안 작성").fill(title);
await page.getByRole("button", { name: "추가", exact: true }).click();
// 생성 후 상세 모달이 열림(댓글 입력창으로 확인)
await expect(page.getByPlaceholder("댓글 입력…")).toBeVisible();
// 모달 닫기 → 칸반 카드에 작업명 표시
await page.keyboard.press("Escape");
await expect(page.getByPlaceholder("댓글 입력…")).toBeHidden();
await expect(page.getByText(title).first()).toBeVisible();
expect(errs, errs.join("\n")).toHaveLength(0);
});