Skip to content

Commit

Permalink
Merge pull request #20 from hchayan/hchayan
Browse files Browse the repository at this point in the history
[1단계 - 자동차 경주 구현] 인치(한창희) 미션 제출합니다.
  • Loading branch information
Vallista committed Feb 13, 2021
2 parents bb0bad5 + 8fea822 commit 558dcfe
Show file tree
Hide file tree
Showing 22 changed files with 2,077 additions and 86 deletions.
61 changes: 20 additions & 41 deletions README.md
Expand Up @@ -14,55 +14,34 @@

## 🔥 Projects!
<p align="middle">
<img width="400" src="https://techcourse-storage.s3.ap-northeast-2.amazonaws.com/7c76e809d82a4a3aa0fd78a86be25427">
<img width="500" src="./documents/preview.gif">
</p>

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

### 🎯🎯 step2
- [ ] 자동차 경주 게임의 턴이 진행 될 때마다 1초의 텀(progressive 재생)을 두고 진행한다.
- [ ] 애니메이션 구현을 위해 setInterval, setTimeout, requestAnimationFrame 을 활용한다.
- [ ] 정상적으로 게임의 턴이 다 동작된 후에는 결과를 보여주고, 2초 후에 축하의 alert 메세지를 띄운다.
- [ ] 위 기능들이 정상적으로 동작하는지 Cypress를 이용해 테스트한다.
### 🧾 구현 테스트 목록
- [x] 자동차 이름을 입력하고 확인 버튼을 누르면 시도 횟수 입력 영역이 보여진다
- [x] 시도 횟수를 입력하고 확인 버튼 클릭하면 자동차 게임 진행 영역이 보여진다
- [x] 자동차 이름을 입력한 순서대로 자동차들을 생성한다
- [x] 시도 횟수보다 화살표의 개수가 적거나 같아야한다
- [x] 가장 많은 화살표를 가지고 있는 자동차의 이름이 우승자에 있어야 한다
- [x] 다시 시작하기 버튼을 누르면 자동차 입력 영역만 보이고, 입력값이 초기화된다
- [x] 자동차 이름이 5글자를 초과하면 안된다
- [x] 자동차 이름이 공백이면 안된다
- [x] 자동차 이름이 중복이면 안된다
- [x] 입력한 시도 횟수가 자연수가 아니면 안된다

<br>

## ⚙️ Before Started

#### <img alt="Tip" src="https://img.shields.io/static/v1.svg?label=&message=Tip&style=flat-square&color=673ab8"> 로컬에서 서버 띄워서 손쉽게 static resources 변경 및 확인하는 방법

로컬에서 웹서버를 띄워 html, css, js 등을 실시간으로 손쉽게 테스트해 볼 수 있습니다. 이를 위해서는 우선 npm이 설치되어 있어야 합니다. 구글에 `npm install` 이란 키워드로 각자의 운영체제에 맞게끔 npm을 설치해주세요. 이후 아래의 명령어를 통해 실시간으로 웹페이지를 테스트해볼 수 있습니다.

```
npm install -g live-server
```

실행은 아래의 커맨드로 할 수 있습니다.

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

<br>

## 👏 Contributing

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

<br>

## 🐞 Bug Report

버그를 발견한다면, [Issues](https://github.com/woowacourse/javascript-racingcar/issues) 에 등록 후 @eastjun에게 dm을 보내주세요.

<br>

## 📝 License

Expand Down
1 change: 1 addition & 0 deletions cypress.json
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions 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"
}
172 changes: 172 additions & 0 deletions cypress/integration/racingcar.spec.js
@@ -0,0 +1,172 @@
class CypressWrapper {
type = (name, value) => {
try {
this._getCy(name).type(value);
} catch (error) {
new Error(error);
}

return this;
};

click = name => {
try {
this._getCy(name).click();
} catch (error) {
new Error(error);
}

return this;
};

should = (name, ...param) => {
try {
this._getCy(name).should(...param);
} catch (error) {
new Error(error);
}

return this;
};

_getCy = name => {
return cy.get(name);
};
}

