Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b4ecc44
chore: prettier, eslint 설정
Seojinlucia Oct 29, 2023
fc43a68
docs: 기능 구현 목록 작성
Seojinlucia Oct 29, 2023
1418877
feat: (1-1) 이름 입력 메세지 출력 구현
Seojinlucia Nov 1, 2023
d65ad00
docs: (1-1) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
65849de
feat: (T-1-1) 이름 입력 메세지 출력 테스트 구현
Seojinlucia Nov 1, 2023
b9ea995
docs: (T-1-1) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
8a22c9f
feat: (1-2) 이름 입력 처리 구현
Seojinlucia Nov 1, 2023
d0fe817
docs: (1-2) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
8af9f12
refactor: (T-1-1) 테스트 코드 기대값 배열로 수정
Seojinlucia Nov 1, 2023
1b56932
chore: eslint 규칙 수정
Seojinlucia Nov 1, 2023
4af29d2
feat: (T-1-2) 이름 입력 처리 테스트 구현
Seojinlucia Nov 1, 2023
416f1f6
docs: (T-1-2) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
c7e1dea
docs: (1-2) 이름 입력 처리 기능 구현 목록 추가
Seojinlucia Nov 1, 2023
fa9c7a8
feat: (1-2) 빈 문자열 제거 기능 추가
Seojinlucia Nov 1, 2023
e886ae5
refactor: (T-1-2) 테스트 기대값 오류 수정
Seojinlucia Nov 1, 2023
71a7c87
docs: (1-3) 이름 입력 예외 처리 기능 구현 목록 수정
Seojinlucia Nov 1, 2023
20416cb
feat: (1-3) 이름 입력 예외 처리 구현
Seojinlucia Nov 1, 2023
657833c
docs: (1-3) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
afa85b6
feat: (T-1-3) 이름 입력 예외 처리 테스트 구현
Seojinlucia Nov 1, 2023
ab37388
docs: (T-1-3) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
1c07318
docs: 기능 구현 목록 2번, 3번 순서 수정
Seojinlucia Nov 1, 2023
7a29b8b
feat: (2-1) 시도 횟수 입력 메세지 출력 구현
Seojinlucia Nov 1, 2023
7b646fb
docs: (2-1) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
08a7b9c
feat: (T-2-1) 시도 횟수 입력 메세지 출력 테스트 구현
Seojinlucia Nov 1, 2023
45065bb
docs: (T-2-1) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
b1ea3ba
docs: (2-2) 시도 횟수 입력 처리 기능 구현 목록 추가
Seojinlucia Nov 1, 2023
58cabb1
feat: (2-2) 시도 횟수 입력 처리 구현
Seojinlucia Nov 1, 2023
d5fe6e7
docs: (2-2) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
f23742c
feat: (T-2-2) 시도 횟수 입력 처리 테스트 구현
Seojinlucia Nov 1, 2023
87854a7
docs: (2-3) 시도 횟수 입력 예외 처리 상세 기능 수정
Seojinlucia Nov 1, 2023
f563c7a
feat: (2-3) 시도 횟수 입력 예외 처리 구현
Seojinlucia Nov 1, 2023
7586dcd
docs: (2-3) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
ac2b78f
feat: (T-2-3) 시도 횟수 입력 예외 처리 테스트 구현
Seojinlucia Nov 1, 2023
e2effbe
feat: (3-1) 입력 값에 따라 자동차 생성 구현
Seojinlucia Nov 1, 2023
9d61222
docs: (3-1) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
d624b4e
feat: (T-3-1) 입력 값에 따른 자동차 생성 테스트 구현
Seojinlucia Nov 1, 2023
18fc3b7
feat: (4-1) 0 ~ 9 사이 무작위 값 생성 구현
Seojinlucia Nov 1, 2023
8748d74
docs: (4-1) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
bd86776
feat: (T-4-1) 0 ~ 9 사이 무작위 값 생성 테스트 구현
Seojinlucia Nov 1, 2023
40fb642
feat: (4-2) 4 이상일 경우 한칸 전진 구현
Seojinlucia Nov 1, 2023
cacf81b
feat: (T-4-2) 4 이상일 경우 한칸 전진 테스트 구현
Seojinlucia Nov 1, 2023
6586a2e
docs: (T-4-2) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
7e12518
feat: (4-3) 시도 횟수 만큼 경주 진행 구현
Seojinlucia Nov 1, 2023
1c28513
feat: (T-4-3) 시도 횟수 만큼 경주 진행 테스트 구현
Seojinlucia Nov 1, 2023
5e9feec
docs: (T-4-3) 배운 내용 정리 및 작성
Seojinlucia Nov 1, 2023
f157e6a
feat: (4-4) 마지막 경주로 최종 우승자 구하기 구현
Seojinlucia Nov 1, 2023
a658f7b
feat: (T-4-4) 마지막 경주로 최종 우승자 구하기 테스트 구현
Seojinlucia Nov 1, 2023
87351bd
feat: (5-1) 실행 결과 메세지 출력 구현
Seojinlucia Nov 1, 2023
b41e312
feat: (T-5-1) 실행 결과 메세지 출력 테스트 구현
Seojinlucia Nov 1, 2023
c4d6604
feat: (5-2) 각 차수별 실행 결과 메세지 출력 구현
Seojinlucia Nov 1, 2023
75a0f7b
feat: (T-5-2) 각 차수별 실행 결과 메세지 출력 테스트 구현
Seojinlucia Nov 1, 2023
b5c81f3
feat: (6-1) 최종 우승자 메세지 출력 구현
Seojinlucia Nov 1, 2023
0f38c87
feat: (T-6-1) 최종 우승자 메세지 출력 테스트 구현
Seojinlucia Nov 1, 2023
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: 31 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default {
env: {
browser: true,
es2021: true,
},
extends: ['airbnb-base', 'prettier'],
overrides: [
{
env: {
jest: true,
node: true,
},
files: [
'.eslintrc.{js,cjs}',
],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'max-depth': ['error', 2],
'max-params': ['error', 3],
'max-lines-per-function': ['error', { max: 10 }],
'import/extensions': 'off',
},
};
11 changes: 11 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
singleQuote: true,
semi: true,
useTabs: false,
tabWidth: 2,
trailingComma: "all",
printWidth: 80,
bracketSpacing: true,
arrowParens: "always",
endOfLine: "auto",
};
341 changes: 341 additions & 0 deletions __tests__/BeforeRefactor.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,341 @@
import App from "../src/App.js";
import { Console } from "@woowacourse/mission-utils"
import { Random } from "@woowacourse/mission-utils"

