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

[1단계 - 행운의 로또 미션] 하루(김하루) 미션 제출합니다. #7

Merged
merged 27 commits into from Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
897eca1
docs: 1단계 기능 구현목록 작성
365kim Feb 16, 2021
f21d5f1
chore: 코드포매터, 린터 설정, 사이프레스 설치
365kim Feb 16, 2021
ec9ce87
chore: package.json name, description 수정
SunYoungKwon Feb 16, 2021
7f2c98e
test: 입력된 로또 구입 금액이 로또 한 장의 금액보다 적을 경우
SunYoungKwon Feb 16, 2021
bb2e39c
test: 입력된 로또 구입 금액이 로또 한 장의 금액으로 나누어 떨어지지 않을 경우
SunYoungKwon Feb 16, 2021
e444f59
test: 입력된 로또 구입 금액에 화폐단위 미만의 자릿수가 포함된 경우
SunYoungKwon Feb 16, 2021
777255c
test: 입력된 로또 구입 금액으로 발급한 로또를 화면에 표시
SunYoungKwon Feb 16, 2021
4048b7d
test: 입력된 로또 구입 금액으로 발급한 로또를 화면에 표시
SunYoungKwon Feb 16, 2021
a11716e
test: 번호보기 토글이 비활성화 되어있는 상태에서 토글을 누른 경우
365kim Feb 16, 2021
07cccf5
test: 표시된 로또 번호의 개수, 중복여부, 범위 검사
365kim Feb 16, 2021
c3cf005
test: 번호보기 토글이 활성화된 상태에서 토글을 누른 경우
365kim Feb 16, 2021
d487678
feat: 입력된 로또 구입금액에 최소 화폐단위 미만의 자릿수가 포함된 경우 alert 구현
365kim Feb 17, 2021
3f964cc
feat: 입력된 로또 구입 금액이 로또 한 장의 금액보다 적을 경우 alert 구현
SunYoungKwon Feb 17, 2021
ee39bfa
feat: 입력된 로또 구입 금액이 로또 한 장의 금액으로 나누어 떨어지지 않을 경우 alert 구현
SunYoungKwon Feb 17, 2021
ef8aa1c
chore: 가독성을 위해 alert 메세지에 개행 추가
SunYoungKwon Feb 17, 2021
6be533d
docs: step1 구현목록 추가(로또 번호 중복여부 검사, 정렬)
SunYoungKwon Feb 17, 2021
6dbf11a
feat: 입력한 금액으로 발급된 로또를 화면에 표시
SunYoungKwon Feb 17, 2021
90c0c13
feat: 번호보기 토글 구현
SunYoungKwon Feb 17, 2021
a14833a
feat: 중복없이 로또번호를 생성하는 함수 구현
SunYoungKwon Feb 17, 2021
56dde7e
test: 로또를 재구입하면 기존 로또를 삭제하고 새로 구매한 로또 표시
365kim Feb 18, 2021
691c800
fix: 번호보기 토글이 활성화된 상태에서 로또 재구입 시 번호가 사라지는 문제 해결
365kim Feb 18, 2021
dc041fb
test: Enter키 이벤트로 로또를 구입할 경우
SunYoungKwon Feb 18, 2021
da88187
feat: 로또 구입금액 제출 Enter 키 이벤트리스너 구현
SunYoungKwon Feb 18, 2021
0cfd3fb
refactor: clearInput -> clearInputValue 로 네이밍 변경
365kim Feb 19, 2021
fea675b
refactor: 웹표준 및 접근성에 맞도록 form 태그에 submit 적용, label 태그와 input 태그 연결, s…
365kim Feb 19, 2021
f011d91
refactor: if문의 조건식 Number 타입이 아닌 Boolean 타입으로 전달
365kim Feb 19, 2021
a97b7d2
refactor: indexOf 메서드 대신 includes 적용
365kim Feb 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions .eslintrc.json
@@ -0,0 +1,21 @@
{
"env": {
"browser": true
},
"extends": ["airbnb-base", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"parserOptions": {
"sourceType": "module"
},
"ignorePatterns": ["cypress/"],
"rules": {
"no-new": "off",
"no-alert": "off",
"no-param-reassign": "off",
"no-return-assign": "off",
"import/extensions": "off",
"import/prefer-default-export": "off",
"max-depth": ["error", 2]
//"max-lines-per-function": ["error", 15]
}
}
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -10,7 +10,6 @@
# Icon must end with two \r
Icon


# Thumbnails
._*

Expand Down
9 changes: 9 additions & 0 deletions .prettierrc
@@ -0,0 +1,9 @@
{
"semi": true,
"useTabs": false,
"tabWidth": 2,
"printWidth": 120,
"singleQuote": true,
"arrowParens": "always",
"trailingComma": "es5"
}
31 changes: 19 additions & 12 deletions README.md
Expand Up @@ -21,25 +21,32 @@

### 🎯 step1 구입 기능

- [ ] 로꼬 구입 금액을 입력하면, 금액에 해당하는 로또를 발급해야 한다.
- [ ] 로또 1장의 가격은 1,000원이다.
- [ ] 소비자는 **자동 구매**를 할 수 있어야 한다.
- [ ] 복권 번호는 번호보기 토글 버튼을 클릭하면, 볼 수 있어야 한다.

- 로또 구입 금액을 입력받는다.
- 로또 1장의 가격은 1,000원이다.
- 최소 화폐단위 미만의 자릿수가 포함된 경우 ➡️ `alert` 후 재입력 요청
- 1,000원 미만일 경우 ➡️ `alert` 후 재입력 요청
- 1,000원으로 나누어 떨어지지 않을 경우 ➡️ `alert`으로 거스름돈 금액을 알려주고 구매 계속 진행
- 금액으로 살 수 있는 갯수만큼 로또를 발급한다.
- 모든 로또를 자동구매 옵션으로 발급한다.
- 로또 한 장 당 번호의 개수는 6개로 한다.
- 번호는 1 ~ 45 사이 랜덤값으로 구성한다.
- 각 번호는 서로 중복되지 않는다.
- 각 번호는 오름차순으로 정렬되어 있다.
- 로또 발급 후 번호보기 토글 버튼을 클릭하면 복권번호 표시여부를 결정할 수 있다.
- 표시여부 기본값은 복권번호를 표시하지 않는 것으로 한다.

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

- [ ] 결과 확인하기 버튼을 누르면 당첨 통계, 수익률을 모달로 확인할 수 있다.
- [ ] 로또 당첨 금액은 고정되어 있는 것으로 가정한다.
- [ ] 다시 시작하기 버튼을 누르면 초기화 되서 다시 구매를 시작할 수 있다.

- 결과 확인하기 버튼을 누르면 당첨 통계, 수익률을 모달로 확인할 수 있다.
- 로또 당첨 금액은 고정되어 있는 것으로 가정한다.
- 다시 시작하기 버튼을 누르면 초기화 되서 다시 구매를 시작할 수 있다.

### 🎯🎯🎯 step3 수동 구매

- [ ] 소비자는 수동 구매(스스로 구매 번호를 입력)를 할 수 있어야 한다.
- 소비자는 수동 구매(스스로 구매 번호를 입력)를 할 수 있어야 한다.
- 수동 구매를 위한 input UI는 스스로 구현한다.
- [ ] 수동 구매 후 남는 금액이 있다면 자동으로 구매할 수 있어야 한다.
- [ ] 위 기능들이 정상적으로 동작하는지 Cypress를 이용해 테스트한다.
- 수동 구매 후 남는 금액이 있다면 자동으로 구매할 수 있어야 한다.
- 위 기능들이 정상적으로 동작하는지 Cypress를 이용해 테스트한다.

<br>

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"
}
58 changes: 58 additions & 0 deletions cypress/integration/purchaseAmountInput.spec.js
@@ -0,0 +1,58 @@
import { LOTTO_PRICE, MONETARY_UNIT, ALERT_MESSAGE } from '../../src/js/constants.js';

