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

[2단계 - 행운의 로또 미션] 서니(박선희) 미션 제출합니다. #53

Merged
merged 34 commits into from
Feb 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ad33d0b
refactor: 코드 리뷰에 따른 리팩토링 진행
Puterism Feb 19, 2021
94c61bf
Fix: 중복된 CSS 선택자 제거
Puterism Feb 21, 2021
c0c4a4e
Docs: README.md에 Step2 기능 및 테스트 항목 추가
Puterism Feb 21, 2021
ac13664
Test: 입력 폼에 autoFocus가 적용되는지 확인하도록 테스트 코드 수정
Puterism Feb 21, 2021
b48a6c5
Test: 모달에서 당첨 개수와 수익률이 정상 출력되는지 확인하는 테스트 케이스 추가
Puterism Feb 21, 2021
2f22269
Test: 다시 시작할 때, 구입할 금액 입력 폼만 보이는지 확인하는 테스트 케이스 추가
Puterism Feb 21, 2021
57989a5
Test: 닫기 버튼을 눌렀을 때, 모달이 잘 닫히는지 확인하는 테스트 케이스 추가
Puterism Feb 21, 2021
168e387
Test: 당첨 번호에 2자리 숫자 입력 시, 다음 폼에 autoFocus되는지 확인하는 테스트 케이스 추가
Puterism Feb 21, 2021
bdb7439
Test: 당첨 결과 확인 메소드에 대한 테스트 케이스 추가
Puterism Feb 21, 2021
55e5233
Test: 당첨 번호 입력을 검증하는 테스트 케이스 추가
Puterism Feb 21, 2021
4468432
Feat: 입력 폼에 autofocus 기능 추가
Puterism Feb 21, 2021
0857c93
Refactor: DocumentFragment를 이용한 DOM append 성능 최적화
Puterism Feb 21, 2021
6059757
Merge branch 'cheffe-step1' into cheffe-sunny-step2
Puterism Feb 21, 2021
d75e251
Feat: 당첨 번호를 입력하는 기능
Puterism Feb 21, 2021
3e25871
Feat: 모달 닫기 기능 구현
Puterism Feb 21, 2021
8749ebc
Feat: 당첨 통계 및 수익률을 모달로 확인하는 기능 구현
Puterism Feb 22, 2021
66f7882
Feat: 다시 시작하기 기능 추가
Puterism Feb 22, 2021
0d53698
Refactor: 로또 번호 생성 및 당첨 확인 메소드를 Lotto 클래스로 이동
Puterism Feb 23, 2021
b77a5be
Test (Refactor): 마크업 변경 및 기능 구현에 따른 테스트 코드 수정
Puterism Feb 23, 2021
8e400a0
Refactor: 불필요하게 분리된 메소드 통합
Puterism Feb 23, 2021
a1c59c6
Merge branch 'cheffe-sunny-step2' of https://github.com/Puterism/java…
sunhpark42 Feb 25, 2021
4cd25ee
Chore: 디렉토리 이름 수정
sunhpark42 Feb 25, 2021
c9c2e8b
Refactor: lottoApp의 할당 및 선언
sunhpark42 Feb 25, 2021
042885f
Todo: 수정할 사항 정리 및 주석 작성
sunhpark42 Feb 26, 2021
1316a06
Refactor: if, else if 문을 object Literal 로 변경함
sunhpark42 Feb 26, 2021
0c5e85a
Refactor: 초기화 메서드 분리
sunhpark42 Feb 26, 2021
9f6630c
Refactor: 중복되는 css 속성 처리
sunhpark42 Feb 26, 2021
68476f9
Refactor: winningRankCount object 를 getResult에서 선언
sunhpark42 Feb 26, 2021
cabe5ab
Fix: 일치 개수에 따른 등수를 return 할 때 number[]가 return 되는 에러 수정
sunhpark42 Feb 26, 2021
7df8239
Refactor: 로또 관련 값을 가져오는 함수를 lottoUtils로 분리
sunhpark42 Feb 26, 2021
8dc3f11
Fix: 웹 접근성과 관련하여 html 구조 수정
sunhpark42 Feb 26, 2021
c57eef4
Refactor: selector 상수화
sunhpark42 Feb 26, 2021
c071f43
add: change image for structure
sunhpark42 Feb 26, 2021
2afcf5a
Fix: 마크업 에러 수정
sunhpark42 Feb 28, 2021
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
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ src/js
### step1 테스트 항목

