폼 전송 TDD추가 및 암호화 적용 중
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import { describe, it, expect, afterEach, vi } from "vitest";
|
||||
import { render, screen, cleanup, waitFor } from "@testing-library/react";
|
||||
|
||||
import ProjectSubmissionsPage from "@/app/projects/[slug]/submissions/page";
|
||||
|
||||
// next/navigation 의 useRouter/useParams 를 목으로 대체해
|
||||
// - URL 의 slug 파라미터에 따라 API 요청 경로가 달라지는지
|
||||
// - 인증 실패 시 /login 으로 리다이렉트하는지
|
||||
// 를 검증한다.
|
||||
export const pushMock = vi.fn();
|
||||
export const useParamsMock = vi.fn();
|
||||
|
||||
vi.mock("next/navigation", () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
useRouter: () => ({ push: pushMock }),
|
||||
useParams: () => useParamsMock(),
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.unstubAllGlobals();
|
||||
vi.clearAllMocks();
|
||||
useParamsMock.mockReset();
|
||||
});
|
||||
|
||||
describe("ProjectSubmissionsPage - 폼 제출 내역", () => {
|
||||
it("/api/projects/[slug]/submissions 로부터 받은 제출 내역을 테이블로 렌더링해야 한다", async () => {
|
||||
useParamsMock.mockReturnValue({ slug: "test-project" });
|
||||
|
||||
const submissions = [
|
||||
{
|
||||
id: "1",
|
||||
createdAt: "2025-01-01T12:00:00.000Z",
|
||||
projectSlug: "test-project",
|
||||
payload: {
|
||||
name: "홍길동",
|
||||
email: "user@example.com",
|
||||
phone: "010-1234-5678",
|
||||
birthdate: "1990-01-01",
|
||||
message: "안녕하세요",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
createdAt: "2025-01-02T08:30:00.000Z",
|
||||
projectSlug: "test-project",
|
||||
payload: {
|
||||
name: "김철수",
|
||||
email: "another@example.com",
|
||||
phone: "010-0000-0000",
|
||||
birthdate: "1995-05-05",
|
||||
message: "두 번째 문의입니다.",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify(submissions), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock as any);
|
||||
|
||||
render(<ProjectSubmissionsPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const [url, options] = fetchMock.mock.calls[0] as any;
|
||||
expect(url).toBe("/api/projects/test-project/submissions");
|
||||
expect(options).toBeUndefined();
|
||||
|
||||
expect(await screen.findByText("폼 제출 내역")).toBeTruthy();
|
||||
expect(screen.getByText("test-project")).toBeTruthy();
|
||||
|
||||
expect(screen.getByText("홍길동")).toBeTruthy();
|
||||
expect(screen.getByText("user@example.com")).toBeTruthy();
|
||||
expect(screen.getByText("010-1234-5678")).toBeTruthy();
|
||||
expect(screen.getByText("1990-01-01")).toBeTruthy();
|
||||
expect(screen.getByText("message: 안녕하세요")).toBeTruthy();
|
||||
|
||||
expect(screen.getByText("김철수")).toBeTruthy();
|
||||
expect(screen.getByText("another@example.com")).toBeTruthy();
|
||||
expect(screen.getByText("010-0000-0000")).toBeTruthy();
|
||||
expect(screen.getByText("1995-05-05")).toBeTruthy();
|
||||
expect(screen.getByText("message: 두 번째 문의입니다.")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("API 가 401 을 반환하면 로그인 페이지로 리다이렉트해야 한다", async () => {
|
||||
useParamsMock.mockReturnValue({ slug: "test-project" });
|
||||
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
new Response(
|
||||
JSON.stringify({ message: "폼 제출 내역을 조회하려면 로그인이 필요합니다." }),
|
||||
{
|
||||
status: 401,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock as any);
|
||||
|
||||
render(<ProjectSubmissionsPage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(pushMock).toHaveBeenCalledWith("/login");
|
||||
});
|
||||
});
|
||||
|
||||
it("API 가 404 를 반환하면 에러 메시지를 화면에 표시해야 한다", async () => {
|
||||
useParamsMock.mockReturnValue({ slug: "unknown-project" });
|
||||
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify({ message: "프로젝트를 찾을 수 없습니다." }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock as any);
|
||||
|
||||
render(<ProjectSubmissionsPage />);
|
||||
|
||||
const errorText = await screen.findByText(
|
||||
"프로젝트를 찾을 수 없거나 접근 권한이 없습니다. 프로젝트 소유자 계정으로 다시 로그인해 주세요.",
|
||||
);
|
||||
|
||||
expect(errorText).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -80,6 +80,50 @@ describe("ProjectsPage - 프로젝트 목록", () => {
|
||||
expect(previewLinks[1].closest("a")?.getAttribute("href")).toBe("/preview?slug=test-project-b");
|
||||
});
|
||||
|
||||
it("각 프로젝트 행에는 폼 제출 내역 페이지(/projects/[slug]/submissions)로 이동하는 링크가 있어야 한다", async () => {
|
||||
const projects = [
|
||||
{
|
||||
id: "1",
|
||||
title: "테스트 프로젝트 A",
|
||||
slug: "test-project-a",
|
||||
status: "DRAFT",
|
||||
createdAt: "2025-01-01T00:00:00.000Z",
|
||||
updatedAt: "2025-01-01T00:00:00.000Z",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "테스트 프로젝트 B",
|
||||
slug: "test-project-b",
|
||||
status: "PUBLISHED",
|
||||
createdAt: "2025-01-02T00:00:00.000Z",
|
||||
updatedAt: "2025-01-02T00:00:00.000Z",
|
||||
},
|
||||
];
|
||||
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify(projects), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}),
|
||||
);
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
render(<ProjectsPage />);
|
||||
|
||||
expect(await screen.findByText("테스트 프로젝트 A")).toBeTruthy();
|
||||
expect(await screen.findByText("테스트 프로젝트 B")).toBeTruthy();
|
||||
|
||||
const submissionLinks = screen.getAllByText("폼 제출 내역");
|
||||
expect(submissionLinks).toHaveLength(2);
|
||||
expect(submissionLinks[0].closest("a")?.getAttribute("href")).toBe(
|
||||
"/projects/test-project-a/submissions",
|
||||
);
|
||||
expect(submissionLinks[1].closest("a")?.getAttribute("href")).toBe(
|
||||
"/projects/test-project-b/submissions",
|
||||
);
|
||||
});
|
||||
|
||||
it("삭제 버튼 클릭 시 서버에 DELETE 요청을 보내고 목록과 로컬스토리지를 갱신해야 한다", async () => {
|
||||
const projects = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user