Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2단계 - 자주 가는 음식점] 해리(최현웅) 미션 제출합니다. #155

Merged
merged 70 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
6f0d55a
docs(README.md): 스텝2에서 추가된 요구사항 작성
hwinkr Mar 14, 2024
13e87fa
docs(README.md): 스텝2에서 추가된 요구사항 작성
hwinkr Mar 14, 2024
5eb7e3e
chore(delete js file): 사용하지 않는 js 파일 삭제
hwinkr Mar 15, 2024
ec3f055
feat(Restauran): 추가된 도메인 요구사항 구현
hwinkr Mar 15, 2024
72a62ee
refactor(BaseComponent): TS로 마이그레이션
hwinkr Mar 15, 2024
c09a809
refactor(MenuApp): UI 로직 TS로 마이그레이션
hwinkr Mar 15, 2024
db3dea7
feat(Restaurants): 음식점 도메인 기능 구현 완료
hwinkr Mar 17, 2024
46653ca
feat(style.css): 스타일 추가
hwinkr Mar 17, 2024
41f1b08
feat(RestaurantTab): 모든 음식점, 자주 가는 음식점 구분 컴포넌트 구현
hwinkr Mar 17, 2024
a3b60ad
feat(CategoryImage): 카테고리 이미지 컴포넌트 구현
hwinkr Mar 17, 2024
6d9f200
feat(RestaurantDetail): 음식점 상세 정보 컴포넌트 구현
hwinkr Mar 17, 2024
ecadfc6
docs(README.md): 추가된 기능 요구 사항 구현 완료
hwinkr Mar 17, 2024
b7329e5
chore(assets): 자주 가는 음식점 별 아이콘 이미지 추가
hwinkr Mar 17, 2024
188b413
chore(delete unused files): 폴더 구조 변경으로 인한 사용하지 않는 파일 삭제
hwinkr Mar 17, 2024
f9f8200
refactor(BaseComponent): 점심 뭐 먹지 애플리케이션에서 발생하는 이벤트 타입 네이밍 수정
hwinkr Mar 17, 2024
77b8110
refactor(MenuApp): html 구조 수정
hwinkr Mar 17, 2024
12ebc28
feat(FavoriteIcon): 자주 가는 식당을 추가할 수 있는 별 모양 아이콘 컴포넌트 추가 분리
hwinkr Mar 17, 2024
2d2b05e
feat(Modal): 재사용할 수 있는 모달 컴포넌트 구현
hwinkr Mar 17, 2024
5e184f6
refactor(RestaurantAddForm): 식당 추가 폼 UI TS로 마이그레이션
hwinkr Mar 17, 2024
dbd4317
chore(change directory): 폴더 구조 변경
hwinkr Mar 17, 2024
794955c
refactor(OptionSelector): 식당 목록 필터링, 정렬 컴포넌트 로직 수정
hwinkr Mar 17, 2024
4774765
chore(event constants): 점심 뭐 먹지에서 발생하는 커스텀 이벤트 상수화
hwinkr Mar 17, 2024
6651777
chore(menu constants): 카테고리, 정렬 기준, 탭 메뉴 문자 상수화
hwinkr Mar 17, 2024
d0caaf6
refactor(index.js): 변경된 폴더 구조 반영
hwinkr Mar 17, 2024
5435219
feat(type checker): 타입을 확인하는 유틸 함수 구현
hwinkr Mar 17, 2024
dafd4c6
feat(dom utils): DOM 노드를 찾는 유틸 함수 구현
hwinkr Mar 17, 2024
75ea25d
refactor(OptionSelector): 필터링, 정렬 값의 타입 확인하는 함수 적용
hwinkr Mar 17, 2024
e37988e
feat(Header): 점심 뭐 먹지 헤더 컴포넌트 TS 마이그레이션
hwinkr Mar 17, 2024
a1e1a89
config(types): 점심 뭐 먹지 이벤트, 상수 타입 설정
hwinkr Mar 17, 2024
003fa7c
chore(index.js): 파일명 오타 수정
hwinkr Mar 17, 2024
c0158c9
config(cypress): cypress 테스트 환경 설정
hwinkr Mar 17, 2024
0279431
test(RestaurantAddForm): 음식점 추가 폼 테스트
hwinkr Mar 17, 2024
8ecc48e
test(RestaurantList): 음식점 목록 테스트
hwinkr Mar 17, 2024
e52abc1
test(RestaurantDetail): 음식점 상세 정보 테스트
hwinkr Mar 17, 2024
842dc67
refactor(OptionSelector): 필터링, 정렬 옵션 컴포넌트 리팩터링
hwinkr Mar 17, 2024
f634e1c
chore(RestaurantAddForm): 에러 객체의 메시지 프로퍼티를 alert 함수로 전달하도록 수정
hwinkr Mar 17, 2024
05cbb0a
chore(RestaurantDetail): 상세 정보 컴포넌트 내부 버튼 type 제거
hwinkr Mar 17, 2024
6660602
chore(constants): 음식점 디폴트 데이터 순서 수정
hwinkr Mar 17, 2024
2660380
chore(type): 사용하지 않는 의존성 제거
hwinkr Mar 17, 2024
a695f12
chore(delete unused file): 사용하지 않는 파일 삭제
hwinkr Mar 17, 2024
b7852b8
chore(change file name): 컴포넌트 이름 변경으로 인한 파일 이름 변경 반영
hwinkr Mar 17, 2024
bcb84ad
refactor(RestaurantValidator): 음식점 추가 폼 입력값에 대한 유효성 검증 TS로 마이그레이션
hwinkr Mar 17, 2024
af74fab
refactor(Restaurants): 카테고리, 도보 이동 거리 유효성 검증 로직 수정
hwinkr Mar 17, 2024
b14dedd
refactor(RestaurantAddForm): 음식점 추가 폼 컴포넌트 리팩터링
hwinkr Mar 17, 2024
b4ca733
refactor(ResturantOption): 음식점 옵션 선택 컴포넌트 커스텀 이벤트 이름 수정
hwinkr Mar 17, 2024
eaf0992
feat(RestaurantTextInput): 재사용 가능한 입력 컴포넌트 구현
hwinkr Mar 17, 2024
d73df87
test(RestaurantAddTest): 변경된 id 이름 반영
hwinkr Mar 17, 2024
309dcca
feat(text utils): 문자열 사이의 모든 공백을 제거하는 유틸 함수 분리
hwinkr Mar 18, 2024
fb86be6
chore(change method name): 모달을 여는 이벤트인지 확인하는 메서드 네이밍 수정
hwinkr Mar 18, 2024
c73d982
chore(RestaurantAddForm): restaurant-option 컴포넌트에 전달하는 어트리뷰트 순서 통일
hwinkr Mar 18, 2024
cd60e5f
chore(add dependency): 문자열 사이 모든 공백을 제거하는 함수 의존성 추가
hwinkr Mar 18, 2024
97ceeea
chore(change file name): 파일이름 오타 수정
hwinkr Mar 19, 2024
e58c478
change(change directory): OptionSelectorContainer 파일 경로 수정
hwinkr Mar 19, 2024
7501413
refactor(dom utils): type asseriton 제거
hwinkr Mar 19, 2024
3fb961a
refactor(type utils): 여러 상황에서 범용적으로 사용할 수 있도록 함수명 수정
hwinkr Mar 19, 2024
c3612a2
test(RestaurantDetail): 음식점 상세 모달 테스트 수정
hwinkr Mar 19, 2024
2f2cf84
test(RestaurantList): 음식점 목록 테스트 수정
hwinkr Mar 19, 2024
323add7
test(RestaurantAddTest): 유효하지 않는 상황을 주석으로 설명 추가
hwinkr Mar 19, 2024
8ec47cf
refactor(CategoryImage): 추상화 된 타입 가드 함수를 사용하는 것으로 수정
hwinkr Mar 19, 2024
17691a0
refactor(MenuApp): 모달 컴포넌트에서 사용하지 않는 어트리뷰트 삭제
hwinkr Mar 19, 2024
42d0917
refactor(Modal): 모달 컴포넌트 내부에서 id 어트리뷰트를 사용해서 이벤트를 결정하는 것으로 수정
hwinkr Mar 19, 2024
9e4b00f
refactor(OptionSelector): 추상화된 타입 가드 함수를 사용하는 것으로 수정
hwinkr Mar 19, 2024
1cb1d02
refactor(RestaurantAddForm): 객체 타입 가드 함수 이름 변경 반영
hwinkr Mar 19, 2024
4d0c2f3
refactor(RestaurantDetail): 객체 타입 가드 함수 이름 변경 반영
hwinkr Mar 19, 2024
4599c6c
chore(RestaurantList): 라인 구분 수정
hwinkr Mar 19, 2024
c701028
config: img 파일 타입 설정
hwinkr Mar 19, 2024
c5ade21
chore(change file): 이미지 파일을 관리하는 asset 파일 TS로 확장자 변경
hwinkr Mar 19, 2024
2bebec1
refactor(FavoriteIcon): isFavorite(boolean) 상태 값 결정 로직 삼항 연산자에서 비교 연산…
hwinkr Mar 19, 2024
191f542
chore(Modal): 사용하지 않는 메서드 제거
hwinkr Mar 19, 2024
c818b2d
chore(RestaurantDetail): 참고 링크가 존재하지 않을 경우 보여주는 텍스트 색 수정
hwinkr Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 93 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,114 @@

