diff --git a/apps/web/src/app/__tests__/collections-new.test.tsx b/apps/web/src/app/__tests__/collections-new.test.tsx index ff0678c4..2b18c088 100644 --- a/apps/web/src/app/__tests__/collections-new.test.tsx +++ b/apps/web/src/app/__tests__/collections-new.test.tsx @@ -52,7 +52,10 @@ describe("NewCollectionPage", () => { const description = screen.getByLabelText("description"); expect(name).toHaveAttribute("aria-describedby", "name-counter"); - expect(description).toHaveAttribute("aria-describedby", "description-counter"); + expect(description).toHaveAttribute( + "aria-describedby", + "description-counter", + ); expect(screen.getByText("0/100")).toHaveAttribute("id", "name-counter"); expect(screen.getByText("0/500")).toHaveAttribute( "id", diff --git a/apps/web/src/app/__tests__/home.test.tsx b/apps/web/src/app/__tests__/home.test.tsx index 3224fde5..b550ed00 100644 --- a/apps/web/src/app/__tests__/home.test.tsx +++ b/apps/web/src/app/__tests__/home.test.tsx @@ -52,7 +52,11 @@ describe("Home page", () => { // Add additional assertions to increase coverage on page.tsx guest view expect(screen.getByText(/Research output hosting/)).toBeInTheDocument(); - expect(screen.getByText(/OpenShelf は、論文・スライド・補足資料などをまとめて管理し/)).toBeInTheDocument(); + expect( + screen.getByText( + /OpenShelf は、論文・スライド・補足資料などをまとめて管理し/, + ), + ).toBeInTheDocument(); expect(screen.getByText(/1\. 成果物をまとめる/)).toBeInTheDocument(); expect(screen.getByText(/2\. 公開範囲を選ぶ/)).toBeInTheDocument(); expect(screen.getByText(/3\. 永続URLで共有/)).toBeInTheDocument(); @@ -131,10 +135,15 @@ describe("Home page", () => { expect(screen.getAllByText("公開")).toHaveLength(1); }); - it("handles apiFetch returning not ok", async () => { - authState = { user: { id: "user-1", name: "Alice" }, loading: false, login }; - vi.mocked(apiFetch).mockResolvedValue(new Response("error", { status: 500 })); + authState = { + user: { id: "user-1", name: "Alice" }, + loading: false, + login, + }; + vi.mocked(apiFetch).mockResolvedValue( + new Response("error", { status: 500 }), + ); render(); await waitFor(() => { expect(screen.getByText("まだ成果物がありません")).toBeInTheDocument(); @@ -142,7 +151,11 @@ describe("Home page", () => { }); it("handles apiFetch error gracefully", async () => { - authState = { user: { id: "user-1", name: "Alice" }, loading: false, login }; + authState = { + user: { id: "user-1", name: "Alice" }, + loading: false, + login, + }; vi.mocked(apiFetch).mockRejectedValue(new Error("Network error")); render(); await waitFor(() => { diff --git a/apps/web/src/app/__tests__/paper-edit-page.test.tsx b/apps/web/src/app/__tests__/paper-edit-page.test.tsx index 3d08fb10..8a2f4e91 100644 --- a/apps/web/src/app/__tests__/paper-edit-page.test.tsx +++ b/apps/web/src/app/__tests__/paper-edit-page.test.tsx @@ -1,4 +1,10 @@ -import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from "@testing-library/react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import PaperEditPage from "../papers/[id]/edit/page"; import { apiFetch } from "@/lib/api"; @@ -89,13 +95,17 @@ describe("PaperEditPage", () => { target: { value: "Updated title" }, }); - expect(screen.getByText(`${"Updated title".length}/300`)).toBeInTheDocument(); + expect( + screen.getByText(`${"Updated title".length}/300`), + ).toBeInTheDocument(); fireEvent.change(screen.getByLabelText(/概要/i), { target: { value: "Updated abstract" }, }); - expect(screen.getByText(`${"Updated abstract".length}/5000`)).toBeInTheDocument(); + expect( + screen.getByText(`${"Updated abstract".length}/5000`), + ).toBeInTheDocument(); fireEvent.click(screen.getByLabelText(/公開ページに閲覧数を表示する/i)); fireEvent.change(screen.getByLabelText(/タグ/i), { @@ -141,7 +151,8 @@ describe("PaperEditPage", () => { const patchCall = vi .mocked(apiFetch) .mock.calls.find( - ([url, init]) => url === "/api/papers/paper-1" && init?.method === "PATCH", + ([url, init]) => + url === "/api/papers/paper-1" && init?.method === "PATCH", ); const patchBody = JSON.parse((patchCall?.[1]?.body as string) ?? "{}"); expect(patchBody).toMatchObject({ @@ -321,14 +332,19 @@ describe("PaperEditPage", () => { render(); expect(await screen.findByDisplayValue("Initial")).toBeInTheDocument(); - fireEvent.change(screen.getByLabelText(/タイトル/i), { target: { value: " " } }); + fireEvent.change(screen.getByLabelText(/タイトル/i), { + target: { value: " " }, + }); fireEvent.click(screen.getByRole("button", { name: "保存する" })); - expect(await screen.findByText("タイトルを入力してください。")).toBeInTheDocument(); + expect( + await screen.findByText("タイトルを入力してください。"), + ).toBeInTheDocument(); const patchCalls = vi .mocked(apiFetch) .mock.calls.filter( - ([url, req]) => url === "/api/papers/paper-1" && req?.method === "PATCH", + ([url, req]) => + url === "/api/papers/paper-1" && req?.method === "PATCH", ); expect(patchCalls).toHaveLength(0); }); @@ -363,14 +379,17 @@ describe("PaperEditPage", () => { render(); expect(await screen.findByDisplayValue("Initial")).toBeInTheDocument(); - fireEvent.change(screen.getByLabelText(/発表年/i), { target: { value: "" } }); + fireEvent.change(screen.getByLabelText(/発表年/i), { + target: { value: "" }, + }); fireEvent.click(screen.getByRole("button", { name: "保存する" })); const patchCall = await waitFor(() => vi .mocked(apiFetch) .mock.calls.find( - ([url, req]) => url === "/api/papers/paper-1" && req?.method === "PATCH", + ([url, req]) => + url === "/api/papers/paper-1" && req?.method === "PATCH", ), ); const patchBody = JSON.parse((patchCall?.[1]?.body as string) ?? "{}"); @@ -510,7 +529,8 @@ describe("PaperEditPage", () => { vi .mocked(apiFetch) .mock.calls.find( - ([url, req]) => url === "/api/papers/paper-1" && req?.method === "PATCH", + ([url, req]) => + url === "/api/papers/paper-1" && req?.method === "PATCH", ), ); const patchBody = JSON.parse((patchCall?.[1]?.body as string) ?? "{}"); diff --git a/apps/web/src/app/__tests__/settings-page.test.tsx b/apps/web/src/app/__tests__/settings-page.test.tsx index 63b6335b..03d8a418 100644 --- a/apps/web/src/app/__tests__/settings-page.test.tsx +++ b/apps/web/src/app/__tests__/settings-page.test.tsx @@ -204,7 +204,9 @@ describe("SettingsPage", () => { it("shows github username in preview when display name is blank", () => { render(); - fireEvent.change(screen.getByLabelText("表示名"), { target: { value: " " } }); + fireEvent.change(screen.getByLabelText("表示名"), { + target: { value: " " }, + }); expect(screen.getByText("alice")).toBeInTheDocument(); }); diff --git a/apps/web/src/app/invites/page.tsx b/apps/web/src/app/invites/page.tsx index e2ba982b..b3d5aa58 100644 --- a/apps/web/src/app/invites/page.tsx +++ b/apps/web/src/app/invites/page.tsx @@ -26,9 +26,9 @@ export default function InvitesPage() { const router = useRouter(); const [invites, setInvites] = useState([]); const [fetching, setFetching] = useState(true); - const [processingById, setProcessingById] = useState>( - () => new Map(), - ); + const [processingById, setProcessingById] = useState< + Map + >(() => new Map()); useEffect(() => { if (!loading && !user) router.push("/"); @@ -82,7 +82,9 @@ export default function InvitesPage() { : i, ), ); - toast.success(action === "accept" ? "招待を承認しました" : "招待を拒否しました"); + toast.success( + action === "accept" ? "招待を承認しました" : "招待を拒否しました", + ); } else { toast.error("処理に失敗しました"); } @@ -201,7 +203,13 @@ export default function InvitesPage() { {processingAction === "accept" && ( )} - + 承認 @@ -215,16 +223,24 @@ export default function InvitesPage() { {processingAction === "decline" && ( )} - + 拒否 ) : ( -
対応済み
+
+ 対応済み +
)} - + ); })} diff --git a/apps/web/src/app/orgs/[slug]/__tests__/org-page-client.test.tsx b/apps/web/src/app/orgs/[slug]/__tests__/org-page-client.test.tsx index 05bc9545..7239ecfb 100644 --- a/apps/web/src/app/orgs/[slug]/__tests__/org-page-client.test.tsx +++ b/apps/web/src/app/orgs/[slug]/__tests__/org-page-client.test.tsx @@ -24,6 +24,7 @@ vi.mock("next/link", () => ({ })); vi.mock("next/image", () => ({ + // eslint-disable-next-line @next/next/no-img-element default: ({ alt = "image", ...props }: any) => {alt}, })); @@ -225,7 +226,8 @@ describe("OrgPageClient", () => { ); } if ( - url === "/api/orgs/lab/papers?paginate=1&autoYear=1&venue=ASE&category=report&page=2" + url === + "/api/orgs/lab/papers?paginate=1&autoYear=1&venue=ASE&category=report&page=2" ) { return new Response( JSON.stringify({ @@ -273,14 +275,16 @@ describe("OrgPageClient", () => { expect(screen.getByText("2 / 3")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "次へ" })); - const nextCall = replaceMock.mock.calls[replaceMock.mock.calls.length - 1][0]; + const nextCall = + replaceMock.mock.calls[replaceMock.mock.calls.length - 1][0]; expect(nextCall).toContain("/orgs/lab?"); expect(nextCall).toContain("page=3"); expect(nextCall).toContain("venue=ASE"); expect(nextCall).toContain("category=report"); fireEvent.click(screen.getByRole("button", { name: "前へ" })); - const prevCall = replaceMock.mock.calls[replaceMock.mock.calls.length - 1][0]; + const prevCall = + replaceMock.mock.calls[replaceMock.mock.calls.length - 1][0]; expect(prevCall).toContain("/orgs/lab?"); expect(prevCall).toContain("page=1"); expect(prevCall).toContain("venue=ASE"); @@ -340,14 +344,18 @@ describe("OrgPageClient", () => { ); } if (url === "/api/orgs/lab/collections") { - return new Response(JSON.stringify({ collections: [] }), { status: 200 }); + return new Response(JSON.stringify({ collections: [] }), { + status: 200, + }); } throw new Error(`Unexpected request: ${String(url)}`); }); render(); expect(await screen.findByText("Research Lab")).toBeInTheDocument(); - expect(screen.queryByRole("link", { name: "⚙ 設定" })).not.toBeInTheDocument(); + expect( + screen.queryByRole("link", { name: "⚙ 設定" }), + ).not.toBeInTheDocument(); expect( screen.queryByRole("link", { name: "+ 成果物を追加" }), ).not.toBeInTheDocument(); @@ -381,14 +389,18 @@ describe("OrgPageClient", () => { return new Response(JSON.stringify({ members: [] }), { status: 200 }); } if (url === "/api/orgs/lab/collections") { - return new Response(JSON.stringify({ collections: [] }), { status: 200 }); + return new Response(JSON.stringify({ collections: [] }), { + status: 200, + }); } throw new Error(`Unexpected request: ${String(url)}`); }); render(); expect(await screen.findByText("Research Lab")).toBeInTheDocument(); - expect(await screen.findByText("まだ成果物がありません")).toBeInTheDocument(); + expect( + await screen.findByText("まだ成果物がありません"), + ).toBeInTheDocument(); }); it("applies explicit all-year filter when selecting empty year option", async () => { @@ -431,7 +443,9 @@ describe("OrgPageClient", () => { return new Response(JSON.stringify({ members: [] }), { status: 200 }); } if (url === "/api/orgs/lab/collections") { - return new Response(JSON.stringify({ collections: [] }), { status: 200 }); + return new Response(JSON.stringify({ collections: [] }), { + status: 200, + }); } throw new Error(`Unexpected request: ${String(url)}`); }); @@ -484,7 +498,9 @@ describe("OrgPageClient", () => { return new Response(JSON.stringify({ members: [] }), { status: 200 }); } if (url === "/api/orgs/lab/collections") { - return new Response(JSON.stringify({ collections: [] }), { status: 200 }); + return new Response(JSON.stringify({ collections: [] }), { + status: 200, + }); } throw new Error(`Unexpected request: ${String(url)}`); }); @@ -534,13 +550,17 @@ describe("OrgPageClient", () => { return new Response(JSON.stringify({ members: [] }), { status: 200 }); } if (url === "/api/orgs/lab/collections") { - return new Response(JSON.stringify({ collections: [] }), { status: 200 }); + return new Response(JSON.stringify({ collections: [] }), { + status: 200, + }); } throw new Error(`Unexpected request: ${String(url)}`); }); render(); expect(await screen.findByText("Research Lab")).toBeInTheDocument(); - expect(await screen.findByText("まだ成果物がありません")).toBeInTheDocument(); + expect( + await screen.findByText("まだ成果物がありません"), + ).toBeInTheDocument(); }); }); diff --git a/apps/web/src/app/orgs/[slug]/settings/__tests__/page.test.tsx b/apps/web/src/app/orgs/[slug]/settings/__tests__/page.test.tsx index 83e111a3..4356dd07 100644 --- a/apps/web/src/app/orgs/[slug]/settings/__tests__/page.test.tsx +++ b/apps/web/src/app/orgs/[slug]/settings/__tests__/page.test.tsx @@ -69,6 +69,7 @@ vi.mock("next/link", () => ({ vi.mock("next/image", () => ({ default: ({ src, alt, ...props }: any) => ( + // eslint-disable-next-line @next/next/no-img-element {alt} ), })); diff --git a/apps/web/src/app/papers/[id]/__tests__/badge-snippet.test.tsx b/apps/web/src/app/papers/[id]/__tests__/badge-snippet.test.tsx index 1583feae..d76655a8 100644 --- a/apps/web/src/app/papers/[id]/__tests__/badge-snippet.test.tsx +++ b/apps/web/src/app/papers/[id]/__tests__/badge-snippet.test.tsx @@ -19,6 +19,11 @@ vi.mock("@/components/toast", () => ({ }, })); +vi.mock("next/image", () => ({ + // eslint-disable-next-line @next/next/no-img-element + default: ({ unoptimized: _unoptimized, ...props }: any) => , +})); + describe("BadgeSnippet", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/apps/web/src/app/papers/[id]/__tests__/page-metadata.test.tsx b/apps/web/src/app/papers/[id]/__tests__/page-metadata.test.tsx index 307ff421..6cd75a20 100644 --- a/apps/web/src/app/papers/[id]/__tests__/page-metadata.test.tsx +++ b/apps/web/src/app/papers/[id]/__tests__/page-metadata.test.tsx @@ -91,11 +91,9 @@ describe("papers/[id]/page metadata", () => { expect(metadata.openGraph?.title).toBe("成果物詳細 | OpenShelf"); }); - it("generates generic metadata when id is invalid", async () => { // Generate metadata without await as the component is sync for generating metadata via props const metadata = await generateMetadata({ params: { id: "../bad" } }); expect(metadata.title).toBe("成果物詳細 | OpenShelf"); }); - }); diff --git a/apps/web/src/app/papers/[id]/__tests__/paper-detail-client.test.tsx b/apps/web/src/app/papers/[id]/__tests__/paper-detail-client.test.tsx index e82a3b10..0bfd9d19 100644 --- a/apps/web/src/app/papers/[id]/__tests__/paper-detail-client.test.tsx +++ b/apps/web/src/app/papers/[id]/__tests__/paper-detail-client.test.tsx @@ -40,7 +40,8 @@ vi.mock("next/link", () => ({ })); vi.mock("next/image", () => ({ - default: ({ src, alt, ...props }: any) => ( + default: ({ src, alt, unoptimized: _unoptimized, ...props }: any) => ( + // eslint-disable-next-line @next/next/no-img-element {alt} ), })); @@ -417,7 +418,9 @@ describe("PaperDetailClient", () => { const slideRow = screen.getByText("deck.pptx").closest("li"); expect(slideRow).not.toBeNull(); fireEvent.click( - within(slideRow!).getByRole("button", { name: "deck.pptxをダウンロード" }), + within(slideRow!).getByRole("button", { + name: "deck.pptxをダウンロード", + }), ); await waitFor(() => { @@ -748,18 +751,21 @@ describe("PaperDetailClient", () => { [401, "ログインが必要です"], [404, "成果物が見つかりません"], [500, "成果物の取得に失敗しました"], - ])("maps paper fetch status %s to its error message", async (status, message) => { - vi.mocked(apiFetch).mockResolvedValue(new Response("error", { status })); - - render( - , - ); + ])( + "maps paper fetch status %s to its error message", + async (status, message) => { + vi.mocked(apiFetch).mockResolvedValue(new Response("error", { status })); + + render( + , + ); - expect(await screen.findByText(message)).toBeInTheDocument(); - }); + expect(await screen.findByText(message)).toBeInTheDocument(); + }, + ); it("shows an error when API request fails entirely", async () => { vi.mocked(apiFetch).mockRejectedValue(new Error("Network Error")); @@ -775,5 +781,4 @@ describe("PaperDetailClient", () => { await screen.findByText("成果物の取得に失敗しました"), ).toBeInTheDocument(); }); - }); diff --git a/apps/web/src/app/papers/[id]/badge-snippet.tsx b/apps/web/src/app/papers/[id]/badge-snippet.tsx index 28e35429..be5dcb46 100644 --- a/apps/web/src/app/papers/[id]/badge-snippet.tsx +++ b/apps/web/src/app/papers/[id]/badge-snippet.tsx @@ -1,6 +1,7 @@ "use client"; import { toast } from "@/components/toast"; +import Image from "next/image"; import { useMemo } from "react"; type BadgeSnippetProps = { @@ -110,10 +111,14 @@ export function BadgeSnippet({ paperId, title, siteBase }: BadgeSnippetProps) {
- {`OpenShelf
diff --git a/apps/web/src/app/papers/[id]/cite-button.tsx b/apps/web/src/app/papers/[id]/cite-button.tsx index 086fb330..ae5c1d15 100644 --- a/apps/web/src/app/papers/[id]/cite-button.tsx +++ b/apps/web/src/app/papers/[id]/cite-button.tsx @@ -121,9 +121,7 @@ export function CiteButton({ paperId }: CiteButtonProps) { - - {option.label} の引用を生成中 - + {option.label} の引用を生成中 ) : ( option.label diff --git a/apps/web/src/app/papers/[id]/paper-detail-client.tsx b/apps/web/src/app/papers/[id]/paper-detail-client.tsx index e9abdf4b..e4aa9b5d 100644 --- a/apps/web/src/app/papers/[id]/paper-detail-client.tsx +++ b/apps/web/src/app/papers/[id]/paper-detail-client.tsx @@ -836,11 +836,14 @@ export default function PaperDetailClient({ className="rounded-md border border-gray-200 p-2 dark:border-gray-700" > {imagePreviewUrls[img.id] ? ( - {img.filename} ) : failedImageIds.includes(img.id) ? (
diff --git a/apps/web/src/app/upload/__tests__/page.test.tsx b/apps/web/src/app/upload/__tests__/page.test.tsx index 88fd19ef..cc999461 100644 --- a/apps/web/src/app/upload/__tests__/page.test.tsx +++ b/apps/web/src/app/upload/__tests__/page.test.tsx @@ -82,7 +82,9 @@ describe("UploadPage", () => { fireEvent.change(screen.getByLabelText(/タイトル/i), { target: { value: " My paper " }, }); - expect(screen.getByText(`${" My paper ".length}/300`)).toBeInTheDocument(); + expect( + screen.getByText(`${" My paper ".length}/300`), + ).toBeInTheDocument(); fireEvent.change(screen.getByLabelText(/概要/i), { target: { value: "Abstract" },