Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
85b8cfc
Docs: 문서 양식 불러오기
HarrySeop May 7, 2024
9a1f949
Docs: 구현할 기능 목록 최신화
HarrySeop May 7, 2024
8e4001d
Feat: InputView 구현
SEYEON-PARK May 7, 2024
0f29029
Docs: 가이드 수정
HarrySeop May 7, 2024
8b2cb69
Merge branch 'main' of https://github.com/SEYEON-PARK/javascript-lotto
HarrySeop May 7, 2024
c2e80f1
Modify: 함수 선언 변경
HarrySeop May 7, 2024
5a05a28
Add: Constant 사용을 위한 기본 구조 선언
HarrySeop May 7, 2024
52f8f92
Feat: 로또 구입 금액 유효성 검사
HarrySeop May 8, 2024
c293d69
Add: TDD 구현
leemanjae02 May 8, 2024
26850b9
Modify: 구매 금액 TDD 완성
SEYEON-PARK May 8, 2024
37f41aa
Modify: 페어 Prettier 설정 통일 + 구현 목록 업데이트
HarrySeop May 8, 2024
4015299
Feat: 로또 당첨 번호 입력 유효성 검사 및 TDD 구현
leemanjae02 May 8, 2024
9bb0fec
Modify: Vanila JS 방식으로 구조 변경
HarrySeop May 9, 2024
168e69d
Modify: 구입금액 관련 상수화 완료
HarrySeop May 9, 2024
6be323f
Modify: 로또 당첨 금액 관련 상수화 완료
HarrySeop May 9, 2024
1f0c886
Feat: 로또 번호 생성기 완성
leemanjae02 May 9, 2024
f8ef6b8
Feat: 보너스 번호 입력 구현
leemanjae02 May 9, 2024
4b61c3f
Add: 보너스 번호 유효성 검사 추가 및 TDD 구현
leemanjae02 May 9, 2024
0265fef
Modify: 진행상황 업데이트
HarrySeop May 9, 2024
a115b5b
Modify: 로또 유효성 검사 수정
SEYEON-PARK May 9, 2024
eefc62d
Modify: 로또 유효성 검사 추가 수정
SEYEON-PARK May 9, 2024
e01e156
Modify: 로또 유효성 검사 완료
SEYEON-PARK May 9, 2024
f12bb7e
Modify: 보너스 유효성 검사 완료, 진행상황 업데이트
HarrySeop May 9, 2024
113ebe4
Add: 로또 클래스 함수 추가
HarrySeop May 9, 2024
facbad1
Feat: 출력문 구현 중, 오타 수정
HarrySeop May 9, 2024
33dab06
Modify: 모듈 방식 수정...
HarrySeop May 9, 2024
bc71e2d
Modify: eslint 관련 코드 수정, 코드 최신화
SEYEON-PARK May 9, 2024
ab54c9d
Add: 로또 출력 메시지 구현
SEYEON-PARK May 9, 2024
6758765
Add: 당첨번호 입력, 보너스 번호 입력 구현 완료, 모듈 형식 변경
HarrySeop May 10, 2024
cb8d3ed
Delete: 타입 검사 콘솔 제거
HarrySeop May 10, 2024
dbe6fa5
Add: 출력문 추가
HarrySeop May 10, 2024
d92a17a
Feat: 당첨 개수 반환 함수 추가
leemanjae02 May 10, 2024
5729a3d
Modify: export문 추가
leemanjae02 May 10, 2024
73c6d28
ADD : 맞은 개수 확인 코드 추가
SEYEON-PARK May 10, 2024
b299347
Modify: 함수 이름 변경
SEYEON-PARK May 12, 2024
ad8c2a6
Feat: 당첨 내역 출력 코드 작성
SEYEON-PARK May 12, 2024
4e5d2cb
Feat: 수익률 기능 추가
SEYEON-PARK May 13, 2024
bf388a0
Modify: 수익률 계산 부분 정규식 수정
leemanjae02 May 13, 2024
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
62 changes: 32 additions & 30 deletions __tests__/ApplicationTest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import App from "../src/App.js";
import { MissionUtils } from "@woowacourse/mission-utils";
/* eslint-disable max-lines-per-function */
/* eslint-disable arrow-body-style */
/* eslint-disable import/extensions */
import { MissionUtils } from '@woowacourse/mission-utils';
import App from '../src/App.js';

