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

[1단계 - 영화 목록 불러오기] 윤생(이윤성) 미션 제출합니다. #6

Merged
merged 52 commits into from
Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e2865f8
docs: 기능 요구 사항 작성
ksone02 Mar 14, 2023
7bc8633
feat: 컴포넌트 분리 구현
ksone02 Mar 14, 2023
294448a
feat: 이미지 밑 스타일 임포트
ksone02 Mar 14, 2023
cd2da9f
feat: 인기 영화 데이터 fetch 후 render 구현
ksone02 Mar 15, 2023
04cade7
feat: 인기 영화 더보기 기능 구현
ksone02 Mar 15, 2023
b55d92f
feat: 영화 검색 기능 구현
ksone02 Mar 15, 2023
354c7c8
feat: API 키 ignore
ksone02 Mar 15, 2023
1d3bb2f
refactor: 컴포넌트 폴더 분리
ksone02 Mar 15, 2023
0c225ea
feat: 영화 API 페이지 상태관리
ksone02 Mar 15, 2023
5cd6447
refactor: 중복되는 api 호출 로직 모듈화
ksone02 Mar 15, 2023
f7d9a32
refactor: 중복되는 렌더링 로직 모듈화
ksone02 Mar 15, 2023
99e1c57
refactor: initialRender 메서드 변경
ksone02 Mar 15, 2023
9cfe83e
feat: 에러에 따른 렌더링 구현
ksone02 Mar 15, 2023
9ce56fd
feat: 에러 컴포넌트 구현
ksone02 Mar 15, 2023
27d2315
test: 영화 리뷰 미션 e2e 테스트 작성
ksone02 Mar 16, 2023
4c0081f
refactor: Movie List Deprecated 된 코드 제거
ksone02 Mar 16, 2023
4bc7246
refactor: console.log 제거
ksone02 Mar 16, 2023
cef4beb
refactor: MoreButton 컴포넌트 코드 가독성 개선
ksone02 Mar 16, 2023
2847cc7
refactor: 코드 상수화
ksone02 Mar 16, 2023
52b82d7
refactor: event 위임 로직 if문 switch문으로 변경
ksone02 Mar 16, 2023
7671371
refactor: Movie list HTML 구조 변경
ksone02 Mar 16, 2023
cf4c7c0
refactor: header HTML 구조 변경
ksone02 Mar 16, 2023
b416e43
refactor: Movie List Title HTML 구조 변경
ksone02 Mar 16, 2023
042fdf2
refactor: App 내의 불필요한 document 선언 제거
ksone02 Mar 16, 2023
5472125
refactor: Skeleton 불필요한 li태그 제거
ksone02 Mar 16, 2023
f92c4ee
refactor: App 내부 변수명 통일
ksone02 Mar 16, 2023
188c7a8
refactor: App 불필요한 필드 제거
ksone02 Mar 16, 2023
8daad3f
refactor: List Method 네이밍 변경
ksone02 Mar 16, 2023
051e31a
refactor: API 처리 로직 구조 변경
ksone02 Mar 16, 2023
a76098d
feat: 로고 클릭시 input value 초기화
ksone02 Mar 16, 2023
e0adc38
refactor: url 생성 책임 분리
ksone02 Mar 16, 2023
0f334d1
refactor: App 더보기 버튼 액션 변경 메소드 분리
ksone02 Mar 16, 2023
4e865a7
refactor: App 컴포넌트 코드 가독성 개선
ksone02 Mar 16, 2023
c90298e
refactor: TITLE 상수화 객체 타입 단언
ksone02 Mar 16, 2023
5ba2e9b
refactor: 컴포넌트 전체적 가독성 개선
ksone02 Mar 16, 2023
d977044
refactor: constants 디렉토리 변경
ksone02 Mar 16, 2023
37dec5a
docs: README.md 작성
ksone02 Mar 16, 2023
2b4d446
refactor: 사용되지 않는 매개변수 선언 제거
2yunseong Mar 20, 2023
c320fcd
fix: html 마크업 수정
2yunseong Mar 20, 2023
15c8f36
refactor: cypress 불필요한 파일 제거
2yunseong Mar 20, 2023
6836501
refactor: 웹 접근성 개선
2yunseong Mar 20, 2023
81241e9
refactor: 마크업 구조 변경에 따른 테스트 수정
2yunseong Mar 20, 2023
f3d200d
chore: 기술에 대한 정보를 제공하는 주석 추가
2yunseong Mar 20, 2023
749e8f2
refactor: early return / 모호한 네이밍 수정
2yunseong Mar 20, 2023
fe64426
test: 검색창 공백 입력 시/ 입력이 비어있을 시 테스트 추가
2yunseong Mar 20, 2023
7c81f7e
fix: 공백을 입력 시 검색을 하는 오류 수정
2yunseong Mar 20, 2023
298e925
test: 테스트 명 수정
2yunseong Mar 20, 2023
4e6de67
feat: 오류 메세지 페이지에 CTA 버튼 추가
2yunseong Mar 20, 2023
eaf2fbc
refactor: fetch 재사용화를 위한 책임 분리
2yunseong Mar 22, 2023
6e07659
fix: totalPage 상태값 갱신하지 않은 오류 해결
2yunseong Mar 23, 2023
2079624
test: 엘리먼트 선택자 변경
2yunseong Mar 23, 2023
7563963
refactor: custom component 네이밍 변경
2yunseong Mar 23, 2023
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules

