Skip to content
Open
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
c256ca3
docs: 요구사항 명세서 및 테스트 시나리오 작성
GamjaIsMine02 Mar 31, 2026
d51c3f2
init: 프로젝트 초기 설정
GamjaIsMine02 Mar 31, 2026
0292b66
feat: 영화 불러오기 기능 구현
GamjaIsMine02 Mar 31, 2026
edc0811
feat: 영화 20개 출력
GamjaIsMine02 Mar 31, 2026
1abceb5
feat: 더 보기 기능 구현
GamjaIsMine02 Mar 31, 2026
32daaab
feat: 검색 기능 구현
GamjaIsMine02 Mar 31, 2026
de28417
feat: 검색 결과가 없을 때의 상황 구현
GamjaIsMine02 Mar 31, 2026
ae0d491
refactor: 영화 목록을 추가하는 로직을 메서드로 분리
GamjaIsMine02 Apr 1, 2026
a316471
refactor: 영화 목록을 불러오는 api 로직을 메서드로 분리
GamjaIsMine02 Apr 1, 2026
7f8b604
fix: 불필요한 코드 삭제
GamjaIsMine02 Apr 1, 2026
c8156ec
refactor: querySelector로 dom을 조작하는 코드에서 타입 단언을 제거 및 메서드로 분리
GamjaIsMine02 Apr 1, 2026
ee830e4
docs: 요구사항 명세서 업데이트
GamjaIsMine02 Apr 1, 2026
d21e1be
refactor: 이벤트 리스너 메서드를 bind 메서드로 분리
GamjaIsMine02 Apr 1, 2026
e743ebc
fix: 영화 alt 속성값을 각 영화의 title로 수정, main.ts에서 String.raw 코드 제거
GamjaIsMine02 Apr 1, 2026
8da7434
fix: modal.html에서 index.html을 사용하도록 변경
GamjaIsMine02 Apr 1, 2026
5cd3817
fix: 검색바 css & 페이지 타이틀 css 수정
GamjaIsMine02 Apr 1, 2026
74150b9
fix: index.html 경로 수정 및 이미지 경로 정상화
GamjaIsMine02 Apr 1, 2026
00b3354
fix: footer css 수정
GamjaIsMine02 Apr 1, 2026
b9d4435
feat: skeleton UI 적용 및 사용하지 않는 import 제거
GamjaIsMine02 Apr 1, 2026
1dcfc6a
fix: skeleton UI를 위해서 강제로 딜레이하는 로직 제거
GamjaIsMine02 Apr 2, 2026
9d9c9e3
fix: css가 늦게 올라가는 버그 수정
GamjaIsMine02 Apr 2, 2026
5d5098f
feat: 영화 포스터 클릭 시 백그라운드에 영화 정보를 띄우는 기능 구현
GamjaIsMine02 Apr 2, 2026
df6e2ed
fix: 백그라운드에 영화 평점이 소수점 1자리까지로 수정
GamjaIsMine02 Apr 2, 2026
f281375
docs: 기능 요구사항에 따라 E2E 테스트 시나리오 추가
GamjaIsMine02 Apr 2, 2026
dd13f81
fix: 최초 접속 시 인기순 첫 번째 영화를 백그라운드에 띄워지도록 수정
GamjaIsMine02 Apr 2, 2026
350ace7
test: cypresss E2E 테스트 코드 추가
GamjaIsMine02 Apr 2, 2026
011997c
fix: api 키 변경
yuncic Apr 2, 2026
f8e44f8
fix: logo, star_empty 이미지 경로 하드코딩 제거
yuncic Apr 2, 2026
0ed101c
- feat: api 호출 에러 처리 기능 추가
yuncic Apr 5, 2026
2f54796
fix: main.ts 초기 로딩 시 에러 처리 누락 수정(callMovieList 적용)
yuncic Apr 5, 2026
0504185
refactor: bindMovieEvent.ts 함수 역할 분리
yuncic Apr 5, 2026
b20fbd2
test: cy.intercept()로 API mock 적용 및 실패 케이스 테스트 추가
yuncic Apr 5, 2026
fdad549
fix: cypress test URL local 주소 -> 배포 주소
yuncic Apr 6, 2026
8b4d0c6
refactor: API 설정을 BASE_API 객체로 분리
yuncic Apr 6, 2026
2254261
fix: 오타 수정
yuncic Apr 6, 2026
c564d97
refactor: request 함수로 API 호출 로직 분리 및 경로 상수화
yuncic Apr 6, 2026
51249f1
docs: step2 기능 명세서 작성
yuncic Apr 7, 2026
856e62a
feat: 영화 상세정보 모달 구현
yuncic Apr 7, 2026
6127209
feat: 별점 매기기 기능 추가
yuncic Apr 7, 2026
a56ffcf
fix: 별점 없는 영화 값 예외 처리를 위한 삼항연상자 추가
yuncic Apr 8, 2026
fcbdab6
feat: 영화 포스터 호버 추가
yuncic Apr 8, 2026
e03ba10
feat: 무한 스크롤 기능 추가
yuncic Apr 8, 2026
6d5ac60
feat: 모바일 반응형 웹 구현
yuncic Apr 8, 2026
4cb37f6
feat: 태블릿 반응형 웹 구현
yuncic Apr 8, 2026
40af500
fix: nav 고정 height값 수정 및 썸네일 repeat 우선순위에 따라 재배치
yuncic Apr 8, 2026
1d39687
fix: 이미지 경로 수정
yuncic Apr 8, 2026
1630e6a
fix: 미디어쿼리 범위 수정
yuncic Apr 9, 2026
959a082
feat: 영화 상세정보 모달 테스트 추가 및 관련 데이터 파일 생성
yuncic Apr 9, 2026
dca0674
test: 무한 스크롤 테스트 추가
yuncic Apr 9, 2026
b49c1e1
fix: 테스트에서 사용된 URL을 최신 버전으로 업데이트
yuncic Apr 9, 2026
e796c59
test: E2E 별점 매기기 테스트 추가
yuncic Apr 9, 2026
de26508
fix: .prettierrc에서 semi 옵션을 true로 수정
yuncic Apr 10, 2026
384a13f
Merge upstream/yuncic into step2
yuncic Apr 10, 2026
1d6520e
refactor: IntersectionObserver 콜백에서 forEach 대신 entries[0] 사용
yuncic Apr 10, 2026
517e398
refactor:
yuncic Apr 13, 2026
f1d8509
refactor: localStorage 내부 동작 검증 테스트 제거
yuncic Apr 13, 2026
5ad7506
refactor: localStorage 직접 접근을 StarRatingStorage로 추상화
yuncic Apr 13, 2026
97d46db
test: 무한스크롤 테스트를 fixture 기반 동적 검증으로 개선
yuncic Apr 13, 2026
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
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_KEY=293805c00692398c046c66e6b7b51d4d
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

