# 장바구니 페이지

## 장바구니 리스팅 기능

> 장바구니에 담긴 상품들을 표시하는 화면

### User Scenario

* 헤더 부분에 장바구니 페이지로 이동되는 링크가 제공된다.
* 사용자가 장바구니에 담은 상품들을 볼 수 있다.
* 장바구니가 비어있는 경우 '장바구니가 비었습니다' 텍스트가 노출된다.
* 각 상품 별로 리스팅된다.
  * 상품의 옵션이 다른 경우 별도로 리스팅된다.
* 상품별로 이름, 옵션, 가격, 수량, 총 가격이 표시된다.
* 장바구니에 담긴 상품의 총 합계 금액이 표시된다.

<details>

<summary>Unit Test</summary>

#### CartView\.test.tsx

```typescript
import type { UseQueryResult } from "@tanstack/react-query";
import { render, screen, waitFor } from "@testing-library/react";
import { Route } from "react-router-dom";

import { cart, emptyCart } from "@/fixtures/cart";
import { useFetchCartList } from "@/services/useCart";
import { withAllContexts, withRouter } from "@/tests/utils";
import type { Cart } from "@/types/cart";

import CartView from "./CartView";

const context = describe;

jest.mock("@/services/useCart");

describe("CartView", () => {
  function renderCartView() {
    return render(
      withAllContexts(withRouter(<Route path="/" element={<CartView />} />))
    );
  }

  beforeEach(() => {
    jest.clearAllMocks();
  });

  context("when the cart is empty", () => {
    it('render text "장바구니가 비었습니다"', () => {
      jest
        .mocked(useFetchCartList)
        .mockImplementation(
          () => ({ data: emptyCart } as UseQueryResult<Cart>)
        );

      renderCartView();

      expect(screen.getByText(/장바구니가 비었습니다/)).toBeInTheDocument();
    });
  });

  context("when the cart is not empty", () => {
    it("render items", async () => {
      jest
        .mocked(useFetchCartList)
        .mockImplementation(() => ({ data: cart } as UseQueryResult<Cart>));

      renderCartView();

      await Promise.all(
        cart.lineItems.map(async (item) => {
          await waitFor(() =>
            expect(screen.getByText(item.product.name)).toBeInTheDocument()
          );
          await waitFor(() =>
            expect(screen.getByText(item.quantity)).toBeInTheDocument()
          );
        })
      );
    });
  });
});

```

</details>

<details>

<summary>E2E Test</summary>

```typescript
/// <reference types="cypress" />
import "@testing-library/cypress/add-commands";
import backdoor from "../../helper/backdoor";

// 헤더 부분에 장바구니 페이지로 이동되는 링크가 제공된다.
// 사용자가 장바구니에 담은 상품들을 볼 수 있다.
// 장바구니가 비어있는 경우 '장바구니가 비었습니다' 텍스트가 노출된다.
// 각 상품 별로 리스팅된다.
// 상품의 옵션이 다른 경우 별도로 리스팅된다.
// 상품별로 이름, 옵션, 가격, 수량, 총 가격이 표시된다.
// 장바구니에 담긴 상품의 총 합계 금액이 표시된다.

describe("Cart List", () => {
  beforeEach(() => {
    backdoor();
    cy.visit("products/0BV000PRO0001");
  });

  context("when add product to cart", () => {
    it("product appears on the cart page", () => {
      cy.findByRole("combobox", { name: "컬러" }).click();
      cy.findByRole("option", { name: "grey" }).click();
      cy.findByRole("combobox", { name: "사이즈" }).click();
      cy.findByRole("option", { name: "ONE" }).click();

      cy.findByRole("button", { name: "장바구니 담기" }).click();
      cy.findByText(/장바구니에 담았습니다/).should("exist");

      cy.findByRole("link", { name: "Cart" }).click();

      cy.findByText(/CBCL 하트자수맨투맨/).should("exist");
      cy.findAllByRole("row").should("have.length", 1);
    });

    context("when the product has different options", () => {
      it("added to the cart separately", () => {
        cy.findByRole("combobox", { name: "컬러" }).click();
        cy.findByRole("option", { name: "grey" }).click();
        cy.findByRole("combobox", { name: "사이즈" }).click();
        cy.findByRole("option", { name: "ONE" }).click();

        cy.findByRole("button", { name: "장바구니 담기" }).click();
        cy.findByText(/장바구니에 담았습니다/).should("exist");

        cy.findByRole("link", { name: "Cart" }).click();

        cy.findAllByRole("row").should("have.length", 1);

        cy.visit("/products");
        cy.findByRole("link", { name: /CBCL 하트자수맨투맨/ }).click();

        cy.findByRole("combobox", { name: "컬러" }).click();
        cy.findByRole("option", { name: "blue" }).click();
        cy.findByRole("combobox", { name: "사이즈" }).click();
        cy.findByRole("option", { name: "ONE" }).click();
        cy.findByText(/장바구니에 담았습니다/).should("exist");

        cy.findByRole("link", { name: "Cart" }).click();

        cy.findByText(/256,000원/).should("exist");
        cy.findAllByRole("row").should("have.length", 2);
      });
    });

    context("when empty cart", () => {
      it('display "장바구니가 비었습니다', () => {
        cy.visit("/cart");

        cy.findByText(/장바구니가 비었습니다/).should("exist");
      });
    });
  });
});

```

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/dev_road/week-9/undefined-3.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