const mockInput = (input) => {
Console.readLineAsync = jest.fn(() => Promise.resolve(input));
};

describe("App class", () => {
describe("getCarName method", () => {
test("사용자 입력", async () => {
const testInput = "pobi,woni,jun";
mockInput(testInput);

const app = new App();
const result = await app.getCarName();

expect(Console.readLineAsync).toHaveBeenCalledTimes(1);
expect(Console.readLineAsync).toHaveBeenCalledWith("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)\n");
expect(result).toEqual(["pobi", "woni", "jun"]);
});
});

describe("sanitizeCarName method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("이름 사이에 공백이 없을 때", () => {
const input = "pobi,woni,jun";
const result = app.sanitizeCarName(input);
expect(result).toEqual(["pobi", "woni", "jun"]);
});

test("이름 앞뒤에 공백이 있을 때", () => {
const input = " pobi ,woni , jun ";
const result = app.sanitizeCarName(input);
expect(result).toEqual(["pobi", "woni", "jun"]);
});

test("공백만 있는 이름이 있을 때", () => {
const input = "pobi, , jun";
const result = app.sanitizeCarName(input);
expect(result).toEqual(["pobi", "jun"]);
});

test("연속적으로 여러 개의 콤마가 나올 때", () => {
const input = "pobi,,,woni,,jun";
const result = app.sanitizeCarName(input);
expect(result).toEqual(["pobi", "woni", "jun"]);
});
});

describe("Error Handling in validateCarName", () => {
let app;
beforeEach(() => {
app = new App();
});

test("자동차 이름이 5자를 초과할 때", async () => {
mockInput("pobipobi,woni,jun");
await expect(app.getCarName()).rejects.toThrow("[ERROR] 자동차 이름은 5자를 초과할 수 없습니다.");
});

test("자동차 이름이 중복될 때", async () => {
mockInput("pobi,pobi,jun");
await expect(app.getCarName()).rejects.toThrow("[ERROR] 중복되는 자동차 이름이 있습니다.");
});

test("자동차 이름이 최소 2대 미만일 때", async () => {
mockInput("pobi");
await expect(app.getCarName()).rejects.toThrow("[ERROR] 최소 두 대 이상의 자동차 이름을 입력해주세요.");
});
});

describe("getRound method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("사용자로부터 시도할 횟수 입력 받기", async () => {
const testInput = "5";
mockInput(testInput);

const result = await app.getRoundNumber();

expect(Console.readLineAsync).toHaveBeenCalledWith("시도할 횟수는 몇 회인가요?\n");
expect(result).toBe(5);
});
});

describe("sanitizeRoundNumber method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("숫자에 공백이 없을 때", () => {
const input = "3";
const result = app.sanitizeRoundNumber(input);
expect(result).toEqual(3);
});

test("앞뒤에 공백이 있을 때", () => {
const input = " 3 ";
const result = app.sanitizeRoundNumber(input);
expect(result).toEqual(3);
});

test("숫자 사이에 공백이 있을 때", () => {
const input = "1 0";
const result = app.sanitizeRoundNumber(input);
expect(result).toEqual(10);
});
});

describe("Error Handling in validateRound", () => {
let app;
beforeEach(() => {
app = new App();
});

test("시도 횟수가 1 미만일 때", async () => {
mockInput("0");
await expect(app.getRoundNumber()).rejects.toThrow("[ERROR] 최소 1 이상의 횟수를 입력해주세요.");
});

test("시도 횟수가 10 초과일 때", async () => {
mockInput("11");
await expect(app.getRoundNumber()).rejects.toThrow("[ERROR] 최대 10 이하의 횟수를 입력해주세요.");
});

test("시도 횟수가 자연수 형식이 아닐 때", async () => {
mockInput("2.5");
await expect(app.getRoundNumber()).rejects.toThrow("[ERROR] 횟수는 자연수 형식이어야 합니다");
});

test("시도 횟수가 문자일 때", async () => {
mockInput("삼");
await expect(app.getRoundNumber()).rejects.toThrow("[ERROR] 횟수는 자연수 형식이어야 합니다");
});
});

