|
|
// 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<typeof vi.fn>).mockResolvedValue(BUNDLE);
|
|
|
});
|
|
|
|
|
|
async function renderReady() {
|
|
|
const utils = render(<CalendarClient />, { 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();
|
|
|
});
|