# 기능 요구사항

## 1. UI
# Domain

### header
- [x] 음식점을 카테고리별로 필터링 한다.
- [x] 음식점을 이름순, 거리순으로 정렬한다.
- [x] 음식점 등록 폼을 제출하면 추가한다.
- [x] 추가하기 버튼을 클릭할 경우 입력에 대한 유효성 검증을 한다
- 음식점 이름은 10글자 이하여야한다.
- 음식점 설명은 300자 이하여야한다.
- 유효한 참고 링크인지 검증한다.
- [x] 음식점 이름으로 식당의 정보를 찾을 수 있도록 한다.
- [x] 유효한 입력일 경우 localStorage에 추가한다.
- [x] 자주 가는 음식점인지 아닌지에 대해 상태를 변경(토글)한다.
- [x] 자주 가는 음식점 목록을 필터링한다.
- [x] 음식점 목록에 있는 음식점을 삭제한다.

```ts
addFavoriteRestaurant(restaurantName ; string) : boolean
getFavoriteRestaurant() : Restaurant[]
delete(restaurantName : string) : boolean
```

# UI

- [x] 점심 뭐 먹지 타이틀
- [x] 음식점 추가 버튼
## header

#### event
- UI
- [x] 점심 뭐 먹지 타이틀
- [x] 음식점 추가 버튼
- Event
- [x] 음식점 추가 버튼을 클릭하면 음식점 추가 폼 모달이 보여져야한다.