- [x] 사용자가 로또 구입 금액을 입력하고 확인 버튼을 누르면 금액에 맞는 로또가 화면에 보여진다.
- [x] 구입 금액 입력 폼에 autoFocus가 되는지 확인한다.
- [x] 구입 금액 입력 후, 당첨 번호 입력 폼에 autoFocus가 되는지 확인한다.
- [x] 사용자가 토글 버튼을 누르면 로또의 번호를 볼 수 있다.
- [x] 각 로또 안의 번호가 중복되지 않았는지 확인한다.

Expand All @@ -99,9 +101,32 @@ src/js

### 🎯🎯 step2 당첨 결과 기능

- [ ] 결과 확인하기 버튼을 누르면 당첨 통계, 수익률을 모달로 확인할 수 있다.
- [ ] 로또 당첨 금액은 고정되어 있는 것으로 가정한다.
- [ ] 다시 시작하기 버튼을 누르면 초기화 되서 다시 구매를 시작할 수 있다.
- [x] 페이지를 처음 로드했을 때, 구입할 금액 입력 폼에 autoFocus되도록 수정 구현 (step1 보완)
- [x] 구입할 금액 입력 후, 당첨 번호 입력 폼에 autoFocus되도록 구현
- [x] 당첨 번호 입력 중, 2자리 숫자가 입력되면 바로 다음 입력 폼으로 넘어가도록 구현
- [x] 결과 확인하기 버튼을 누르면 당첨 통계, 수익률을 모달로 확인할 수 있다.
- [x] 결과 확인하기 버튼을 누르면 모달이 뜨도록 구현
- [x] 입력된 당첨 번호가 1 ~ 45 사이의 중복되지 않는 숫자인지 확인한다.
- [x] 모달이 떴을 때, 당첨금의 당첨 개수와 총 수익률이 계산되어 보여지도록 구현
- [x] 모달이 뜬 상태에서 닫기를 눌렀을 때, 기존 화면에 변화가 없도록 한다.
- [x] 로또 당첨 금액은 고정되어 있는 것으로 가정한다.
- [x] 다시 시작하기 버튼을 누르면 초기화 되서 다시 구매를 시작할 수 있다.
- [x] 처음 접속했을 때의 화면(구입할 금액 입력 폼만 보이는 상태)처럼 초기화한다.

### step2 테스트 항목

- [x] 로또 구입 후, 당첨 번호를 입력하고 결과 확인하기 버튼을 누르면, 모달에서 당첨 개수와 총 수익률을 확인할 수 있다.
- [x] 모달이 잘 뜨는지 확인한다.
- [x] 당첨금과 수익률이 정상 출력되는지 확인한다.
- [x] (Assertion) 로또 번호와 당첨 번호를 비교하여 일치하는 갯수가 정확하게 나오는지 확인한다.
- [x] 다시 시작하기 버튼을 눌렀을 때, 구입할 금액 입력 폼만 보이는지 확인한다.
- [x] 닫기 버튼을 눌렀을 때, 모달이 잘 닫히는지 확인한다.
- [x] 당첨 번호를 입력할 때, 2자리 숫자가 입력되면, 자동으로 다음 폼으로 focus되는지 확인한다.

#### 예외사항(테스트)

- [x] 입력된 당첨 번호가 1 ~ 45 사이의 숫자가 아니거나, 중복되면
- [x] '1 ~ 45 사이의 숫자를 중복되지 않게 입력해주세요' 라는 경고창을 띄운다.

### 🎯🎯🎯 step3 수동 구매

Expand Down
149 changes: 143 additions & 6 deletions cypress/integration/lotto.spec.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import { ALERT_MESSAGE } from '../../src/js/constants.js';
import { ALERT_MESSAGE, VALUE } from '../../src/js/constants.js';
import Lotto from '../../src/js/models/Lotto.js';

