대시보드, GNB 추가
CI / test (push) Successful in 4m58s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Successful in 1m45s

This commit is contained in:
2025-12-08 06:59:52 +09:00
parent b40be693ee
commit 9d8c4538c7
18 changed files with 1595 additions and 86 deletions
+87 -2
View File
@@ -146,11 +146,96 @@ describe("AllProjectSubmissionsPage - 전체 폼 제출 내역", () => {
const backLink = await screen.findByRole("link", { name: "프로젝트 목록" });
expect(backLink).toBeTruthy();
expect(backLink.closest("a")?.getAttribute("href")).toBe("/projects");
});
backLink.click();
it("상단 GNB 에 대시보드/프로젝트 목록/전체 제출 내역 링크가 있어야 한다", async () => {
const fetchMock = vi.fn().mockResolvedValue(
new Response(JSON.stringify([]), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
);
vi.stubGlobal("fetch", fetchMock as any);
render(<AllProjectSubmissionsPage />);
const dashboardLink = await screen.findByRole("link", { name: "대시보드" });
expect(dashboardLink.closest("a")?.getAttribute("href")).toBe("/dashboard");
const projectsLink = await screen.findByRole("link", { name: "프로젝트 목록" });
expect(projectsLink.closest("a")?.getAttribute("href")).toBe("/projects");
const submissionsLink = await screen.findByRole("link", { name: "전체 제출 내역" });
expect(submissionsLink.closest("a")?.getAttribute("href")).toBe("/projects/submissions");
});
it("GNB에서 현재 페이지인 '전체 제출 내역' 탭에 aria-current=\"page\"가 설정되어야 한다", async () => {
const fetchMock = vi.fn().mockResolvedValue(
new Response(JSON.stringify([]), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
);
vi.stubGlobal("fetch", fetchMock as any);
render(<AllProjectSubmissionsPage />);
await waitFor(() => {
expect(pushMock).toHaveBeenCalledWith("/projects");
expect(fetchMock).toHaveBeenCalledTimes(1);
});
const dashboardLink = await screen.findByRole("link", { name: "대시보드" });
const projectsLink = await screen.findByRole("link", { name: "프로젝트 목록" });
const submissionsLink = await screen.findByRole("link", { name: "전체 제출 내역" });
expect(submissionsLink.getAttribute("href")).toBe("/projects/submissions");
expect(submissionsLink.getAttribute("aria-current")).toBe("page");
expect(dashboardLink.getAttribute("aria-current")).toBeNull();
expect(projectsLink.getAttribute("aria-current")).toBeNull();
});
it("상단 메뉴 안 '로그아웃' 버튼을 클릭하면 /api/auth/logout 으로 POST 요청을 보내고 /login 으로 이동해야 한다", async () => {
const fetchMock = vi.fn().mockImplementation((input: any, init?: any) => {
const url = typeof input === "string" ? input : input.url;
const method = init?.method ?? "GET";
if (url === "/api/projects/submissions" && method === "GET") {
return Promise.resolve(
new Response(JSON.stringify([]), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
);
}
if (url === "/api/auth/logout" && method === "POST") {
return Promise.resolve(new Response(JSON.stringify({ message: "ok" }), { status: 200 }));
}
return Promise.resolve(new Response(null, { status: 500 }));
});
vi.stubGlobal("fetch", fetchMock as any);
render(<AllProjectSubmissionsPage />);
const menuButton = await screen.findByRole("button", { name: "메뉴" });
menuButton.click();
const logoutButton = await screen.findByRole("button", { name: "로그아웃" });
logoutButton.click();
await waitFor(() => {
const logoutCall = fetchMock.mock.calls.find(([url, options]) => {
return url === "/api/auth/logout" && options?.method === "POST";
});
expect(logoutCall).toBeDefined();
expect(pushMock).toHaveBeenCalledWith("/login");
});
});
});