// frontend/tests/calendar/views.test.tsx — 뷰 전환 + 집중 블록/카테고리 토글 import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { beforeEach, expect, test, vi } from "vitest"; import { CalendarClient } from "@/components/calendar/CalendarClient"; import { DialogProvider } from "@/components/Dialog"; import { calendarApi } from "@/lib/calendar/api"; import type { WeekBundle } from "@/lib/calendar/types"; vi.mock("@/lib/calendar/api"); const BUNDLE: WeekBundle = { today: 8, today_date: "2026-06-08", mode: "demo", week: [7, 8, 9, 10, 11, 12, 13], weekdays: ["일", "월", "화", "수", "목", "금", "토"], calendars: [ { id: "work", name: "업무", tone: "blue", on: true, count: 1 }, { id: "health", name: "건강", tone: "coral", on: true, count: 1 }, ], events: [ { id: "w1", day: 8, date: "2026-06-08", start: "11:00", end: "11:45", title: "디자인 리뷰", cal: "work", loc: "회의실 B", note: "", soon: false, people: [], rrule: "", reminders: [], attendees: [], response_status: "", actions: [], has_meeting: false, meet_label: "", }, { id: "h1", day: 8, date: "2026-06-08", start: "20:00", end: "20:30", title: "스트레칭 & 명상", cal: "health", loc: "", note: "", soon: false, people: [], rrule: "", reminders: [], attendees: [], response_status: "", actions: [], has_meeting: false, meet_label: "", }, ], focus_blocks: [ { id: "f3", day: 8, date: "2026-06-08", start: "15:10", end: "16:25", title: "분기 리포트 초안 마무리", type: "deep", tag: "딥 워크", task_id: null, auto: true, }, ], }; beforeEach(() => { localStorage.clear(); (calendarApi.week as never as ReturnType).mockResolvedValue(BUNDLE); }); async function renderReady() { const utils = render(, { wrapper: DialogProvider }); // bundle 로딩 완료까지 대기 (주 헤더 표시) await screen.findByText("6월 7일 – 13일"); return utils; } test("뷰 세그먼트 전환 week→month→day", async () => { await renderReady(); const weekTab = screen.getByRole("tab", { name: "주" }); const monthTab = screen.getByRole("tab", { name: "월" }); const dayTab = screen.getByRole("tab", { name: "일" }); expect(weekTab).toHaveAttribute("aria-selected", "true"); fireEvent.click(monthTab); await waitFor(() => expect(monthTab).toHaveAttribute("aria-selected", "true")); expect(screen.getByRole("heading", { name: "2026년 6월" })).toBeInTheDocument(); fireEvent.click(dayTab); await waitFor(() => expect(dayTab).toHaveAttribute("aria-selected", "true")); // 일 뷰 어젠다 헤더 expect(screen.getByText(/6월 8일 월요일/)).toBeInTheDocument(); }); test("집중 블록 토글 off 시 .fblock 미렌더", async () => { const { container } = await renderReady(); // 주 뷰에 deep focus 블록이 렌더됨 expect(container.querySelector(".fblock")).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: /집중 블록/ })); await waitFor(() => expect(container.querySelector(".fblock")).toBeNull()); }); test("카테고리 토글 off 시 해당 cal 이벤트 숨김", async () => { await renderReady(); // 건강 이벤트(스트레칭) 가 주 뷰에 표시됨 expect(screen.getByText("스트레칭 & 명상")).toBeInTheDocument(); // 건강 카테고리 토글 off fireEvent.click(screen.getByRole("button", { name: /건강/ })); await waitFor(() => expect(screen.queryByText("스트레칭 & 명상")).toBeNull()); // 업무 이벤트는 유지 expect(screen.getByText("디자인 리뷰")).toBeInTheDocument(); }); test("주 뷰 빈 슬롯 클릭 시 생성 모달 오픈", async () => { const { container } = await renderReady(); // 빈 시간 컬럼(.wk-col) 클릭 → 생성 에디터 오픈 const col = container.querySelector(".wk-col") as HTMLElement; expect(col).toBeTruthy(); fireEvent.click(col); const dialog = await screen.findByRole("dialog", { name: "새 일정" }); expect(dialog).toBeInTheDocument(); }); test("이벤트 클릭은 슬롯 생성으로 새지 않고 팝오버를 연다", async () => { await renderReady(); // 이벤트 블록 클릭 → 팝오버(편집/삭제 액션) 표시, 생성 모달 아님 fireEvent.click(screen.getByText("디자인 리뷰")); expect(await screen.findByRole("button", { name: /편집/ })).toBeInTheDocument(); expect(screen.queryByRole("dialog", { name: "새 일정" })).toBeNull(); });