-
Notifications
You must be signed in to change notification settings - Fork 155
[2단계 - 상세 정보 & UI/UX 개선하기] 지오 미션 제출합니다. #298
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
base: juhyeong424
Are you sure you want to change the base?
Changes from all commits
8d8858d
69b0178
9a45d8f
d917b40
7a3c081
00009dc
bf933f3
5f0eaa0
682e829
31a17df
c48a176
7f658aa
ce7db89
eaed4fe
6b1be4b
c3526f8
17d923c
f12c5b2
36e5796
92e447d
9d05973
b442d56
7316134
a8edbdd
20d1c6c
5b2562d
0a79a40
b32c9f1
97edac8
4e30d5d
6fc68a1
427ac7e
4388c00
583979b
6372874
cf60130
005948a
f0731f6
bb0af88
ed2f501
b354c9f
34547ea
4981cfb
2248b05
1c3ef4e
71a596f
eb3c691
dd5ed03
d0fd565
6a37118
78f3283
3185d90
d1ce73a
0fde7dc
58ff9de
0204c07
59c2981
f81da0a
0908fcf
bb87b4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ on: | |
| branches: | ||
| - main | ||
| - step1 | ||
| - step2 | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,24 +7,53 @@ | |
| - [x] 영화 목록 api를 호출한다. | ||
| - [x] 지금 인기 있는 영화 목록 20개를 보여준다. | ||
| - [x] 제일 먼저 있는 영화를 배너로 띄운다. | ||
| - [x] 더보기 버튼을 누르면 20개의 영화 목록을 추가로 불러온다. | ||
| - [x] 브라우저의 화면 끝에 도달하면 20개의 검색과 관련된 영화 목록을 추가로 불러온다. | ||
| - [x] 불러올 영화 목록이 없으면 더보기 버튼이 사라진다. | ||
|
Comment on lines
+10
to
11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 무한 스크롤 설명과 ‘더보기 버튼’ 서술이 서로 충돌합니다. 체크리스트/시나리오에서 무한 스크롤을 설명하면서 동시에 더보기 버튼 소거를 검증하고 있어, 현재 동작 기준이 문서상 모호합니다. 한 기준으로 통일해 주세요. Also applies to: 20-21, 135-140 🤖 Prompt for AI Agents |
||
| - [x] 모든 레이아웃을 반응형으로 만든다. | ||
|
|
||
| ## 검색 시 UI | ||
|
|
||
| - [x] 검색어를 입력하고 버튼을 마우스로 누르거나 엔터키를 누른다. | ||
| - [x] 검색한 영화 목록 api를 호출한다. | ||
| - [x] 해당 검색어에 대한 영화 목록 20개를 보여준다. | ||
| - [x] 검색어를 검색 제목에 띄운다. | ||
| - [x] 더보기 버튼을 누르면 20개의 검색과 관련된 영화 목록을 추가로 불러온다. | ||
| - [x] 브라우저의 화면 끝에 도달하면 20개의 검색과 관련된 영화 목록을 추가로 불러온다. | ||
| - [x] 불러올 영화 목록이 없으면 더보기 버튼이 사라진다. | ||
| - [x] 검색 결과가 없으면 `검색 결과가 없습니다.` 라는 문구를 띄운다. | ||
| - [x] 모든 레이아웃을 반응형으로 만든다. | ||
|
|
||
| ## 영화 상세 정보 조회 | ||
| - [x] 영화를 누르면 해당 영화에 대한 상세 정보가 모달로 나타난다. | ||
| - [x] 모달에 있는 내용은 영화 썸네일, 제목, 개봉일, 장르, 평균 평점, 별점 매기기, 줄거리이다. | ||
| - [x] 닫기 버튼을 누르거나 ESC를 누르면 모달 창을 닫을 수 있다. | ||
| - [x] 상세 정보 데이터를 불러올 동안, 로딩 스피너가 돌아간다. | ||
| - [x] 모든 레이아웃을 반응형으로 만든다. | ||
|
|
||
| ## 별점 매기기 | ||
| - [x] 영화에 대한 별점을 줄 수 있다. | ||
| - [x] localstorage에 내가 매긴 별점이 저장된다. | ||
| - [x] 새로고침이나 모달 창을 닫아도 내가 매긴 별점이 사라지지 않는다. | ||
| - [x] 별점은 총 5개로 구성된다. | ||
| - [x] 한 개당 2점이며 1점 단위는 고려하지 않는다. | ||
| - [x] 내가 선택한 별점에 따라 점수가 변한다. | ||
| - [x] 점수에 따라 나타나는 문구가 달라진다. | ||
| - [x] 2점: 최악이예요 | ||
| - [x] 4점: 별로예요 | ||
| - [x] 6점: 보통이에요 | ||
| - [x] 8점: 재미있어요 | ||
| - [x] 10점: 명작이에요 | ||
|
|
||
| ## 예외 처리 | ||
|
|
||
| - [x] 기본 UI에서 영화 정보를 불러오지 못했다면 `영화 정보를 불러오지 못했습니다. 다시 시도해주세요.` 라는 문구를 띄운다. | ||
| - [x] 영화 이미지를 불러오지 못했다면 `No Image` 이미지를 띄운다. | ||
| - [x] 검색 시, 영화 정보를 불러오지 못했다면 `영화 검색 결과를 불러오지 못했습니다. 다시 시도해주세요.` 라는 문구를 띄운다. | ||
| - [x] 모달 창에 해당하는 영화 상세 정보를 불러오지 못했다면 모달 창 내부에 `영화 상세 정보를 불러오지 못했습니다. 다시 시도해주세요.` 라는 문구를 띄운다. | ||
| - [x] 영화 상세 보기 에러 시, 다시 시도하기 버튼을 누르면 해당 영화의 상세 정보 데이터를 다시 불러온다. | ||
| - [x] 인기있는 영화 리스트를 불러오지 못했을 시, 다시 시도하기 버튼을 생성한다. | ||
| - [x] 검색 영화 리스트를 불러오지 못했을 시, 다시 시도하기 버튼을 생성한다. | ||
| - [x] 배너 정보를 불러오지 못했을 시, 다시 시도하기 버튼을 생성한다. | ||
| - [x] 다시 시도하기 버튼을 누르면 불러오지 못한 데이터를 다시 불러온다. | ||
|
|
||
| ## 공통 | ||
|
|
||
|
|
@@ -61,18 +90,29 @@ | |
| | language | string | 언어 | | ||
| | page | number | 페이지 번호 | | ||
|
|
||
| ### Details | ||
|
|
||
| | 항목 | 내용 | | ||
| | -------- | ----------------------------------------------------------------- | | ||
| | Method | GET | | ||
| | Endpoint | /3/movie/movie_id | | ||
| | 설명 | 사용자가 클릭한 영화에 대한 상세 정보를 가져오는 API입니다. | | ||
|
Comment on lines
+98
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Details endpoint의 path parameter 표기를 명확히 해주세요.
🤖 Prompt for AI Agents |
||
|
|
||
| | Query Parameter | 타입 | 설명 | | ||
| | --------------- | ------ | ------------- | | ||
| | language | string | 언어 | | ||
|
|
||
| ### E2E 테스트 시나리오 | ||
|
|
||
| 1. 첫 진입 시점 | ||
|
|
||
| - api 호출 | ||
| - 베너 | ||
| - 리스트 (스켈레톤) | ||
| - 더보기 버튼 | ||
|
|
||
| 2. 더보기 클릭 | ||
| 2. 스크롤 아래로 내림 | ||
|
|
||
| - 더보기를 클릭했을 때 | ||
| - 스크롤을 아래로 내렸을 때 | ||
| - api 호출 | ||
| - 리스트 (스켈레톤) | ||
|
|
||
|
|
@@ -92,9 +132,21 @@ | |
| - 제목 키워드 포함 | ||
| - 비어있을 때 문구 출력 | ||
|
|
||
| 5. 검색(더보기 없어지기) | ||
| 5. 무한 스크롤 작동 안함 | ||
|
|
||
| - 키워드 입력 | ||
| - 검색버튼 클릭했을 때 | ||
| - 검색어로 api 호출 | ||
| - 마지막 데이터에서 더보기 버튼이 사라지는지 확인 | ||
|
|
||
| 6. 모달 테스트 | ||
|
|
||
| - 특정 영화를 클릭했을 때 해당 영화에 관한 상세 정보 모달이 열리는지 확인 | ||
| - 해당 모달 안에 필요한 정보가 들어있는지 확인 | ||
| - 나의 별점 이미지를 클릭하면 해당되는 평가 문구와 별점이 화면에 나오는지 확인 | ||
| - 나의 별점이 localStorage에 저장되는지 확인 | ||
| - 이미 평점이 localStorage에 있을 때, 해당 영화 모달을 열면 저장된 내용이 화면에 반영되는지 확인 | ||
|
|
||
| 7. 예외 테스트 | ||
|
|
||
| - api 데이터를 불러오지 못했을 때, 다시 시도하기 버튼을 눌러 api를 재호출하는지 테스트 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| describe("영화 모달 테스트", () => { | ||
| beforeEach(() => { | ||
| cy.mockPopularMovies(1); | ||
| cy.getMovieDetail(83533); | ||
| cy.visit("/"); | ||
| }); | ||
|
|
||
| it("인기 있는 영화 목록이 호출이 되는지 테스트한다.", () => { | ||
| cy.wait("@getPopularMoviesPage1").its("response.body.results").should("be.an", "array"); | ||
| }); | ||
|
|
||
| it("영화 id가 83533인 영화를 클릭했을 때, 해당 영화의 상세 정보를 불러오는지 테스트한다.", () => { | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.wait("@getMovieDetail83533"); | ||
| cy.get(".modal").should("exist"); | ||
| }); | ||
|
|
||
| it("모달 안에 제목, 별점, 내용이 있는지 확인하는 테스트", () => { | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.get(".modal").should("exist"); | ||
| cy.wait("@getMovieDetail83533") | ||
| .its("response.body") | ||
| .then((movieDetail) => { | ||
| cy.get(".modal-description-title").should("contain", movieDetail.title); | ||
| cy.get("#modal-description-year").should("contain", movieDetail.release_date.split("-")[0]); | ||
| cy.get("#modal-description-genre").should("contain", movieDetail.genres.map((item: {id: Number, name: string}) => item.name).join(", ")); | ||
| cy.get("#modal-rate-number").should("contain", movieDetail.vote_average.toFixed(1)); | ||
| cy.get("#modal-detail-description").should("contain", movieDetail.overview); | ||
| }); | ||
| }); | ||
|
|
||
| it ("모달 창의 닫기 버튼을 눌렀을 때 닫히는지 테스트", () => { | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.get(".modal").should("exist"); | ||
| cy.wait("@getMovieDetail83533") | ||
| cy.get(".close-modal").click(); | ||
| cy.get(".modal-background").should("not.be.visible"); | ||
| }); | ||
|
|
||
| it ("모달 창의 ESC를 눌렀을 때 닫히는지 테스트", () => { | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.get(".modal").should("exist"); | ||
| cy.wait("@getMovieDetail83533") | ||
| cy.get("body").type('{esc}'); | ||
| cy.get(".modal-background").should("not.be.visible"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("나의 평점 테스트", () => { | ||
| beforeEach(() => { | ||
| cy.mockPopularMovies(1); | ||
| cy.getMovieDetail(83533); | ||
| cy.visit("/"); | ||
| }); | ||
|
|
||
| it("별 이미지를 클릭한만큼 나의 평점 설정 가능 테스트", () => { | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.wait("@getMovieDetail83533"); | ||
| cy.get(".modal").should("exist"); | ||
| const evaluation: Record<number, string> = { | ||
| 0: "나의 별점을 눌러보세요.", | ||
| 1: "최악이예요", | ||
| 2: "별로예요", | ||
| 3: "보통이에요", | ||
| 4: "재미있어요", | ||
| 5: "명작이에요", | ||
| }; | ||
| for (let i = 1; i <= 5; i++) { | ||
| cy.get(`#my-star-image-${i}`).click(); | ||
| cy.get("#my-star-evaluation").should("contain", evaluation[i]) | ||
| cy.get("#my-star-score").should("contain", `${i * 2} / 10`); | ||
|
|
||
| for (let j = 1; j <= 5; j++) { | ||
| if (j <= i) { | ||
| cy.get(`#my-star-image-${j}`) | ||
| .should("have.attr", "src") | ||
| .and("include", "star_filled.png") | ||
| } else { | ||
| cy.get(`#my-star-image-${j}`) | ||
| .should("have.attr", "src") | ||
| .and("include", "star_empty.png") | ||
| } | ||
| }; | ||
| }; | ||
| }); | ||
|
|
||
| it("별 이미지를 클릭한만큼 나의 평점이 localstorage에 저장되는지 테스트", () => { | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.wait("@getMovieDetail83533"); | ||
| cy.get(".modal").should("exist"); | ||
| const evaluation: Record<number, string> = { | ||
| 0: "나의 별점을 눌러보세요.", | ||
| 1: "최악이예요", | ||
| 2: "별로예요", | ||
| 3: "보통이에요", | ||
| 4: "재미있어요", | ||
| 5: "명작이에요", | ||
| }; | ||
| for (let i = 1; i <= 5; i++) { | ||
| cy.get(`#my-star-image-${i}`).click(); | ||
| cy.get("#my-star-evaluation").should("contain", evaluation[i]) | ||
| cy.get("#my-star-score").should("contain", `${i * 2} / 10`); | ||
| cy.window().its("localStorage").invoke("getItem", 83533).should("eq", `${i * 2}`); | ||
|
|
||
| for (let j = 1; j <= 5; j++) { | ||
| if (j <= i) { | ||
| cy.get(`#my-star-image-${j}`) | ||
| .should("have.attr", "src") | ||
| .and("include", "star_filled.png") | ||
| } else { | ||
| cy.get(`#my-star-image-${j}`) | ||
| .should("have.attr", "src") | ||
| .and("include", "star_empty.png") | ||
| } | ||
| }; | ||
| }; | ||
| }); | ||
|
|
||
| it("local storage에 이미 평점이 있다면, 모달을 열었을 때 해당 평점이 화면에 반영되는 테스트", () => { | ||
| cy.visit("/", { | ||
| onBeforeLoad(win) { | ||
| win.localStorage.setItem("83533", "10"); | ||
| }, | ||
| }); | ||
|
|
||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.wait("@getMovieDetail83533"); | ||
|
|
||
| cy.get("#my-star-evaluation").should("contain", "명작이에요"); | ||
| cy.get("#my-star-score").should("contain", "10 / 10"); | ||
| cy.get(`#my-star-image-5`).should("have.attr", "src").and("include", "star_filled.png"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("데이터 로딩 시 에러 화면을 띄우고, 다시 시도하기 버튼으로 복구하는 테스트", () => { | ||
| beforeEach(() => { | ||
| cy.mockPopularMovies(1); | ||
| cy.getMovieDetailNetworkError(83533); | ||
| cy.visit("/"); | ||
| cy.wait("@getPopularMoviesPage1"); | ||
| cy.get(`[data-movie-id=83533]`).click(); | ||
| cy.wait("@getMovieDetailNetworkError"); | ||
| }); | ||
|
|
||
| it("데이터 로딩 시 에러 화면을 띄우고, 다시 시도하기 버튼으로 복구하는 테스트", () => { | ||
| cy.get(".modal-error-container").should("not.have.class", "hidden"); | ||
| cy.get(".retry-button").should("be.visible"); | ||
|
|
||
| cy.getMovieDetail(83533); | ||
| cy.get(".retry-button").click(); | ||
|
|
||
| cy.wait("@getMovieDetail83533") | ||
| .its("response.body") | ||
| .then((movieDetail) => { | ||
| cy.get(".modal-description-title").should("contain", movieDetail.title); | ||
| cy.get("#modal-description-year").should("contain", movieDetail.release_date.split("-")[0]); | ||
| cy.get("#modal-description-genre").should("contain", movieDetail.genres.map((item: {id: Number, name: string}) => item.name).join(", ")); | ||
| cy.get("#modal-rate-number").should("contain", movieDetail.vote_average.toFixed(1)); | ||
| cy.get("#modal-detail-description").should("contain", movieDetail.overview); | ||
| }); | ||
|
|
||
| cy.get(".modal-error-container").should("have.class", "hidden"); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
step2푸시까지 Pages 실배포되면 안정 브랜치 결과물을 덮어쓸 수 있습니다Line 8로 인해
step2브랜치 푸시도deploy잡을 타며github-pages환경에 배포됩니다. 학습/개발 브랜치 산출물이 운영 URL에 올라갈 수 있으니, 배포는main에서만 실행되도록 제한하는 게 안전합니다.수정 예시
🤖 Prompt for AI Agents