Skip to content

Commit de34f49

Browse files
committed
temp
1 parent ba5936f commit de34f49

File tree

9 files changed

+284
-99
lines changed

9 files changed

+284
-99
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"test": "vitest",
1212
"test:ui": "vitest --ui",
1313
"test:coverage": "vitest run --coverage",
14+
"test:unit": "vitest easy.dateUtils",
1415
"build": "tsc -b && vite build",
1516
"lint:eslint": "eslint . --ext ts,tsx --report-unused-disable-directives",
1617
"lint:tsc": "tsc --pretty",

src/__mocks__/handlers.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import { randomUUID } from 'crypto';
2+
13
import { http, HttpResponse } from 'msw';
24

3-
import { events } from '../__mocks__/response/events.json' assert { type: 'json' };
4-
import { Event } from '../types';
5+
import { events as initialEvents } from '../__mocks__/response/events.json' assert { type: 'json' };
6+
import type { Event } from '../types';
7+
8+
let events = [...initialEvents];
9+
10+
export const resetEvents = () => {
11+
events = [...initialEvents];
12+
};
513

614
export const handlers = [
715
http.get('/api/events', () => {
@@ -10,30 +18,37 @@ export const handlers = [
1018

1119
http.post('/api/events', async ({ request }) => {
1220
const newEvent = (await request.json()) as Event;
13-
newEvent.id = String(events.length + 1);
21+
newEvent.id = randomUUID();
22+
23+
events = [...events, newEvent];
24+
1425
return HttpResponse.json(newEvent, { status: 201 });
1526
}),
1627

1728
http.put('/api/events/:id', async ({ params, request }) => {
1829
const { id } = params;
19-
const updatedEvent = (await request.json()) as Event;
20-
const index = events.findIndex((event) => event.id === id);
2130

22-
if (index !== -1) {
23-
return HttpResponse.json({ ...events[index], ...updatedEvent });
31+
const index = events.findIndex((event) => event.id === id);
32+
if (index === -1) {
33+
return new HttpResponse(null, { status: 404 });
2434
}
2535

26-
return new HttpResponse(null, { status: 404 });
36+
const updatedEvent = (await request.json()) as Event;
37+
events[index] = { ...events[index], ...updatedEvent };
38+
39+
return HttpResponse.json(events[index]);
2740
}),
2841

2942
http.delete('/api/events/:id', ({ params }) => {
3043
const { id } = params;
31-
const index = events.findIndex((event) => event.id === id);
3244

33-
if (index !== -1) {
34-
return new HttpResponse(null, { status: 204 });
45+
const index = events.findIndex((event) => event.id === id);
46+
if (index === -1) {
47+
return new HttpResponse(null, { status: 404 });
3548
}
3649

37-
return new HttpResponse(null, { status: 404 });
50+
events.splice(index, 1);
51+
52+
return new HttpResponse(null, { status: 204 });
3853
}),
3954
];
Lines changed: 1 addition & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1 @@
1-
{
2-
"events": [
3-
{
4-
"id": "2b7545a6-ebee-426c-b906-2329bc8d62bd",
5-
"title": "팀 회의",
6-
"date": "2025-08-20",
7-
"startTime": "10:00",
8-
"endTime": "11:00",
9-
"description": "주간 팀 미팅",
10-
"location": "회의실 A",
11-
"category": "업무",
12-
"repeat": { "type": "none", "interval": 0 },
13-
"notificationTime": 1
14-
},
15-
{
16-
"id": "09702fb3-a478-40b3-905e-9ab3c8849dcd",
17-
"title": "점심 약속",
18-
"date": "2025-08-21",
19-
"startTime": "12:30",
20-
"endTime": "13:30",
21-
"description": "동료와 점심 식사",
22-
"location": "회사 근처 식당",
23-
"category": "개인",
24-
"repeat": { "type": "none", "interval": 0 },
25-
"notificationTime": 1
26-
},
27-
{
28-
"id": "da3ca408-836a-4d98-b67a-ca389d07552b",
29-
"title": "프로젝트 마감",
30-
"date": "2025-08-25",
31-
"startTime": "09:00",
32-
"endTime": "18:00",
33-
"description": "분기별 프로젝트 마감",
34-
"location": "사무실",
35-
"category": "업무",
36-
"repeat": { "type": "none", "interval": 0 },
37-
"notificationTime": 1
38-
},
39-
{
40-
"id": "dac62941-69e5-4ec0-98cc-24c2a79a7f81",
41-
"title": "생일 파티",
42-
"date": "2025-08-28",
43-
"startTime": "19:00",
44-
"endTime": "22:00",
45-
"description": "친구 생일 축하",
46-
"location": "친구 집",
47-
"category": "개인",
48-
"repeat": { "type": "none", "interval": 0 },
49-
"notificationTime": 1
50-
},
51-
{
52-
"id": "80d85368-b4a4-47b3-b959-25171d49371f",
53-
"title": "운동",
54-
"date": "2025-08-22",
55-
"startTime": "18:00",
56-
"endTime": "19:00",
57-
"description": "주간 운동",
58-
"location": "헬스장",
59-
"category": "개인",
60-
"repeat": { "type": "none", "interval": 0 },
61-
"notificationTime": 1
62-
}
63-
]
64-
}
1+
{"events":[{"id":"2b7545a6-ebee-426c-b906-2329bc8d62bd","title":"팀 회의","date":"2025-08-20","startTime":"10:00","endTime":"11:00","description":"주간 팀 미팅","location":"회의실 A","category":"업무","repeat":{"type":"none","interval":0},"notificationTime":1},{"id":"09702fb3-a478-40b3-905e-9ab3c8849dcd","title":"점심 약속","date":"2025-08-21","startTime":"12:30","endTime":"13:30","description":"동료와 점심 식사","location":"회사 근처 식당","category":"개인","repeat":{"type":"none","interval":0},"notificationTime":1},{"id":"da3ca408-836a-4d98-b67a-ca389d07552b","title":"프로젝트 마감","date":"2025-08-25","startTime":"09:00","endTime":"18:00","description":"분기별 프로젝트 마감","location":"사무실","category":"업무","repeat":{"type":"none","interval":0},"notificationTime":1},{"id":"dac62941-69e5-4ec0-98cc-24c2a79a7f81","title":"생일 파티","date":"2025-08-28","startTime":"19:00","endTime":"22:00","description":"친구 생일 축하","location":"친구 집","category":"개인","repeat":{"type":"none","interval":0},"notificationTime":1},{"id":"80d85368-b4a4-47b3-b959-25171d49371f","title":"운동","date":"2025-08-22","startTime":"18:00","endTime":"19:00","description":"주간 운동","location":"헬스장","category":"개인","repeat":{"type":"none","interval":0},"notificationTime":1},{"id":"58b866b9-5304-4285-9862-068447cee4a4","title":"테스트 회의","date":"2025-08-25","startTime":"15:00","endTime":"16:00","description":"테스트 회의입니다","location":"회의실 3-1","category":"업무","repeat":{"type":"none","interval":1},"notificationTime":10}]}

src/__tests__/medium.integration.spec.tsx

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
11
import CssBaseline from '@mui/material/CssBaseline';
22
import { ThemeProvider, createTheme } from '@mui/material/styles';
3-
import { render, screen, within, act } from '@testing-library/react';
3+
import { render, screen, within } from '@testing-library/react';
44
import { UserEvent, userEvent } from '@testing-library/user-event';
5-
import { http, HttpResponse } from 'msw';
65
import { SnackbarProvider } from 'notistack';
76
import { ReactElement } from 'react';
87

9-
import {
10-
setupMockHandlerCreation,
11-
setupMockHandlerDeletion,
12-
setupMockHandlerUpdating,
13-
} from '../__mocks__/handlersUtils';
148
import App from '../App';
15-
import { server } from '../setupTests';
16-
import { Event } from '../types';
17-
18-
const theme = createTheme();
9+
import type { Event } from '../types';
1910

2011
// ! HINT. 이 유틸을 사용해 리액트 컴포넌트를 렌더링해보세요.
2112
const setup = (element: ReactElement) => {
13+
const theme = createTheme();
2214
const user = userEvent.setup();
2315

2416
// ? Medium: 여기서 Provider로 묶어주는 동작은 의미있을까요? 있다면 어떤 의미일까요?
@@ -41,7 +33,6 @@ const saveSchedule = async (
4133
const { title, date, startTime, endTime, location, description, category } = form;
4234

4335
await user.click(screen.getAllByText('일정 추가')[0]);
44-
4536
await user.type(screen.getByLabelText('제목'), title);
4637
await user.type(screen.getByLabelText('날짜'), date);
4738
await user.type(screen.getByLabelText('시작 시간'), startTime);
@@ -51,14 +42,32 @@ const saveSchedule = async (
5142
await user.click(screen.getByLabelText('카테고리'));
5243
await user.click(within(screen.getByLabelText('카테고리')).getByRole('combobox'));
5344
await user.click(screen.getByRole('option', { name: `${category}-option` }));
54-
5545
await user.click(screen.getByTestId('event-submit-button'));
5646
};
5747

5848
// ! HINT. "검색 결과가 없습니다"는 초기에 노출되는데요. 그럼 검증하고자 하는 액션이 실행되기 전에 검증해버리지 않을까요? 이 테스트를 신뢰성있게 만드려면 어떻게 할까요?
5949
describe('일정 CRUD 및 기본 기능', () => {
60-
it('입력한 새로운 일정 정보에 맞춰 모든 필드가 이벤트 리스트에 정확히 저장된다.', async () => {
50+
it.only('입력한 새로운 일정 정보에 맞춰 모든 필드가 이벤트 리스트에 정확히 저장된다.', async () => {
6151
// ! HINT. event를 추가 제거하고 저장하는 로직을 잘 살펴보고, 만약 그대로 구현한다면 어떤 문제가 있을 지 고민해보세요.
52+
const { user } = setup(<App />);
53+
54+
await saveSchedule(user, {
55+
title: '테스트 회의',
56+
date: '2025-10-03',
57+
startTime: '15:00',
58+
endTime: '16:00',
59+
description: '테스트 회의입니다',
60+
location: '회의실 3-1',
61+
category: '업무',
62+
});
63+
64+
const eventList = within(screen.getByTestId('event-list'));
65+
66+
expect(eventList.getByText('테스트 회의')).toBeInTheDocument();
67+
expect(eventList.getByText('2025-10-03')).toBeInTheDocument();
68+
expect(eventList.getByText('15:00 - 16:00')).toBeInTheDocument();
69+
expect(eventList.getByText('테스트 회의입니다')).toBeInTheDocument();
70+
expect(eventList.getByText('회의실 3-1')).toBeInTheDocument();
6271
});
6372

6473
it('기존 일정의 세부 정보를 수정하고 변경사항이 정확히 반영된다', async () => {});

src/__tests__/unit/easy.dateUtils.spec.ts

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,118 @@ describe('getDaysInMonth', () => {
5151
});
5252

5353
describe('getWeekDates', () => {
54-
it('주중의 날짜(수요일)에 대해 올바른 주의 날짜들을 반환한다', () => {});
54+
it.only('주중의 날짜(수요일)에 대해 올바른 주의 날짜들을 반환한다', () => {
55+
const wednesday = new Date(2025, 7, 6); // 2025-08-06 수요일
56+
const weekDates = getWeekDates(wednesday);
57+
58+
// 해당 주는 2025-08-03(일) ~ 2025-08-09(토)
59+
expect(weekDates).toHaveLength(7);
60+
expect(weekDates[0]).toEqual(new Date(2025, 7, 3)); // 2025-08-03 일요일
61+
expect(weekDates[1]).toEqual(new Date(2025, 7, 4)); // 2025-08-04 월요일
62+
expect(weekDates[2]).toEqual(new Date(2025, 7, 5)); // 2025-08-05 화요일
63+
expect(weekDates[3]).toEqual(new Date(2025, 7, 6)); // 2025-08-06 수요일
64+
expect(weekDates[4]).toEqual(new Date(2025, 7, 7)); // 2025-08-07 목요일
65+
expect(weekDates[5]).toEqual(new Date(2025, 7, 8)); // 2025-08-08 금요일
66+
expect(weekDates[6]).toEqual(new Date(2025, 7, 9)); // 2025-08-09 토요일
67+
});
5568

56-
it('주의 시작(월요일)에 대해 올바른 주의 날짜들을 반환한다', () => {});
69+
it.only('주의 시작(월요일)에 대해 올바른 주의 날짜들을 반환한다', () => {
70+
const monday = new Date(2025, 7, 4); // 2025-08-04 월요일
71+
const weekDates = getWeekDates(monday);
72+
73+
// 해당 주는 2025-08-03(일) ~ 2025-08-09(토)
74+
expect(weekDates).toHaveLength(7);
75+
expect(weekDates[0]).toEqual(new Date(2025, 7, 3)); // 2025-08-03 일요일
76+
expect(weekDates[1]).toEqual(new Date(2025, 7, 4)); // 2025-08-04 월요일
77+
expect(weekDates[2]).toEqual(new Date(2025, 7, 5)); // 2025-08-05 화요일
78+
expect(weekDates[3]).toEqual(new Date(2025, 7, 6)); // 2025-08-06 수요일
79+
expect(weekDates[4]).toEqual(new Date(2025, 7, 7)); // 2025-08-07 목요일
80+
expect(weekDates[5]).toEqual(new Date(2025, 7, 8)); // 2025-08-08 금요일
81+
expect(weekDates[6]).toEqual(new Date(2025, 7, 9)); // 2025-08-09 토요일
82+
});
5783

58-
it('주의 끝(일요일)에 대해 올바른 주의 날짜들을 반환한다', () => {});
84+
it.only('주의 끝(일요일)에 대해 올바른 주의 날짜들을 반환한다', () => {
85+
const sunday = new Date(2025, 7, 3); // 2025-08-03 일요일
86+
const weekDates = getWeekDates(sunday);
87+
88+
// 해당 주는 2025-08-03(일) ~ 2025-08-09(토)
89+
expect(weekDates).toHaveLength(7);
90+
expect(weekDates[0]).toEqual(new Date(2025, 7, 3)); // 2025-08-03 일요일
91+
expect(weekDates[1]).toEqual(new Date(2025, 7, 4)); // 2025-08-04 월요일
92+
expect(weekDates[2]).toEqual(new Date(2025, 7, 5)); // 2025-08-05 화요일
93+
expect(weekDates[3]).toEqual(new Date(2025, 7, 6)); // 2025-08-06 수요일
94+
expect(weekDates[4]).toEqual(new Date(2025, 7, 7)); // 2025-08-07 목요일
95+
expect(weekDates[5]).toEqual(new Date(2025, 7, 8)); // 2025-08-08 금요일
96+
expect(weekDates[6]).toEqual(new Date(2025, 7, 9)); // 2025-08-09 토요일
97+
});
5998

60-
it('연도를 넘어가는 주의 날짜를 정확히 처리한다 (연말)', () => {});
99+
it.only('연도를 넘어가는 주의 날짜를 정확히 처리한다 (연말)', () => {
100+
const date = new Date(2025, 11, 31); // 2025-12-31 수요일
101+
const weekDates = getWeekDates(date);
102+
103+
expect(weekDates).toHaveLength(7);
104+
expect(weekDates[0]).toEqual(new Date(2025, 11, 28)); // 2025-12-28 일요일
105+
expect(weekDates[1]).toEqual(new Date(2025, 11, 29)); // 2025-12-29 월요일
106+
expect(weekDates[2]).toEqual(new Date(2025, 11, 30)); // 2025-12-30 화요일
107+
expect(weekDates[3]).toEqual(new Date(2025, 11, 31)); // 2025-12-31 수요일
108+
expect(weekDates[4]).toEqual(new Date(2026, 0, 1)); // 2026-01-01 목요일
109+
expect(weekDates[5]).toEqual(new Date(2026, 0, 2)); // 2026-01-02 금요일
110+
expect(weekDates[6]).toEqual(new Date(2026, 0, 3)); // 2026-01-03 토요일
111+
});
61112

62-
it('연도를 넘어가는 주의 날짜를 정확히 처리한다 (연초)', () => {});
113+
it.only('연도를 넘어가는 주의 날짜를 정확히 처리한다 (연초)', () => {
114+
const date = new Date(2025, 0, 1); // 2025-01-01 수요일
115+
const weekDates = getWeekDates(date);
116+
117+
expect(weekDates).toHaveLength(7);
118+
expect(weekDates[0]).toEqual(new Date(2024, 11, 29)); // 2024-12-29 일요일
119+
expect(weekDates[1]).toEqual(new Date(2024, 11, 30)); // 2024-12-30 월요일
120+
expect(weekDates[2]).toEqual(new Date(2024, 11, 31)); // 2024-12-31 화요일
121+
expect(weekDates[3]).toEqual(new Date(2025, 0, 1)); // 2025-01-01 수요일
122+
expect(weekDates[4]).toEqual(new Date(2025, 0, 2)); // 2025-01-02 목요일
123+
expect(weekDates[5]).toEqual(new Date(2025, 0, 3)); // 2025-01-03 금요일
124+
expect(weekDates[6]).toEqual(new Date(2025, 0, 4)); // 2025-01-04 토요일
125+
});
63126

64-
it('윤년의 2월 29일을 포함한 주를 올바르게 처리한다', () => {});
127+
it.only('윤년의 2월 29일을 포함한 주를 올바르게 처리한다', () => {
128+
const date = new Date(2024, 1, 29); // 2024-02-29 목요일
129+
const weekDates = getWeekDates(date);
130+
131+
expect(weekDates).toHaveLength(7);
132+
expect(weekDates[0]).toEqual(new Date(2024, 1, 25)); // 2024-02-25 일요일
133+
expect(weekDates[1]).toEqual(new Date(2024, 1, 26)); // 2024-02-26 월요일
134+
expect(weekDates[2]).toEqual(new Date(2024, 1, 27)); // 2024-02-27 화요일
135+
expect(weekDates[3]).toEqual(new Date(2024, 1, 28)); // 2024-02-28 수요일
136+
expect(weekDates[4]).toEqual(new Date(2024, 1, 29)); // 2024-02-29 목요일
137+
expect(weekDates[5]).toEqual(new Date(2024, 2, 1)); // 2024-03-01 금요일
138+
expect(weekDates[6]).toEqual(new Date(2024, 2, 2)); // 2024-03-02 토요일
139+
});
65140

66-
it('월의 마지막 날짜를 포함한 주를 올바르게 처리한다', () => {});
141+
it.only('월의 마지막 날짜를 포함한 주를 올바르게 처리한다', () => {
142+
const date = new Date(2025, 7, 31); // 2025-08-31 일요일
143+
const weekDates = getWeekDates(date);
144+
145+
// 해당 주는 2025-08-31(일) ~ 2025-09-06(토)
146+
expect(weekDates).toHaveLength(7);
147+
expect(weekDates[0]).toEqual(new Date(2025, 7, 31)); // 2025-08-31 일요일
148+
expect(weekDates[1]).toEqual(new Date(2025, 8, 1)); // 2025-09-01 월요일
149+
expect(weekDates[2]).toEqual(new Date(2025, 8, 2)); // 2025-09-02 화요일
150+
expect(weekDates[3]).toEqual(new Date(2025, 8, 3)); // 2025-09-03 수요일
151+
expect(weekDates[4]).toEqual(new Date(2025, 8, 4)); // 2025-09-04 목요일
152+
expect(weekDates[5]).toEqual(new Date(2025, 8, 5)); // 2025-09-05 금요일
153+
expect(weekDates[6]).toEqual(new Date(2025, 8, 6)); // 2025-09-06 토요일
154+
});
155+
156+
it.only('입력된 Date 객체가 함수 실행 후 변경되는 부작용이 있다', () => {
157+
const originalDate = new Date(2025, 7, 6); // 2025-08-06 수요일
158+
const originalTime = originalDate.getTime();
159+
160+
getWeekDates(originalDate);
161+
162+
// 부작용 확인: 원본 객체가 변경됨 (버그)
163+
expect(originalDate.getTime()).not.toBe(originalTime);
164+
expect(originalDate).toEqual(new Date(2025, 7, 3)); // 2025-08-03 일요일로 변경됨
165+
});
67166
});
68167

69168
describe('getWeeksAtMonth', () => {

src/setupTests.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { setupServer } from 'msw/node';
22
import '@testing-library/jest-dom';
33

4-
import { handlers } from './__mocks__/handlers';
4+
import { handlers, resetEvents } from './__mocks__/handlers';
55

66
// ! Hard 여기 제공 안함
77
/* msw */
@@ -16,8 +16,8 @@ beforeAll(() => {
1616

1717
beforeEach(() => {
1818
expect.hasAssertions(); // ? Med: 이걸 왜 써야하는지 물어보자
19-
2019
vi.setSystemTime(new Date('2025-10-01')); // ? Med: 이걸 왜 써야하는지 물어보자
20+
resetEvents();
2121
});
2222

2323
afterEach(() => {

0 commit comments

Comments
 (0)