describe('구매금액 입력 검사', () => {
beforeEach(() => {
cy.visit('http://localhost:5500/');
});

it('입력된 로또 구입 금액에 화폐단위 미만의 자릿수가 포함된 경우 alert을 띄우고 input창을 초기화한다.', () => {
const invalidMoney = MONETARY_UNIT / 10;
const { PURCHASE_AMOUNT_IS_INVALID_MONEY } = ALERT_MESSAGE;
const alertStub = cy.stub();

cy.on('window:alert', alertStub);
cy.get('.purchase-amount-input').type(invalidMoney);
cy.get('.purchase-amount-button')
.click()
.then(() => {
const actualMessage = alertStub.getCall(0).lastArg;
expect(actualMessage).to.equal(PURCHASE_AMOUNT_IS_INVALID_MONEY);
});
cy.get('.purchase-amount-input').should('have.text', '');
cy.get('.purchase-amount-input').should('have.focus');
});

it('입력된 로또 구입 금액이 로또 한 장의 금액보다 적을 경우 alert을 띄우고 input창을 초기화한다.', () => {
const tooLowAmount = Math.floor(LOTTO_PRICE * 0.9);
const { PURCHASE_AMOUNT_IS_TOO_LOW } = ALERT_MESSAGE;
const alertStub = cy.stub();

cy.on('window:alert', alertStub);
cy.get('.purchase-amount-input').type(tooLowAmount);
cy.get('.purchase-amount-button')
.click()
.then(() => {
const actualMessage = alertStub.getCall(0).lastArg;
expect(actualMessage).to.equal(PURCHASE_AMOUNT_IS_TOO_LOW);
});
cy.get('.purchase-amount-input').should('have.text', '');
cy.get('.purchase-amount-input').should('have.focus');
});

it('입력된 로또 구입 금액이 로또 한 장의 금액으로 나누어 떨어지지 않을 경우 alert으로 거스름돈 금액을 알려주고 구매한 로또를 표시한다.', () => {
const amountWithChange = Math.ceil(LOTTO_PRICE * 1.1);
const { PURCHASE_AMOUNT_HAS_CHANGE } = ALERT_MESSAGE;
const alertStub = cy.stub();

cy.on('window:alert', alertStub);
cy.get('.purchase-amount-input').type(amountWithChange);
cy.get('.purchase-amount-button')
.click()
.then(() => {
const change = amountWithChange % LOTTO_PRICE;
const actualMessage = alertStub.getCall(0).lastArg;
expect(actualMessage).to.equal(PURCHASE_AMOUNT_HAS_CHANGE(change));
});
cy.get('.purchased-lotto-section').should('be.visible');
});
});
67 changes: 67 additions & 0 deletions cypress/integration/purchasedLottoUI.spec.js
@@ -0,0 +1,67 @@
import {
LOTTO_PRICE,
PURCHASED_QUANTITY_MESSAGE,
LOTTO_NUMBER_SEPARATOR,
LOTTO_MIN_NUMBER,
LOTTO_MAX_NUMBER,
LOTTO_NUMBERS_LENGTH,
} from '../../src/js/constants.js';

