상품 상세 페이지

상품 상세보기 기능

상품 상세 정보를 얻어서 표시하는 화면

조회 API를 호출할 때 상품을 찾을 수 없는 경우와 API 오류가 발생한 경우를 고려하여 사용자에게 명확한 안내 메시지 제공

  • 상품을 찾을 수 없는 경우 → "해당 상품을 찾을 수 없습니다."

  • API 오류가 발생한 경우 → "오류가 발생했습니다. 잠시 후 다시 시도해주세요."

User Scenario

  • 상품 이름, 이미지, 옵션, 설명이 노출된다.

  • 장바구니에 상품을 담을 수 있는 버튼을 제공한다.

  • 사용자는 상품 상세페이지에서 상품 옵션과 수량을 선택하여 장바구니에 담을 수 있다.

Unit Test

ProductDetail.test.tsx

import { render, screen, waitFor } from "@testing-library/react";
import { Route } from "react-router-dom";

import { productDetails } from "@/fixtures";
import { withAllContexts, withRouter } from "@/tests/utils";
import { numberFormat } from "@/utils/numberFormat";

import ProductDetail from "./ProductDetail";

const context = describe;

describe("ProductDetail", () => {
  const detail = productDetails[0];

  function renderProductDetail(productId: string) {
    return render(
      withAllContexts(
        withRouter(
          <Route path="/:id" element={<ProductDetail />} />,
          `/${productId}`
        )
      )
    );
  }

  context("with an valid product id", () => {
    it("render correctly", async () => {
      renderProductDetail(detail.id);

      await waitFor(() =>
        expect(
          screen.getByRole("heading", { name: detail.name })
        ).toBeInTheDocument()
      );

      expect(
        screen.getByText(`${numberFormat(detail.price)}원`)
      ).toBeInTheDocument();

      const line = detail.description.split("\n");

      line.forEach((str) => {
        expect(screen.getByText(new RegExp(str))).toBeInTheDocument();
      });

      detail.images.forEach((img) => {
        expect(screen.getByRole("img")).toHaveAttribute("src", img.url);
      });
    });
  });

  context("with an invalid product id", () => {
    it('render text "해당 상품을 찾을 수 없습니다."', async () => {
      renderProductDetail("soso");

      await waitFor(() =>
        expect(
          screen.getByText("해당 상품을 찾을 수 없습니다.")
        ).toBeInTheDocument()
      );
    });
  });
});
E2E Test
/// <reference types="cypress" />
import "@testing-library/cypress/add-commands";

describe("Product Detail", () => {
  beforeEach(() => {
    cy.visit("http://localhost:8081/products");
    cy.findByRole("link", { name: /CBCL 하트자수맨투맨/ }).click();
  });

  it("display product detail", () => {
    cy.findByText(/CBCL 하트자수맨투맨/).should("exist");
    cy.findByText(/편하게 입을 수 있는 맨투맨/).should("exist");
    cy.findByRole("img").should("exist");
    cy.findByRole("button", { name: "장바구니 담기" }).should("exist");
  });

  context("when change the quantity of a product", () => {
    it('default quantity is "1"', () => {
      cy.findByRole("textbox").should("have.value", "1");
    });

    it('changeable minimum qunatity is "1"', () => {
      cy.findByRole("textbox").should("have.value", "1");

      cy.findByRole("button", { name: "decrease" }).click();
      cy.findByRole("button", { name: "decrease" }).click();

      cy.findByRole("textbox").should("have.value", "1");
    });

    it('changeable maximum quantity is "10"', () => {
      Array.from({ length: 12 }).forEach(() => {
        cy.findByRole("button", { name: "increase" }).click();
      });

      cy.findByRole("textbox").should("have.value", "10");
    });

    it("the price changes as well", () => {
      cy.findByText(/128,000/).should("exist");

      cy.findByRole("button", { name: "increase" }).click();

      cy.findByText(/128,000/).should("not.exist");
      cy.findByText(/256,000/).should("exist");
    });
  });

  context("when change option", () => {
    it("display selected option", () => {
      cy.findByRole("combobox", { name: "컬러" }).click();
      cy.findByRole("option", { name: "grey" }).click();

      cy.findByText(/grey/).should("exist");
    });
  });

  context("when product no found", () => {
    it('display "해당 상품을 찾을 수 없습니다."', () => {
      cy.visit("http://localhost:8081/products/xxx");

      cy.findByText(/해당 상품을 찾을 수 없습니다./).should("exist");
    });
  });
});

장바구니 상품 담기 기능

장바구니에 상품을 담는다라는 것은 정확히는 상품 그 자체가 장바구니에 담기는것이 아니라, 해당 상품과 관련된 다양한 옵션 정보, 수량 등이 함께 조합되어 장바구니의 아이템으로 구성되는 것을 의미한다.

User Scenario

  • 장바구니에 담기 버튼을 클릭하면 선택한 상품의 옵션, 수량 값이 장바구니에 추가된다.

  • 장바구니에 담을 상품의 옵션과 수량을 변경할 수 있다.

    • 상품 수량의 기본값은 1이다.

    • 수량은 1 ~ 10 범위 내에서 선택이 가능하다.

  • 수량이 변경될 때 마다 상품 가격도 변경된다.

  • 장바구니 담기가 성공하면 '장바구니에 담았습니다' 텍스트가 노출된다.

Unit Test

ProductFormStore.test.tsx

AddToCartForm.test.tsx

Quantity.test.tsx

E2E Test

Last updated