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.

145 lines
4.7 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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();
});