+### 배경 지식
+- [requestAnimationFrame()에 관한 설명](https://m.blog.naver.com/psj9102/221745767536)
+- [async & await](https://joshua1988.github.io/web-development/javascript/js-async-await/)
+ - [자바스크립트의 비동기 처리와 콜백함수](https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/)
+ - [Promise](https://joshua1988.github.io/web-development/javascript/promise-for-beginners/)
+> ```
+> async function 함수명() {
+> await 비동기_처리_메서드_명();
+> }
+> ```
+> 먼저 함수의 앞에 async 라는 예약어를 붙입니다. 그러고 나서 함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await를 붙입니다. 여기서 주의하셔야 할 점은 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작합니다.
+
+### 구현 사항
+- View
+ - [X] 시도 횟수 입력창 조회
+ - [X] 입력된 자동차 수대로 div 만들고 이름 설정하기
+ - [X] 원하는 플레이어에 화살표 추가하기
+ - [x] 스피너 끄기
+ - [x] 최종 우승자 조회
+ - [x] 다시 시작하기 버튼 조회
+
+- Controller
+ - [X] 이름 입력 버튼 이벤트(횟수 입력창 보여주기)
+ - [X] 시도 횟수 버튼 이벤트(게임시작)
+ - 자동차 경주 게임의 턴이 진행 될 때마다 1초의 텀(progressive 재생)을 두고 진행한다.
+ - [x] 다시 시작하기 이벤트
+
+- Model
+ - [X] Car
+
+
### 🎯 step1
-- [ ] 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
-- [ ] 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
-- [ ] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
-- [ ] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
-- [ ] 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
-- [ ] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
-- [ ] 우승자가 여러명일 경우 ,를 이용하여 구분한다.
+- [x] 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
+- [x] 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
+- [x] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
+- [x] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
+- [x] 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
+- [x] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
+- [x] 우승자가 여러명일 경우 ,를 이용하여 구분한다.
### 🎯🎯 step2
-- [ ] 자동차 경주 게임의 턴이 진행 될 때마다 1초의 텀(progressive 재생)을 두고 진행한다.
- - [ ] 애니메이션 구현을 위해 setInterval, setTimeout, requestAnimationFrame 을 활용한다.
-- [ ] 정상적으로 게임의 턴이 다 동작된 후에는 결과를 보여주고, 2초 후에 축하의 alert 메세지를 띄운다.
-- [ ] 위 기능들이 정상적으로 동작하는지 Cypress를 이용해 테스트한다.
+- [x] 자동차 경주 게임의 턴이 진행 될 때마다 1초의 텀(progressive 재생)을 두고 진행한다.
+ - [x] 애니메이션 구현을 위해 setInterval, setTimeout, requestAnimationFrame 을 활용한다.
+- [x] 정상적으로 게임의 턴이 다 동작된 후에는 결과를 보여주고, 2초 후에 축하의 alert 메세지를 띄운다.
+- [] 위 기능들이 정상적으로 동작하는지 Cypress를 이용해 테스트한다.
diff --git a/cypress.json b/cypress.json
new file mode 100644
index 000000000..b3845bc43
--- /dev/null
+++ b/cypress.json
@@ -0,0 +1,3 @@
+{
+ "baseUrl": "http://localhost:5501"
+}
diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json
new file mode 100644
index 000000000..02e425437
--- /dev/null
+++ b/cypress/fixtures/example.json
@@ -0,0 +1,5 @@
+{
+ "name": "Using fixtures to represent data",
+ "email": "hello@cypress.io",
+ "body": "Fixtures are a great way to mock data for responses to routes"
+}
diff --git a/cypress/integration/racingcar.js b/cypress/integration/racingcar.js
new file mode 100644
index 000000000..a061bdd1f
--- /dev/null
+++ b/cypress/integration/racingcar.js
@@ -0,0 +1,68 @@
+import { MSG } from '../../src/js/constants.js';
+
+const checkStyle = (elem, str) => {
+ cy.get(elem).should('have.attr', 'style', str);
+};
+
+const inputString = (elem, str) => {
+ cy.get(elem).type(str);
+};
+
+describe('이름 입력 테스트', () => {
+ it('6개 자동차 입력 시, 경고창 뜨는지 확인', () => {
+ cy.visit('/');
+ cy.get('#names input').type('a, b, c, d, e, f');
+ cy.on('window:alert', txt => {
+ expect(txt).to.contains(MSG.NAME_ERROR);
+ });
+ });
+ it('6자 이상의 이름을 입력했을 때, 경고창 뜨는지 확인', () => {
+ cy.reload();
+ cy.get('#names input').type('aaaaaa, b, c, d, e, f');
+ cy.on('window:alert', txt => {
+ expect(txt).to.contains(MSG.NAME_ERROR);
+ });
+ });
+ it('정상적인 이름 입력 시, 반복 횟수 입력창 뜨는지 확인', () => {
+ cy.reload();
+ cy.get('#names input').type('aa, bb, cc, dd, ee');
+ cy.get('#names button').click();
+ checkStyle('#repetition', 'display: block;');
+ });
+});
+
+describe('숫자 입력 테스트', () => {
+ it('음수 입력했을 때, 경고창 뜨고 입력이 지워졌는지 확인', () => {
+ cy.get('#repetition input').type('-1');
+ cy.get('#repetition button').click();
+ cy.on('window:alert', txt => {
+ expect(txt).to.contains(MSG.REPETITION_ERROR);
+ });
+ cy.get('#repetition input').should('have.value', '');
+ });
+ it('4를 입력했을 때, result 섹션이 뜨는지 확인', () => {
+ cy.get('#repetition input').type('4');
+ cy.get('#repetition button').click();
+ checkStyle('#result', 'display: block;');
+ });
+ it('이어서 champion 섹션이 뜨는지 확인', () => {
+ checkStyle('#champion', 'display: block;');
+ });
+ it('경기 종료 메세지가 뜨는지 확인', () => {
+ cy.wait(2000);
+ cy.on('window:alert', txt => {
+ expect(txt).to.contains(MSG.GAMEOVER_MSG);
+ });
+ });
+});
+
+describe('다시 시작하기 테스트', () => {
+ it('다시 시작하기 버튼을 눌렀을 때, 자동차 이름 입력칸이 비워졌는지 확인', () => {
+ cy.get('#champion button').click();
+ cy.get('#names input').should('have.value', '');
+ });
+ it('result와 champion의 style이 display: none으로 설정됐는지 확인', () => {
+ checkStyle("#result", 'display: none;');
+ checkStyle("#champion", 'display: none;');
+ });
+});
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
new file mode 100644
index 000000000..59b2bab6e
--- /dev/null
+++ b/cypress/plugins/index.js
@@ -0,0 +1,22 @@
+///
+// ***********************************************************
+// This example plugins/index.js can be used to load plugins
+//
+// You can change the location of this file or turn off loading
+// the plugins file with the 'pluginsFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/plugins-guide
+// ***********************************************************
+
+// This function is called when a project is opened or re-opened (e.g. due to
+// the project's config changing)
+
+/**
+ * @type {Cypress.PluginConfig}
+ */
+// eslint-disable-next-line no-unused-vars
+module.exports = (on, config) => {
+ // `on` is used to hook into various events Cypress emits
+ // `config` is the resolved Cypress config
+}
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
new file mode 100644
index 000000000..119ab03f7
--- /dev/null
+++ b/cypress/support/commands.js
@@ -0,0 +1,25 @@
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
diff --git a/cypress/support/index.js b/cypress/support/index.js
new file mode 100644
index 000000000..d68db96df
--- /dev/null
+++ b/cypress/support/index.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/index.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/index.html b/index.html
index ea5891942..d964d231c 100644
--- a/index.html
+++ b/index.html
@@ -30,37 +30,12 @@