-
Notifications
You must be signed in to change notification settings - Fork 155
[2단계 - 상세 정보 & UI/UX 개선하기] 윤돌 미션 제출합니다. #319
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
Open
yundol777
wants to merge
59
commits into
woowacourse:yundol777
Choose a base branch
from
yundol777:main
base: yundol777
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
55077e5
docs: 리드미 기능 목록 작성
yundol777 3047053
chore: cypress 초기 설정
yundol777 035c2bd
docs: 리드미 기능 목록 추가
yundol777 8e8ca7f
test: 영화 목록 데이터 가져오기 테스트
yundol777 0485407
feat: 인기있는 영화 목록 api 호출 함수 구현
yundol777 77ac51b
feat: api 호출 타입 추가
yundol777 d8c538b
feat: 메인 UI 리스트 렌더링 및 베너 생성
yundol777 4a7aa14
test: 검색한 영화 목록 가져오는 테스트 추가
yundol777 6b0ea86
feat: 입력 값으로 검색 된 영화 목록 렌더링
yundol777 71bf185
style: 검색창 스타일 및 레이아웃 변경
yundol777 fff280c
refactor: 파일 구조 분리
yundol777 baf93ab
feat: 빈 리스트 출력 뷰 추가
yundol777 2b23868
feat: api 에러 처리 추가
yundol777 8a5277e
feat: 이미지 에러 처리 추가
yundol777 4fd2a35
feat: 빈 리스트 및 에러 리스트 출력 시 더보기 버튼 제거
yundol777 14e0dd6
refacotr: 파일 구조 변경
yundol777 9bbe7de
fix: 사라진 버튼이 재검색 시 안생기는 오류 수정
yundol777 fffdec3
feat: 스켈레톤 ui 구현 및 연동
yundol777 5735ebc
refactor: 파일 구조 변경
yundol777 27c9c42
style: 에러 출력 UI 스타일 변경
yundol777 be68938
test: cypress E2E 통합 테스트 추가
yundol777 fc4607f
docs: 리드미 수정
yundol777 b8835d9
chore: cypress 타입 설정 추가
yundol777 1df96e8
refactor: 요소 속성 수정
yundol777 07ada58
refactor: 이미지 및 css 파일 이동
yundol777 7e3c3d3
refactor: 상태 객체 생성
yundol777 e0b74fb
refactor: 숫자 상수 처리
yundol777 1da63c5
refactor: 파일 구조 분리
yundol777 3c7ce0a
fix: 깃허브 액션을 이용해 토큰 연결
yundol777 f971c97
fix: 이미지 경로 수정
yundol777 a139cbe
fix: 이미지 경로 수정
yundol777 6a686c1
fix: 에러 처리 로직 수정
yundol777 8f68000
refactor: main, view 파일 구조 분리
yundol777 f78362d
refactor: state 상태 구조 분리
yundol777 8facdcd
refactor: 더보기 버튼 구조 개선
yundol777 588b4ef
refactor: 에러 메세지 구조 개선
yundol777 4b4ab53
fix: 에러 처리 시 더보기 버튼 제거
yundol777 107536b
fix: 배포 시 이미지 깨지는 오류 해결
yundol777 c355424
fix: 중복 클릭 방지 수정
yundol777 6b6b4e0
feat: 오류 시 더 보기 버튼 제거
yundol777 a7976c1
docs: 추가 구현 사항 리드미 추가
yundol777 1577564
feat: 영화 항목 클릭 시 모달 UI 출력
yundol777 5ba2a00
feat: esc 입력 시 모달 UI 숨김
yundol777 42b64cc
feat: api를 호출하고 상세 정보를 출력한다.
yundol777 73854ae
feat: 별점을 눌렀을 때 해당 별점을 저장 및 출력
yundol777 7e31518
docs: 기능 목록 추가
yundol777 2abb244
styel: 모달 스타일 적용
yundol777 7cb4a74
feat: 더보기 버튼 무한 스크롤로 대체
yundol777 9e2d6b0
feat: 모달 상세 정보 스켈레톤 UI 구현
yundol777 68ed04d
feat: 상세 정보 fetch 실패 시, 에러 UI 모달 출력
yundol777 1d281a9
feat: 모달 바깥 영역 클릭 시 모달 닫힘 기능 추가
yundol777 d1d8679
style: 반응형 UI 추가 및 모달 로딩 처리 변경
yundol777 4a220f2
feat: 배너 버튼 기능 연결
yundol777 bd610e9
test: e2e 테스트 수정 및 추가
yundol777 f5c7589
refactor: 컨트롤러 에러 처리 중복 분리
yundol777 8885cc8
refactor: 오타 수정
yundol777 c6d7e20
feat: 로고 클릭 시 새로고침 기능 추가
yundol777 d1fb715
merge: keep local changes over upstream
yundol777 23b153e
fix: 배포 시 이미지 깨지는 오류 해결
yundol777 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,104 +1,95 @@ | ||
| describe("홈 화면 테스트", () => { | ||
| beforeEach(() => { | ||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=1").as( | ||
| "getMovies", | ||
| ); | ||
| cy.visit("http://localhost:5173"); | ||
| }); | ||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=1", { | ||
| statusCode: 200, | ||
| body: { | ||
| page: 1, | ||
| results: Array.from({ length: 20 }, (_, index) => ({ | ||
| id: index + 1, | ||
| title: `영화 ${index + 1}`, | ||
| poster_path: `/poster-${index + 1}.jpg`, | ||
| vote_average: 7.5, | ||
| })), | ||
| total_pages: 2, | ||
| total_results: 40, | ||
| }, | ||
| }).as("page1"); | ||
|
|
||
| it("API 호출 확인", () => { | ||
| cy.wait("@getMovies").its("response.body.results").should("be.an", "array"); | ||
| cy.visit("http://localhost:5173"); | ||
| }); | ||
|
|
||
| it("배너 안의 요소를 확인", () => { | ||
| cy.wait("@getMovies") | ||
| .its("response.body.results") | ||
| .then((results) => { | ||
| cy.get(".title").should("contain", results[0].title); | ||
| cy.get(".rate-value").should("contain", results[0].vote_average); | ||
| cy.get(".background-container") | ||
| .invoke("css", "background-image") | ||
| .should("include", results[0].poster_path); | ||
| }); | ||
| it("초기 진입 시 영화 목록이 렌더된다.", () => { | ||
| cy.wait("@page1"); | ||
| cy.get(".thumbnail-item").should("have.length", 20); | ||
| }); | ||
|
|
||
| it("리스트 안의 요소를 확인", () => { | ||
| cy.wait("@getMovies") | ||
| .its("response.body.results") | ||
| .then((results) => { | ||
| cy.get(".thumbnail").each(($el, index) => { | ||
| cy.wrap($el) | ||
| .should("have.attr", "src") | ||
| .and("include", results[index].poster_path); | ||
| }); | ||
| it("초기 진입 시 베너가 렌더된다.", () => { | ||
| cy.wait("@page1"); | ||
|
|
||
| cy.get(".item-rate").each(($el, index) => { | ||
| cy.wrap($el).should("contain", results[index].vote_average); | ||
| }); | ||
|
|
||
| cy.get(".item-title").each(($el, index) => { | ||
| cy.wrap($el).should("contain", results[index].title); | ||
| }); | ||
| }); | ||
| cy.get(".top-rated-movie").should("be.visible"); | ||
| cy.get(".title").should("contain", "영화 1"); | ||
| cy.get(".rate-value").should("contain", "7.5"); | ||
| }); | ||
|
|
||
| it("더보기 버튼이 있는지 확인", () => { | ||
| cy.get(".thumbnail-add-button").should("exist"); | ||
| it("배너의 자세히 보기 버튼 클릭 시 모달이 열린다.", () => { | ||
| cy.wait("@page1"); | ||
|
|
||
| cy.get(".top-rated-movie .detail").click(); | ||
| cy.get(".modal-background").should("have.class", "active"); | ||
| cy.get(".modal").should("be.visible"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("더보기 버튼 테스트", () => { | ||
| describe("영화 리스트 기능 테스트", () => { | ||
| beforeEach(() => { | ||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=1").as( | ||
| "page1", | ||
| ); | ||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=2").as( | ||
| "page2", | ||
| ); | ||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=1", { | ||
| statusCode: 200, | ||
| body: { | ||
| page: 1, | ||
| results: Array.from({ length: 20 }, (_, index) => ({ | ||
| id: index + 1, | ||
| title: `영화 ${index + 1}`, | ||
| poster_path: `/poster-${index + 1}.jpg`, | ||
| vote_average: 7.5, | ||
| })), | ||
| total_pages: 2, | ||
| total_results: 40, | ||
| }, | ||
| }).as("page1"); | ||
|
|
||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=2", { | ||
| statusCode: 200, | ||
| body: { | ||
| page: 2, | ||
| results: Array.from({ length: 20 }, (_, index) => ({ | ||
| id: index + 21, | ||
| title: `영화 ${index + 21}`, | ||
| poster_path: `/poster-${index + 21}.jpg`, | ||
| vote_average: 8.1, | ||
| })), | ||
| total_pages: 2, | ||
| total_results: 40, | ||
| }, | ||
| }).as("page2"); | ||
|
|
||
| cy.visit("http://localhost:5173"); | ||
| }); | ||
|
|
||
| it("버튼 클릭 시 API 호출 확인", () => { | ||
| cy.get(".thumbnail-add-button").click(); | ||
| it("영화 카드 클릭 시 모달이 열린다.", () => { | ||
| cy.wait("@page1"); | ||
|
|
||
| cy.wait("@page2").its("response.body.results").should("be.an", "array"); | ||
| cy.get(".item").first().click(); | ||
| cy.get(".modal-background").should("have.class", "active"); | ||
| cy.get(".modal").should("be.visible"); | ||
| }); | ||
|
|
||
| it("리스트 안의 요소를 확인", () => { | ||
| let page1Results: Movies[]; | ||
|
|
||
| cy.wait("@page1") | ||
| .its("response.body.results") | ||
| .then((results1) => { | ||
| page1Results = results1; | ||
| }); | ||
|
|
||
| cy.get(".thumbnail-add-button").click(); | ||
|
|
||
| cy.wait("@page2") | ||
| .its("response.body.results") | ||
| .then((results2) => { | ||
| const allResults = [...page1Results, ...results2]; | ||
|
|
||
| cy.get(".thumbnail").should("have.length", allResults.length); | ||
|
|
||
| cy.get(".thumbnail").each(($el, index) => { | ||
| cy.wrap($el) | ||
| .should("have.attr", "src") | ||
| .and("include", allResults[index].poster_path); | ||
| }); | ||
| it("무한 스크롤 시 다음 페이지가 붙는다.", () => { | ||
| cy.wait("@page1"); | ||
| cy.get(".thumbnail-item").should("have.length", 20); | ||
|
|
||
| cy.get(".item-title").each(($el, index) => { | ||
| cy.wrap($el).should("contain", allResults[index].title); | ||
| }); | ||
| cy.get("#sentinel").scrollIntoView(); | ||
| cy.wait("@page2"); | ||
|
|
||
| cy.get(".item-rate").each(($el, index) => { | ||
| cy.wrap($el).should( | ||
| "contain", | ||
| String(allResults[index].vote_average), | ||
| ); | ||
| }); | ||
| }); | ||
| cy.get(".thumbnail-item").should("have.length", 40); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| describe("모달 테스트", () => { | ||
| beforeEach(() => { | ||
| cy.intercept("GET", "**/movie/popular?language=ko-KR®ion=KR&page=1", { | ||
| statusCode: 200, | ||
| body: { | ||
| page: 1, | ||
| results: Array.from({ length: 20 }, (_, index) => ({ | ||
| id: index + 1, | ||
| title: `영화 ${index + 1}`, | ||
| poster_path: `/poster-${index + 1}.jpg`, | ||
| vote_average: 7.5, | ||
| })), | ||
| total_pages: 2, | ||
| total_results: 40, | ||
| }, | ||
| }).as("page1"); | ||
|
|
||
| cy.intercept("GET", "**/movie/1?language=ko-KR®ion=KR", { | ||
| statusCode: 200, | ||
| body: { | ||
| id: 1, | ||
| title: "영화 1", | ||
| poster_path: "/poster-1.jpg", | ||
| vote_average: 7.5, | ||
| release_date: "2024-01-01", | ||
| genres: [ | ||
| { id: 1, name: "액션" }, | ||
| { id: 2, name: "모험" }, | ||
| ], | ||
| overview: "영화 1의 줄거리입니다.", | ||
| }, | ||
| }).as("movieDetail"); | ||
|
|
||
| cy.visit("http://localhost:5173"); | ||
| cy.wait("@page1"); | ||
| cy.get(".top-rated-movie .detail").click(); | ||
| cy.wait("@movieDetail"); | ||
| }); | ||
|
|
||
| it("모달이 열리면 상세정보가 렌더된다.", () => { | ||
| cy.get(".modal-background").should("have.class", "active"); | ||
| cy.get(".modal").should("be.visible"); | ||
| cy.get(".modal .modal-title-section h2").should("contain", "영화 1"); | ||
| cy.get(".modal .detail").should("contain", "영화 1의 줄거리입니다."); | ||
| }); | ||
|
|
||
| it("x 버튼을 누르면 모달이 닫힌다.", () => { | ||
| cy.get(".close-modal").click(); | ||
| cy.get(".modal-background").should("not.have.class", "active"); | ||
| cy.get(".modal").should("not.be.visible"); | ||
| }); | ||
|
|
||
| it("esc 버튼을 누르면 모달이 닫힌다.", () => { | ||
| cy.get("body").type("{esc}"); | ||
| cy.get(".modal-background").should("not.have.class", "active"); | ||
| cy.get(".modal").should("not.be.visible"); | ||
| }); | ||
|
|
||
| it("모달 바깥을 누르면 모달이 닫힌다.", () => { | ||
| cy.get(".modal-background").click("topLeft"); | ||
| cy.get(".modal-background").should("not.have.class", "active"); | ||
| cy.get(".modal").should("not.be.visible"); | ||
| }); | ||
|
|
||
| it("처음 모달이 열리면 별점 초기값이 보인다.", () => { | ||
| cy.get(".myrate-comment").should("contain", "별점을 입력해주세요"); | ||
| cy.get(".myrate-score").should("contain", "(0/10)"); | ||
| }); | ||
|
|
||
| it("3번째 별을 클릭하면 별점이 반영된다.", () => { | ||
| cy.get(".myrate-stars img").eq(2).click(); | ||
| cy.get(".myrate-comment").should("contain", "보통이에요"); | ||
| cy.get(".myrate-score").should("contain", "(6/10)"); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🧩 Analysis chain
🏁 Script executed:
Repository: woowacourse/javascript-movie-review
Length of output: 1900
모달 오픈 테스트에서 상세 정보 API를 stub하지 않고 있습니다.
이 테스트는 ".detail" 버튼 클릭 시 모달을 여는 것을 검증하지만, 모달 열기는
modalController→getDetail함수를 통해/movie/{id}엔드포인트로 실제 API 요청을 보냅니다. 인기 목록(/movie/popular)만 stub하고 상세 정보 요청을 stub하지 않으면, 테스트가 네트워크 지연이나 응답 시간에 따라 불안정해질 수 있습니다.cypress/e2e/modal.cy.ts를 보면 동일한 상세 정보 요청을 line 18에서cy.intercept("GET", "**/movie/1?language=ko-KR®ion=KR", { ... })방식으로 stub하고 있습니다. 현재 코드 (lines 34-39, 78-83)에서도 같은 방식으로 상세 정보 엔드포인트를 stub하도록 추가해야 테스트의 안정성을 높일 수 있습니다.modal.cy.ts의 stub 패턴을 참고하여 어떤 요청을 어떻게 고정해야 할지 검토해보세요.🤖 Prompt for AI Agents