src/constants/key.js
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
# javascript-movie-review

FE 5기 레벨1 영화관 미션

## 페어프로그래밍 룰

1. 드라이버가 주도 네비게이터가 조언
2. 드라이버 변경시간 30분
3. 네비게이터는 집중을 잃지 않는다.
4. 2시간마다 15분 휴식
5. 모르는 부분 / 논의가 필요한 부분 나오면 타이머 중단
6. 화장실이나 급한 사정은 유도리 있게

## 컴포넌트를 분리하려는 목적

1. 재사용성이 좋아진다.
- 문제 요구사항을 분석했을 때, 재사용 되는 요소들이 많았다고 생각이 들었다.(더보기 버튼, 영화 목록 아이템, 페이지 타이틀)
- 또한, 앱의 기능이 확장 되었을 때, 재사용 하려 만든 요소를 이용해 쉽게 기능을 확장할 수 있다고 판단이 들었다.
2. 가독성이 좋아진다.
- 파일을 컴포넌트 단위로 나누고, 컴포넌트를 UI, 기능별로 나누어 한 파일에 한 UI, 기능 단위의 로직만 들어가게 되어 개발자 입장에서 보기 좋은 프로젝트가 된다.
3. 독립성이 향상된다.
- 컴포넌트로 나누게 되면 각각의 다른 컴포넌트(기능)들의 간섭을 덜 받게 되어 로직의 안정성이 높아진다.

## 개발 환경

- node v16.14.0
- npm 8.3.1

## API Key 설정 방법

- API 키를 숨기기 위해 레퍼지토리에서 숨김 처리 해두었습니다.

1. src/constants 디렉토리 하위에 key.js 생성
2. 다음의 내용 입력

```javascript
export const API_KEY = "사용할 API 키";
```

## e2e 테스트 방법

1. npm run start로 local server 구동(포트 8081 고정)
2. npm run test-e2e로 cypress 구동
3. E2E-Testing -> Start E2E Testing in Chrome 클릭
4. cypress 창이 뜨면 movie-e2e.cy.ts 클릭
10 changes: 10 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from "cypress";

export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
supportFile: false,
},
});
190 changes: 190 additions & 0 deletions cypress/e2e/movie-e2e.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
describe("인기 영화 목록 페이지", () => {
beforeEach(() => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);
});
Comment on lines +2 to +5

Choose a reason for hiding this comment

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

👍
적용안된 부분이 보이는데 적용해주시면 좋을 것 같아요~

Copy link
Author

Choose a reason for hiding this comment

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

viewport 말씀하시는건가요?? intercept를 접속 이전에 실행해야 되는걸로 알고 있어 나머지는 중복으로 작성했는데, 다시 한번 알아볼께요!


it("초기 접속 시에 인기 영화목록 상위 20개가 화면에 출력된다.", () => {
cy.get("movie-list-page").find("movie-item").should("exist");
cy.get("movie-list-page").children().should("have.length", 20);
});

it("헤더 로고 클릭 시 처음 인기 영화목록이 출력된다.", () => {
cy.get("img[data-action='popular']").click();

cy.get("movie-list-page").find("movie-item").should("exist");
cy.get("movie-list-page").should("exist");
});
});