실제 API 키가 저장소에 노출되어 즉시 조치가 필요합니다.

Line 1에 비밀값이 하드코딩되어 커밋되었습니다. 키 유출 상태이므로 즉시 키 폐기(rotate) 후 재발급하고, 저장소에는 placeholder만 남기는 방식으로 바꿔주세요.

보안 조치 예시 (placeholder로 교체)
-VITE_API_KEY=293805c00692398c046c66e6b7b51d4d
+VITE_API_KEY=YOUR_TMDB_API_KEY

원하시면 .env.example 기준으로 팀 온보딩 흐름(로컬 설정 문서 포함)까지 같이 정리해드릴게요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VITE_API_KEY=293805c00692398c046c66e6b7b51d4d
VITE_API_KEY=YOUR_TMDB_API_KEY
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 1-1: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env at line 1, The .env file contains a real secret value
(VITE_API_KEY=293805c00692398c046c66e6b7b51d4d) and must be remediated:
immediately revoke/rotate the exposed API key with the provider, remove the
secret from the repository by replacing the value in .env with a non-secret
placeholder (e.g., VITE_API_KEY=REPLACE_ME), add .env to .gitignore, commit a
safe .env.example that documents the placeholder and required vars, and consider
purging the secret from git history (git filter-repo / BFG) or opening a
follow-up task to do so; ensure any deployment/CI secrets are updated to use the
new rotated key.