- [x] 음식점 추가 버튼을 클릭하면 음식점 추가 폼 모달이 보여져야한다.
## option-selector

### option-selector
- UI

- [x] 카테고리를 필터링할 수 있는 옵션 버튼
- [x] 이름, 거리순을 정렬할 수 있는 옵션 버튼

#### event
- Event

- [x] 카테코리를 선택하면 선택된 카테고리에 해당되는 레스토랑 리스트를 보여준다
- [x] 이름, 거리순을 정렬하는 버튼을 클릭하면 정렬된 레스토랑 리스트를 보여준다.

### restaurant-list
## restaurant-list

- UI

- [x] restaurant-item

- 음식점 목록을 확인할 수 있다.
- 카테고리
- 레스토랑 이름
- (캠퍼스로 부터의) 거리
- 설명, 설명은 2줄 이상이 넘어가면 ...으로 내용을 숨긴다

- Event

- [x] 카테고리별 필터링, 정렬 옵션이 변경되는 이벤트가 발생하면 그에 맞춰 보여줘야 할 음식점 목록을 변경한다.
- [x] 모든 음식점, 자주 가는 음식점을 클릭할 경우 그에 맞춰 보여줘야 할 음식점 목록을 변경한다.

## favorite-icon

- UI

- [x] 별 모양 아이콘

