From de4ed818eb5bf94aa2c8dc7234642715d957ada7 Mon Sep 17 00:00:00 2001
From: Ryohei Kamei <231205751@ccmailg.meijo-u.ac.jp>
Date: Fri, 11 Oct 2024 18:15:17 +0900
Subject: [PATCH 1/3] =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=82=92=E9=81=A9=E5=88=87=E3=81=AB?=
 =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88?=
 =?UTF-8?q?=E5=88=86=E5=89=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/user/login/components/LoginForm.tsx | 119 ++++++++++++++++++++++++
 app/user/login/page.tsx                 | 117 +----------------------
 2 files changed, 121 insertions(+), 115 deletions(-)
 create mode 100644 app/user/login/components/LoginForm.tsx

diff --git a/app/user/login/components/LoginForm.tsx b/app/user/login/components/LoginForm.tsx
new file mode 100644
index 0000000..6767209
--- /dev/null
+++ b/app/user/login/components/LoginForm.tsx
@@ -0,0 +1,119 @@
+"use client";
+
+import { useState } from "react";
+import { SubmitHandler, useForm } from "react-hook-form";
+import { useRouter } from "next/navigation";
+import useUser from "app/hooks/useUser";
+import toast from "react-hot-toast";
+import { Input } from "@/components/ui/input";
+
+const LoginForm = () => {
+  const {
+    register,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<{ email: string; password: string }>({
+    defaultValues: {
+      email: "",
+      password: "",
+    },
+  });
+  const { signIn } = useUser();
+  const router = useRouter();
+  const [loading, setLoading] = useState(false);
+
+  const doLogin: SubmitHandler<{ email: string; password: string }> = async (
+    formData
+  ) => {
+    try {
+      setLoading(true);
+      const { error } = await signIn({
+        email: formData.email,
+        password: formData.password,
+      });
+      if (error) {
+        // エラー時の処理
+        toast.error("メールアドレス、もしくはパスワードが違います");
+        setLoading(false);
+      } else {
+        // 成功時の処理
+        toast.success("ログインしました");
+        router.push("/");
+        setLoading(false);
+      }
+    } catch (error) {
+      toast.error("予期せぬエラーが発生しました");
+      setLoading(false);
+    }
+  };
+
+  return (
+    <form onSubmit={handleSubmit(doLogin)} className="w-full max-w-md">
+      <div className="mb-4">
+        <Input
+          id="email"
+          placeholder="メールアドレス"
+          disabled={loading}
+          {...register("email", {
+            required: {
+              value: true,
+              message: "メールアドレスを入力してください",
+            },
+            pattern: {
+              value:
+                /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]+.[A-Za-z0-9]+$/,
+              message: "有効なメールアドレスを入力してください",
+            },
+          })}
+          className="border border-gray-300 rounded-md px-3 py-2 w-full"
+        />
+        {errors.email && (
+          <div className="text-red-500 text-sm">{errors.email.message}</div>
+        )}
+      </div>
+
+      <div className="mb-4">
+        <Input
+          id="password"
+          type="password"
+          disabled={loading}
+          placeholder="パスワード"
+          {...register("password", {
+            required: {
+              value: true,
+              message: "パスワードを入力してください",
+            },
+            minLength: {
+              value: 6,
+              message: "パスワードは6文字以上にしてください",
+            },
+          })}
+          className="border border-gray-300 rounded-md px-3 py-2 w-full"
+        />
+        {errors.password && (
+          <div className="text-red-500 text-sm">{errors.password.message}</div>
+        )}
+      </div>
+
+      <button
+        type="submit"
+        className={`text-white px-4 py-2 rounded-md focus:outline-none ${
+          loading
+            ? "cursor-not-allowed bg-gray-400"
+            : "bg-blue-500 hover:bg-blue-600 transition-colors duration-300 focus:border-blue-500 focus:ring"
+        }`}
+      >
+        {loading ? (
+          <div className="flex justify-center items-center">
+            <p>ログイン中・・・</p>
+            <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white" />
+          </div>
+        ) : (
+          "ログイン"
+        )}
+      </button>
+    </form>
+  );
+};
+
+export default LoginForm;
diff --git a/app/user/login/page.tsx b/app/user/login/page.tsx
index 7b7a26e..446bf0e 100644
--- a/app/user/login/page.tsx
+++ b/app/user/login/page.tsx
@@ -1,125 +1,12 @@
-"use client";
-import { useState } from "react";
-import { SubmitHandler, useForm } from "react-hook-form";
-import { useRouter } from "next/navigation";
-import useUser from "app/hooks/useUser";
-import toast from "react-hot-toast";
-import { Input } from "@/components/ui/input";
+import LoginForm from "./components/LoginForm";
 
 const LoginPage = () => {
-  const {
-    register,
-    handleSubmit,
-    formState: { errors },
-  } = useForm<{ email: string; password: string }>({
-    defaultValues: {
-      email: "",
-      password: "",
-    },
-  });
-  const { signIn } = useUser();
-  const router = useRouter();
-  const [loading, setLoading] = useState(false);
-
-  const doLogin: SubmitHandler<{ email: string; password: string }> = async (
-    formData
-  ) => {
-    try {
-      setLoading(true);
-      const { error } = await signIn({
-        email: formData.email,
-        password: formData.password,
-      });
-      if (error) {
-        // エラー時の処理
-        toast.error("メールアドレス、もしくはパスワードが違います");
-        setLoading(false);
-      } else {
-        // 成功時の処理
-        toast.success("ログインしました");
-        router.push("/");
-        setLoading(false);
-      }
-    } catch (error) {
-      toast.error("予期せぬエラーが発生しました");
-      setLoading(false);
-    }
-  };
-
   return (
     <div className="mx-auto max-w-3xl lg:max-w-2xl px-4 sm:px-6 lg:px-8 pb-16 pt-40 text-center lg:pt-32">
       <div className="bg-slate-50 rounded-xl px-4 py-4 shadow-lg ring-1 ring-gray-400">
         <p className="text-2xl text-center text-gray-900">ログイン画面</p>
         <div className="flex justify-center pt-8">
-          <form onSubmit={handleSubmit(doLogin)} className="w-full max-w-md">
-            <div className="mb-4">
-              <Input
-                id="email"
-                placeholder="メールアドレス"
-                disabled={loading}
-                {...register("email", {
-                  required: {
-                    value: true,
-                    message: "メールアドレスを入力してください",
-                  },
-                  pattern: {
-                    value:
-                      /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]+.[A-Za-z0-9]+$/,
-                    message: "有効なメールアドレスを入力してください",
-                  },
-                })}
-                className="border border-gray-300 rounded-md px-3 py-2 w-full"
-              />
-              {errors.email && (
-                <div className="text-red-500 text-sm">
-                  {errors.email.message}
-                </div>
-              )}
-            </div>
-
-            <div className="mb-4">
-              <Input
-                id="password"
-                type="password"
-                disabled={loading}
-                placeholder="パスワード"
-                {...register("password", {
-                  required: {
-                    value: true,
-                    message: "パスワードを入力してください",
-                  },
-                  minLength: {
-                    value: 6,
-                    message: "パスワードは6文字以上にしてください",
-                  },
-                })}
-                className="border border-gray-300 rounded-md px-3 py-2 w-full"
-              />
-              {errors.password && (
-                <div className="text-red-500 text-sm">
-                  {errors.password.message}
-                </div>
-              )}
-            </div>
-
-            <button
-              type="submit"
-              className={`text-white px-4 py-2 rounded-md focus:outline-none ${
-                loading
-                  ? "cursor-not-allowed bg-gray-400"
-                  : "bg-blue-500 hover:bg-blue-600 transition-colors duration-300 focus:border-blue-500 focus:ring"
-              }`}
-            >
-              {loading ? (
-                <div className="flex justify-center items-center">
-                  <p>ログイン中・・・</p>
-                  <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white" />
-                </div>
-              ) : (
-                "ログイン"
-              )}
-            </button>
-          </form>
+          <LoginForm />
         </div>
       </div>
     </div>

From 27c1bd2331f8292e82ff5941b0d81ce5ba9cc2e5 Mon Sep 17 00:00:00 2001
From: Ryohei Kamei <231205751@ccmailg.meijo-u.ac.jp>
Date: Fri, 11 Oct 2024 18:15:46 +0900
Subject: [PATCH 2/3] =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?=
 =?UTF-8?q?=E7=99=BB=E9=8C=B2=E7=94=BB=E9=9D=A2=E3=82=92=E9=81=A9=E5=88=87?=
 =?UTF-8?q?=E3=81=AB=E5=88=86=E5=89=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/user/register/components/RegisterForm.tsx | 108 ++++++++++++++++++
 app/user/register/page.tsx                    | 107 +----------------
 2 files changed, 110 insertions(+), 105 deletions(-)
 create mode 100644 app/user/register/components/RegisterForm.tsx

diff --git a/app/user/register/components/RegisterForm.tsx b/app/user/register/components/RegisterForm.tsx
new file mode 100644
index 0000000..6c7a21e
--- /dev/null
+++ b/app/user/register/components/RegisterForm.tsx
@@ -0,0 +1,108 @@
+"use client";
+import { useRouter } from "next/navigation";
+import { SubmitHandler, useForm } from "react-hook-form";
+import useUser from "app/hooks/useUser";
+import { Input } from "@/components/ui/input";
+import toast from "react-hot-toast";
+import { useState } from "react";
+
+const RegisterForm = () => {
+  const {
+    register,
+    handleSubmit,
+    formState: { errors },
+  } = useForm<{ email: string; password: string }>({
+    defaultValues: {
+      email: "",
+      password: "",
+    },
+  });
+
+  const { signUp } = useUser();
+  const router = useRouter();
+  const [loading, setLoading] = useState(false);
+
+  const doRegister: SubmitHandler<{ email: string; password: string }> = async (
+    formData
+  ) => {
+    try {
+      setLoading(true);
+      await signUp({ email: formData.email, password: formData.password });
+      alert(
+        "登録いただいたメールアドレスに確認メールを送信しましたのでご確認ください!(メールの送信は数分かかる場合がありますのでご了承ください)"
+      );
+      router.push("/");
+    } catch (error) {
+      toast.error("エラーが発生しました");
+      setLoading(false);
+    }
+  };
+
+  return (
+    <form onSubmit={handleSubmit(doRegister)} className="w-full max-w-md">
+      <div className="mb-4">
+        <Input
+          id="email"
+          placeholder="メールアドレス"
+          {...register("email", {
+            required: {
+              value: true,
+              message: "メールアドレスを入力してください",
+            },
+            pattern: {
+              value:
+                /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]+.[A-Za-z0-9]+$/,
+              message: "有効なメールアドレスを入力してください",
+            },
+          })}
+          className="border border-gray-300 rounded-md px-3 py-2 w-full"
+        />
+        {errors.email && (
+          <div className="text-red-500 text-sm">{errors.email.message}</div>
+        )}
+      </div>
+
+      <div className="mb-4">
+        <Input
+          id="password"
+          type="password"
+          placeholder="パスワード"
+          {...register("password", {
+            required: {
+              value: true,
+              message: "パスワードを入力してください",
+            },
+            minLength: {
+              value: 6,
+              message: "パスワードは6文字以上にしてください",
+            },
+          })}
+          className="border border-gray-300 rounded-md px-3 py-2 w-full"
+        />
+        {errors.password && (
+          <div className="text-red-500 text-sm">{errors.password.message}</div>
+        )}
+      </div>
+
+      <button
+        type="submit"
+        className={`text-white px-4 py-2 rounded-md focus:outline-none ${
+          loading
+            ? "cursor-not-allowed bg-gray-400"
+            : "bg-blue-500 hover:bg-blue-600 transition-colors duration-300 focus:border-blue-500 focus:ring"
+        }`}
+      >
+        {loading ? (
+          <div className="flex justify-center items-center">
+            <p>読み込み中・・・</p>
+            <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white" />
+          </div>
+        ) : (
+          "新規登録"
+        )}
+      </button>
+    </form>
+  );
+};
+
+export default RegisterForm;
diff --git a/app/user/register/page.tsx b/app/user/register/page.tsx
index 60bb9d0..d85c212 100644
--- a/app/user/register/page.tsx
+++ b/app/user/register/page.tsx
@@ -1,115 +1,12 @@
-"use client";
-import { useRouter } from "next/navigation";
-import { SubmitHandler, useForm } from "react-hook-form";
-import useUser from "app/hooks/useUser";
-import { Input } from "@/components/ui/input";
-import toast from "react-hot-toast";
-import { useState } from "react";
+import RegisterForm from "./components/RegisterForm";
 
 const RegisterPage = () => {
-  const {
-    register,
-    handleSubmit,
-    formState: { errors },
-  } = useForm<{ email: string; password: string }>({
-    defaultValues: {
-      email: "",
-      password: "",
-    },
-  });
-
-  const { signUp } = useUser();
-  const router = useRouter();
-  const [loading, setLoading] = useState(false);
-
-  const doRegister: SubmitHandler<{ email: string; password: string }> = async (
-    formData
-  ) => {
-    try {
-      setLoading(true);
-      await signUp({ email: formData.email, password: formData.password });
-      alert(
-        "登録いただいたメールアドレスに確認メールを送信しましたのでご確認ください!(メールの送信は数分かかる場合がありますのでご了承ください)"
-      );
-      router.push("/");
-    } catch (error) {
-      toast.error("エラーが発生しました");
-      setLoading(false);
-    }
-  };
-
   return (
     <div className="mx-auto max-w-3xl lg:max-w-2xl px-4 sm:px-6 lg:px-8 pb-16 pt-40 text-center lg:pt-32">
       <div className="bg-slate-50 rounded-xl px-4 py-4 shadow-lg ring-1 ring-gray-400">
         <p className="text-2xl text-center text-gray-900">新規登録画面</p>
         <div className="flex justify-center pt-8">
-          <form onSubmit={handleSubmit(doRegister)} className="w-full max-w-md">
-            <div className="mb-4">
-              <Input
-                id="email"
-                placeholder="メールアドレス"
-                {...register("email", {
-                  required: {
-                    value: true,
-                    message: "メールアドレスを入力してください",
-                  },
-                  pattern: {
-                    value:
-                      /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]+.[A-Za-z0-9]+$/,
-                    message: "有効なメールアドレスを入力してください",
-                  },
-                })}
-                className="border border-gray-300 rounded-md px-3 py-2 w-full"
-              />
-              {errors.email && (
-                <div className="text-red-500 text-sm">
-                  {errors.email.message}
-                </div>
-              )}
-            </div>
-
-            <div className="mb-4">
-              <Input
-                id="password"
-                type="password"
-                placeholder="パスワード"
-                {...register("password", {
-                  required: {
-                    value: true,
-                    message: "パスワードを入力してください",
-                  },
-                  minLength: {
-                    value: 6,
-                    message: "パスワードは6文字以上にしてください",
-                  },
-                })}
-                className="border border-gray-300 rounded-md px-3 py-2 w-full"
-              />
-              {errors.password && (
-                <div className="text-red-500 text-sm">
-                  {errors.password.message}
-                </div>
-              )}
-            </div>
-
-            <button
-              type="submit"
-              className={`text-white px-4 py-2 rounded-md focus:outline-none ${
-                loading
-                  ? "cursor-not-allowed bg-gray-400"
-                  : "bg-blue-500 hover:bg-blue-600 transition-colors duration-300 focus:border-blue-500 focus:ring"
-              }`}
-            >
-              {loading ? (
-                <div className="flex justify-center items-center">
-                  <p>読み込み中・・・</p>
-                  <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white" />
-                </div>
-              ) : (
-                "新規登録"
-              )}
-            </button>
-          </form>
+          <RegisterForm />
         </div>
       </div>
     </div>

From 96868b29008f5a316c2af6360d2cea498081783d Mon Sep 17 00:00:00 2001
From: Ryohei Kamei <231205751@ccmailg.meijo-u.ac.jp>
Date: Fri, 11 Oct 2024 18:16:03 +0900
Subject: [PATCH 3/3] =?UTF-8?q?=E7=99=BB=E9=8C=B2=E7=94=BB=E9=9D=A2?=
 =?UTF-8?q?=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=B3=E3=83=BC=E3=83=89?=
 =?UTF-8?q?=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 __test__/RegisterPage.test.tsx | 60 ++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 __test__/RegisterPage.test.tsx

diff --git a/__test__/RegisterPage.test.tsx b/__test__/RegisterPage.test.tsx
new file mode 100644
index 0000000..af2e039
--- /dev/null
+++ b/__test__/RegisterPage.test.tsx
@@ -0,0 +1,60 @@
+import { act, render, screen } from "@testing-library/react";
+import user from "@testing-library/user-event";
+import RegisterPage from "app/user/register/page";
+
+// モック化したuseRouterをインポート
+jest.mock("next/navigation", () => ({
+  useRouter: jest.fn(),
+}));
+
+describe("<Page />", () => {
+  test("RegisterPageコンポーネントが正しく表示されるかのテスト", async () => {
+    render(<RegisterPage />);
+
+    await act(async () => {});
+
+    expect(screen.getByText("新規登録画面")).toBeInTheDocument();
+    expect(screen.getByPlaceholderText("メールアドレス")).toBeInTheDocument();
+    expect(screen.getByPlaceholderText("パスワード")).toBeInTheDocument();
+    expect(screen.getByRole("button")).toBeInTheDocument();
+  });
+
+  test("メールアドレスとパスワード欄が未入力の場合、エラーメッセージが表示されるかのテスト", async () => {
+    render(<RegisterPage />);
+
+    const submitButton = screen.getByRole("button");
+    await user.click(submitButton);
+
+    await act(async () => {});
+
+    expect(
+      screen.getByText("メールアドレスを入力してください")
+    ).toBeInTheDocument();
+
+    expect(
+      screen.getByText("パスワードを入力してください")
+    ).toBeInTheDocument();
+  });
+
+  test("適切なメールアドレス,適切なパスワードを入力していない場合、エラーメッセージが表示されるテスト", async () => {
+    render(<RegisterPage />);
+
+    const emailInput = screen.getByPlaceholderText("メールアドレス");
+    const passwordInput = screen.getByPlaceholderText("パスワード");
+    const submitButton = screen.getByRole("button");
+
+    await user.type(emailInput, "test");
+    await user.type(passwordInput, "pass");
+    await user.click(submitButton);
+
+    await act(async () => {});
+
+    expect(
+      screen.getByText("有効なメールアドレスを入力してください")
+    ).toBeInTheDocument();
+
+    expect(
+      screen.getByText("パスワードは6文字以上にしてください")
+    ).toBeInTheDocument();
+  });
+});