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

[yechoi] typescript-racingcar #2

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:cypress/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
}
};
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

.vscode
cypress/integration/examples

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"liveServer.settings.port": 5501,
"typescript.tsdk": "node_modules/typescript/lib"
}
53 changes: 42 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,51 @@
<img width="400" src="https://techcourse-storage.s3.ap-northeast-2.amazonaws.com/7c76e809d82a4a3aa0fd78a86be25427">
</p>

### 배경 지식
- [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를 이용해 테스트한다.

<br>

Expand Down
3 changes: 3 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"baseUrl": "http://localhost:5501"
}
5 changes: 5 additions & 0 deletions cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "[email protected]",
"body": "Fixtures are a great way to mock data for responses to routes"
}
68 changes: 68 additions & 0 deletions cypress/integration/racingcar.js
Original file line number Diff line number Diff line change
@@ -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;');
});
});
22 changes: 22 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// 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
}
25 changes: 25 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -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) => { ... })
20 changes: 20 additions & 0 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -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')
30 changes: 3 additions & 27 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,42 +30,18 @@ <h1 class="text-center">🏎️ 자동차 경주 게임</h1>
</form>
</section>
<section class="d-flex justify-center mt-5">
<div class="mt-4 d-flex">
<div class="mr-2">
<div class="car-player">EAST</div>
<div class="forward-icon mt-2">⬇️️</div>
<div class="forward-icon mt-2">⬇️️</div>
</div>
<div class="mr-2">
<div class="car-player">WEST</div>
<div class="forward-icon mt-2">⬇️️</div>
</div>
<div class="mr-2">
<div class="car-player">SOUTH</div>
<div class="d-flex justify-center mt-3">
<div class="relative spinner-container">
<span class="material spinner"></span>
</div>
</div>
</div>
<div class="mr-2">
<div class="car-player">NORTH</div>
<div class="d-flex justify-center mt-3">
<div class="relative spinner-container">
<span class="material spinner"></span>
</div>
</div>
</div>
<div class="mt-4 justify-center d-flex">
</div>
</section>
<section class="d-flex justify-center mt-5">
<div>
<h2>🏆 최종 우승자: EAST, WEST 🏆</h2>
<h2 class="d-flex justify-center"></h2>
<div class="d-flex justify-center">
<button type="button" class="btn btn-cyan">다시 시작하기</button>
</div>
</div>
</section>
</div>
</body>
<script type="module" src="./src/js/index.js"></script>
</html>
Loading