diff --git a/tests/components/notes/NoteList.test.tsx b/tests/components/notes/NoteList.test.tsx new file mode 100644 index 0000000..ff2b988 --- /dev/null +++ b/tests/components/notes/NoteList.test.tsx @@ -0,0 +1,95 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import NoteList from "@/app/(protected)/notes/_components/NoteList"; +import { Note } from "@/hooks/queries/notes"; + +describe("NoteList", () => { + const mockNotes: Note[] = [ + { + id: 1, + title: "첫 번째 노트", + todo: { id: 1, title: "할 일 1", done: false }, + updatedAt: "2026-01-15T10:00:00Z", + }, + { + id: 2, + title: "두 번째 노트", + todo: { id: 2, title: "할 일 2", done: true }, + updatedAt: "2026-01-16T10:00:00Z", + }, + ]; + + const mockOnEditNote = jest.fn(); + const mockOnDeleteNote = jest.fn(); + + const renderNoteList = () => { + return render( + , + ); + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("렌더링", () => { + it("노트 목록이 화면에 렌더링된다", () => { + renderNoteList(); + + expect(screen.getByText("첫 번째 노트")).toBeInTheDocument(); + expect(screen.getByText("두 번째 노트")).toBeInTheDocument(); + }); + + it("노트가 최신순으로 정렬된다", () => { + renderNoteList(); + + const articles = screen.getAllByRole("article"); + expect(articles[0]).toHaveTextContent("두 번째 노트"); + expect(articles[1]).toHaveTextContent("첫 번째 노트"); + }); + + it("노트 클릭 시 상세 페이지로 이동하는 링크가 있다", () => { + renderNoteList(); + + const links = screen.getAllByRole("link"); + expect(links[0]).toHaveAttribute("href", "/notes/2"); + expect(links[1]).toHaveAttribute("href", "/notes/1"); + }); + }); + + describe("사용자 인터랙션", () => { + it("수정 버튼 클릭 시 onEditNote가 호출된다", async () => { + const user = userEvent.setup(); + + renderNoteList(); + + const dropdownButtons = screen.getAllByLabelText("노트 옵션 메뉴"); + await user.click(dropdownButtons[0]); + + const editButton = screen.getByText("수정하기"); + await user.click(editButton); + + expect(mockOnEditNote).toHaveBeenCalledWith(2); + expect(mockOnEditNote).toHaveBeenCalledTimes(1); + }); + + it("삭제 버튼 클릭 시 onDeleteNote가 호출된다", async () => { + const user = userEvent.setup(); + + renderNoteList(); + + const dropdownButtons = screen.getAllByLabelText("노트 옵션 메뉴"); + await user.click(dropdownButtons[0]); + + const deleteButton = screen.getByText("삭제하기"); + await user.click(deleteButton); + + expect(mockOnDeleteNote).toHaveBeenCalledWith(2); + expect(mockOnDeleteNote).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/tests/components/notes/NoteListContainer.test.tsx b/tests/components/notes/NoteListContainer.test.tsx new file mode 100644 index 0000000..a228f90 --- /dev/null +++ b/tests/components/notes/NoteListContainer.test.tsx @@ -0,0 +1,66 @@ +import { screen } from "@testing-library/react"; +import { useNotesQuery, useDeleteNoteMutation } from "@/hooks/queries/notes"; +import NoteListContainer from "@/app/(protected)/notes/_components/NoteListContainer"; +import { renderWithQueryClient } from "tests/test-utils"; + +jest.mock("@/hooks/queries/notes"); + +jest.mock("next/navigation", () => ({ + useRouter: () => ({ + push: jest.fn(), + }), +})); + +describe("NoteListContainer", () => { + const goalId = 1; + + beforeEach(() => { + jest.clearAllMocks(); + + jest.mocked(useDeleteNoteMutation).mockReturnValue({ + mutate: jest.fn(), + } as unknown as ReturnType); + }); + + describe("노트가 없을 때", () => { + it("노트가 없으면 EmptyState를 렌더링한다", () => { + jest.mocked(useNotesQuery).mockReturnValue({ + data: { + totalCount: 0, + nextCursor: null, + goal: { id: goalId, title: "목표 1" }, + notes: [], + }, + } as unknown as ReturnType); + + renderWithQueryClient(); + + expect(screen.getByText("아직 등록된 노트가 없어요")).toBeInTheDocument(); + }); + }); + + describe("노트가 있을 때", () => { + it("GoalBanner와 NoteList를 렌더링한다", () => { + jest.mocked(useNotesQuery).mockReturnValue({ + data: { + totalCount: 1, + nextCursor: null, + goal: { id: goalId, title: "목표 1" }, + notes: [ + { + id: 1, + title: "첫 번째 노트", + todo: { id: 1, title: "할 일 1", done: false }, + updatedAt: "2026-01-17T10:00:00Z", + }, + ], + }, + } as unknown as ReturnType); + + renderWithQueryClient(); + + expect(screen.getByText("목표 1")).toBeInTheDocument(); + expect(screen.getByText("첫 번째 노트")).toBeInTheDocument(); + }); + }); +});