describe('LOTTO 테스트', () => {
beforeEach(() => {
cy.visit('http://localhost:5500/');
});

it('사용자가 로또 구입 금액을 입력하고 확인 버튼을 누르면 금액에 맞는 로또가 화면에 보여진다.', () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 로직이 점점 길어지고 있는데, 테스트 로직을 분리해보는 건 어떨까요?
가령,도메인 단위로 나눠볼 수도 있겠구요! UI, 기능테스트로 분리해볼 수도 있겠구요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 기능이 증가함에 따라서 점점 길어지고 있는 것 같아 분리를 해볼 계획이었습니다 : ) 이전에는 에러처리와 메인 기능으로 분리했었는데, 도메인이나 UI/기능으로 구분하는 것도 좋네요!

cy.get('.lotto-list-container').should('not.be.visible');
cy.get('.winning-number-form-container').should('not.be.visible');
cy.get('.lotto-list-section').should('not.be.visible');
cy.get('.winning-number-form-section').should('not.be.visible');

cy.get('#money-input').type('10000');
// Note: Cypress 체크 시 autofocus가 잡히지 않는 문제가 있어 수동으로 focus를 잡아둠
cy.get('#money-input').should('have.attr', 'autofocus', 'autofocus').focus();

cy.focused().should('have.attr', 'id', 'money-input').type('10000');
cy.get('#money-submit-button').click();

cy.get('.lotto-list-container').should('be.visible');
cy.get('.winning-number-form-container').should('be.visible');
cy.get('.lotto-list-section').should('be.visible');
cy.get('.winning-number-form-section').should('be.visible');

cy.get('.lotto-count').should('have.text', '10');
cy.get('.lotto').should('have.length', '10');

cy.get('.winning-number').first().should('be.focused');
});

it('사용자가 토글 버튼을 누르면 로또의 번호를 볼 수 있다.', () => {
Expand Down Expand Up @@ -72,4 +78,135 @@ describe('LOTTO 테스트', () => {

cy.get('.lotto-list').children().should('have.length', 5);
});

it('로또 구입 후, 당첨 번호를 입력하고 결과 확인하기 버튼을 누르면, 모달에서 당첨 개수와 총 수익률을 확인할 수 있다.', () => {
const winningNumbers = [9, 11, 3, 25, 21, 2];
cy.get('#money-input').type('10000');
cy.get('#money-submit-button').click();

cy.get('.winning-number').each((winningNumberInput, index) => {
cy.wrap(winningNumberInput).type(winningNumbers[index]);
});
cy.get('.bonus-number').type(45);
cy.get('.open-result-modal-button').click();

cy.get('.modal').should('be.visible');

cy.get('.winning-count').each((count) => {
cy.wrap(count)
.invoke('text')
.should('match', /^[0-9]+$/);
});

cy.get('.winning-rate')
.first()
.invoke('text')
.should('match', /^[0-9]+\.[0-9]+$/);
});

it('다시 시작하기 버튼을 눌렀을 때, 구입할 금액 입력 폼만 보이는지 확인한다.', () => {
const winningNumbers = [9, 11, 3, 25, 21, 2];
cy.get('#money-input').type('10000');
cy.get('#money-submit-button').click();

cy.get('.winning-number').each((winningNumberInput, index) => {
cy.wrap(winningNumberInput).type(winningNumbers[index]);
});
cy.get('.bonus-number').type(45);
cy.get('.open-result-modal-button').click();

cy.get('.modal').should('be.visible');

cy.get('.restart-button').click();

cy.get('#money-input').should('have.value', '').and('be.focused');
cy.get('.lotto-list-section').should('not.be.visible');
cy.get('.winning-number-form-section').should('not.be.visible');
cy.get('.modal').should('not.be.visible');
});

it('닫기 버튼을 눌렀을 때, 모달이 잘 닫히는지 확인한다.', () => {
const winningNumbers = [9, 11, 3, 25, 21, 2];
cy.get('#money-input').type('10000');
cy.get('#money-submit-button').click();

cy.get('.winning-number').each((winningNumberInput, index) => {
cy.wrap(winningNumberInput).type(winningNumbers[index]);
});
cy.get('.bonus-number').type(45);
cy.get('.open-result-modal-button').click();

cy.get('.modal').should('be.visible');
cy.get('.modal-close').click();

cy.get('.modal').should('not.be.visible');
});

it('당첨 번호를 입력할 때, 2자리 숫자가 입력되면, 자동으로 다음 폼으로 focus되는지 확인한다.', () => {
const winningNumbers = [10, 11, 30, 25, 21, 20];
cy.get('#money-input').type('10000');
cy.get('#money-submit-button').click();

cy.get('.winning-number').each((winningNumberInput, index) => {
cy.wrap(winningNumberInput)
.should('have.value', '')
.and('be.focused')
.type(winningNumbers[index]);
});
cy.get('.bonus-number').should('have.value', '').and('be.focused').type(45);
});

it('(Assertion) 로또 번호와 당첨 번호를 비교하여 일치하는 갯수가 정확하게 나오는지 확인한다.', () => {
const winningNumbers = [3, 9, 11, 20, 21, 25];
const bonusNumber = 45;

const lottos = [
{
sample: new Lotto([3, 9, 11, 20, 21, 25]),
rank: VALUE.RANK.FIRST,
},
{
sample: new Lotto([3, 9, 11, 20, 21, 45]),
rank: VALUE.RANK.SECOND,
},
{
sample: new Lotto([3, 9, 11, 20, 21, 44]),
rank: VALUE.RANK.THIRD,
},
{
sample: new Lotto([3, 9, 11, 20, 22, 44]),
rank: VALUE.RANK.FOURTH,
},
{
sample: new Lotto([3, 9, 11, 19, 22, 44]),
rank: VALUE.RANK.FIFTH,
},
];

lottos.forEach(({ sample, rank }) => {
expect(sample.getWinningRank(winningNumbers, bonusNumber)).to.be.equal(rank);
});
});

it('입력된 당첨 번호가 중복되면 경고창을 띄운다.', () => {
const winningNumbers = [9, 11, 9, 1, 21, 45];
const bonusNumber = 10;
const alertStub = cy.stub();

cy.get('#money-input').type('10000');
cy.get('#money-submit-button').click();

cy.get('.winning-number').each((winningNumberInput, index) => {
cy.wrap(winningNumberInput).type(winningNumbers[index]);
});

cy.get('.bonus-number').type(bonusNumber);

cy.on('window:alert', alertStub);
cy.get('.open-result-modal-button')
.click()
.then(() => {
expect(alertStub.getCall(0)).to.be.calledWith(ALERT_MESSAGE.INVALID_WINNING_NUMBER_INPUT);
});
});
});
68 changes: 39 additions & 29 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,63 +9,73 @@
</head>

