회원가입
회원가입 기능
User Scenario
이메일, 이름, 패스워드를 입력하여 회원가입을 한다.
필드 값이 유효성에 어긋날시 에러 메시지가 노출된다.
유효성에 어긋나거나 값이 채워지지 않은 경우 회원가입 버튼은 Disabled 상태가 된다.
회원가입에 성공하면 회원가입 성공 페이지로 이동된다.
회원가입 성공시 로그인 상태가 된다.
Unit Test
SignupForm.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Route } from "react-router-dom";
import VALID_MSG from "@/constants/validMsg";
import { withAllContexts, withRouter } from "@/tests/utils";
import SignupForm from "./SignupForm";
const context = describe;
describe("SignupForm", () => {
function renderSignupForm() {
return render(
withAllContexts(withRouter(<Route path="/" element={<SignupForm />} />))
);
}
it("render correctly", () => {
renderSignupForm();
expect(screen.getByRole("textbox", { name: "E-mail" })).toBeInTheDocument();
expect(screen.getByRole("textbox", { name: "Name" })).toBeInTheDocument();
expect(screen.getByLabelText("Password")).toBeInTheDocument();
expect(screen.getByLabelText("Password Confirm")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "회원가입" }));
});
context("when email value is invalid", () => {
it(`display error message "${VALID_MSG.EMAIL}"`, async () => {
renderSignupForm();
const emailInput = screen.getByRole("textbox", { name: "E-mail" });
await userEvent.type(emailInput, "xxxxxxx");
expect(emailInput).toHaveValue("xxxxxxx");
expect(screen.getByText(VALID_MSG.EMAIL)).toBeInTheDocument();
});
it("submit button is disabled", async () => {
renderSignupForm();
const emailInput = screen.getByRole("textbox", { name: "E-mail" });
await userEvent.type(emailInput, "xxxxxxx");
expect(screen.getByRole("button", { name: "회원가입" })).toBeDisabled();
});
});
context("when name value is invalid", () => {
it(`display error message "${VALID_MSG.NAME}"`, async () => {
renderSignupForm();
const nameInput = screen.getByRole("textbox", { name: "Name" });
await userEvent.type(nameInput, "xxxxxxx");
expect(nameInput).toHaveValue("xxxxxxx");
expect(screen.getByText(VALID_MSG.NAME)).toBeInTheDocument();
});
it("submit button is disabled", async () => {
renderSignupForm();
const nameInput = screen.getByRole("textbox", { name: "Name" });
await userEvent.type(nameInput, "xxxxxxx");
expect(screen.getByRole("button", { name: "회원가입" })).toBeDisabled();
});
});
context("when password value is invalid", () => {
it(`display error message "${VALID_MSG.PASSWORD}"`, async () => {
renderSignupForm();
const passwordInput = screen.getByLabelText("Password");
await userEvent.type(passwordInput, "123123");
expect(passwordInput).toHaveValue("123123");
expect(screen.getByText(VALID_MSG.PASSWORD)).toBeInTheDocument();
});
it("submit button is disabled", async () => {
renderSignupForm();
const passwordInput = screen.getByLabelText("Password");
await userEvent.type(passwordInput, "123123");
expect(screen.getByRole("button", { name: "회원가입" })).toBeDisabled();
});
});
context("when password confirm value is invalid", () => {
it(`display error message "${VALID_MSG.PASSWORD_C}"`, async () => {
renderSignupForm();
await userEvent.type(screen.getByLabelText("Password"), "password");
const passwordConfirmInput = screen.getByLabelText("Password Confirm");
await userEvent.type(passwordConfirmInput, "pass");
expect(passwordConfirmInput).toHaveValue("pass");
expect(screen.getByText(VALID_MSG.PASSWORD_C)).toBeInTheDocument();
});
it("submit button is disabled", async () => {
renderSignupForm();
await userEvent.type(screen.getByLabelText("Password"), "password");
await userEvent.type(screen.getByLabelText("Password Confirm"), "pass");
expect(screen.getByRole("button", { name: "회원가입" })).toBeDisabled();
});
});
context("when the form is empty", () => {
it("submit button is disabled", async () => {
renderSignupForm();
expect(screen.getByRole("button", { name: "회원가입" })).toBeDisabled();
});
});
});
SignupFormStore.test.ts
import VALID_MSG from "@/constants/validMsg";
import SignupFormStore from "./SignupFormStore";
const context = describe;
describe("SignupFormStore", () => {
let store: SignupFormStore;
beforeEach(() => {
store = new SignupFormStore();
});
describe("change email", () => {
it("email is changed", () => {
const email = "v_oyb@naver.com";
store.changeEmail(email);
expect(store.email).toBe(email);
});
context("when value is invalid", () => {
it(`error is ${VALID_MSG.EMAIL}`, () => {
store.changeEmail("invalid email");
expect(store.error).toBe(VALID_MSG.EMAIL);
});
});
context("when value is valid", () => {
it("error is empty", () => {
store.changeEmail("tester@example.com");
expect(store.error).toBeFalsy();
});
});
});
describe("change name", () => {
it("name is changed", () => {
const name = "정태웅";
store.changeName(name);
expect(store.name).toBe(name);
});
context("when value is invalid", () => {
it(`error is ${VALID_MSG.NAME}`, () => {
store.changeName("invalid name");
expect(store.error).toBe(VALID_MSG.NAME);
});
});
context("when value is valid", () => {
it("error is empty", () => {
store.changeName("정태웅");
expect(store.error).toBeFalsy();
});
});
});
describe("change password", () => {
it("password is changed", () => {
const password = "password";
store.changePassword(password);
expect(store.password).toBe(password);
});
context("when value is invalid", () => {
it(`error is ${VALID_MSG.PASSWORD}`, () => {
store.changePassword("123123");
expect(store.error).toBe(VALID_MSG.PASSWORD);
});
});
context("when value is valid", () => {
it("error is empty", () => {
store.changePassword("password");
expect(store.error).toBeFalsy();
});
});
});
describe("change password confirm", () => {
it("password confirm value is changed", () => {
const password = "password";
store.changePasswordConfirm(password);
expect(store.passwordConfirm).toBe(password);
});
context("when password entered doesn't match", () => {
it(`error is ${VALID_MSG.PASSWORD_C}`, () => {
store.changePassword("password");
store.changePasswordConfirm("password1");
expect(store.error).toBe(VALID_MSG.PASSWORD_C);
});
});
context("when value is valid", () => {
it("error is empty", () => {
store.changePassword("password");
store.changePasswordConfirm("password");
expect(store.error).toBeFalsy();
});
});
});
describe("with reset", () => {
it("all field values are initialized", () => {
store.changeEmail("tester@example.com");
store.changeName("tester");
store.changePassword("password");
store.changePasswordConfirm("password");
store.reset();
expect(store.email).toBeFalsy();
expect(store.name).toBeFalsy();
expect(store.password).toBeFalsy();
expect(store.passwordConfirm).toBeFalsy();
});
});
});
E2E Test
/// <reference types="cypress" />
// 이메일, 이름, 패스워드를 입력하여 회원가입을 한다.
// 필드 값이 유효성에 어긋날시 에러 메시지가 노출된다.
// 유효성에 어긋나거나 값이 채워지지 않은 경우 회원가입 버튼은 Disabled 상태가 된다.
// 회원가입에 성공하면 회원가입 성공 페이지로 이동된다.
// 회원가입에 성공시 로그인 상태가 된다.
describe("Signup", () => {
beforeEach(() => {
cy.backdoor();
cy.visit("/signup");
});
context("when form values are invalid", () => {
it("unable to signup", () => {
cy.findByRole("textbox", { name: "E-mail" }).type("invalid email");
cy.findByText("이메일 형식이 올바르지 않습니다.").should("exist");
cy.findByRole("textbox", { name: "Name" }).type("invalid name");
cy.findByText("이름 형식이 올바르지 않습니다.").should("exist");
cy.findByLabelText("Password").type("invalid password1@");
cy.findByText("비밀번호 형식이 올바르지 않습니다.").should("exist");
cy.findByLabelText("Password Confirm").type("invalid password");
cy.findByText("비밀번호가 일치하지 않습니다.").should("exist");
cy.findByRole("button", { name: "회원가입" }).should("be.disabled");
});
});
context("when signup succeed ", () => {
beforeEach(() => {
cy.findByRole("textbox", { name: "E-mail" }).type("tester2@example.com");
cy.findByRole("textbox", { name: "Name" }).type("테스터");
cy.findByLabelText("Password").type("password");
cy.findByLabelText("Password Confirm").type("password");
cy.findByRole("button", { name: "회원가입" }).click();
});
it("redirected signup complete page", () => {
cy.findByText(/Signup Complete/).should("exist");
cy.url().should("include", "/signupComplete");
});
it("will be logged in status", () => {
cy.findByRole("link", { name: "Login" }).should("not.exist");
cy.findByRole("link", { name: "Cart" }).should("exist");
cy.findByRole("button", { name: "Logout" }).should("exist");
});
});
});
Last updated