7 changes: 6 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"endOfLine": "auto"
"endOfLine": "auto",
"printWidth": 120,
"tabWidth": 4,
"singleQuote": true,
"trailingComma": "all",
"semi": false
}
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
# javascript-movie-review

FE 레벨1 영화 리뷰 미션
FE 레벨1 영화 리뷰 미션 step2

## 요구사항 명세서

## 영화 상세정보 조회 모달

### 이벤트

- [x] 클릭시 모달 창 띄우기
- [x] 우측 상단 x버튼 혹은 모달 밖 화면 클릭 시 모달 창 끄기

### UI

- [x] 포스터 클릭시 상세정보 모달 띄우기
- [x] 포스터 사진 (poster_path)
- [x] 제목 (title)
- [x] 평균 별점 (vote_average)
- [x] 내 별점
- [x] 줄거리 (overview)

### 내 별점

- local storage 사용

* [x] 사용자는 영화에 대해 별점을 줄 수 있으며 새로고침하더라도 사용자가 남긴 별점은 유지되어야 한다.
* [x] 별점은 5개로 구성되어 있으며 한 개당 2점이며 1점 단위는 고려하지 않는다.
* [x] 2점: 최악이예요
* [x] 4점: 별로예요
* [x] 6점: 보통이에요
* [x] 8점: 재미있어요
* [x] 10점: 명작이에요

## 무한 스크롤

- [x] 더보기 버튼 대신 무한스크롤 적용

## 반응형 웹

- 분기별로 잘라서 3가지 타입으로 진행

- [x] 데스크톱
- [x] 태블릿
- [x] 모바일

## E2E 테스트