<body>
<div id="app" class="p-3">
<main id="app" class="p-3">
<div class="d-flex justify-center mt-5">
<div class="w-100">
<h1 class="text-center">🎱 행운의 로또</h1>
<form class="mt-5" id="money-input-form">
<label class="mb-2 d-inline-block">구입할 금액을 입력해주세요.</label>
<label for="money-input" class="mb-2 d-inline-block">구입할 금액을 입력해주세요.</label>
<div class="d-flex">
<input id="money-input" type="number" min="0" class="w-100 mr-2 pl-2" placeholder="구입 금액" />
<input id="money-input" type="number" min="0" class="w-100 mr-2 pl-2" placeholder="구입 금액" autofocus
required />
<button id="money-submit-button" type="submit" class="btn btn-cyan">확인</button>
</div>
</form>
<section class="lotto-list-container mt-9 hidden">
<section class="lotto-list-section mt-9 hidden">
<div class="d-flex">
<label class="flex-auto my-0">총 <span class="lotto-count">
<!--로또 개수--></span>개를 구매하였습니다.</label>
<div class="flex-auto d-flex justify-end pr-1">
<label class="switch" id="lotto-numbers-toggle">
<input type="checkbox" class="lotto-numbers-toggle-button" />
<label for="lotto-numbers-toggle-button" class="switch" id="lotto-numbers-toggle">
<input type="checkbox" id="lotto-numbers-toggle-button" class="lotto-numbers-toggle-button" />
<span class="text-base font-normal">번호보기</span>
</label>
</div>
</div>
<div class="lotto-list d-flex flex-wrap">
<div class="lotto-list-container d-flex flex-wrap">
<!-- 로또 목록 -->
</div>
</section>
<section class="winning-number-form-container hidden">
<form class="mt-9">
<label class="flex-auto d-inline-block mb-3">지난 주 당첨번호 6개와 보너스 넘버 1개를 입력해주세요.</label>
<section class="winning-number-form-section hidden">
<form class="mt-9" id="winning-number-form">
<label class="flex-auto d-inline-block mb-3">지난 주 당첨번호 6개와 보너스 넘버 1개를
입력해주세요.</label>
<div class="d-flex">
<div>
<h4 class="mt-0 mb-3 text-center">당첨 번호</h4>
<label for="first-winning-number" class="mt-0 mb-3 text-center text-base d-block font-semibold">당첨
번호</label>
<div>
<input type="number" class="winning-number mx-1 text-center" />
<input type="number" class="winning-number mx-1 text-center" />
<input type="number" class="winning-number mx-1 text-center" />
<input type="number" class="winning-number mx-1 text-center" />
<input type="number" class="winning-number mx-1 text-center" />
<input type="number" class="winning-number mx-1 text-center" />
<input type="number" name="winning-number" aria-label="winning-number1" id="first-winning-number"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

