From ed17a184645649a3819d43c584d06f3b713b1dcd Mon Sep 17 00:00:00 2001 From: K-shir0 Date: Sun, 9 Jul 2023 21:00:14 +0900 Subject: [PATCH] =?UTF-8?q?add:=20ScoringIndexPage=20=E3=81=AE=20=E5=8D=98?= =?UTF-8?q?=E4=BD=93=20=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __test__/pages/scoring/index.test.tsx | 311 ++++++++++++++++++++++++++ pages/scoring/index.tsx | 5 +- types/Problem.ts | 4 +- 3 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 __test__/pages/scoring/index.test.tsx diff --git a/__test__/pages/scoring/index.test.tsx b/__test__/pages/scoring/index.test.tsx new file mode 100644 index 0000000..1f1670d --- /dev/null +++ b/__test__/pages/scoring/index.test.tsx @@ -0,0 +1,311 @@ +import "@testing-library/jest-dom"; + +import { act, render, screen } from "@testing-library/react"; +import { Mock, vi } from "vitest"; + +import useAuth from "@/hooks/auth"; +import useProblems from "@/hooks/problems"; +import Index from "@/pages/scoring"; +import { testProblem } from "@/types/Problem"; +import { testAdminUser, testUser } from "@/types/User"; + +vi.mock("next/router", () => require("next-router-mock")); +vi.mock("@/hooks/auth"); +vi.mock("@/hooks/problems"); +vi.mock("@/components/Navbar", () => ({ + __esModule: true, + default: () =>
, +})); +vi.mock("@/components/LoadingPage", () => ({ + __esModule: true, + default: () =>
, +})); +beforeEach(() => { + // toHaveBeenCalledTimes がテストごとにリセットされるようにする + vi.clearAllMocks(); +}); + +describe("Scoring", () => { + test("未ログインで、NotFound が表示される", async () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: null, + }); + (useProblems as Mock).mockReturnValue({ + problems: [], + isLoading: false, + }); + render(); + + // when + expect( + screen.queryByText("This page could not be found.") + ).toBeInTheDocument(); + + // then + expect(screen.queryByTestId("navbar")).not.toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("ログイン済みで問題が取得できない場合、NotFound が表示される", async () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [], + isLoading: false, + }); + render(); + + // when + expect( + screen.queryByText("This page could not be found.") + ).toBeInTheDocument(); + + // then + expect(screen.queryByTestId("navbar")).not.toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("参加者でアクセスした場合、NotFound が表示される", async () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [], + isLoading: false, + }); + render(); + + // when + expect( + screen.queryByText("This page could not be found.") + ).toBeInTheDocument(); + + // then + expect(screen.queryByTestId("navbar")).not.toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("問題を取得中の場合、ローディング画面が表示される", async () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [], + isLoading: true, + }); + render(); + + // when + expect(screen.queryByTestId("loading")).toBeInTheDocument(); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("問題一覧が表示される", async () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [testProblem], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + expect(screen.queryByText("採点")).toBeInTheDocument(); + expect(tds[1]).toHaveTextContent("/-/-"); + expect(tds[2]).toHaveTextContent("id"); + expect(tds[3]).toHaveTextContent("XYZ"); + expect(tds[4]).toHaveTextContent("テスト問題タイトル"); + expect(tds[5]).toHaveTextContent("--- title: テスト --- #..."); + expect(tds[6]).toHaveTextContent("100"); + expect(tds[7]).toHaveTextContent("150"); + expect(tds[8]).toHaveTextContent(""); + expect(tds[9]).toHaveTextContent("自分"); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("15分未満の問題がある場合、未採点の ~15分 に表示される", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, unchecked: 1 }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + expect(tds[1]).toHaveTextContent("1/-/-"); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("15分以上かつ19分以下の問題がある場合、未採点の 15~19分 に表示される", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, unchecked_near_overdue: 1 }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + expect(tds[1]).toHaveTextContent("/1/-"); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("20分以上の問題がある場合、未採点の 20分~ に表示される", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, unchecked_overdue: 1 }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + expect(tds[1]).toHaveTextContent("/-/1"); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("20文字未満の問題文は、20文字目以降が省略されない", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, body: "a".repeat(19) }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + expect(tds[5]).toHaveTextContent("a".repeat(19)); + }); + + test("20文字以上の問題文は、20文字目以降が省略される", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, body: "a".repeat(20) }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + // aaa... となる + expect(tds[5]).toHaveTextContent(`${"a".repeat(20)}...`); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("問題文が Null の場合、空文字が表示される", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, body: null }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + // aaa... となる + expect(tds[5]).toHaveTextContent(""); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("問題作成者id が自分でない場合空文字が表示される", () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, author_id: "other" }], + isLoading: false, + }); + render(); + const tds = screen.queryAllByRole("cell"); + + // when + expect(tds[9]).toHaveTextContent(""); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(1); + expect(useProblems).toHaveBeenCalledTimes(2); + }); + + test("問題をクリックした場合、問題文が表示される", async () => { + // setup + (useAuth as Mock).mockReturnValue({ + user: testAdminUser, + }); + (useProblems as Mock).mockReturnValue({ + problems: [{ ...testProblem, author_id: "other" }], + isLoading: false, + }); + render(); + await act(async () => { + await screen.queryAllByRole("row")[1].click(); + }); + + // when + expect(screen.queryByText("採点する")).toBeInTheDocument(); + + // then + expect(screen.queryByTestId("navbar")).toBeInTheDocument(); + expect(useAuth).toHaveBeenCalledTimes(2); + expect(useProblems).toHaveBeenCalledTimes(4); + }); +}); diff --git a/pages/scoring/index.tsx b/pages/scoring/index.tsx index 71478cd..1fb4e74 100644 --- a/pages/scoring/index.tsx +++ b/pages/scoring/index.tsx @@ -8,7 +8,8 @@ import ICTSCCard from "@/components/Card"; import LoadingPage from "@/components/LoadingPage"; import MarkdownPreview from "@/components/MarkdownPreview"; import useAuth from "@/hooks/auth"; -import { useProblem, useProblems } from "@/hooks/problem"; +import useProblem from "@/hooks/problem"; +import useProblems from "@/hooks/problems"; import BaseLayout from "@/layouts/BaseLayout"; function Index() { @@ -31,7 +32,7 @@ function Index() { ); } - if (!isFullAccess || isReadOnly || problems === null) { + if (!isFullAccess || isReadOnly || problems.length === 0) { return ; } diff --git a/types/Problem.ts b/types/Problem.ts index 462763d..a25545b 100644 --- a/types/Problem.ts +++ b/types/Problem.ts @@ -38,9 +38,9 @@ export const testProblem: Problem = { title: "テスト問題タイトル", body: "---\ntitle: テスト\n---\n# テスト本文", point: 100, - solved_criterion: 100, + solved_criterion: 150, previous_problem_id: null, - author_id: "author_id", + author_id: "1", unchecked: null, unchecked_near_overdue: null, unchecked_overdue: null,