인프런 커뮤니티 질문&답변

sos님의 프로필 이미지
sos

작성한 질문수

따라하며 배우는 리액트 테스트 [2023.11 업데이트]

상품 가격, 옵션 가격을 더한 총 가격 구하기

toHaveTextContent 에서 에러가 자꾸 나는데 아무리 찾아도 잘 모르겠습니다.

해결된 질문

작성

·

511

·

수정됨

0

- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요!
- 먼저 유사한 질문이 있었는지 검색해보세요.
- 서로 예의를 지키며 존중하는 문화를 만들어가요.
- 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.

 

선생님 안녕하세요! 강의 너무 잘 듣고 있습니다! 감사해요

오류를 잡으려고 노력해봤는데도 잘 안돼서 질문 남깁니다

calculate.test.js파일의 toHaveTextContent()부분에서 모두 오류가 나고 있습니다. 선생님이 주신 소스코드와 제 코드를 모두 비교해봤는데 다 똑같더라구요. 제가 인지하지 못한 오류가 있는지 한 번 봐주실 수 있으실까요? 부탁드립니다ㅜ

  • 오류 부분

질문.jpg

  • calculate.test.js

    import { render, screen } from "../../../test-utils";
    import userEvent from "@testing-library/user-event";
    import Type from "../Type";
    import OrderPage from "../OrderPage";
    
    test("update products total when products change", async () => {
      render(<Type orderType="products" />);
      const productsTotal = screen.getByText("상품 총 가격: ", { exact: false });
      expect(productsTotal).toHaveTextContent("0");
    
      // 아메리카 여행 상품 한개 올리기
      const americaInput = await screen.findByRole("spinbutton", {
        name: "America",
      });
      userEvent.clear(americaInput);
      userEvent.type(americaInput, "1"); // 이 상품을 하나 산다는 뜻
      expect(americaInput).toHaveTextContent("1000");
    });
    

- Type.js

import React, { useContext, useEffect, useState } from "react";
import Products from "./Products";
import axios from "axios";
import ErrorBanner from "../../components/ErrorBanner";
import Options from "./Options";
import { OrderContext } from "../../contexts/OrderContext";

const Type = ({ orderType }) => {
  const [items, setItems] = useState([]);
  const [error, setError] = useState(false);
  const [orderDatas, updateItemCount] = useContext(OrderContext);
  // OrderContext.js의  return [{ ...orderCounts, totals }, updateItemCount]; 을 구조분해
  useEffect(() => {
    loadItems(orderType);
  }, [orderType]);
  const loadItems = async (orderType) => {
    try {
      let response = await axios.get(`http://localhost:5000/${orderType}`);
      setItems(response.data);
    } catch (error) {
      setError(true);
    }
  };
  if (error) {
    return <ErrorBanner message="에러가 발생했습니다" />;
  }
  const ItemComonents = orderType === "products" ? Products : Options;
  const optionItems = items.map((item) => (
    <ItemComonents
      style={{ border: "2px solid red" }}
      key={item.name}
      name={item.name}
      imagePath={item.imagePath}
      updateItemCount={(itemName, newItemCount) =>
        updateItemCount(itemName, newItemCount, orderType)
      }
    />
  ));
  let orderTypeKorean = orderType === "products" ? "상품" : "옵션";

  return (
    <div>
      <h2>주문종류</h2>
      <p>하나의 가격</p>
      <p>
        {orderTypeKorean} 총 가격: {orderDatas.totals[orderType]}
      </p>
      <div
        style={{
          display: "flex",
          flexDirection: orderType === "options" && "column", //
        }}
      >
        {optionItems}
      </div>
    </div>
  );
};

export default Type;
  • orderContext.js

    import { createContext, useState, useMemo, useEffect } from "react";
    
    export const OrderContext = createContext();
    
    const pricePerItem = {
      products: 1000,
      options: 500,
    };
    function calculateSubtotal(orderType, orderCounts) {
      let optionCount = 0;
      for (const count of orderCounts[orderType].values()) {
        optionCount += count;
      }
    
      return optionCount * pricePerItem[orderType];
    }
    
    export function OrderContextProvider(props) {
      const [orderCounts, setOrderCounts] = useState({
        products: new Map(),
        options: new Map(),
      });
    
      const [totals, setTotals] = useState({
        products: 0,
        options: 0,
        total: 0,
      });
      useEffect(() => {
        const productsTotal = calculateSubtotal("products", orderCounts);
        const optionsTotal = calculateSubtotal("options", orderCounts);
        const total = productsTotal + optionsTotal;
        setTotals({
          products: productsTotal,
          options: optionsTotal,
          total,
        });
      }, [orderCounts]);
    
      const value = useMemo(() => {
        function updateItemCount(itemName, newItemCount, orderType) {
          const newOrderCounts = { ...orderCounts };
          const orderCountsMap = orderCounts[orderType];
          orderCountsMap.set(itemName, parseInt(newItemCount));
          setOrderCounts(newOrderCounts);
        }
        return [{ ...orderCounts, totals }, updateItemCount];
      }, [orderCounts, totals]);
    
      return <OrderContext.Provider value={value} {...props} />;
    }
    

 

답변 2

1

sos님의 프로필 이미지
sos
질문자

이전에 해결하지 못한 문제였지만, 복습을 하면서 해결했습니다.

혹시 저와 같은 오류를 겪으신 분들께 도움이 될까 싶어 해결 방법을 남겨봅니다.

toHaveTextContent의 오류라 생각한 문제는 사실 userEvent가 제대로 작동하지 않아 발생한 문제였습니다.

공식문서에 보면, userEvent.setup()을 호출해 사용하는 방법을 추천하고 있습니다. 저는 이렇게 불러옴으로 문제를 해결했습니다.

We recommend invoking userEvent.setup() before the component is rendered.

https://testing-library.com/docs/user-event/intro/

//package.json 
"@testing-library/user-event": "^14.4.3"
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { OrderContextProvider } from "../../../contexts/OrderContext";
import Type from "../Type";

test("update product's total when products change", async () => {
  ✅ const event = userEvent.setup();
  render(<Type orderType="products" />, { wrapper: OrderContextProvider });

  const productsTotal = screen.getByText("총 가격:", { exact: false });
  expect(productsTotal).toHaveTextContent("0");

  //아메리카 여행 상품 한 개 올리기
  const americaInput = await screen.findByRole("spinbutton", {
    name: "America",
  });

✅  await event.clear(americaInput);
✅  await event.type(americaInput, "1");
  expect(productsTotal).toHaveTextContent("1000");
});

0

John Ahn님의 프로필 이미지
John Ahn
지식공유자

안녕하세요!

현재 가격 계산하는 부분의 기능에 오타가 있을 것 같은데

깃허브 저장소에 프로젝트를 올리셨다면

깃허브 저장소 주소 주시면

직접 한번 해보겠습니다!

감사합니다.

sos님의 프로필 이미지
sos

작성한 질문수

질문하기