describe("인기 영화 목록 더보기", () => {
it("사용자가 마지막 페이지에 도달했을 때, 더보기 버튼이 화면에서 보이지 않아야 한다.", () => {
cy.intercept(
{
method: "GET",
url: /^https:\/\/api.themoviedb.org\/3\/movie\/popular*/,
},
{ fixture: "movie-popular.json" }
).as("getPopularMovies");

cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.wait("@getPopularMovies").then(() => {
cy.get("button[data-action='more_popular']").should("not.be.visible");
});
});
it("인기 영화 페이지에서 더보기 버튼 클릭시 영화 목록이 추가된다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.get("button[data-action='more_popular']").click();

cy.get("movie-list-page").find("movie-item").should("exist");
cy.get("movie-list-page").should("have.length", 2);
});
});

describe("검색 목록 확인", () => {
it("검색창에 해리포터를 입력하고 검색버튼 클릭시, 제목에 '해리포터'가 포함된 영화 목록이 화면에 나온다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.get(".search-box").find("input").type("해리포터");
cy.get("button[data-action='search']")
.click()
.then(() => {
cy.get(".item-title").each(() => {
cy.get("h3.item-title").contains(/해\s*리\s*포\s*터/);
});
});
});

it("검색창에 해리포터를 입력하고 엔터키를 누르면, 제목에 '해리포터'가 포함된 영화 목록이 화면에 나타난다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.get(".search-box")
.find("input")
.type("해리포터{enter}")
.then(() => {
cy.get(".item-title").each(() => {
cy.get("h3.item-title").contains(/해\s*리\s*포\s*터/);
});
});
});

it("검색창에 검색결과가 없는 검색어를 입력한 후 검색하는데, 결과가 존재하지 않을 시 검색결과 없음 페이지가 화면에 나타난다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.get(".search-box")
.find("input")
.type("sdmnfbikrdnfivjdnfodmfhfidff{enter}")
.then(() => {
cy.get(".no-result-title").contains("검색 결과를 찾지 못하였습니다.");
});
});

it("검색창에 입력을 하지 않고 엔터키를 누르면 사용자에게 검색어가 없다는 경고창이 화면에 나온다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

const stub = cy.stub();

cy.on("window:alert", stub);

cy.get(".search-box")
.find("input")
.type("{enter}")
.then(() => {
expect(stub.getCall(0)).to.be.calledWith("검색어를 입력해주세요.");
});
});

it("검색창에 입력을 하지 않고 검색버튼을 클릭하면 사용자에게 검색어가 없다는 경고창이 화면에 나온다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

const stub = cy.stub();

cy.on("window:alert", stub);

cy.get(".search-button")
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith("검색어를 입력해주세요.");
});
});

it("검색창에 공백만을 입력하고 검색버튼을 클릭하면 사용자에게 검색어가 없다는 경고창이 화면에 나온다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

const stub = cy.stub();
cy.on("window:alert", stub);

cy.get(".search-box").find("input").type(" ");
cy.get(".search-button")
.click()
.then(() => {
expect(stub.getCall(0)).to.be.calledWith("검색어를 입력해주세요.");
});
});

it("검색창에 공백만을 입력하고 엔터키를 누르면 사용자에게 검색어가 없다는 경고창이 화면에 나온다.", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

const stub = cy.stub();
cy.on("window:alert", stub);

cy.get(".search-box")
.find("input")
.type(" {enter}")
.then(() => {
expect(stub.getCall(0)).to.be.calledWith("검색어를 입력해주세요.");
});
});
});

describe("검색목록 더보기 확인", () => {
it("검색결과 페이지에서 더보기 버튼 클릭시 검색목록이 추가된다. ", () => {
cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.get(".search-box")
.find("input")
.type("전쟁{enter}")
.then(() => {
cy.get("button[data-action='more_search']").click();

cy.get("movie-list-page").find("movie-item").should("exist");
cy.get("movie-list-page").should("have.length", 2);
})
.then(() => {
cy.get("h3.item-title").each(() => {
cy.get("h3.item-title").contains(/전\s*쟁/);
});
});
});
});

describe("네트워크 에러 확인", () => {
it("네트워크 접속 오류 시 에러 페이지가 화면에 나타난다.", () => {
cy.intercept(
{
method: "GET",
url: /^https:\/\/api.themoviedb.org\/3\/movie\/popular*/,
},
{ statusCode: 401 }
).as("getErrorData");

cy.visit("http://localhost:8081/");
cy.viewport(1920, 1080);

cy.wait("@getErrorData").then(() => {
cy.get(".error-title").contains("오류");
});
});
});
Loading