const mockQuestions = (inputs) => {
MissionUtils.Console.readLineAsync = jest.fn();
Expand All @@ -19,7 +22,7 @@ const mockRandoms = (numbers) => {
};

const getLogSpy = () => {
const logSpy = jest.spyOn(MissionUtils.Console, "print");
const logSpy = jest.spyOn(MissionUtils.Console, 'print');
logSpy.mockClear();
return logSpy;
};
Expand All @@ -28,8 +31,8 @@ const runException = async (input) => {
// given
const logSpy = getLogSpy();

const RANDOM_NUMBERS_TO_END = [1,2,3,4,5,6];
const INPUT_NUMBERS_TO_END = ["1000", "1,2,3,4,5,6", "7"];
const RANDOM_NUMBERS_TO_END = [1, 2, 3, 4, 5, 6];
const INPUT_NUMBERS_TO_END = ['1000', '1,2,3,4,5,6', '7'];

mockRandoms([RANDOM_NUMBERS_TO_END]);
mockQuestions([input, ...INPUT_NUMBERS_TO_END]);
Expand All @@ -39,15 +42,15 @@ const runException = async (input) => {
await app.play();

// then
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]"));
}
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('[ERROR]'));
};

describe("로또 테스트", () => {
describe('로또 테스트', () => {
beforeEach(() => {
jest.restoreAllMocks();
})
});

test("기능 테스트", async () => {
test('기능 테스트', async () => {
// given
const logSpy = getLogSpy();

Expand All @@ -61,38 +64,37 @@ describe("로또 테스트", () => {
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
]);
mockQuestions(["8000", "1,2,3,4,5,6", "7"]);
mockQuestions(['8000', '1,2,3,4,5,6', '7']);

// when
const app = new App();
await app.play();
app.play();

// then
const logs = [
"8개를 구매했습니다.",
"[8, 21, 23, 41, 42, 43]",
"[3, 5, 11, 16, 32, 38]",
"[7, 11, 16, 35, 36, 44]",
"[1, 8, 11, 31, 41, 42]",
"[13, 14, 16, 38, 42, 45]",
"[7, 11, 30, 40, 42, 43]",
"[2, 13, 22, 32, 38, 45]",
"[1, 3, 5, 14, 22, 45]",
"3개 일치 (5,000원) - 1개",
"4개 일치 (50,000원) - 0개",
"5개 일치 (1,500,000원) - 0개",
"5개 일치, 보너스 볼 일치 (30,000,000원) - 0개",
"6개 일치 (2,000,000,000원) - 0개",
"총 수익률은 62.5%입니다.",
'8개를 구매했습니다.',
'[8, 21, 23, 41, 42, 43]',
'[3, 5, 11, 16, 32, 38]',
'[7, 11, 16, 35, 36, 44]',
'[1, 8, 11, 31, 41, 42]',
'[13, 14, 16, 38, 42, 45]',
'[7, 11, 30, 40, 42, 43]',
'[2, 13, 22, 32, 38, 45]',
'[1, 3, 5, 14, 22, 45]',
'3개 일치 (5,000원) - 1개',
'4개 일치 (50,000원) - 0개',
'5개 일치 (1,500,000원) - 0개',
'5개 일치, 보너스 볼 일치 (30,000,000원) - 0개',
'6개 일치 (2,000,000,000원) - 0개',
'총 수익률은 62.5%입니다.',
];

logs.forEach((log) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log));
});
});

test("예외 테스트", async () => {
await runException("1000j");
test('예외 테스트', async () => {
await runException('1000j');
});
});

31 changes: 31 additions & 0 deletions __tests__/BonusTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* eslint-disable no-new */
/* eslint-disable import/extensions */
import Bonus from '../src/Bonus.js';
import { ERROR_MESSAGE } from '../src/constants/Message.js';

// eslint-disable-next-line max-lines-per-function
describe('보너스 클래스 테스트', () => {
test('보너스 번호가 숫자가 아니면 예외 발생', () => {
expect(() => {
new Bonus('굿');
}).toThrow(ERROR_MESSAGE.non_numeric_bonus);
});

test('보너스 번호 테스트', () => {
const bonus = new Bonus('5', [1, 2, 3, 4, 6, 7]);
const bonusNumber = bonus.getBonusNumber();
expect(bonusNumber).toBe(5);
});

test('보너스 번호가 1부터 45의 숫자가 아닌경우 예외 발생', () => {
expect(() => {
new Bonus('46');
}).toThrow(ERROR_MESSAGE.out_of_range_bonus);
});

test('보너스 번호가 로또 번호와 중복된 경우 예외 발생', () => {
expect(() => {
new Bonus('6', [1, 2, 3, 4, 5, 6]);
}).toThrow(ERROR_MESSAGE.duplicate_bonus);
});
});
45 changes: 36 additions & 9 deletions __tests__/LottoTest.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
import Lotto from "../src/Lotto.js";
/* eslint-disable no-new */
/* eslint-disable import/extensions */
import Lotto from '../src/Lotto.js';
import { ERROR_MESSAGE } from '../src/constants/Message.js';

describe("로또 클래스 테스트", () => {
test("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.", () => {
// eslint-disable-next-line max-lines-per-function
describe('로또 클래스 테스트', () => {
test('로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.', () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 6, 7]);
}).toThrow("[ERROR]");
new Lotto('1,2,3,4,5,6,7');
}).toThrow(ERROR_MESSAGE.out_of_length_lotto);
});

// TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
test("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.", () => {
test('로또 번호가 숫자가 아닐 경우 예외가 발생한다.', () => {
expect(() => {
new Lotto([1, 2, 3, 4, 5, 5]);
}).toThrow("[ERROR]");
new Lotto('1,2,3,굿,6,7');
}).toThrow(ERROR_MESSAGE.non_numeric_lotto);
});

// // TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
test('로또 번호가 1이상 45이하가 아닌경우 예외가 발생한다.', () => {
expect(() => {
new Lotto('0, 5, 100, 5, 2, 3');
}).toThrow(ERROR_MESSAGE.out_of_range_lotto);
});

test('로또 당첨 번호는 중복일 수 없습니다.', () => {
expect(() => {
new Lotto('1,2,3,4,4,6');
}).toThrow(ERROR_MESSAGE.duplicate_lotto);
});

test('로또 금액 배열로 변환 테스트', () => {
const lotto = new Lotto('1,2,3,4,5,6');
const test = lotto.changeStringToArray('1,2,3,4,5,6');
expect(test).toStrictEqual([1, 2, 3, 4, 5, 6]);
});
// test(',테스트', () => {
// expect(() => {
// new Lotto([, 1, 2, 3, 4, 5]);
// }).toThrow('[ERROR]');
// });

// 아래에 추가 테스트 작성 가능
});
49 changes: 49 additions & 0 deletions __tests__/PurchaseTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-disable import/extensions */
import Purchase from '../src/Purchase.js';
import { ERROR_MESSAGE } from '../src/constants/Message.js';

// eslint-disable-next-line max-lines-per-function
describe('로또 구입금액 테스트', () => {
test('입력금액을 통해 구매수량 구하기', () => {
const purchase = new Purchase('10000');
const amount = purchase.getQuantity();
expect(amount).toBe(10);
});

test('0이하의 숫자를 입력할 경우 에러 발생', () => {
expect(() => {
// eslint-disable-next-line no-new
new Purchase('-1');
}).toThrow(ERROR_MESSAGE.non_positive_money);
});

test('숫자가 아닌 경우 에러 발생', () => {
expect(() => {
// eslint-disable-next-line no-new
new Purchase('1000j');
}).toThrow('[ERROR]');
});

test('공백을 입력한 경우 에러 발생', () => {
expect(() => {
// eslint-disable-next-line no-new
new Purchase(' ');
}).toThrow('[ERROR]');
});

test('1000으로 나누어 떨어지지 않으면 에러 발생', () => {
expect(() => {
// eslint-disable-next-line no-new
new Purchase('30');
}).toThrow('[ERROR]');
});

// TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
// test('로또 번호에 중복된 숫자가 있으면 예외가 발생한다.', () => {
// expect(() => {
// new Lotto([1, 2, 3, 4, 5, 5]);
// }).toThrow('[ERROR]');
// });

// 아래에 추가 테스트 작성 가능
});
45 changes: 45 additions & 0 deletions docs/Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# GDSC Pair Programming Guide