describe('구매한 로또 UI 검사', () => {
before(() => {
cy.visit('http://localhost:5500/');
});

const numOfLotto = 3;

it('Enter키 이벤트로 로또를 구입한 후 입력된 로또 구입 금액으로 발급한 로또를 화면에 표시한다.', () => {
cy.get('.purchase-amount-input')
.type(LOTTO_PRICE * numOfLotto)
.type('{enter}');
cy.get('.purchased-lotto-section').should('be.visible');
cy.get('.purchased-lotto-label').should('have.text', PURCHASED_QUANTITY_MESSAGE(numOfLotto));
cy.get('.lotto-ticket-container > li').should('have.length', numOfLotto);
cy.get('.lotto-numbers-toggle-button').should('not.be.checked');
});

it('번호보기 토글이 비활성화 되어 있는 상태에서 토글을 누르면, 로또 아이콘이 세로로 배치되고 로또 번호가 표시된다.', () => {
cy.get('.switch').click();
cy.get('.lotto-numbers-toggle-button').should('be.checked');
cy.get('.lotto-ticket-container').should('have.class', 'flex-col');
cy.get('.lotto-numbers').should('be.visible');
});

it('표시된 로또 번호의 개수, 중복여부, 범위를 검사한다.', () => {
cy.get('.lotto-numbers').each(($el) => {
const lottoNumbers = $el.text().split(LOTTO_NUMBER_SEPARATOR);

expect(lottoNumbers.length).to.be.equal(LOTTO_NUMBERS_LENGTH);
expect(lottoNumbers.length).to.be.equal(new Set(lottoNumbers).size);
lottoNumbers.forEach((lottoNumber) => {
expect(Number(lottoNumber)).to.be.within(LOTTO_MIN_NUMBER, LOTTO_MAX_NUMBER);
});
});
});

it('로또를 재구입하면 기존에 구매한 로또를 삭제하고 새로 구매한 로또를 보여준다.', () => {
const nextNumOfLotto = numOfLotto * 2;

cy.get('.purchase-amount-input')
.clear()
.type(LOTTO_PRICE * nextNumOfLotto);
cy.get('.purchase-amount-button').click();
cy.get('.lotto-ticket-container > li').should('have.length', nextNumOfLotto);
});

it('번호보기 토글이 활성화된 상태에서 재구입을 하면, 토글 상태가 변하지 않고 새로 구매한 로또번호를 보여준다.', () => {
cy.get('.lotto-numbers-toggle-button').should('be.checked');
cy.get('.lotto-numbers').should('be.visible');
});

it('번호보기 토글이 활성화된 상태에서 토글을 누르면, 로또 아이콘이 가로로 배치되고 로또 번호가 사라진다.', () => {
cy.get('.switch').click();
cy.get('.lotto-numbers-toggle-button').should('not.be.checked');
cy.get('.purchased-lotto-section').should('not.have.class', 'flex-col');
cy.get('.lotto-numbers').should('not.be.visible');
});
});
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')