Skip to content

Commit

Permalink
[클린코드 1기 조은규] [Step1] 자동차 경주 미션 (next-step#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
EungyuCho authored Dec 1, 2021
1 parent 362a393 commit 32cae54
Show file tree
Hide file tree
Showing 20 changed files with 669 additions and 47 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
yarn.lock
dist
build
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,33 @@
</p>

## 🔥 Projects!

<p align="middle">
<img width="400" src="https://techcourse-storage.s3.ap-northeast-2.amazonaws.com/7c76e809d82a4a3aa0fd78a86be25427">
</p>

<p align="middle">
<a href="https://next-step.github.io/js-racingcar/">🖥️ 데모 링크</a>
<a href="https://eungyucho.github.io/js-racingcar/">🖥️ 데모 링크</a>
</p>

### 🎯 step1
- [ ] 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- [ ] 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- [ ] 자동차 이름은 `쉼표(,)`를 기준으로 구분하며 이름은 5자 이하만 가능하다.
- [ ] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- [ ] 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.
- [ ] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
- [ ] 우승자가 여러명일 경우 `,`를 이용하여 구분한다.

- [x] 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- [x] 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- [x] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
- [x] 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- [x] 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다.

### 🎯🎯 step2

- [ ] 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
- [ ] 우승자가 여러명일 경우 ,를 이용하여 구분한다.

### 🎯🎯 step3

- [ ] 자동차 경주 게임의 턴이 진행 될 때마다 1초의 텀(progressive 재생)을 두고 진행한다.
- [ ] 애니메이션 구현을 위해 `setInterval`, `setTimeout`, `requestAnimationFrame` 을 활용한다.
- [ ] 애니메이션 구현을 위해 `setInterval`, `setTimeout`, `requestAnimationFrame` 을 활용한다.
- [ ] 정상적으로 게임의 턴이 다 동작된 후에는 결과를 보여주고, 2초 후에 축하의 alert 메세지를 띄운다.
- [ ] 위 기능들이 정상적으로 동작하는지 Cypress를 이용해 테스트한다.

<br>

Expand All @@ -58,7 +63,7 @@ live-server 폴더명

## 👏 Contributing

만약 미션 수행 중에 개선사항이 보인다면, 언제든 자유롭게 PR을 보내주세요.
만약 미션 수행 중에 개선사항이 보인다면, 언제든 자유롭게 PR을 보내주세요.

<br>

Expand Down
1 change: 1 addition & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
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"
}
79 changes: 79 additions & 0 deletions cypress/integration/racing.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/// <reference types="cypress" />

import {
CAR_NAMES,
CAR_NAMES_ARRAY,
WRONG_CAR_NAMES,
} from '../support/constants'

describe('🏎️ 자동차 경주 게임', () => {
beforeEach(() => {
cy.visit('https://eungyucho.github.io/js-racingcar/')
cy.get('[id=car_name_input]').as('carNameInput')
cy.get('[id=car_name_button]').as('carNameButton')
cy.get('[id=game_count_input]').as('gameCountInput')
cy.get('[id=game_count_button]').as('gameCountButton')
cy.get('[id=game_count_button]').as('gameCountButton')
})

it('유효하지 않은 자동차 이름을 입력하면 경고메세지를 띄워준다.', () => {
cy.get('@carNameInput').should('be.visible')
cy.get('@carNameButton').should('be.visible')
cy.get('@gameCountInput').should('not.be.visible')
cy.get('@gameCountButton').should('not.be.visible')

cy.get('@carNameInput').type(WRONG_CAR_NAMES)
cy.alertMessageWillBeEqual(
'자동차의 이름은 한글자 ~ 5글자 사이만 가능합니다.'
)
cy.get('@carNameButton').click()
cy.get('@gameCountInput').should('not.be.visible')
cy.get('@gameCountButton').should('not.be.visible')
})

it('유효한 자동차 이름을 입력하면 시도할 횟수를 입력받을 수 있다.', () => {
cy.get('@carNameInput').should('be.visible')
cy.get('@carNameButton').should('be.visible')
cy.get('@gameCountInput').should('not.be.visible')
cy.get('@gameCountButton').should('not.be.visible')

cy.get('@carNameInput').type(CAR_NAMES)
cy.get('@carNameButton').click()
cy.get('@gameCountInput').should('be.visible')
cy.get('@gameCountButton').should('be.visible')
})

it('게임횟수에 3을 입력하면 횟수를 입력하면 입력했던 자동차 이름들이 Road에 세팅된다.', () => {
cy.get('@carNameInput').type(CAR_NAMES)
cy.get('@carNameButton').click()
cy.get('@gameCountInput').type(3)
cy.get('@gameCountButton').click('')

cy.getPlayers()
.should('be.visible')
.should('have.length', 4)
.each((player, index) =>
cy.wrap(player).should('have.text', CAR_NAMES_ARRAY[index])
)
})

it('게임횟수에 3을 입력하면 횟수를 입력하면 게임을 시작한다.', () => {
cy.get('@carNameInput').type(CAR_NAMES)
cy.get('@carNameButton').click()
cy.get('@gameCountInput').type(3)
cy.get('@gameCountButton').click('')

cy.wait(3000)

cy.getPlayers()
.should('be.visible')
.should('have.length', 4)
.each((player) =>
cy
.wrap(player)
.parent()
.children('.forward-icon')
.should('have.length.lessThan', 4)
)
})
})
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
}
9 changes: 9 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Cypress.Commands.add('alertMessageWillBeEqual', (text) => {
cy.on('window:alert', (txt) => {
expect(text).to.equal(txt)
})
})