코드 컨벤션의 기반 스타일 가이드는 [Airbnb 자바스크립트 스타일 가이드](https://github.com/airbnb/javascript)를 기준으로 합니다.
기본 Airbnb 스타일 가이드 외의 아래의 가이드를 지켜야 합니다.

## 네이밍 컨벤션

- 소스의 변수명, 클래스명 등에는 영문 이외의 언어를 사용하지 않습니다.
- 클래스, 메서드 등의 이름에는 특수 문자를 사용하지 않습니다.

| 폴더명 | • snake_case |
| -------- | ------------ |
| 파일명 | • PascalCase |
| 클래스명 | • PascalCase |

## 함수 이름 짓기

- 함수 하나는 한 가지의 일만 해야 하므로 함수명은 직관적으로 작성해야 합니다.

| 함수명 | 의미 | 예시 |
| --------- | -------------------- | ------------- |
| show... | 무엇을 보여주는 함수 | showMessage() |
| get... | 값을 반환하는 함수 | getNumber() |
| calc... | 값을 반환하는 함수 | calcSum() |
| create... | 무엇을 생성하는 함수 | createForm() |

---

# 커밋 컨벤션

💡커밋은 논리적으로 구분이 되고, 일관성이 유지되는 단위로 최대한 작게 쪼개서 작성합니다.

## 커밋 메세지 컨벤션

1. 영어 컨벤션은 5개만 사용 (**Feat, Add, Modify, Docs, Delete**)

- **Feat** : 기능 추가 (초기 기능 추가할 때)
- **Add** : 코드 추가 (어떠한 기능 내에 기능을 더 추가할 때)
- **Modify** : 코드 수정 ( 버그 수정, 코드 지우고, 추가하고, 수정하는 모든 과정들 )
- **Docs** : 문서 수정 (기능 구현 목록 수정, 추가 등등)
- **Delete** : 코드 삭제 (코드만 지우는 것)

2. **영어 컨벤션** + **커밋 메시지** 형식으로 작성
- 예시 : Feat: 로그인 기능구현 (영어:(공백)한글 커밋 메세지)
- 깃모지는 선택사항이지만 사용 시 영어 앞에 사용합니다.
50 changes: 50 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## 📈 구현할 기능 목록

- 입력

- [x] 로또를 위한 구입 금액 입력받기
- [x] 로또 당첨번호 입력받기(번호는 쉼표를 기준으로 구분한다.)
- [x] 로또 보너스 번호 입력받기

- 출력

- [ ] 발행한 로또 수량 출력하기
- [ ] 발행한 로또 번호 출력하기
- [ ] 당첨 내역(통계) 출력하기
- [ ] 수익률 출력하기(소수점 둘째자리에서 반올림 하기)

- 기능

- [x] 입력받은 금액을 1000원으로 나눠서 장수로 반환하는 기능 구현하기
- [x] 1부터 45까지 6개의 숫자를 랜덤으로 반환하는 기능 구현하기
- 우테코 라이브러리 사용하기
- [x] 당첨번호 비교 후, 당첨된 매수 반환하는 기능 구현하기
- [x] 수익률 계산하는 기능 구현하기
- [x] 세자리 마다 쉼표로 구분하기
- [x] 소수점 둘째 자리에서 반올림 하기
- [x] 수익률이 정수일 경우 소수점 첫째 자리에 0을 표시한다(ex: 100.0%)

## 🎯 예외 예상 상황 목록

- 로또를 위한 구입 금액 입력 부분

- [x] 숫자가 아닌 것을 입력했을 경우
- [x] 0 이하의 숫자를 입력했을 경우 (0, 음수)
- [x] 입력을 하지 않았을 경우
- [x] 1,000원으로 나누어 떨어지지 않는 경우

- 로또 당첨번호 입력 부분

- [x] 1이상 45 이하 숫자가 아닌 경우
- [x] (쉼표로 구분한 당첨번호가) 6개가 아닌 경우
- [x] 중복을 입력한 경우

- [ ] ,가 맨 앞에 오는 경우
- [ ] ,가 맨 뒤에 오는 경우
- [ ] ,가 중복되서 입력 되는 경우
Comment on lines +42 to +44
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

이 부분을 정규식과 if문으로 검사를 하려고 생각해봤지만 동작이 되는 코드를 작성하기 위해 우선순위를 미뤄두었다가 구현하지는 못했습니다.
조건에 따른 검사를 할 때 정규식을 사용하면 되겠다는 생각이 먼저 드는 편인데 정규식을 사용해 조건을 검사하는 것이 복잡한 정규식일 경우 가독성이 안 좋지 않을까? 하는 생각에 정규식을 사용하는 것이 좋은 코드인지는아직 잘 모르겠습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

정규식이 복잡하다면 적용해볼 수 있는 방법이 무엇일까요?


- 로또 보너스 번호 입력

- [x] 숫자가 아닌 것을 입력했을 경우
- [x] 1이상 45 이하 숫자가 아닌 경우
- [x] 당첨번호랑 중복일 경우
12 changes: 11 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
/* eslint-disable import/extensions */
import Game from './Game.js';

class App {
async play() {}
// eslint-disable-next-line class-methods-use-this
play() {
const game = new Game();
game.start();
}
}

const app = new App();
app.play();

export default App;
Loading