Skip to content

Commit

Permalink
Merge pull request #26 from kameiryohei/refacta-register-page
Browse files Browse the repository at this point in the history
ログイン/登録ページのコンポーネント分割と、登録ページのテストを作成
  • Loading branch information
kameiryohei authored Oct 11, 2024
2 parents 3b329c7 + 96868b2 commit 9ca22cb
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 220 deletions.
60 changes: 60 additions & 0 deletions __test__/RegisterPage.test.tsx
Original file line number Diff line number Diff line change
@@ -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();
});
});
119 changes: 119 additions & 0 deletions app/user/login/components/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -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;
117 changes: 2 additions & 115 deletions app/user/login/page.tsx
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
Loading

0 comments on commit 9ca22cb

Please sign in to comment.