- [x] 포스터를 클릭하면 모달창이 뜬다.
- [x] 내 별점을 클릭하면 별점이 적용이 된다.
- [x] 스크롤을 끝까지 내리면 다음 영화 리스트가 나온다.
57 changes: 57 additions & 0 deletions cypress/e2e/clickTest.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
describe('click poster test', () => {
beforeEach(() => {
cy.intercept('GET', '**/movie/popular**', { fixture: 'popularMovies.json' })
cy.intercept('GET', '**/search/movie**', { fixture: 'searchMovies.json' })
})

it('인기순 영화 페이지에서 두 번째 포스터를 클릭하면 백그라운드에 해당 영화 정보가 띄워진다.', () => {
cy.visit('https://javascript-movie-review-dvlk.vercel.app/')
cy.get('.thumbnail-list li')
.eq(1)
.find('#title')
.invoke('text')
.then((defaultTitle) => {
cy.get('.thumbnail-list li').eq(1).click()
cy.get('.top-rated-movie')
.find('.title')
.invoke('text')
.should((searchTitle) => {
expect(searchTitle.trim()).to.equal(defaultTitle.trim())
})
})
})

it('인기순 영화 페이지에서 다섯 번째 포스터를 클릭하면 백그라운드에 해당 영화 정보가 띄워진다.', () => {
cy.visit('https://javascript-movie-review-dvlk.vercel.app/')
cy.get('.thumbnail-list li')
.eq(6)
.find('#title')
.invoke('text')
.then((defaultTitle) => {
cy.get('.thumbnail-list li').eq(6).click()
cy.get('.top-rated-movie')
.find('.title')
.invoke('text')
.should((searchTitle) => {
expect(searchTitle.trim()).to.equal(defaultTitle.trim())
})
})
})

it('인기순 영화 페이지에서 열한 번째 포스터를 클릭하면 백그라운드에 해당 영화 정보가 띄워진다.', () => {
cy.visit('https://javascript-movie-review-dvlk.vercel.app/')
cy.get('.thumbnail-list li')
.eq(12)
.find('#title')
.invoke('text')
.then((defaultTitle) => {
cy.get('.thumbnail-list li').eq(12).click()
cy.get('.top-rated-movie')
.find('.title')
.invoke('text')
.should((searchTitle) => {
expect(searchTitle.trim()).to.equal(defaultTitle.trim())
})
})
})
})
33 changes: 33 additions & 0 deletions cypress/e2e/modalTest.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
describe('search test', () => {
beforeEach(() => {
cy.intercept('GET', '**/movie/*', { fixture: 'infoModal.json' })
cy.intercept('GET', '**/movie/popular**', { fixture: 'popularMovies.json' })
cy.intercept('GET', '**/search/movie**', { fixture: 'searchMovies.json' })
})

it('인기순 영화 페이지에서 두 번째 포스터를 클릭하면 영화 상세정보 모달이 띄워진다.', () => {
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')
cy.get('.thumbnail-list li')
.eq(1)
.find('#title')
.invoke('text')
.then((listTitle) => {
cy.get('.thumbnail-list li').eq(1).click()
cy.get('#modalBackground').should('have.class', 'active')
cy.get('#modalTitle')
.invoke('text')
.should((modalTitle) => {
expect(modalTitle.trim()).to.equal(listTitle.trim())
})
})
})

it('검색 결과에서 포스터를 클릭하면 모달이 열린다.', () => {
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')
cy.get('.search-bar').type('스파이더맨')
cy.get('.search-btn').click()
cy.get('.thumbnail-list li').first().click()
cy.get('#modalBackground').should('have.class', 'active')
cy.get('#modalTitle').should('not.be.empty')
})
})
71 changes: 71 additions & 0 deletions cypress/e2e/moreBtnTest.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
describe('infinite scroll test', () => {
beforeEach(() => {
cy.intercept('GET', '**/movie/popular**', { fixture: 'popularMovies.json' })
})

it('스크롤을 내리면 추가 영화 목록을 불러온다.', () => {
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')

cy.get('.thumbnail-list li').should('have.length', 20)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 40)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 60)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 80)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 100)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 120)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 140)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 160)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 180)

cy.get('.thumbnail-list li').last().scrollIntoView()

cy.get('.thumbnail-list li').should('have.length', 200)
})
})

// describe('more btn test', () => {
// beforeEach(() => {
// cy.intercept('GET', '**/movie/popular**', { fixture: 'popularMovies.json' })
// cy.intercept('GET', '**/search/movie**', { fixture: 'searchMovies.json' })
// })
// it('페이지 접속 후 더보기 버튼을 1번 누르면 영화 개수가 40개가 된다.', () => {
// cy.visit('https://javascript-movie-review-dvlk.vercel.app/')

// cy.get('.item').should('have.length', 20)

// cy.get('.display-more-btn').click()

