From 3a63c84532d3d2b80dce37d2c2db670b0c9277fb Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Sun, 7 Jun 2026 02:13:54 +0000
Subject: [PATCH 1/2] refactor: use next/image in BadgeSnippet
Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
---
.../[id]/__tests__/badge-snippet.test.tsx | 5 ++++
.../[id]/__tests__/page-metadata.test.tsx | 2 --
.../__tests__/paper-detail-client.test.tsx | 30 +++++++++++--------
.../web/src/app/papers/[id]/badge-snippet.tsx | 7 ++++-
apps/web/src/app/papers/[id]/cite-button.tsx | 4 +--
5 files changed, 29 insertions(+), 19 deletions(-)
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..a8c7acdc 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, jsx-a11y/alt-text
+ 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..9181a8fe 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
@@ -417,7 +417,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 +750,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 +780,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) {
-
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
From 3030ffdbdb8df985e96274405d16168a942eb1fa Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Sun, 7 Jun 2026 02:29:37 +0000
Subject: [PATCH 2/2] refactor: use next/image in BadgeSnippet
Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
---
.../app/__tests__/collections-new.test.tsx | 5 ++-
apps/web/src/app/__tests__/home.test.tsx | 23 +++++++---
.../app/__tests__/paper-edit-page.test.tsx | 40 +++++++++++++-----
.../src/app/__tests__/settings-page.test.tsx | 4 +-
apps/web/src/app/invites/page.tsx | 32 ++++++++++----
.../[slug]/__tests__/org-page-client.test.tsx | 42 ++++++++++++++-----
.../[slug]/settings/__tests__/page.test.tsx | 1 +
.../[id]/__tests__/badge-snippet.test.tsx | 2 +-
.../__tests__/paper-detail-client.test.tsx | 3 +-
.../app/papers/[id]/paper-detail-client.tsx | 7 +++-
.../src/app/upload/__tests__/page.test.tsx | 4 +-
11 files changed, 122 insertions(+), 41 deletions(-)
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