const cw = new CypressWrapper();

function testCar(value) {
cw.type("#car-input", value)
.click("#car-btn")
.should("#count", "have.css", "display", "block");
}

function testCount(value) {
cw.type("#count-input", value)
.click("#count-btn")
.should("#process", "have.css", "display", "block");
}

describe("ui-play", () => {
before(() => {
cy.visit("http://localhost:5500/");
});

it("자동차 섹션을 입력하고 버튼을 클릭하면 횟수 영역이 보여진다", () => {
testCar("a,b,c,d");
});

it("시도 횟수를 입력하고 버튼을 클릭하면 진행 영역이 보여진다", () => {
testCount(5);
});

it("자동차 이름을 입력한 순서대로 자동차들을 생성한다", () => {
const cars = ["a", "b", "c", "d"];
cy.get(".car-player").each((v, i, arr) => {
cw.should(v, "have.text", cars[i]);
});
});

it("시도 횟수보다 화살표의 개수가 적거나 같아야한다", () => {
cy.get(".process-car").each(v => {
if (v.find(".forward-icon").length > 0) {
cy.get(v).find(".forward-icon").its("length").should("be.lte", 5);
}
});
});

it("가장많은 화살표를 가지고 있는 차의 이름이 우승자에 있어야 한다", () => {
let largestCount = 0;
cy.get(".process-car")
.each(v => {
if (v.find(".forward-icon").length > largestCount) {
largestCount = v.find(".forward-icon").length;
}
})
.then(() => {
cy.get(".process-car").each(v => {
if (v.find(".forward-icon").length === largestCount) {
const winner = v.find(".car-player")[0].outerText;
cy.get("#result-winner").contains(winner);
}
});
});
});

it("다시 시작하기 버튼을 클릭하면 자동차 섹션만 보이고, 입력 값이 초기화된다", () => {
cw.click("#reset-btn")
.should("#count", "have.css", "display", "none")
.should("#process", "have.css", "display", "none")
.should("#result", "have.css", "display", "none");

cy.get("#process").children().should("not.exist");
cw.should("#car-input", "have.value", "");
});
});

describe("ui-input-vaild-check", () => {
beforeEach(() => {
cy.visit("http://localhost:5500/");
cy.window()
.then(win => cy.stub(win, "alert"))
.as("alertStub");
});

it("자동차 이름이 5글자 초과하면 alert 출력", () => {
testCar("overFive,a,b,c,d");
cw.should(
"@alertStub",
"be.calledWith",
"자동차 이름의 길이는 최대 5글자 입니다."
);
});

it("자동차 이름에 공백 있으면 alert 출력", () => {
testCar("a,b,,c,d");
cw.should(
"@alertStub",
"be.calledWith",
"자동차 이름은 공백이 될 수 없습니다."
);
});

it("자동차 이름에 중복 있으면 alert 출력", () => {
testCar("a,b,a,c,d");
cw.should(
"@alertStub",
"be.calledWith",
"자동차 이름은 중복이 될 수 없습니다."
);
});

it("입력한 시도 횟수가 공백이면 alert 출력", () => {
testCar("a,b,c,d");
testCount(" ");
cw.should(
"@alertStub",
"be.calledWith",
"시도 횟수는 공백 혹은 문자가 될 수 없습니다."
);
});

it("입력한 시도 횟수가 0 이하면 alert 출력", () => {
testCar("a,b,c,d");
testCount(-1);
cw.should(
"@alertStub",
"be.calledWith",
"시도 횟수는 0보다 작거나 같을 수 없습니다."
);
});

it("입력한 시도 횟수가 소수면 alert 출력", () => {
testCar("a,b,c,d");
testCount(4.21);
cw.should(
"@alertStub",
"be.calledWith",
"시도 횟수는 소수가 될 수 없습니다."
);
});
});
21 changes: 21 additions & 0 deletions cypress/plugins/index.js
@@ -0,0 +1,21 @@
/// <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}
*/
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
@@ -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
@@ -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')
Binary file added documents/preview.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 558dcfe

Please sign in to comment.