- Event

- [x] 아이콘을 클릭할 때마다, 아이콘 이미지가 토글되어야 한다. (filled <-> lined)
- [x] 아이콘을 클릭할 때마다, 식당 아이템의 isFavorite 상태가 토글되어야 한다. (true <-> false)
- [x] 아이콘을 클릭할 때마다, 식당 목록 컴포넌트가 렌더링 되어 변경된 상태를 보여줄 수 있어야한다.

## category-image

- [x] 선택된 옵션에 따라 레스토랑 리스트를 보여준다
- UI

- 음식점 목록을 확인할 수 있다.
- 카테고리
- 레스토랑 이름
- (캠퍼스로 부터의) 거리
- 설명
- 설명은 2줄 이상이 넘어가면 ...으로 내용을 숨긴다
- [x] 전체, 한식, 중식,,, 등 카테고리에 따라 다른 이미지를 보여준다.

### restaurant-add-form
## restaurant-detail

- UI
- 음식점 목록 중 하나를 클릭하면 해당 음식점에 대한 세부 정보를 나타낸다.

- [x] 음식점 이름, 카테고리, 캠퍼스로부터의 거리, 자주 가는 음식점인지의 여부
- [x] 음식점 상세 설명, ...으로 표시하지 않고 모든 내용을 보여준다,
- [x] 참고링크

- Event

- [x] 음식점 세부 정보의 바깥 영역을 클릭하거나, 닫기 버튼을 클릭하면 `restaurant-detail` 컴포넌트는 사라져야한다.
- [x] 음식점 세부 정보에서 자주 가는 음식점인지의 여부를 변경하면, 변경이 반영되어야한다.
- [x] 참고 링크를 클릭하면 해당 링크로 이동시킨다.
- [x] 삭제하기 버튼을 클릭할 경우, 해당 음식점을 삭제한 후 `restaurant-detail` 컴포넌트는 사라져야한다.

## restaurant-tab

- UI

- [x] 식당 목록 선택 옵션 (모든 음식점, 자주 가는 음식점)
- [x] 클릭한 버튼에 액티브 스타일이 적용되어야 하며, 이전에 클릭한 버튼의 액티브 스타일은 사라져야한다.

- Event

- [x] 특정 버튼을 클릭할 때마다, 보여줘야 할 음식점 목록의 변경을 트리거한다.

## restaurant-add-form

- UI

- [x] 새로운 음식점 타이틀
- [x] 카테코리 선택 옵션 (한식, 중식, 일식, 양식, 아시안, 기타)
Expand All @@ -46,19 +121,8 @@
- [x] 취소, 추가 버튼
- [ ] 유효하지 않은 입력값이 들어온 경우 해당 입력값 아래에 빨간색 에러 메시지를 보여준다.

#### event
- Event

- [x] 취소 버튼을 클릭하면 추가 폼을 리셋하고 닫는다.
- [x] 추가하기 버튼을 클릭하면 추가 폼을 닫고, 추가된 레스토랑을 레스토랑 리스트에 반영한다.
- [x] 추가 폼 외부를 클릭 시 폼을 닫는다. 외부를 클릭할 경우 입력한 값들을 리셋하지 않고 닫기만 한다.

## 2. Domain