// cy.get('.item').should('have.length', 40)
// })
// it('페이지 접속 후 더보기 버튼을 10번 누르면 영화 개수가 220개가 된다.', () => {
// cy.visit('https://javascript-movie-review-dvlk.vercel.app/')
// cy.get('.item').should('have.length', 20)
// for (let i = 0; i < 10; i++) {
// cy.get('.display-more-btn').click()
// }
// cy.get('.item').should('have.length', 220)
// })
// })
69 changes: 69 additions & 0 deletions cypress/e2e/searchTest.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
describe('search test', () => {
beforeEach(() => {
cy.intercept('GET', '**/movie/popular**', { fixture: 'popularMovies.json' })
cy.intercept('GET', '**/search/movie**', { fixture: 'searchMovies.json' })
})
it('검색어를 입력한 뒤 검색 버튼을 누르면 필터링된 영화 목록을 보여준다.', () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

우선 테스트 시나리오를 유저 행동 기반으로 작성한 점은 정말 좋다고 생각해요.
다만 현재 해당 테스트 시나리오는 한번에 2개를 검증하고 있는데 이렇게 하신 의도가 있을까요?
따로따로 나눠서 해도 되지 않을까 해서요!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

검색을 하지 않았을때 뽑은 타이틀 텍스트와 비교하기 위해 그랬던 것인데, fixture 데이터를 정의했을 시점에 수정했어야하는데 놓친 것 같습니다..!
해당 부분 수정했습니다!

cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')

cy.get('.thumbnail-list li')
.first()
.find('#title')
.invoke('text')
.then((defaultTitle) => {
cy.get('.search-bar').type('스파이더맨')
cy.get('.search-btn').click()

cy.get('.thumbnail-list li')
.first()
.find('#title')
.invoke('text')
.should((searchTitle) => {
expect(searchTitle.trim()).not.to.equal(defaultTitle.trim())
})
})
})

it('검색어를 입력한 뒤 엔터키를 누르면 필터링된 영화 목록을 보여준다.', () => {
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')

cy.get('.thumbnail-list li')
.first()
.find('#title')
.invoke('text')
.then((defaultTitle) => {
cy.get('.search-bar').type('스파이더맨{enter}')

cy.get('.thumbnail-list li')
.first()
.find('#title')
.invoke('text')
.should((searchTitle) => {
expect(searchTitle.trim()).not.to.equal(defaultTitle.trim())
})
})
})
it("검색란에 검색어를 입력해도 결과가 존재하지 않다면 '검색 결과가 없습니다' 텍스트를 띄운다", () => {
cy.intercept('GET', '**/search/movie**', { body: { results: [] } })
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')
cy.get('.search-bar').type('ㄴㅇ러ㅏㅗㅁ라ㅗ어ㅏ로머ㅏJklhdskldh')
cy.get('.search-btn').click()
cy.get('.search-error-text').should('have.text', '검색 결과가 없습니다.')
})

it('네트워크 오류 시 알림을 띄운다', () => {
cy.intercept('GET', '**/movie/popular**', { forceNetworkError: true })
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')
cy.on('window:alert', (text) => {
expect(text).to.equal('네트워크 오류가 발생하였습니다.')
})
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# alert 리스너/visit 순서 점검
rg -n -C2 "cy\\.visit\\(|cy\\.on\\('window:alert'" cypress/e2e/searchTest.cy.ts -S

Repository: woowacourse/javascript-movie-review

Length of output: 1530


Alert 리스너가 page load 이후에 등록되어 이벤트를 놓칠 수 있습니다.

현재 코드 순서를 살펴보세요: cy.intercept()cy.visit()cy.on('window:alert')

페이지가 초기 로드되는 동안 alert가 발생할 수 있는데, 리스너가 그 이후에 등록되면 alert를 감지하지 못합니다.

순서를 어떻게 변경해야 할까요? 이벤트 리스너가 언제 등록되어야 alert 이벤트를 확실히 받을 수 있을지 생각해봐주세요. Cypress 문서의 cy.on() 부분을 참고하면 도움이 될 겁니다.

Also applies to: 62-68

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cypress/e2e/searchTest.cy.ts` around lines 54 - 60, The alert listener is
registered after cy.visit(), so it can miss alerts fired during page load; move
the cy.on('window:alert', ...) call to before cy.visit(...) (and keep
cy.intercept('GET', '**/movie/popular**', { forceNetworkError: true }) before
visit as well) so that the window:alert handler is attached prior to navigation;
apply the same change for the other test block that uses cy.on('window:alert')
around the failing network intercept.


it('API 오류 시 알림을 띄운다', () => {
cy.intercept('GET', '**/movie/popular**', { statusCode: 401 })
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')
cy.on('window:alert', (text) => {
expect(text).to.equal('데이터를 불러오지 못했습니다.')
})
})
})
5 changes: 0 additions & 5 deletions cypress/e2e/spec.cy.ts

This file was deleted.

74 changes: 74 additions & 0 deletions cypress/e2e/starRatingTest.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
describe('별점 매기기 테스트', () => {
beforeEach(() => {
cy.intercept('GET', '**/movie/*', { fixture: 'infoModal.json' })
cy.intercept('GET', '**/movie/popular**', { fixture: 'popularMovies.json' })
cy.clearLocalStorage()
cy.visit('https://javascript-movie-review-dvlk-a6xn5spuo-yun-cics-projects.vercel.app/')
cy.get('.thumbnail-list li').first().click()
cy.get('#modalBackground').should('have.class', 'active')
})

it('별점 클릭 시 해당 별점 텍스트가 표시된다.', () => {
cy.get('.star-icon[data-value="2"]').click()
cy.get('.my-rate-text').should('have.text', '최악이에요 (2/10)')

cy.get('.star-icon[data-value="4"]').click()
cy.get('.my-rate-text').should('have.text', '별로예요 (4/10)')

cy.get('.star-icon[data-value="6"]').click()
cy.get('.my-rate-text').should('have.text', '보통이에요 (6/10)')

cy.get('.star-icon[data-value="8"]').click()
cy.get('.my-rate-text').should('have.text', '재미있어요 (8/10)')

cy.get('.star-icon[data-value="10"]').click()
cy.get('.my-rate-text').should('have.text', '명작이에요 (10/10)')
})

it('별점 클릭 시 클릭한 별까지 채워진 별로 변경된다.', () => {
// 클릭 전 빈 별 src 저장
cy.get('.star-icon[data-value="10"]')
.invoke('attr', 'src')
.then((emptySrc) => {
cy.get('.star-icon[data-value="6"]').click()

// 클릭한 별까지는 src가 빈 별과 달라야 함
cy.get('.star-icon[data-value="2"]').invoke('attr', 'src').should('not.equal', emptySrc)
cy.get('.star-icon[data-value="4"]').invoke('attr', 'src').should('not.equal', emptySrc)
cy.get('.star-icon[data-value="6"]').invoke('attr', 'src').should('not.equal', emptySrc)

// 클릭하지 않은 별은 여전히 빈 별이어야 함
cy.get('.star-icon[data-value="8"]').invoke('attr', 'src').should('equal', emptySrc)
cy.get('.star-icon[data-value="10"]').invoke('attr', 'src').should('equal', emptySrc)
})
})

it('별점 클릭 시 localStorage에 별점이 저장된다.', () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2;

E2E 테스트에선 유저 행동 기반으로 작성하는게 좋아요.
현재 로컬 스토리지에 저장하는지 여부는 유저 입장에서 굳이 알 필요가 없기도 해요.
유저 관점으로 테스트 시나리오를 작성한다면 어떻게 해볼 수 있을까요?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

인터렉션 관점에서 생각했어야하는데 구현 세부사항으로 빠져버렸네요 ㅠ
중요한건 "별점을 매기고 모달을 껏다 켜도 남아있는가" 이니까
해당 부분 테스트만 남기고 내부 동작 검증 테스트는 지웠습니다!

cy.get('.star-icon[data-value="8"]').click()

// infoModal.json movie id 83533
cy.window().then((win) => {
expect(win.localStorage.getItem('rating_83533')).to.equal('8')
})
})

it('모달을 닫았다가 다시 열면 이전에 저장한 별점이 복원된다.', () => {
cy.get('.star-icon[data-value="6"]').click()
cy.get('.my-rate-text').should('have.text', '보통이에요 (6/10)')

cy.get('#closeModal').click()
cy.get('#modalBackground').should('not.have.class', 'active')

cy.get('.thumbnail-list li').first().click()
cy.get('#modalBackground').should('have.class', 'active')

cy.get('.my-rate-text').should('have.text', '보통이에요 (6/10)')
cy.get('.star-icon[data-value="8"]')
.invoke('attr', 'src')
.then((emptySrc) => {
cy.get('.star-icon[data-value="2"]').invoke('attr', 'src').should('not.equal', emptySrc)
cy.get('.star-icon[data-value="6"]').invoke('attr', 'src').should('not.equal', emptySrc)
cy.get('.star-icon[data-value="8"]').invoke('attr', 'src').should('equal', emptySrc)
})
})
})
Loading