From ba6c9339525c4053f39d6f7881108f0cffffd214 Mon Sep 17 00:00:00 2001 From: 2021048695 Date: Fri, 26 Jul 2024 23:51:38 +0900 Subject: [PATCH 1/6] =?UTF-8?q?docs:=20=EA=B5=AC=ED=98=84=ED=95=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index e3ee5a24..a7940ded 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ # 카카오 테크 캠퍼스 - 프론트엔드 카카오 선물하기 편 + +# 1단계: Form 부분 테스트 코드 작성 +[ ] Jest와 React Testing Libaray를 사용하여 테스트 기반 환경 구축 +[ ] MSW를 사용하여 Mock API가 동작하도록 구현. (상세 API / 옵션 API) +[ ] 단위 테스트로 작성하면 좋을 테스트가 있다면 단위테스트 코드 작성. +[ ] 상품 상세 페이지와 관련된 통합 테스트 코드 작성. +[ ] 결제하기 페이지의 Form과 관련된 통합 테스트 코드 작성. + 현금영수증 Checkbox가 false인 경우 현금영수증 종류, 현금영수증 번호 field가 비활성화 되어있는지 확인하는 테스트 코드 작성. (만약 true인 경우 현금영수증 종류, 번호 field에 값이 입력 되어야 함) + form의 validation 로직이 정상 동작하는지 확인하는 테스트 코드 작성. + +# 2단계: 로그인, 관심 상품 등록 / 삭제, 관심 목록 구현 +[ ] 로그인 기능 구현. +[ ] 회원가입 화면을 만들고, 회원가입 기능이 동작되게 구현. (회원가입을 하면 로그인이 되도록) + 회원가입 버튼은 로그인 화면 하단에 배치하고, 로그인 화면은 그대로 사용 +[ ] 상품 상세 페이지에서 관심 등록 버튼 생성 +[ ] 상품 상세 페이지에서 관심 버튼을 클릭 시 관심 추가 + 관심 등록 성공 시 Alert로 "관심 등록 완료" 메시지를 노출 +[ ] 나의 계정 페이지에서 관심 목록 리스트 생성 + 관심 목록 리스트는 chakra UI를 사용하여 구현 + 관심 목록 API는 카카오테크 선물하기 API 노션의 response 데이터 사용 +[ ] 관심 목록 리스트에서 관심 삭제가 가능하도록 구현 + 관심 삭제 시 목록에서 사라지도록 구현 \ No newline at end of file From 0781b8f36853f74c794c303dce3cc88ab4325900 Mon Sep 17 00:00:00 2001 From: 2021048695 Date: Sun, 28 Jul 2024 07:21:58 +0900 Subject: [PATCH 2/6] =?UTF-8?q?chore:=20Jest=EC=99=80=20React=20Testing=20?= =?UTF-8?q?Libaray=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++++++++-------- craco.config.js | 7 +++++++ package.json | 5 +++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a7940ded..14327bec 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,25 @@ # 카카오 테크 캠퍼스 - 프론트엔드 카카오 선물하기 편 # 1단계: Form 부분 테스트 코드 작성 -[ ] Jest와 React Testing Libaray를 사용하여 테스트 기반 환경 구축 + +[x] Jest와 React Testing Libaray를 사용하여 테스트 기반 환경 구축 [ ] MSW를 사용하여 Mock API가 동작하도록 구현. (상세 API / 옵션 API) [ ] 단위 테스트로 작성하면 좋을 테스트가 있다면 단위테스트 코드 작성. [ ] 상품 상세 페이지와 관련된 통합 테스트 코드 작성. [ ] 결제하기 페이지의 Form과 관련된 통합 테스트 코드 작성. - 현금영수증 Checkbox가 false인 경우 현금영수증 종류, 현금영수증 번호 field가 비활성화 되어있는지 확인하는 테스트 코드 작성. (만약 true인 경우 현금영수증 종류, 번호 field에 값이 입력 되어야 함) - form의 validation 로직이 정상 동작하는지 확인하는 테스트 코드 작성. +현금영수증 Checkbox가 false인 경우 현금영수증 종류, 현금영수증 번호 field가 비활성화 되어있는지 확인하는 테스트 코드 작성. (만약 true인 경우 현금영수증 종류, 번호 field에 값이 입력 되어야 함) +form의 validation 로직이 정상 동작하는지 확인하는 테스트 코드 작성. # 2단계: 로그인, 관심 상품 등록 / 삭제, 관심 목록 구현 + [ ] 로그인 기능 구현. [ ] 회원가입 화면을 만들고, 회원가입 기능이 동작되게 구현. (회원가입을 하면 로그인이 되도록) - 회원가입 버튼은 로그인 화면 하단에 배치하고, 로그인 화면은 그대로 사용 +회원가입 버튼은 로그인 화면 하단에 배치하고, 로그인 화면은 그대로 사용 [ ] 상품 상세 페이지에서 관심 등록 버튼 생성 [ ] 상품 상세 페이지에서 관심 버튼을 클릭 시 관심 추가 - 관심 등록 성공 시 Alert로 "관심 등록 완료" 메시지를 노출 +관심 등록 성공 시 Alert로 "관심 등록 완료" 메시지를 노출 [ ] 나의 계정 페이지에서 관심 목록 리스트 생성 - 관심 목록 리스트는 chakra UI를 사용하여 구현 - 관심 목록 API는 카카오테크 선물하기 API 노션의 response 데이터 사용 +관심 목록 리스트는 chakra UI를 사용하여 구현 +관심 목록 API는 카카오테크 선물하기 API 노션의 response 데이터 사용 [ ] 관심 목록 리스트에서 관심 삭제가 가능하도록 구현 - 관심 삭제 시 목록에서 사라지도록 구현 \ No newline at end of file +관심 삭제 시 목록에서 사라지도록 구현 diff --git a/craco.config.js b/craco.config.js index 724a886c..7e0784d8 100644 --- a/craco.config.js +++ b/craco.config.js @@ -6,4 +6,11 @@ module.exports = { '@': path.resolve(__dirname, 'src'), }, }, + jest: { + configure: { + moduleNameMapper: { + '^@/(.*)$': '/src/$1', + }, + }, + }, }; diff --git a/package.json b/package.json index 1df1cf87..95500e27 100644 --- a/package.json +++ b/package.json @@ -83,5 +83,10 @@ }, "overrides": { "react-refresh": "0.11.0" + }, + "jest": { + "moduleNameMapper": { + "^axios$": "axios/dist/node/axios.cjs" + } } } From 3061536bd28ab21e6e909dc51eb18205864e98df Mon Sep 17 00:00:00 2001 From: 2021048695 Date: Mon, 29 Jul 2024 08:00:58 +0900 Subject: [PATCH 3/6] =?UTF-8?q?chore:=20MSW=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/mocks/server.ts | 6 ++++++ src/setupTests.ts | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/mocks/server.ts diff --git a/README.md b/README.md index 14327bec..d679060d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # 1단계: Form 부분 테스트 코드 작성 [x] Jest와 React Testing Libaray를 사용하여 테스트 기반 환경 구축 -[ ] MSW를 사용하여 Mock API가 동작하도록 구현. (상세 API / 옵션 API) +[x] MSW를 사용하여 Mock API가 동작하도록 구현. (상세 API / 옵션 API) [ ] 단위 테스트로 작성하면 좋을 테스트가 있다면 단위테스트 코드 작성. [ ] 상품 상세 페이지와 관련된 통합 테스트 코드 작성. [ ] 결제하기 페이지의 Form과 관련된 통합 테스트 코드 작성. diff --git a/src/mocks/server.ts b/src/mocks/server.ts new file mode 100644 index 00000000..df47ac30 --- /dev/null +++ b/src/mocks/server.ts @@ -0,0 +1,6 @@ +import { setupServer } from 'msw/node'; + +import { categoriesMockHandler } from '@/api/hooks/categories.mock'; +import { productsMockHandler } from '@/api/hooks/products.mock'; + +export const server = setupServer(...categoriesMockHandler, ...productsMockHandler); diff --git a/src/setupTests.ts b/src/setupTests.ts index 8f2609b7..c60987a7 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -3,3 +3,9 @@ // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom import '@testing-library/jest-dom'; + +import { server } from './mocks/server'; + +beforeAll(() => server.listen()); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); From 7d74612b3345a53e4cbdc5aeba949c2824e0f82e Mon Sep 17 00:00:00 2001 From: 2021048695 Date: Mon, 29 Jul 2024 08:30:04 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20Button=EA=B3=BC=20Click=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/common/Button/index.test.tsx | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/components/common/Button/index.test.tsx diff --git a/README.md b/README.md index d679060d..f0095dd8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [x] Jest와 React Testing Libaray를 사용하여 테스트 기반 환경 구축 [x] MSW를 사용하여 Mock API가 동작하도록 구현. (상세 API / 옵션 API) -[ ] 단위 테스트로 작성하면 좋을 테스트가 있다면 단위테스트 코드 작성. +[x] 단위 테스트로 작성하면 좋을 테스트가 있다면 단위테스트 코드 작성. [ ] 상품 상세 페이지와 관련된 통합 테스트 코드 작성. [ ] 결제하기 페이지의 Form과 관련된 통합 테스트 코드 작성. 현금영수증 Checkbox가 false인 경우 현금영수증 종류, 현금영수증 번호 field가 비활성화 되어있는지 확인하는 테스트 코드 작성. (만약 true인 경우 현금영수증 종류, 번호 field에 값이 입력 되어야 함) diff --git a/src/components/common/Button/index.test.tsx b/src/components/common/Button/index.test.tsx new file mode 100644 index 00000000..bb318101 --- /dev/null +++ b/src/components/common/Button/index.test.tsx @@ -0,0 +1,20 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +import { Button } from './index'; + +test('버튼 렌더링과 클릭 테스트', () => { + const mockClickHandler = jest.fn(); + const buttonText = 'Click'; + + render( + , + ); + const buttonElement = screen.getByTestId('custom-button'); + fireEvent.click(buttonElement); + + expect(buttonElement).toBeInTheDocument(); + expect(mockClickHandler).toHaveBeenCalledTimes(1); +}); From 8eabe933e4877683cca16fadf6dac92ce199766e Mon Sep 17 00:00:00 2001 From: 2021048695 Date: Tue, 30 Jul 2024 23:24:14 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EA=B2=B0=EC=A0=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=9D=98=20Form?= =?UTF-8?q?=EA=B3=BC=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- package-lock.json | 93 +++++++++++++++++- package.json | 1 + .../features/Order/OrderForm/index.test.tsx | 87 ++++++++++++++++ src/pages/Goods/Detail/index.test.tsx | 98 +++++++++++++++++++ 5 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 src/components/features/Order/OrderForm/index.test.tsx create mode 100644 src/pages/Goods/Detail/index.test.tsx diff --git a/README.md b/README.md index f0095dd8..14f2a4b4 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ [x] Jest와 React Testing Libaray를 사용하여 테스트 기반 환경 구축 [x] MSW를 사용하여 Mock API가 동작하도록 구현. (상세 API / 옵션 API) [x] 단위 테스트로 작성하면 좋을 테스트가 있다면 단위테스트 코드 작성. -[ ] 상품 상세 페이지와 관련된 통합 테스트 코드 작성. -[ ] 결제하기 페이지의 Form과 관련된 통합 테스트 코드 작성. +[x] 상품 상세 페이지와 관련된 통합 테스트 코드 작성. +[x] 결제하기 페이지의 Form과 관련된 통합 테스트 코드 작성. 현금영수증 Checkbox가 false인 경우 현금영수증 종류, 현금영수증 번호 field가 비활성화 되어있는지 확인하는 테스트 코드 작성. (만약 true인 경우 현금영수증 종류, 번호 field에 값이 입력 되어야 함) form의 validation 로직이 정상 동작하는지 확인하는 테스트 코드 작성. # 2단계: 로그인, 관심 상품 등록 / 삭제, 관심 목록 구현 -[ ] 로그인 기능 구현. +[ ] 로그인 기능 구현.ㄴ [ ] 회원가입 화면을 만들고, 회원가입 기능이 동작되게 구현. (회원가입을 하면 로그인이 되도록) 회원가입 버튼은 로그인 화면 하단에 배치하고, 로그인 화면은 그대로 사용 [ ] 상품 상세 페이지에서 관심 등록 버튼 생성 diff --git a/package-lock.json b/package-lock.json index ed0d135c..fbc45892 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "@storybook/react": "^7.6.17", "@storybook/react-webpack5": "^7.6.17", "@storybook/test": "^7.6.17", + "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -9376,6 +9377,79 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@storybook/test/node_modules/@testing-library/dom": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@storybook/test/node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@storybook/test/node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "node_modules/@storybook/test/node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@storybook/test/node_modules/@testing-library/dom/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@storybook/test/node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/@storybook/test/node_modules/@testing-library/jest-dom": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz", @@ -11168,22 +11242,22 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/dom/node_modules/ansi-styles": { @@ -11201,6 +11275,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/@testing-library/dom/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", diff --git a/package.json b/package.json index 95500e27..50116d84 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@storybook/react": "^7.6.17", "@storybook/react-webpack5": "^7.6.17", "@storybook/test": "^7.6.17", + "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/src/components/features/Order/OrderForm/index.test.tsx b/src/components/features/Order/OrderForm/index.test.tsx new file mode 100644 index 00000000..be096ea4 --- /dev/null +++ b/src/components/features/Order/OrderForm/index.test.tsx @@ -0,0 +1,87 @@ +// src\components\features\Order\OrderForm\index.test.tsx + +import '@testing-library/jest-dom'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; + +import { AuthProvider, useAuth } from '@/provider/Auth'; + +import { OrderForm } from './index'; // 결제하기 폼 컴포넌트 경로 + +const setupQueryClient = new QueryClient(); + +const customRender = (ui: React.ReactNode, { route = '/' } = {}) => { + window.history.pushState({}, 'Test page', route); + return render( + + + + + + + + + , + ); +}; + +// Mocking AuthProvider and useAuth +jest.mock('@/provider/Auth', () => ({ + ...jest.requireActual('@/provider/Auth'), + useAuth: jest.fn(), +})); + +describe('OrderForm Component Tests', () => { + beforeEach(() => { + (useAuth as jest.Mock).mockReturnValue({ id: 'testUser', name: 'testUser', token: 'testUser' }); + }); + + test('현금영수증 Checkbox가 false인 경우 현금영수증 종류, 현금영수증 번호 field가 비활성화 되어있는지 확인', async () => { + customRender(); + + const cashReceiptCheckbox = screen.getByLabelText('현금영수증 신청'); + const cashReceiptType = screen.getByLabelText('현금영수증 종류'); + const cashReceiptNumber = screen.getByPlaceholderText('(-없이) 숫자만 입력해주세요.'); + + expect(cashReceiptCheckbox).toBeInTheDocument(); + expect(cashReceiptType).toBeDisabled(); + expect(cashReceiptNumber).toBeDisabled(); + + fireEvent.click(cashReceiptCheckbox); + + await waitFor(() => { + expect(cashReceiptType).toBeEnabled(); + expect(cashReceiptNumber).toBeEnabled(); + }); + + fireEvent.change(cashReceiptNumber, { target: { value: '010' } }); + + await waitFor(() => { + expect(cashReceiptNumber).toHaveValue('010'); + }); + }); + + test('form의 validation 로직이 정상 동작하는지 확인하는 테스트', async () => { + customRender(); + + const submitBtn = screen.getByRole('button', { name: /결제하기/i }); + + fireEvent.click(submitBtn); + + await waitFor(() => { + expect(screen.getByText('메시지를 입력해주세요.')).toBeInTheDocument(); + }); + + const messageField = screen.getByPlaceholderText('선물과 함께 보낼 메시지를 적어보세요'); + + fireEvent.change(messageField, { target: { value: '축하해요' } }); + + fireEvent.click(submitBtn); + + await waitFor(() => { + expect(screen.queryByText('메시지를 입력해주세요.')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/src/pages/Goods/Detail/index.test.tsx b/src/pages/Goods/Detail/index.test.tsx new file mode 100644 index 00000000..9a284fc1 --- /dev/null +++ b/src/pages/Goods/Detail/index.test.tsx @@ -0,0 +1,98 @@ +import '@testing-library/jest-dom'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { render, screen, waitFor } from '@testing-library/react'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; + +import { productsMockHandler } from '@/api/hooks/products.mock'; +import { useGetProductDetail } from '@/api/hooks/useGetProductDetail'; +import { useGetProductOptions } from '@/api/hooks/useGetProductOptions'; +import { AuthProvider } from '@/provider/Auth'; + +import { GoodsDetailPage } from './index'; + +const queryClient = new QueryClient(); +const server = setupServer(...productsMockHandler); + +const renderWithProviders = (ui: React.ReactNode, { route = '/products/3245119' } = {}) => { + window.history.pushState({}, 'Test page', route); + + return render( + + + + + + + + + , + ); +}; + +jest.mock('@/provider/Auth', () => ({ + AuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}, +})); + +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom'); + return { + ...originalModule, + useParams: () => ({ productId: '3245119' }), + }; +}); + +jest.mock('@/api/hooks/useGetProductDetail', () => ({ + useGetProductDetail: jest.fn(), +})); + +jest.mock('@/api/hooks/useGetProductOptions', () => ({ + useGetProductOptions: jest.fn(), +})); + +const mockProductDetail = { + id: 3245119, + name: '[단독각인] 피렌체 1221 에디션 오드코롱 50ml (13종 택1)', + imageUrl: + 'https://st.kakaocdn.net/product/gift/product/20240215083306_8e1db057580145829542463a84971ae3.png', + price: 145000, +}; + +const mockProductOptions = [ + { id: 1, name: 'Option A', quantity: 10, productId: 1 }, + { id: 2, name: 'Option B', quantity: 20, productId: 1 }, +]; + +describe('GoodsDetailPage Tests', () => { + beforeEach(() => { + (useGetProductDetail as jest.Mock).mockReturnValue({ data: mockProductDetail }); + (useGetProductOptions as jest.Mock).mockReturnValue({ data: mockProductOptions }); + }); + + test('상품 상세 페이지 렌더링 및 정보 표시 테스트', async () => { + renderWithProviders(, { route: '/products/3245119' }); + + await waitFor(() => { + expect( + screen.getByText('[단독각인] 피렌체 1221 에디션 오드코롱 50ml (13종 택1)'), + ).toBeInTheDocument(); + expect(screen.getByText('145000원')).toBeInTheDocument(); + }); + }); + + test('상품 상세 페이지 에러 처리 테스트', async () => { + server.use( + rest.get('https://api.example.com/api/products/:productId', (_req, res, ctx) => { + return res(ctx.status(500), ctx.json({ message: 'Internal Server Error' })); + }), + ); + + renderWithProviders(, { route: '/products/error' }); + + await waitFor(() => { + expect(screen.getByText('에러 페이지')).toBeInTheDocument(); + }); + }); +}); From e70f9617bc05ff612c8a5cebfb80dda7122fc3c5 Mon Sep 17 00:00:00 2001 From: 2021048695 Date: Tue, 30 Jul 2024 23:55:48 +0900 Subject: [PATCH 6/6] =?UTF-8?q?docs:=20=EC=A7=88=EB=AC=B8=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EB=8B=B5=EB=B3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++++++++++- src/components/common/Button/index.test.tsx | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14f2a4b4..93f1958a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ form의 validation 로직이 정상 동작하는지 확인하는 테스트 코 # 2단계: 로그인, 관심 상품 등록 / 삭제, 관심 목록 구현 -[ ] 로그인 기능 구현.ㄴ +[ ] 로그인 기능 구현. [ ] 회원가입 화면을 만들고, 회원가입 기능이 동작되게 구현. (회원가입을 하면 로그인이 되도록) 회원가입 버튼은 로그인 화면 하단에 배치하고, 로그인 화면은 그대로 사용 [ ] 상품 상세 페이지에서 관심 등록 버튼 생성 @@ -23,3 +23,13 @@ form의 validation 로직이 정상 동작하는지 확인하는 테스트 코 관심 목록 API는 카카오테크 선물하기 API 노션의 response 데이터 사용 [ ] 관심 목록 리스트에서 관심 삭제가 가능하도록 구현 관심 삭제 시 목록에서 사라지도록 구현 + +- 질문 1. Test code를 작성해보면서 좋았던 점과 아쉬웠던 점에 대해 말해주세요. + 직접 실행해서 하나씩 확인 하는 것보다 체계적으로 코드의 작동을 확인 할 수 있음. 하지만 Fail을 할 경우 테스트 코드의 어느 부분에서 오류가 났는지를 알 수가 없어서 수정에 어려움이 있었음. + +- 질문 2. 스스로 생각했을 때 좋은 컴포넌트란 무엇인지 본인만의 기준을 세우고 설명해 주세요. + 재사용성, 독립성을 가진 컴포턴트가 좋은 컴포넌트라고 생각. 또한 외부와의 인터페이스는 props로 명확하게 정의해야 하고, 불필요한 리렌더링을 최소화하면 좋음. + +- 질문 3. 스스로 생각했을 때 공통 컴포넌트를 만들 때 가장 중요한 요소 2개를 선택하고 이유와 함께 설명해주세요. + 재사용성: 동일한 컴포넌트를 사용함으로써 코드 중복을 줄이고 유지보수를 용이하게 함. + 일관성: 전반적인 스타일과 동작을 통일성 있게 유지하면 UX에 좋은 영향을 줄 수 있다고 생각함. diff --git a/src/components/common/Button/index.test.tsx b/src/components/common/Button/index.test.tsx index bb318101..80b700a6 100644 --- a/src/components/common/Button/index.test.tsx +++ b/src/components/common/Button/index.test.tsx @@ -1,6 +1,6 @@ import { fireEvent, render, screen } from '@testing-library/react'; -import React from 'react'; +// import React from 'react'; import { Button } from './index'; test('버튼 렌더링과 클릭 테스트', () => {