접근성 측면에서 사용하신 부분은 좋아보입니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id="first-winning-number"는 어디서 쓰일까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분은 label에서 for을 이용하기 위해서 속성을 주었습니다. 지금 보니 이렇게 사용할거면 js 내에서 focus를 줄 때 이용했어도 좋을 것 같네요.

class="winning-number mx-1 text-center" min="1" max="45" required />
<input type="number" name="winning-number" aria-label="winning-number2"
class="winning-number mx-1 text-center" min="1" max="45" required />
<input type="number" name="winning-number" aria-label="winning-number3"
class="winning-number mx-1 text-center" min="1" max="45" required />
<input type="number" name="winning-number" aria-label="winning-number4"
class="winning-number mx-1 text-center" min="1" max="45" required />
<input type="number" name="winning-number" aria-label="winning-number5"
class="winning-number mx-1 text-center" min="1" max="45" required />
<input type="number" name="winning-number" aria-label="winning-number6"
class="winning-number mx-1 text-center" min="1" max="45" required />
</div>
</div>
<div class="bonus-number-container flex-grow">
<h4 class="mt-0 mb-3 text-center">보너스 번호</h4>
<label for="bonus-number" class="mt-0 mb-3 text-center text-base d-block font-semibold">보너스 번호</label>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중복된 클래스를 사용하지 않기 위해서, class style을 잘 활용하고 계시는 것 같습니다!

<div class="d-flex justify-center">
<input type="number" class="bonus-number text-center" />
<input type="number" name="bonus-number" id="bonus-number" aria-label="bonus-number"
class="bonus-number text-center" min="1" max="45" required />
</div>
</div>
</div>
<button type="button" class="open-result-modal-button mt-5 btn btn-cyan w-100">
<button type="submit" class="open-result-modal-button mt-5 btn btn-cyan w-100">
결과 확인하기
</button>
</form>
</section>
</div>
</div>

<div class="modal">
<div class="modal hidden">
<div class="modal-inner p-10">
<div class="modal-close">
<svg viewbox="0 0 40 40">
Expand All @@ -87,38 +97,38 @@ <h2 class="text-center">🏆 당첨 통계 🏆</h2>
<tr class="text-center">
<td class="p-3">3개</td>
<td class="p-3">5,000</td>
<td class="p-3">n개</td>
<td class="p-3"><span class="winning-count" data-rank="5"></span>개</td>
</tr>
<tr class="text-center">
<td class="p-3">4개</td>
<td class="p-3">50,000</td>
<td class="p-3">n개</td>
<td class="p-3"><span class="winning-count" data-rank="4"></span>개</td>
</tr>
<tr class="text-center">
<td class="p-3">5개</td>
<td class="p-3">1,500,000</td>
<td class="p-3">n개</td>
<td class="p-3"><span class="winning-count" data-rank="3"></span>개</td>
</tr>
<tr class="text-center">
<td class="p-3">5개 + 보너스볼</td>
<td class="p-3">30,000,000</td>
<td class="p-3">n개</td>
<td class="p-3"><span class="winning-count" data-rank="2"></span>개</td>
</tr>
<tr class="text-center">
<td class="p-3">6개</td>
<td class="p-3">2,000,000,000</td>
<td class="p-3">n개</td>
<td class="p-3"><span class="winning-count" data-rank="1"></span>개</td>
</tr>
</tbody>
</table>
</div>
<p class="text-center font-bold">당신의 총 수익률은 %입니다.</p>
<p class="text-center font-bold">당신의 총 수익률은 <span class="winning-rate"></span>%입니다.</p>
<div class="d-flex justify-center mt-5">
<button type="button" class="btn btn-cyan">다시 시작하기</button>
<button type="button" class="btn btn-cyan restart-button">다시 시작하기</button>
</div>
</div>
</div>
</div>
</main>
<script type="module" src="./src/js/index.js"></script>
</body>

Expand Down
21 changes: 2 additions & 19 deletions src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,10 @@ button:disabled {

.hidden {
visibility: hidden;
}

.lotto-numbers {
font-size: 1rem;
}

.lotto-list .lotto {
display: flex;
flex-direction: row;
align-items: center;
opacity: 0;
}

.lotto-list .lotto .lotto-numbers {
display: none;
font-size: 1rem;
padding-left: 5px;
}

.lotto-list.show-number {
flex-direction: column;
}

.lotto-list.show-number .lotto .lotto-numbers {
display: inline;
}
4 changes: 4 additions & 0 deletions src/css/shared/modules/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
display: inline-block;
}

.d-none {
display: none;
}

/* Layout - Top / Right / Bottom / Left */

.mt-2 {
Expand Down
Loading