|
| 1 | +import type { SavingTipSummary } from "@repo/schema/tip"; |
1 | 2 | import { beforeEach, describe, expect, it, vi } from "vitest"; |
| 3 | +import { bookmarkSavingTip, fetchAllSavingTips, unbookmarkSavingTip } from "@/api/tip"; |
2 | 4 | import { fireEvent, render, screen, waitFor } from "@/lib/test/react"; |
| 5 | +import { SavingTipList } from "./saving-tip-list"; |
3 | 6 |
|
4 | | -const bookmarkTip = vi.fn(); |
5 | | -const fetchTips = vi.fn(); |
6 | | -const unbookmarkTip = vi.fn(); |
7 | | - |
8 | | -vi.mock("@repo/bridge", () => ({ isNativeApp: () => false })); |
9 | | -vi.mock("@/lib/open-external", () => ({ openExternalLink: vi.fn() })); |
10 | 7 | vi.mock("@/api/tip", () => ({ |
11 | | - bookmarkTip: (id: number) => bookmarkTip(id), |
12 | | - fetchTips: () => fetchTips(), |
13 | | - unbookmarkTip: (id: number) => unbookmarkTip(id), |
| 8 | + bookmarkSavingTip: vi.fn(), |
| 9 | + fetchAllSavingTips: vi.fn(), |
| 10 | + fetchSavingTips: vi.fn(), |
| 11 | + unbookmarkSavingTip: vi.fn(), |
14 | 12 | })); |
15 | 13 |
|
16 | | -import { SavingTipList } from "./saving-tip-list"; |
| 14 | +const TIPS: SavingTipSummary[] = [ |
| 15 | + { |
| 16 | + bookmarked: false, |
| 17 | + category: "식비", |
| 18 | + description: "배달 메뉴 대신 집에서 직접 만드는 레시피 찾아보기", |
| 19 | + id: 1, |
| 20 | + sourceUrl: "https://www.youtube.com/watch?v=nZw2A76aZaw", |
| 21 | + subcategory: "배달음식", |
| 22 | + title: "집밥 레시피 활용팁", |
| 23 | + }, |
| 24 | + { |
| 25 | + bookmarked: true, |
| 26 | + category: "생활", |
| 27 | + description: "당근마켓서 중고구매하고 안 쓰는 옷은 판매하기", |
| 28 | + id: 2, |
| 29 | + sourceUrl: "https://example.com/tip", |
| 30 | + subcategory: "의류", |
| 31 | + title: "당근마켓 중고활용팁", |
| 32 | + }, |
| 33 | +]; |
17 | 34 |
|
18 | 35 | describe("SavingTipList", () => { |
19 | 36 | beforeEach(() => { |
20 | 37 | vi.clearAllMocks(); |
21 | | - fetchTips.mockResolvedValue([ |
22 | | - { id: 101, title: "집밥 레시피 활용팁", category: "식비", bookmarked: false }, |
23 | | - { id: 102, title: "밀프렙 식단관리팁", category: "식비", bookmarked: false }, |
24 | | - ]); |
25 | | - bookmarkTip.mockResolvedValue(undefined); |
26 | | - unbookmarkTip.mockResolvedValue(undefined); |
| 38 | + vi.mocked(fetchAllSavingTips).mockResolvedValue(TIPS); |
| 39 | + vi.mocked(bookmarkSavingTip).mockResolvedValue(undefined); |
| 40 | + vi.mocked(unbookmarkSavingTip).mockResolvedValue(undefined); |
27 | 41 | }); |
28 | 42 |
|
29 | | - it("팁 북마크 API로 별 저장을 요청한다", async () => { |
| 43 | + it("서버에서 받은 팁과 원문 링크를 그린다", async () => { |
30 | 44 | render(<SavingTipList />); |
31 | 45 |
|
32 | | - const star = await screen.findByRole("button", { name: "집밥 레시피 활용팁 저장" }); |
33 | | - await waitFor(() => expect(star).toHaveAttribute("aria-busy", "false")); |
34 | | - fireEvent.click(star); |
35 | | - |
36 | | - expect(star).toHaveAttribute("aria-pressed", "true"); |
37 | | - await waitFor(() => expect(bookmarkTip).toHaveBeenCalledWith(101)); |
| 46 | + expect(await screen.findByText("집밥 레시피 활용팁")).toBeInTheDocument(); |
| 47 | + expect(fetchAllSavingTips).toHaveBeenCalledWith(null, 100); |
| 48 | + expect(screen.getByRole("link", { name: /집밥 레시피 활용팁/ })).toHaveAttribute( |
| 49 | + "href", |
| 50 | + "https://www.youtube.com/watch?v=nZw2A76aZaw", |
| 51 | + ); |
38 | 52 | }); |
39 | 53 |
|
40 | | - it("저장됨 화면에서는 API가 저장됨으로 준 팁만 보여준다", async () => { |
41 | | - fetchTips.mockResolvedValue([ |
42 | | - { id: 101, title: "집밥 레시피 활용팁", category: "식비", bookmarked: false }, |
43 | | - { id: 102, title: "밀프렙 식단관리팁", category: "식비", bookmarked: true }, |
44 | | - ]); |
| 54 | + it("카테고리를 누르면 해당 카테고리 API를 다시 조회한다", async () => { |
| 55 | + render(<SavingTipList />); |
| 56 | + await screen.findByText("집밥 레시피 활용팁"); |
| 57 | + |
| 58 | + fireEvent.click(screen.getByRole("button", { name: "생활" })); |
45 | 59 |
|
| 60 | + await waitFor(() => expect(fetchAllSavingTips).toHaveBeenCalledWith("생활", 100)); |
| 61 | + }); |
| 62 | + |
| 63 | + it("저장한 팁만 저장 목록에 남긴다", async () => { |
46 | 64 | render(<SavingTipList savedOnly />); |
47 | 65 |
|
48 | | - expect(await screen.findByText("밀프렙 식단관리팁")).toBeInTheDocument(); |
| 66 | + expect(await screen.findByText("당근마켓 중고활용팁")).toBeInTheDocument(); |
49 | 67 | expect(screen.queryByText("집밥 레시피 활용팁")).not.toBeInTheDocument(); |
50 | 68 | }); |
51 | 69 |
|
52 | | - it("북마크 요청이 끝날 때까지 같은 팁을 다시 토글하지 않는다", async () => { |
53 | | - let completeSave: (() => void) | undefined; |
54 | | - bookmarkTip.mockImplementation( |
55 | | - () => |
56 | | - new Promise<void>((resolve) => { |
57 | | - completeSave = resolve; |
58 | | - }), |
59 | | - ); |
| 70 | + it("별을 누르면 서버 북마크 API를 호출한다", async () => { |
60 | 71 | render(<SavingTipList />); |
| 72 | + await screen.findByText("집밥 레시피 활용팁"); |
61 | 73 |
|
62 | | - const star = await screen.findByRole("button", { name: "집밥 레시피 활용팁 저장" }); |
63 | | - await waitFor(() => expect(star).toHaveAttribute("aria-busy", "false")); |
64 | | - fireEvent.click(star); |
65 | | - fireEvent.click(star); |
| 74 | + fireEvent.click(screen.getByRole("button", { name: "집밥 레시피 활용팁 저장" })); |
66 | 75 |
|
67 | | - await waitFor(() => expect(bookmarkTip).toHaveBeenCalledTimes(1)); |
68 | | - completeSave?.(); |
69 | | - await waitFor(() => expect(star).toHaveAttribute("aria-busy", "false")); |
| 76 | + await waitFor(() => expect(bookmarkSavingTip).toHaveBeenCalledWith(1)); |
70 | 77 | }); |
71 | 78 | }); |
0 commit comments