- [x] 음식점을 카테고리별로 필터링 한다.
- [x] 음식점을 이름순, 거리순으로 정렬한다.
- [x] 음식점 등록 폼을 제출하면 추가한다.
- [ ] 추가하기 버튼을 클릭할 경우 입력에 대한 유효성 검증을 한다
- 음식점 이름은 10글자 이하여야한다.
- 음식점 설명은 300자 이하여야한다.
- 유효한 참고 링크인지 검증한다.
- [ ] 유효한 입력일 경우 localStorage에 추가한다.
4 changes: 1 addition & 3 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: "http://localhost:8080",
},
});
36 changes: 36 additions & 0 deletions cypress/e2e/RestaurantDetail.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
describe("음식점 상세 정보 테스트", () => {
beforeEach(() => {
cy.visit("/");
cy.get(".restaurant-list-container").children().first().click();
cy.get("#restaurant-detail-wrapper").should("have.class", "modal--open");
});

it("음식점 목록 중 하나의 음식점을 클릭하면, 해당 음식점 상세 정보를 보여주는 모달이 렌더링된다.", () => {
cy.get("div.restaurant-detail").should("contain", "도스타코스 선릉점");
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음식점 목록의 첫번째 음식점을 클릭하면, 이라는 표현은 어떨까요? beforeEach의 처음에 first()를 놓치면 전체적으로 맥락을 이해하기 어려울 것 같아요.

그리고 역시 첫 번째 요소가 "도스타코스 선릉점(맛있겠다..)"임을 알아야 이해되는 테스트로 보여요. 같이 개선해보면 좋을 것 같아요 :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 방법으로 테스트 코드를 작성하면 디폴트 데이터가 바뀔 때마다 테스트 코드도 함께 수정해줘야겠군요...변경에 취약한 테스트 코드를 작성한 것 같아요.

  • 도스타코스 선릉점인걸 몰라도
  • 디폴트 데이터가 변경되어도

테스트 코드가 통과되는데 아무 문제가 없도록 클래스 이름을 확인하는 것으로 테스트 케이스를 수정해봤습니다!


it("음식점 상세 정보를 보여주는 모달에서 닫기 버튼을 클릭하면 모달이 사라진다.", () => {
cy.get("#detail-close-button").click();

cy.get("#restaurant-detail-wrapper").should("not.have.class", "modal--open");
});

it("음식점 상세 정보를 보여주는 모달에서 모달 영역 외부를 클릭하면 모달이 사라진다.", () => {
cy.get("#restaurant-detail-backdrop").click("top", {
force: true,
});

cy.get("#restaurant-detail-wrapper").should("not.have.class", "modal--open");
});

it("음식점 상세 정보를 보여주는 모달에서 삭제하기 버튼을 클릭하면 해당 음식점은 사라져야한다.", () => {
const EXPECTED_RESTAURANT_LENGTH = 5;

cy.get("#delete-button").click();

cy.get(".restaurant-list-container").children().should("have.length", EXPECTED_RESTAURANT_LENGTH);
cy.get("#restaurant-detail-wrapper").should("not.have.class", "modal--open");

cy.get(".restaurant-list-container").children().first().should("contain", "이태리키친");
});
});
104 changes: 104 additions & 0 deletions cypress/e2e/RestaurantList.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const FILTERING_TEST_DATA = [
{
category: "한식",
expectedRestaurantName: "피양콩 할마니",
},
{
category: "아시안",
expectedRestaurantName: "호아빈 삼성점",
},
{
category: "일식",
expectedRestaurantName: "잇쇼우",
},
{
category: "중식",
expectedRestaurantName: "친친",
},

{
category: "양식",
expectedRestaurantName: "이태리키친",
},
{
category: "기타",
expectedRestaurantName: "도스타코스 선릉점",
},
];

describe("음식점 목록 테스트", () => {
beforeEach(() => {
cy.visit("/");
});

it("사용자가 처음 방문할 경우 기본 제공 음식점 목록이 렌더링된다", () => {
const EXPECTED_RESTAURANT_LENGTH = 6;
cy.get(".restaurant-list-container").children().should("have.length", EXPECTED_RESTAURANT_LENGTH);
});
Comment on lines +34 to +37
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

첫 방문 시 이 순서로 노출되는 것이 맞을까요~?
아니라면 이 경우에 대한 테스트를 추가하고, 코드도 보완되면 좋겠습니다.

Screenshot 2024-03-19 at 12 10 30 AM

Copy link
Author

@hwinkr hwinkr Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피그마에는 저 순서로 구성되어 있지만, 처음 점심 뭐 먹지 페이지에 방문했을 때 카테고리에 따라서 필터링하는 옵션은 전체, 정렬 옵션은 이름순으로 되어있어 피그마의 순서와 다르긴 하지만 디폴트 데이터 6개를 이름순으로 정렬된 상태로 보여주는 것으로 수정했었습니다. 처음 방문할 경우 이름순으로 정렬되어있음을 테스트 하는 것을 놓쳤네요..! 이 경우도 테스트를 해봐야겠습니다


it("사용자가 선택한 카테고리에 맞는 음식점 목록이 보여져야 한다.", () => {
FILTERING_TEST_DATA.forEach(({ category, expectedRestaurantName }) => {
const FILTERED_RESTAURANT_LIST = 1;

cy.get("select#category-option-select").select(category);

cy.get(".restaurant-list-container").children().should("have.length", FILTERED_RESTAURANT_LIST);
cy.get(".restaurant-list-container").children().last().should("have.attr", "name", expectedRestaurantName);
});

const EXPECTED_RESTAURANT_LENGTH = 6;
cy.get("select#category-option-select").select("전체");
cy.get(".restaurant-list-container").children().should("have.length", EXPECTED_RESTAURANT_LENGTH);
});

it("사용자가 이름순 정렬 옵션을 선택할 경우, 이름순으로 목록이 정렬된다", () => {
const EXPECTED_NAME_SEQUENCE = ["도스타코스 선릉점", "친친", "잇쇼우", "피양콩 할마니", "호아빈 삼성점", "이태리키친"];

cy.get("select#sort-option-select").select("거리순");

cy.get(".restaurant-list-container")
.children()
.each((element, index) => {
cy.wrap(element.attr("name")).should("equal", EXPECTED_NAME_SEQUENCE[index]);
});
});

it("사용자가 거리순 정렬 옵션을 선택할 경우, 거리순으로 음식점 목록이 정렬된다", () => {
const EXPECTED_NAME_SEQUENCE = ["도스타코스 선릉점", "이태리키친", "잇쇼우", "친친", "피양콩 할마니", "호아빈 삼성점"];

cy.get("select#sort-option-select").select("이름순");

cy.get(".restaurant-list-container")
.children()
.each((element, index) => {
cy.wrap(element.attr("name")).should("equal", EXPECTED_NAME_SEQUENCE[index]);
});
});

it("사용자가 별 아이콘을 클릭하면, 자주 가는 음식점 탭에서 확인할 수 있다.", () => {
const EXPECTED_FAVORITE_RESTAURANT_LENGTH = 2;

cy.get("favorite-icon").first().click();
cy.get("favorite-icon").last().click();

cy.get("restaurant-tab[text='자주 가는 음식점']").click();

cy.get(".restaurant-list-container").children().should("have.length", EXPECTED_FAVORITE_RESTAURANT_LENGTH);
});

it("자주 가는 음식점 탭에서 별 아이콘을 클릭하면, 모든 음식점에서 탭에서만 확인할 수 있다.", () => {
const EMPTY_FAVORITE_RESTAURANT_LENGTH = 0;

cy.get("favorite-icon").first().click();
cy.get("favorite-icon").last().click();

cy.get("restaurant-tab[text='자주 가는 음식점']").click();

cy.get("favorite-icon").first().click();
cy.get("favorite-icon").last().click();

cy.get("restaurant-tab[text='자주 가는 음식점']").click();

cy.get(".restaurant-list-container").children().should("have.length", EMPTY_FAVORITE_RESTAURANT_LENGTH);
});
});
Loading