describe("makeCar method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("자동차 생성하기", () => {
const carName = ["pobi", "woni", "jun"];
app.makeCar(carName);

expect(app.car).toEqual([
{ name: "pobi", distance: 0, move: expect.any(Function), shouldMove: expect.any(Function) },
{ name: "woni", distance: 0, move: expect.any(Function), shouldMove: expect.any(Function) },
{ name: "jun", distance: 0, move: expect.any(Function), shouldMove: expect.any(Function) }
]);
});
});

describe("getRandomNumber method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("0 ~ 9 사이 무작위 값 생성 테스트", () => {
const results = new Set();

for (let i = 0; i < 100; i++) {
const randomValue = app.getRandomNumber();
results.add(randomValue);

expect(randomValue).toBeGreaterThanOrEqual(0);
expect(randomValue).toBeLessThanOrEqual(9);
}

expect(results.size).toBe(10);
});
});

describe("makeCar method movement test", () => {
let app;
let car;
beforeEach(() => {
app = new App();
const carName = ["testCar"];
app.makeCar(carName);
car = app.car[0];
});

test("랜덤 숫자가 4 이상이면 전진", () => {
jest.spyOn(Random, 'pickNumberInRange').mockReturnValue(4);
expect(car.shouldMove()).toBeTruthy();

jest.spyOn(Random, 'pickNumberInRange').mockReturnValue(5);
expect(car.shouldMove()).toBeTruthy();

jest.spyOn(Random, 'pickNumberInRange').mockReturnValue(9);
expect(car.shouldMove()).toBeTruthy();
});

test("랜덤 숫자가 4 미만이면 정지", () => {
jest.spyOn(Random, 'pickNumberInRange').mockReturnValue(1);
expect(car.shouldMove()).toBeFalsy();

jest.spyOn(Random, 'pickNumberInRange').mockReturnValue(2);
expect(car.shouldMove()).toBeFalsy();

jest.spyOn(Random, 'pickNumberInRange').mockReturnValue(3);
expect(car.shouldMove()).toBeFalsy();
});
});

describe("runRace method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("자동차는 총 횟수 만큼 움직여야 한다", async () => {
jest.spyOn(app, 'getCarName').mockResolvedValue(["pobi"]);
jest.spyOn(app, 'getRoundNumber').mockResolvedValue(3);
jest.spyOn(Console, 'readLineAsync').mockResolvedValue("");

jest.spyOn(app, 'getRandomNumber').mockReturnValue(4);

await app.play();

const moveCount = app.car[0].distance;

expect(moveCount).toBe(3);
});
});

describe("getWinner method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("단독 우승자 결정하기", () => {
const carName = ["pobi", "woni", "jun"];
app.makeCar(carName);

jest.spyOn(Random, 'pickNumberInRange').mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(3);

app.runRace(3);

const winner = app.getWinner();
expect(winner).toEqual(["pobi"]);
});

test("다중 우승자 결정하기", () => {
const carName = ["pobi", "woni", "jun"];
app.makeCar(carName);

jest.spyOn(Random, 'pickNumberInRange').mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4)
.mockReturnValueOnce(4);

app.runRace(3);

const winner = app.getWinner();
expect(winner).toEqual(["pobi", "woni", "jun"]);
});
});

describe("printResultHeader method", () => {
let app;
beforeEach(() => {
app = new App();
jest.spyOn(Console, 'print');
});

test("실행 결과 출력", () => {
app.printResultHeader();
expect(Console.print).toHaveBeenCalledWith("\n실행 결과");
});
});

describe("printRoundResult method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("차수별 실행 결과 출력", () => {
const carName = ["pobi", "woni", "jun"];
app.makeCar(carName);
jest.spyOn(Console, 'print').mockImplementation(() => {});

app.car[0].distance = 3; // pobi
app.car[1].distance = 2; // woni
app.car[2].distance = 1; // jun

app.printRoundResult();

expect(Console.print).toHaveBeenCalledWith("pobi : ---");
expect(Console.print).toHaveBeenCalledWith("woni : --");
expect(Console.print).toHaveBeenCalledWith("jun : -");
});
});

describe("printWinner method", () => {
let app;
beforeEach(() => {
app = new App();
});

test("최종 우승자 메세지 출력", () => {
app.car = [
{ name: "pobi", distance: 5 },
{ name: "woni", distance: 5 },
{ name: "jun", distance: 3 }
];

const printSpy = jest.spyOn(Console, 'print');

const winners = app.getWinner();
app.printWinner(winners);

expect(printSpy).toHaveBeenCalledWith("최종 우승자 : pobi, woni");

printSpy.mockRestore();
});
});
});
Loading