Cypress.Commands.add('getPlayers', () => {
return cy.get('.car-player')
})
5 changes: 5 additions & 0 deletions cypress/support/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const CAR_NAMES = 'EAST, WEST, SOUTH, NORTH'
const CAR_NAMES_ARRAY = CAR_NAMES.split(',').map((name) => name.trim())
const WRONG_CAR_NAMES = CAR_NAMES + ', VERY_LONG_NAME'

export { CAR_NAMES, WRONG_CAR_NAMES, CAR_NAMES_ARRAY }
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')
63 changes: 27 additions & 36 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,57 @@
<head>
<meta charset="UTF-8" />
<title>🏎️ 자동차 경주 게임</title>
<script type="module" src="./build/index.js"></script>
<link rel="stylesheet" href="./src/css/index.css" />
</head>
<body>
<div id="app">
<section class="d-flex justify-center mt-5">
<form>
<fieldset>
<fieldset class="car-name-fieldset">
<h1 class="text-center">🏎️ 자동차 경주 게임</h1>
<p>
5자 이하의 자동차 이름을 콤마로 구분하여 입력해주세요. <br />
예시) EAST, WEST, SOUTH, NORTH
</p>
<div class="d-flex">
<input type="text" class="w-100 mr-2" placeholder="자동차 이름" />
<button type="button" class="btn btn-cyan">확인</button>
<input
type="text"
class="w-100 mr-2"
placeholder="자동차 이름"
id="car_name_input"
/>
<button type="button" class="btn btn-cyan" id="car_name_button">
확인
</button>
</div>
</fieldset>
<fieldset>
<fieldset class="game-count-fieldset">
<p>시도할 횟수를 입력해주세요.</p>
<div class="d-flex">
<input type="number" class="w-100 mr-2" placeholder="시도 횟수" />
<button type="button" class="btn btn-cyan">확인</button>
<input
type="number"
class="w-100 mr-2"
placeholder="시도 횟수"
id="game_count_input"
/>
<button type="button" class="btn btn-cyan" id="game_count_button">
확인
</button>
</div>
</fieldset>
</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>
<section class="d-flex justify-center mt-5 racing-road-section">
<div class="mt-4 d-flex" id="racing-road-container"></div>
</section>
<section class="d-flex justify-center mt-5">
<section class="d-flex justify-center mt-5 winner-section">
<div>
<h2>🏆 최종 우승자: EAST, WEST 🏆</h2>
<div class="d-flex justify-center">
<button type="button" class="btn btn-cyan">다시 시작하기</button>
<button type="button" class="btn btn-cyan" id="reset_button">
다시 시작하기
</button>
</div>
</div>
</section>
Expand Down
13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"scripts": {
"build": "esbuild --bundle src/css/index.css --outfile=build/out.css",
"deploy": "gh-pages -d build",
"start": "esbuild src/ts/index.ts --bundle --outfile=build/index.js --watch"
},
"devDependencies": {
"cypress": "^9.1.0",
"esbuild": "^0.13.15",
"gh-pages": "^3.2.3",
"typescript": "^4.5.2"
}
}
10 changes: 10 additions & 0 deletions src/ts/constants/ErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const NAME_LENGTH_INVALID_ERROR =
'자동차의 이름은 한글자 ~ 5글자 사이만 가능합니다.'
const GAME_COUNT_INVALID_ERROR = '게임횟수는 1이상으로 입력해주세요.'

const ERROR_MESSAGES = Object.freeze({
NAME_LENGTH_INVALID_ERROR,
GAME_COUNT_INVALID_ERROR,
})

export default ERROR_MESSAGES
Loading

0 comments on commit 32cae54

Please sign in to comment.