-
Notifications
You must be signed in to change notification settings - Fork 155
[2단계 - 상세 정보 & UI/UX 개선하기] 클라우디 미션 제출합니다. #314
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
bel1c10ud
wants to merge
55
commits into
woowacourse:bel1c10ud
Choose a base branch
from
bel1c10ud:bel1c10ud
base: bel1c10ud
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 54 commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
39dafc9
docs(readme): 기능 요구사항 초안 작성
bigcloud07 b3296d5
docs(readme): 서비스 전체 시나리오 작성
bigcloud07 ab1d8e0
docs(readme): 시나리오1 수정
bigcloud07 50cb4e5
test: 사용 시나리오 의사코드 작성
bigcloud07 6b8c140
feat: index.html 템플릿 파일 적용
bigcloud07 ea14686
test: E2E 테스트 작성완료
bigcloud07 a4d44aa
feat: 영화 목록 조회 기능 추가
bigcloud07 236c392
refactor: 영화 목록 조회 기능 1차 리팩토링
bel1c10ud 4cee2c7
feat: 영화 검색 기능 추가
bel1c10ud aa8e270
test: e2e 테스트 문법 에러 수정
bel1c10ud 71d65a9
docs(readme): 구현 현황 업데이트
bel1c10ud 8d21654
feat: 토스트 기능 추가
bel1c10ud 6efc6e7
feat: API 응답 시간이 길어지는 경우 예외를 발생시키는 동작 추가
bel1c10ud e9f4f06
docs(readme): API 요청에 실패하거나 응답이 길어져서 실패하는 경우에 대한 시나리오 추가
bel1c10ud 80ee884
test: API 응답에 실패하거나 응답이 길어져 실패하는 경우에 대한 모킹 및 테스트 추가
bel1c10ud 071708c
refactor: 하드코딩된 URL 환경 변수로 분리
bel1c10ud bbb2389
feat: 더보기 버튼을 짧은 주기로 누르는 경우 무시하도록 쓰로틀 추가
bel1c10ud 60aebbe
chore: 파비콘 추가
bel1c10ud 487ba7c
fix: 배포 환경 대응
bel1c10ud 89a4dca
fix: 로컬 환경에서 평점 아이콘이 정상적으로 출력되지 않는 문제 해결
bel1c10ud 8378310
fix: 배포 환경에 search 페이지가 포함되지 않는 문제 해결
bel1c10ud 3cd1d57
fix: 이미지에 base url이 적용되어있지 않았던 부분 수정
bel1c10ud 3aa31f9
fix: 잘못된 mock 반환값 수정
bel1c10ud 379526d
fix: 누락된 img alt 추가
bel1c10ud 259e7bb
fix: 잘못된 마크업 구조 개선
bel1c10ud 110e656
fix: 오타 수정
bel1c10ud 0e05eeb
feat: 커스텀 에러 클래스 도입 및 에러 핸들링 개선
bel1c10ud 4072641
test: 변경된 에러 형식에 대응하도록 테스트 수정
bel1c10ud a25542c
docs(readme): 구현 현황 업데이트
bel1c10ud 425ace6
fix: API 요청 실패시 skeleton이 사라지지 않는 문제 해결
bel1c10ud fbca497
test: 더보기 버튼과 Skeleton UI 테스트 추가
bel1c10ud f519882
refactor: render 관련 유틸 함수 분리
bel1c10ud 391401b
chore: 함수 이름과 일치하지 않는 파일 이름 수정
bel1c10ud 07ced67
refactor: 에러 핸들링 개선
bel1c10ud 99b0ae3
docs(readme): 2단계 기능 요구사항 작성
bel1c10ud f740937
feat: 영화 상세 정보 모달 구현
bel1c10ud b23b175
feat: 별점 매기기 기능 구현
bel1c10ud 4b01e75
feat: 최상단 영화 클릭시 모달이 띄워지는 기능 추가
bel1c10ud ed8013b
feat: 영화 목록 무한 스크롤 적용
bel1c10ud b4667b5
feat: 반응형 디자인 적용
bel1c10ud 543aa6a
feat: 양방향 무한 스크롤 구현
bel1c10ud c9f8c24
feat: 영화 상세 정보 모달 비동기 로딩 스피너 추가
bel1c10ud 5294f49
feat: 평점 메시지 추가
bel1c10ud 86d6791
fix: 최상단 영화가 정상적으로 갱신되지않는 문제 해결
bel1c10ud ab7c31a
docs(readme): 구현 현황 업데이트
bel1c10ud feb0712
feat: 상단 무한 스크롤 적용
bel1c10ud 2884bd6
refactor: 반응형 디자인 개선
bel1c10ud e5ed376
fix: 잘못된 에러 핸들링 및 비동기 처리 수정
bel1c10ud bf31206
docs(readme): 사용 시나리오 업데이트
bel1c10ud 7373d6c
test: 사용 시나리오 기반으로 E2E 테스트 업데이트
bel1c10ud 567c312
feat: 테스트 환경에서 ESC키를 사용해 모달을 닫을 수 있도록 이벤트 핸들러 추가
bel1c10ud 6c50efd
feat: 모달이 열려있는 경우 스크롤할 수 없도록 처리
bel1c10ud 6aaa165
refactor: 에러 핸들링 개선
bel1c10ud 8995f5e
fix: 모달이 열릴때 스크롤이 상단으로 이동하는 문제 해결
bel1c10ud af04faf
Merge remote-tracking branch 'upstream/bel1c10ud' into bel1c10ud
bel1c10ud 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,3 +22,5 @@ dist-ssr | |
| *.njsproj | ||
| *.sln | ||
| *.sw? | ||
|
|
||
| .env | ||
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,3 +1,123 @@ | ||
| # javascript-movie-review | ||
|
|
||
| FE 레벨1 영화 리뷰 미션 | ||
|
|
||
| ## 페어 | ||
|
|
||
| @bigcloud07 | ||
| @bel1c10ud | ||
|
|
||
| # 서비스 전체 시나리오 작성 | ||
|
|
||
| ## [시나리오 1] Movie List 탐색 - 무한 스크롤 | ||
|
|
||
| User -> Main Page -> 영화 목록이 보인다 -> 스크롤하여 페이지 끝에 도달하면 다음 영화 목록을 불러온다 | ||
|
|
||
| ## [시나리오 1-1] Movie List 탐색 - 로딩 중 스켈레톤 UI 표시 | ||
|
|
||
| User -> Main Page -> 영화 목록이 보인다 -> 스크롤하여 페이지 끝에 도달하면 다음 영화 목록을 불러온다 -> 스켈레톤 UI가 표시된다. | ||
|
|
||
| ## [시나리오 1-2] Movie List 탐색 - 마지막 페이지 도달시 추가 로드 없음 | ||
|
|
||
| User -> Main Page -> 영화 목록이 보인다 -> 스크롤하여 페이지 끝에 도달한다 -> 마지막 페이지 도달 -> 더 이상 영화 목록을 불러오지 않는다 | ||
|
|
||
| ## [시나리오 2] 검색 - 결과 있음 | ||
|
|
||
| User -> Main Page -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다 | ||
|
|
||
| ## [시나리오 2-1] 검색 - 결과 있는 경우의 재검색 | ||
|
|
||
| User -> Main Page -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다 -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다 | ||
|
|
||
| ## [시나리오 3] 검색 - 결과 없음 | ||
|
|
||
| User -> Main Page -> 검색창에 "$$%@@" 입력 -> 검색 버튼 클릭 -> "결과가 없습니다" 메시지가 보인다 | ||
|
|
||
| ## [시나리오 3-1] 검색 - 결과 없는 경우의 재검색 | ||
|
|
||
| User -> Main Page -> 검색창에 "$$%@@" 입력 -> 검색 버튼 클릭 -> "결과가 없습니다" 메시지가 보인다 -> 검색창에 "Inception" 입력 -> 검색 버튼 클릭 -> "Inception" 카드가 보인다 | ||
|
|
||
| ## [시나리오 4] 에러 - 영화 조회 API 요청이 실패했을 때 | ||
|
|
||
| User -> Main Page -> API 요청 실패 -> 화면 하단에 "API 에러" 타이틀과 에러 메세지를 담은 토스트를 띄운다. | ||
|
|
||
| ## [시나리오 5] 에러 - 영화 조회 API 요청에 대한 응답이 길어졌을 때 | ||
|
|
||
| User -> Main Page -> API 요청 -> 응답 대기 시간이 10초를 넘어간다 -> 화면 하단에 "API 에러" 타이틀과 "API 응답 시간이 10000ms를 초과했습니다." 메세지를 담은 토스트를 띄운다. | ||
|
|
||
| ## [시나리오 6] 영화 상세 정보 조회 - 모달 열기 | ||
|
|
||
| User -> Main Page -> 영화 카드 클릭 -> 영화 상세 정보가 모달로 표시된다 | ||
|
|
||
| ## [시나리오 6-1] 영화 상세 정보 조회 - 닫기 버튼으로 모달 닫기 | ||
|
|
||
| User -> Main Page -> 영화 카드 클릭 -> 모달이 표시된다 -> 닫기 버튼 클릭 -> 모달이 닫힌다 | ||
|
|
||
| ## [시나리오 6-2] 영화 상세 정보 조회 - ESC 키로 모달 닫기 | ||
|
|
||
| User -> Main Page -> 영화 카드 클릭 -> 모달이 표시된다 -> ESC 키 입력 -> 모달이 닫힌다 | ||
|
|
||
| ## [시나리오 7] 별점 매기기 | ||
|
|
||
| User -> Main Page -> 영화 카드 클릭 -> 모달이 표시된다 -> 별점 선택 -> 별점이 저장된다 | ||
|
|
||
| ## [시나리오 7-1] 별점 유지 - 새로고침 후 | ||
|
|
||
| User -> Main Page -> 영화 카드 클릭 -> 별점 선택 -> 페이지 새로고침 -> 같은 영화 클릭 -> 이전에 선택한 별점이 유지된다 | ||
|
|
||
| ## [시나리오 8] URL 페이지 파라미터로 접속 - 해당 페이지 로드 | ||
|
|
||
| User -> URL에 page=2를 포함하여 접속 -> 2페이지의 영화 목록이 표시된다 | ||
|
|
||
| ## [시나리오 8-1] URL 페이지 파라미터 접속 후 상단 무한 스크롤 | ||
|
|
||
| User -> URL에 page=2를 포함하여 접속 -> 2페이지 영화 목록이 표시된다 -> 상단으로 스크롤 -> 1페이지 영화 목록이 추가로 표시된다 | ||
|
|
||
| ## [시나리오 8-2] URL 페이지 파라미터 접속 후 하단 무한 스크롤 - 마지막 페이지 | ||
|
|
||
| User -> URL에 page=2(마지막 페이지)를 포함하여 접속 -> 2페이지 영화 목록이 표시된다 -> 하단으로 스크롤 -> 더 이상 영화 목록을 불러오지 않는다 | ||
|
|
||
| ## [시나리오 9] 새로고침 후 스크롤 위치 복원 | ||
|
|
||
| User -> Main Page -> 영화 목록 탐색 -> 특정 영화가 화면에 들어옴 -> URL에 해당 영화의 ID와 페이지가 기록된다 -> 새로고침 -> 이전에 화면에 보였던 영화 위치로 자동 스크롤된다 | ||
|
|
||
| # 기능 요구 사항 | ||
|
|
||
| ## 🎬 영화 목록 조회 | ||
|
|
||
| - [x] 영화 목록의 1페이지를 불러오며 페이지끝에 도달하면 그 다음의 영화 목록을 불러 올 수 있다. | ||
| - [x] 영화는 한 번의 요청당 20개씩 영화 목록을 보여준다. | ||
| - [x] 영화 목록을 불러오는 동안 Skeleton UI 표시 | ||
|
|
||
| ## 🔎 검색 | ||
|
|
||
| - [x] 영화 검색 API를 이용하여 내가 보고 싶은 영화를 검색할 수 있다. 엔터키를 눌러 검색할 수 있다. | ||
| - [x] 검색 버튼을 클릭하여 검색할 수 있다 | ||
| - [x] 영화 목록 조회와 같이 검색한 결과에 한해 정보를 보여주는 화면의 요구사항은 동일하다 | ||
|
|
||
| ## 📺 영화 상세 정보 조회 | ||
|
|
||
| - [x] 영화 포스터나 제목 또는 메인 화면의 자세히 보기를 누른경우 영화 상세 정보를 모달로 출력한다. | ||
| - [x] 닫기 버튼 또는 ESC를 누르면 모달이 닫힌다. | ||
| - [x] 모달창을 반응형 레이아웃으로 구현한다. | ||
| - [x] 사용자는 영화에 대해 별점을 남길 수 있다 | ||
| - [x] 별점은 5개로 구성되어있으며 한개장 2점이다, 1점 단위는 고려하지 않는다. | ||
| - [x] 새로고침하더라도 별점은 유지되어야 한다 | ||
| - [x] localStorag로 구현하돼 서버 API로 쉽고 안전하게 갈아끼울 수 있는 구조로 개발하여야한다. | ||
|
|
||
| ## 공통 | ||
|
|
||
| ### 예외처리 | ||
|
|
||
| > 에러가 발생했을 때, 해당 내용에 해당하는 에러 메세지를 하단 toast UI를 통해 보여준다. | ||
|
|
||
| - [x] 영화 조회 API 요청이 실패 했을 때 | ||
| - [x] 영화 조회 API 요청에 대한 응답이 길어졌을 때 | ||
|
|
||
| ### 프로그래밍 요구사항 | ||
|
|
||
| - 테스트 전략을 세우고, 단위 테스트(vitest), E2E 테스트(cypress)를 진행한다. | ||
| - 핵심이 되는 기능이라고 생각하는 기능 플로우를 선정하고 그에 대한 E2E 테스트를 추가한다. | ||
| - E2E 테스트 도구를 이용하여 UI Test를 진행해 본다. | ||
| - 비동기 통신에서 일어날 수 있는 다양한 상황을 고려해 본다. | ||
| - 예외 처리가 발생하는 경우에 사용자를 위한 메세지를 띄워 사용자에게 알려준다. |
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,5 +1,202 @@ | ||
| describe("template spec", () => { | ||
| it("passes", () => { | ||
| const TMDB_POPULAR_URL_PATTERN = "**/movie/popular**"; | ||
| const TMDB_SEARCH_URL_PATTERN = "**/search/movie**"; | ||
| const TMDB_MOVIE_DETAIL_URL_PATTERN = /\/movie\/\d+/; | ||
|
|
||
| describe("Movie App", () => { | ||
| beforeEach(() => { | ||
| cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, (req) => { | ||
| const url = new URL(req.url); | ||
| const page = url.searchParams.get("page"); | ||
| req.reply({ fixture: page === "1" ? "popular-movies-p1.json" : "popular-movies-p2.json" }); | ||
| }).as("popularMovies"); | ||
|
|
||
| cy.intercept("GET", TMDB_SEARCH_URL_PATTERN, (req) => { | ||
| const url = new URL(req.url); | ||
| const query = url.searchParams.get("query"); | ||
| req.reply({ fixture: query === "$$%@@" ? "empty-search.json" : "search-results.json" }); | ||
| }).as("searchMovies"); | ||
|
|
||
| cy.visit("localhost:5173"); | ||
| cy.wait("@popularMovies"); | ||
| }); | ||
|
|
||
| context("Movie List 탐색 - 무한 스크롤", () => { | ||
| it("초기 목록 20개 표시 후 페이지 끝 스크롤 시 40개 표시", () => { | ||
| cy.get(".item").should("have.length", 20); | ||
| cy.get(".scroll-area").scrollIntoView(); | ||
| cy.wait("@popularMovies"); | ||
| cy.get(".item").should("have.length", 40); | ||
| }); | ||
|
|
||
| it("페이지 끝 스크롤 시 로딩 중 스켈레톤 UI 표시", () => { | ||
| cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, (req) => { | ||
| req.reply({ fixture: "popular-movies-p2.json", delay: 1000 }); | ||
| }).as("delayedPopularMovies"); | ||
| cy.get(".scroll-area").scrollIntoView(); | ||
| cy.get(".skeleton-item").should("exist"); | ||
| cy.wait("@delayedPopularMovies"); | ||
| cy.get(".skeleton-item").should("not.exist"); | ||
| }); | ||
|
|
||
| it("마지막 페이지 도달 시 하단 스크롤 트리거 요소가 제거된다", () => { | ||
| cy.get(".scroll-area").scrollIntoView(); | ||
| cy.wait("@popularMovies"); | ||
| cy.get(".scroll-area").should("not.exist"); | ||
| }); | ||
| }); | ||
|
|
||
| context("검색 - 결과 있음", () => { | ||
| it("검색 결과가 1개 이상 표시된다", () => { | ||
| cy.get(".search-input").type("Inception"); | ||
| cy.get(".search-submit").click(); | ||
| cy.wait("@searchMovies"); | ||
| cy.get(".item").its("length").should("be.gte", 1); | ||
| }); | ||
| }); | ||
|
|
||
| context("검색 - 결과 있는 경우의 재검색", () => { | ||
| it("재검색 시에도 결과가 1개 이상 표시된다", () => { | ||
| cy.get(".search-input").type("Inception"); | ||
| cy.get(".search-submit").click(); | ||
| cy.wait("@searchMovies"); | ||
| cy.get(".item").its("length").should("be.gte", 1); | ||
| cy.get(".search-input").clear().type("Inception"); | ||
| cy.get(".search-submit").click(); | ||
| cy.wait("@searchMovies"); | ||
| cy.get(".item").its("length").should("be.gte", 1); | ||
| }); | ||
| }); | ||
|
|
||
| context("검색 - 결과 없음", () => { | ||
| it("결과 없음 메시지가 표시된다", () => { | ||
| cy.get(".search-input").type("$$%@@"); | ||
| cy.get(".search-submit").click(); | ||
| cy.wait("@searchMovies"); | ||
| cy.contains("검색 결과가 없습니다"); | ||
| }); | ||
| }); | ||
|
|
||
| context("검색 - 결과 없는 경우의 재검색", () => { | ||
| it("결과 없음 후 재검색 시 결과가 표시된다", () => { | ||
| cy.get(".search-input").type("$$%@@"); | ||
| cy.get(".search-submit").click(); | ||
| cy.wait("@searchMovies"); | ||
| cy.contains("검색 결과가 없습니다"); | ||
|
|
||
| cy.get(".search-input").clear().type("Inception"); | ||
| cy.get(".search-submit").click(); | ||
| cy.wait("@searchMovies"); | ||
| cy.get(".item").its("length").should("be.gte", 1); | ||
| }); | ||
| }); | ||
|
|
||
| context("API 요청 실패", () => { | ||
| it("영화 API 실패 시 토스트 에러가 표시된다", () => { | ||
| cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, { statusCode: 500 }).as("failedPopularMovies"); | ||
| cy.visit("localhost:5173"); | ||
| cy.wait("@failedPopularMovies"); | ||
| cy.get(".toast").should("be.visible"); | ||
| }); | ||
| }); | ||
|
|
||
| context("API 요청 타임아웃", () => { | ||
| it("응답이 지연되면 타임아웃 토스트 에러가 표시된다", () => { | ||
| cy.clock(); | ||
| cy.intercept("GET", TMDB_POPULAR_URL_PATTERN, (_req) => { | ||
| }).as("pendingRequest"); | ||
| cy.visit("localhost:5173"); | ||
| cy.tick(10001); | ||
| cy.get(".toast").should("be.visible"); | ||
| }); | ||
| }); | ||
|
|
||
| context("영화 상세 정보 조회 - 모달", () => { | ||
| beforeEach(() => { | ||
| cy.intercept("GET", TMDB_MOVIE_DETAIL_URL_PATTERN, { fixture: "movie-detail.json" }).as("movieDetail"); | ||
| }); | ||
|
|
||
| it("영화 클릭 시 상세 정보 모달이 표시된다", () => { | ||
| cy.get(".item:not(.skeleton-item)").first().click(); | ||
| cy.wait("@movieDetail"); | ||
| cy.get(".modal[open]").should("exist"); | ||
| cy.get(".modal-movie-title").should("exist"); | ||
| }); | ||
|
|
||
| it("닫기 버튼 클릭 시 모달이 닫힌다", () => { | ||
| cy.get(".item:not(.skeleton-item)").first().click(); | ||
| cy.wait("@movieDetail"); | ||
| cy.get(".modal[open]").should("exist"); | ||
| cy.get(".modal-close-button").click(); | ||
| cy.get(".modal[open]").should("not.exist"); | ||
| }); | ||
|
|
||
| it("ESC 키 입력 시 모달이 닫힌다", () => { | ||
| cy.get(".item:not(.skeleton-item)").first().click(); | ||
| cy.wait("@movieDetail"); | ||
| cy.get(".modal[open]").should("exist"); | ||
| cy.get(".modal[open]").trigger("keydown", { key: "Escape", keyCode: 27, which: 27 }); | ||
| cy.get(".modal[open]").should("not.exist"); | ||
| }); | ||
| }); | ||
|
|
||
| context("별점 매기기", () => { | ||
| beforeEach(() => { | ||
| cy.intercept("GET", TMDB_MOVIE_DETAIL_URL_PATTERN, { fixture: "movie-detail.json" }).as("movieDetail"); | ||
| }); | ||
|
|
||
| it("별점 선택 시 localStorage에 별점이 저장된다", () => { | ||
| cy.get(".item:not(.skeleton-item)").first().click(); | ||
| cy.wait("@movieDetail"); | ||
| cy.get(".star-button[data-rating='8']").click(); | ||
| cy.window().then((win) => { | ||
| const ratings = JSON.parse(win.localStorage.getItem("my-ratings") ?? "{}"); | ||
| expect(ratings[1523145]).to.equal(8); | ||
| }); | ||
| }); | ||
|
|
||
| it("새로고침 후에도 선택한 별점이 유지된다", () => { | ||
| cy.get(".item:not(.skeleton-item)").first().click(); | ||
| cy.wait("@movieDetail"); | ||
| cy.get(".star-button[data-rating='8']").click(); | ||
| cy.get(".modal-close-button").click(); | ||
| cy.visit("localhost:5173"); | ||
| cy.wait("@popularMovies"); | ||
| cy.get(".item:not(.skeleton-item)").first().click(); | ||
| cy.wait("@movieDetail"); | ||
| cy.get(".modal-movie-my-rating-selector").should("have.attr", "data-rating", "8"); | ||
| }); | ||
| }); | ||
|
|
||
| context("URL 페이지 파라미터 기반 페이지 로드", () => { | ||
| it("page=2 URL로 접속 시 첫 번째 API 요청이 2페이지 데이터를 요청한다", () => { | ||
| cy.visit("localhost:5173?page=2"); | ||
| cy.wait("@popularMovies").its("request.url").should("include", "page=2"); | ||
| }); | ||
|
|
||
| it("page=2(마지막 페이지) 접속 시 하단 무한 스크롤 트리거 요소가 존재하지 않는다", () => { | ||
| cy.visit("localhost:5173?page=2"); | ||
| cy.wait("@popularMovies"); | ||
| cy.get(".scroll-area").should("not.exist"); | ||
| }); | ||
|
|
||
| it("page=2 접속 후 상단 스크롤 시 1페이지 영화 목록이 추가로 불러와진다", () => { | ||
| cy.visit("localhost:5173?page=2"); | ||
| cy.wait("@popularMovies"); | ||
| cy.wait("@popularMovies"); | ||
| cy.get(".item").should("have.length", 40); | ||
| }); | ||
| }); | ||
|
|
||
| context("스크롤 위치 복원", () => { | ||
| it("viewed-movie-id URL 파라미터로 접속 시 해당 영화가 뷰포트에 표시된다", () => { | ||
| const targetMovieId = "83533"; | ||
| cy.visit(`localhost:5173?page=1&viewed-movie-id=${targetMovieId}`); | ||
| cy.wait("@popularMovies"); | ||
| cy.get(`[data-movie-id="${targetMovieId}"]`).should(($el) => { | ||
| const rect = $el[0].getBoundingClientRect(); | ||
| expect(rect.top, "영화가 뷰포트 상단 아래에 있어야 함").to.be.lt(Cypress.config("viewportHeight") as number); | ||
| expect(rect.bottom, "영화가 뷰포트 상단 위에 있어야 함").to.be.gt(0); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
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,6 @@ | ||
| { | ||
| "page": 1, | ||
| "total_pages": 1, | ||
| "total_results": 0, | ||
| "results": [] | ||
| } |
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,31 @@ | ||
| { | ||
| "id": 1523145, | ||
| "title": "Твоё сердце будет разбито", | ||
| "original_title": "Твоё сердце будет разбито", | ||
| "overview": "테스트용 줄거리입니다. 이 영화에 대한 상세 설명이 여기에 표시됩니다.", | ||
| "release_date": "2026-03-26", | ||
| "genres": [ | ||
| { "id": 10749, "name": "로맨스" }, | ||
| { "id": 18, "name": "드라마" } | ||
| ], | ||
| "popularity": 1037.0135, | ||
| "vote_average": 6.6, | ||
| "vote_count": 27, | ||
| "poster_path": "/iGpMm603GUKH2SiXB2S5m4sZ17t.jpg", | ||
| "backdrop_path": "/1x9e0qWonw634NhIsRdvnneeqvN.jpg", | ||
| "adult": false, | ||
| "video": false, | ||
| "original_language": "ru", | ||
| "belongs_to_collection": null, | ||
| "budget": 0, | ||
| "homepage": "", | ||
| "imdb_id": "", | ||
| "origin_country": ["RU"], | ||
| "production_companies": [], | ||
| "production_countries": [], | ||
| "revenue": 0, | ||
| "runtime": 90, | ||
| "spoken_languages": [], | ||
| "status": "Released", | ||
| "tagline": "" | ||
| } |
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: 840
cy.visit()에 프로토콜이 없는 URL을 넘기고 있습니다.cypress.config.ts에baseUrl이 정의되지 않았는데,cy.visit("localhost:5173")처럼 프로토콜 없이 호출하면 상대 경로로 해석돼 테스트가 실패합니다.두 가지 방법으로 수정할 수 있습니다:
cypress.config.ts에baseUrl: "http://localhost:5173"을 추가하고cy.visit("/")나cy.visit("/?page=2")처럼 호출cy.visit()호출을cy.visit("http://localhost:5173")처럼 절대 URL(프로토콜 포함)로 변경지금 패턴이 이 파일 전반에 반복되고 있으니, 어느 접근이 프로젝트에 맞는지 판단한 후 일관되게 적용해 주세요.
Also applies to: 96-97, 107-107, 162-163, 172-173, 177-178, 183-184, 193-194
🤖 Prompt for AI Agents