Skip to content

Commit

Permalink
[장바구니 미션 Step 1] 푸만능(황준승) 미션 제출합니다. (#165)
Browse files Browse the repository at this point in the history
* chore: 개발 환경 설정

* docs: 기능 요구사항 작성

* chore: styled-component, ts-jest 환경 설치

* chore: 목데이터 추가

* feat: Global Style 추가

* chore: 타입스크립트 module 선언

* feat: Router, RecoilRoot 적용

* feat: GlobalStyle 적용

* chore: 카트 이미지 추가

* feat: cart, product 타입 선언

* feat: cartState, totalAmountState 선언

* feat: Header 컴포넌트 구현

* feat: array 유틸함수, useCart 커스텀 훅 구현

* test: useCart 훅 테스트

* feat: ProductItem 컴포넌트 구현

* feat: ProductList 컴포넌트 구현

* feat: ProductPage 구현

* refactor: console.log 삭제

* feat: NotFoundPage 구현

* feat: product fetch 구현

* feat: LoadingPage 구현

* feat: ErrorBoundary 구현

* feat: Suspense, Errorboundary 적용

* test: cypress e2e 테스트 작성

* feat: LocalStorage 기능 추가

* fix: cart.svg 파일 네이밍 수정

* chore: 배포 환경 설정

* refactor(cypress): mock 데이터 제거 및 코드 수정

* refactor(cypress): mockdata 타입시스템 적용

* refactor(atoms): 불필요한 selector 제거, sum 유틸함수 생성

* refactor(ProductItem): useCart 로직 및 훅 테스트 변경

---------

Co-authored-by: cruelladevil <dev.timetravel@gmail.com>
  • Loading branch information
turtle601 and cruelladevil authored May 17, 2023
1 parent 0191f7c commit 1b20ee7
Show file tree
Hide file tree
Showing 39 changed files with 23,652 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
40 changes: 40 additions & 0 deletions REQUIREMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## 기능 목록

- Header 구현

- 장바구니에 담긴 상품 전체의 갯수 구현

- 상품 목록 구현

- 상품 목록 레이아웃 구현 (grid)
- mock 데이터를 기반으로 정렬하기

- 상품 아이템 구현

- img 파일 import 하기

- 장바구니 버튼
- 장바구니 버튼을 클릭하면 상품이 1개가 담긴다.
- 장바구니 버튼이 숫자 인풋으로 바뀐다.
- 장바구니에 담을 상품의 갯수를 조절할 수 있다.

## mock 데이터

- Products

```json
[
{
"id": 1,
"name": "치킨",
"price": 10000,
"imageUrl": "<http://example.com/chicken.jpg>"
},
{
"id": 2,
"name": "피자",
"price": 20000,
"imageUrl": "<http://example.com/pizza.jpg>"
}
]
```
16 changes: 16 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from "cypress";

export default defineConfig({
component: {
devServer: {
framework: "create-react-app",
bundler: "webpack",
},
},

e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
29 changes: 29 additions & 0 deletions cypress/e2e/fetchProduct.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
describe('장바구니 e2e 테스트', () => {
beforeEach(() => {
cy.intercept({
method: 'GET',
url: 'https://json-server-4140.onrender.com/cart-itmes',
}).as('getCartList');

cy.visit('http://localhost:3000/react-shopping-cart');
});

it('페에지에 접속하면 서버에 있는 CARTLIST 데이터를 렌더링한다.', () => {
cy.wait('@getCartList').then((interception) => {
const cartList = interception.response?.body;
cy.get('[data-cy=product-item]').should('have.length', cartList.length);
});
});

it('장바구니 버튼을 클릭하면 상품을 장바구니에 1개 추가한다.', () => {
cy.get('[data-cy=add-cart').first().children().click();
cy.get('[data-cy=cart-amount').should('contain', 1);
});

it('장바구니 버튼을 클릭한 후 원하는 상품의 개수 만큼 장바구니에 담는다.', () => {
cy.get('[data-cy=add-cart').first().children().click();
cy.get('[data-cy=add-cart]').first().children().type('0');

cy.get('[data-cy=cart-amount').should('contain', 10);
});
});
14 changes: 14 additions & 0 deletions cypress/fixtures/defaultProducts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Product } from './../../src/types/product';

const defaultProducts: Product = {
id: 1,
name: '정사각(420ml)',
price: 43400,
imageUrl:
'https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82',
};

export const createMockProduct = (overwrites: Partial<Product> = {}) => ({
...defaultProducts,
...overwrites,
});
50 changes: 50 additions & 0 deletions cypress/fixtures/mockProducts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[
{
"id": 1,
"name": "정사각(420ml)",
"price": 43400,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 2,
"name": "밀크티(370ml)",
"price": 73400,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 3,
"name": "정사각(370ml)",
"price": 41000,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 4,
"name": "납작(450ml)",
"price": 39900,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 5,
"name": "단지(480ml)",
"price": 38000,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 6,
"name": "납작(260ml)",
"price": 40200,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 7,
"name": "원형(500ml)",
"price": 50200,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
},
{
"id": 8,
"name": "원형(600ml)",
"price": 62400,
"imageUrl": "https://item.kakaocdn.net/do/d0abc6fe74e616536cf07626699bbc707154249a3890514a43687a85e6b6cc82"
}
]
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
20 changes: 20 additions & 0 deletions cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
13 changes: 13 additions & 0 deletions cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"include": ["**/*.ts", "../cypress.d.ts", "e2e/fetchProduct.cy.tsx"],
"compilerOptions": {
"lib": ["es5", "dom"],
"types": ["cypress", "node"],
"target": "es5",
"jsx": "preserve",
"isolatedModules": false,
"allowJs": true,
"noEmit": true
}
}
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
};
Loading

0 comments on commit 1b20ee7

Please sign in to comment.