-
Notifications
You must be signed in to change notification settings - Fork 171
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
[2단계 - 자동차 경주 구현] 지그(송지은) 미션 제출합니다. #36
Changes from 6 commits
3e43a7d
4bb068e
697adfe
04e9f2d
d2bdc65
107387d
d1b1187
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,10 @@ | ||
import { DELAY } from '../../src/js/constants.js'; | ||
|
||
describe('자동차 경주', () => { | ||
const DEFAULT_TRY_COUNT = 5 | ||
const DEFAULT_CAR_UNITS = 4 | ||
const DEFAULT_TRY_COUNT_TIME = DEFAULT_TRY_COUNT * DELAY.TURN_TIME; | ||
|
||
beforeEach(() => { | ||
cy.visit('http://localhost:5501'); | ||
}); | ||
|
@@ -10,7 +16,7 @@ describe('자동차 경주', () => { | |
cy.get('.car-name-btn').click(); | ||
} | ||
|
||
function clickAfterTypeTryCount(tryCount = 5) { | ||
function clickAfterTypeTryCount(tryCount = DEFAULT_TRY_COUNT) { | ||
if (tryCount) { | ||
cy.get('.try-count').type(tryCount); | ||
} | ||
|
@@ -88,10 +94,24 @@ describe('자동차 경주', () => { | |
exceptionAlert('.try-count', '정수를 입력해주세요.'); | ||
}); | ||
|
||
it('레이싱 진행 상황과 함께 우승자가 출력된다', () => { | ||
it('자동차 이름과 시도 횟수 입력 후 확인 버튼을 누르면 1초마다 spinner와 결과가 출력된다', () => { | ||
clickAfterTypeCar(); | ||
clickAfterTypeTryCount(); | ||
|
||
for (let i = 0; i < DEFAULT_TRY_COUNT; i++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
cy.get('.spinner-container').should('have.length', DEFAULT_CAR_UNITS); | ||
cy.wait(DELAY.SPINNER_SEC); | ||
cy.get('.spinner-container').should('not.be.visible'); | ||
cy.get('.forward-icon').should('be.visible'); | ||
cy.wait(DELAY.ARROW_DISPLAYING_SEC); | ||
} | ||
}) | ||
|
||
it('진행 상황이 모두 출력된 후 우승자를 출력한다.', () => { | ||
clickAfterTypeCar(); | ||
clickAfterTypeTryCount(); | ||
|
||
cy.wait(DEFAULT_TRY_COUNT_TIME); | ||
cy.get('.result-container').should('be.visible'); | ||
|
||
cy.document().then(doc => { | ||
|
@@ -114,11 +134,26 @@ describe('자동차 경주', () => { | |
}); | ||
}); | ||
|
||
it('다시 시작하기 버튼 클릭 시 게임이 리셋된다', () => { | ||
it('우승자 출력 후 2초 후에 축하의 alert 메시지를 띄운다.', () => { | ||
clickAfterTypeCar(); | ||
clickAfterTypeTryCount(); | ||
cy.get('.restart-btn').click(); | ||
|
||
cy.wait(DEFAULT_TRY_COUNT_TIME); | ||
|
||
const alertStub = cy.stub(); | ||
cy.on('window:alert', alertStub); | ||
cy.wait(DELAY.WAIT_ALERT_SEC).then(() => { | ||
expect(alertStub.getCall(0)).to.be.calledWith('축하합니다 🎉'); | ||
}); | ||
}) | ||
|
||
it('다시 시작하기 버튼 클릭 시 게임이 리셋된다.', () => { | ||
clickAfterTypeCar(); | ||
clickAfterTypeTryCount(); | ||
|
||
cy.wait(DEFAULT_TRY_COUNT_TIME); | ||
|
||
cy.get('.restart-btn').click(); | ||
resetUI(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 |
||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
.spinner-container { | ||
width: 25px; | ||
height: 25px; | ||
position: relative; | ||
margin: 10px auto; | ||
} | ||
|
||
.spinner::after { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import Car from './models/Car.js'; | ||
import RacingUI from './racingUI.js'; | ||
import { ALERT_MESSAGES, CLASS_NAMES } from './constants.js'; | ||
import { ALERT_MESSAGES, CLASS_NAMES, DELAY } from './constants.js'; | ||
import { | ||
isCarNameFilled, | ||
isCarNameUnderFive, | ||
|
@@ -61,30 +61,40 @@ export default class Racing { | |
}; | ||
|
||
startRace = () => { | ||
this.UIController.showResult(this.cars); | ||
this.moveCars(); | ||
this.getWinners(); | ||
}; | ||
|
||
moveCars = () => { | ||
for (let i = 0; i < this.tryCount; i++) { | ||
this.cars.forEach(car => car.move()); | ||
} | ||
this.UIController.showProgress(this.cars); | ||
const moveInterval = setInterval((function moveEverySecond() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런형태로 짜도 될 것 같네여 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 방법인 것 같습니다! 감사합니다 |
||
if (this.tryCount === 0) { | ||
clearInterval(moveInterval); | ||
this.getWinners(); | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다! 👍 👍 보통 위의 bad case에서 return을 하도록 코딩을 작성하면, 하위의 로직을 돌지 않고 해당 scope 밖으로 던져지므로 성능 (엄청 미비하지만...) 상에서도 좋습니다. 이렇게 return으로 bad case를 먼저 선언하고, 메인 로직 (지금과 같은 상황은 아래의 로직들이 되겠죠) 을 짜는게 일반적인 방법입니다! 문제 없어보입니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 유조님께 먼저 작성드린 피드백 내용인데, 참고하시면 좋을 것 같습니다 😄 |
||
} | ||
this.tryCount--; | ||
this.cars.forEach(car => { | ||
car.move(); | ||
const isCarMoved = car.isMoved; | ||
this.UIController.printProgress(car, isCarMoved); | ||
}); | ||
return moveEverySecond.bind(this); | ||
}).bind(this)(), DELAY.TURN_TIME) | ||
}; | ||
|
||
getWinners = () => { | ||
const sortedCars = this.cars.sort((a, b) => { | ||
return b.getPosition() - a.getPosition(); | ||
return b.position - a.position; | ||
}) | ||
|
||
let maxPosition = sortedCars[0].getPosition(); | ||
let maxPosition = sortedCars[0].position; | ||
for (let car of this.cars) { | ||
if (car.getPosition() === maxPosition) { | ||
if (car.position === maxPosition) { | ||
car.wins(); | ||
} | ||
} | ||
|
||
const winners = this.cars.filter(car => car.getIsWinner()).map(car => car.getName()); | ||
const winners = this.cars.filter(car => car.isWinner).map(car => car.name); | ||
Comment on lines
+89
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this.UIController.showWinners(winners); | ||
document.querySelector(CLASS_NAMES.RESTART_BTN).addEventListener('click', this.restartGame); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,38 @@ | ||
export default class Car { | ||
constructor(name) { | ||
this.name = name; | ||
this.position = 0; | ||
this.isWinner = false; | ||
this._name = name; | ||
this._position = 0; | ||
this._isWinner = false; | ||
this._isMoving = false; | ||
} | ||
|
||
getName() { | ||
return this.name; | ||
get name() { | ||
return this._name; | ||
} | ||
|
||
getPosition() { | ||
return this.position; | ||
get position() { | ||
return this._position; | ||
} | ||
|
||
getIsWinner() { | ||
return this.isWinner; | ||
get isWinner() { | ||
return this._isWinner; | ||
} | ||
Comment on lines
+9
to
19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른 멤버변수들에 대해서도 getter로 변경하고, 중복된 이름을 방지하기 위해 변수명에는 언더바를 붙였습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은것 같습니다! 멤버변수에 _를 붙이는 건 많은 팀에서 하는 코딩 컨벤션 중 하나입니다! |
||
|
||
move() { | ||
const randNumber = Math.random() * 10; | ||
if (randNumber >= 4) { | ||
this.position++; | ||
this._position++; | ||
this._isMoving = true; | ||
} else { | ||
this._isMoving = false; | ||
} | ||
} | ||
|
||
get isMoved() { | ||
return this._isMoving; | ||
} | ||
|
||
wins() { | ||
this.isWinner = true; | ||
this._isWinner = true; | ||
} | ||
Comment on lines
+31
to
37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,8 +14,24 @@ export default class RacingUI { | |
this.clearInput(CLASS_NAMES.TRY_COUNT); | ||
} | ||
|
||
showElement(className) { | ||
if (!document.querySelector(className)) { | ||
return; | ||
} | ||
const allElements = document.querySelectorAll(className) | ||
for (let i = 0; i < allElements.length; i++) { | ||
allElements[i].style.display = ''; | ||
} | ||
Comment on lines
+22
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
hideElement(className) { | ||
document.querySelector(className).style.display = 'none'; | ||
if (!document.querySelector(className)) { | ||
return; | ||
} | ||
Comment on lines
+28
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 선택자가 없어서 발생하는 에러의 경우 그냥 return하지 말고 |
||
const allElements = document.querySelectorAll(className) | ||
for (let i = 0; i < allElements.length; i++) { | ||
allElements[i].style.display = 'none'; | ||
Comment on lines
+32
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [...Array(allElements.length)].map(() => ...) |
||
} | ||
} | ||
|
||
clearText(className) { | ||
|
@@ -26,26 +42,36 @@ export default class RacingUI { | |
document.querySelector(className).value = ''; | ||
} | ||
|
||
showElement(className) { | ||
if (!document.querySelector(className)) { | ||
return; | ||
} | ||
document.querySelector(className).style.display = ''; | ||
} | ||
|
||
showProgress(cars) { | ||
showResult(cars) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 실시간으로 결과를 출력하는 |
||
this.showElement(CLASS_NAMES.PROGRESS_CONTAINER); | ||
|
||
document.querySelector(CLASS_NAMES.PROGRESS_CARS).innerHTML | ||
= cars.map(car => ` | ||
<div> | ||
<div class="car-player mr-2">${car.getName()}</div> | ||
${`<div class="forward-icon mt-2">⬇️️</div>`.repeat(car.getPosition())} | ||
<div class="car-player-container"> | ||
<div id="${car.name}" class="car-player mr-2">${car.name}</div> | ||
<div class="spinner-container"> | ||
<div class="material spinner"></div> | ||
</div> | ||
</div> | ||
`, | ||
).join(''); | ||
} | ||
|
||
printProgress(car, isCarMoved) { | ||
this.showElement(CLASS_NAMES.SPINNER_CONTAINER, true); | ||
|
||
const carElement = document.querySelector(`#${car.name}`); | ||
|
||
setTimeout(() => { | ||
this.hideElement(CLASS_NAMES.SPINNER_CONTAINER, false); | ||
if (isCarMoved) { | ||
carElement.insertAdjacentHTML('afterend', ` | ||
<div class="forward-icon mt-2">⬇️️</div> | ||
`) | ||
} | ||
}, 1000) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 매직넘버 대신 상수로 관리해주세요 😮 |
||
} | ||
|
||
showWinners(winners) { | ||
this.showElement(CLASS_NAMES.RESULT_CONTAINER); | ||
|
||
|
@@ -57,5 +83,9 @@ export default class RacingUI { | |
</div> | ||
</section> | ||
`; | ||
|
||
setTimeout(() => { | ||
alert('축하합니다 🎉') | ||
}, 2000); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 매직넘버 대신 상수로 관리해주세요 222 😮 |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
파라미터에 디폴트 값을 받고 있으니
tryCount
가 존재하는지 확인하는 if문은 없어도 될 것 같아요!