Skip to content

Commit 45f0e4e

Browse files
committed
fix: 테스트 추가 구현 및 serverState 리셋 추가
1 parent ec25df2 commit 45f0e4e

File tree

3 files changed

+117
-35
lines changed

3 files changed

+117
-35
lines changed

src/__mocks__/handlers.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,59 @@ import { Event } from '../types';
66

77
// ! HARD
88
// ! 각 응답에 대한 MSW 핸들러를 작성해주세요. GET 요청은 이미 작성되어 있는 events json을 활용해주세요.
9-
const serverState = { events };
9+
export const serverState = {
10+
data: {
11+
events,
12+
},
13+
14+
reset: () => {
15+
serverState.data = { events };
16+
},
17+
};
1018

1119
export const handlers = [
1220
http.get('/api/events', () => {
13-
return HttpResponse.json({ events: serverState.events });
21+
const events = [...serverState.data.events];
22+
23+
return HttpResponse.json({ events });
1424
}),
1525

1626
http.post('/api/events', async ({ request }) => {
17-
const newEvent: Event = { id: randomUUID(), ...await request.clone().json() };
18-
19-
serverState.events.unshift(newEvent);
27+
const events = [...serverState.data.events];
28+
const newEvent: Event = { id: randomUUID(), ...(await request.clone().json()) };
2029

30+
serverState.data.events = [...events, newEvent];
2131
return HttpResponse.json(newEvent, { status: 201 });
2232
}),
2333

2434
http.put('/api/events/:id', async ({ params, request }) => {
35+
const events = [...serverState.data.events];
2536
const { id } = params;
2637
const updatedEvent: Event = await request.clone().json();
27-
28-
const targetIndex = serverState.events.findIndex(x => x.id === id);
38+
const targetIndex = events.findIndex((x) => x.id === id);
2939
if (targetIndex === -1) {
3040
return HttpResponse.json({ error: 'Not Found' }, { status: 404 });
3141
}
3242

33-
serverState.events[targetIndex] = {
34-
...serverState.events[targetIndex],
35-
...updatedEvent
43+
events[targetIndex] = {
44+
...events[targetIndex],
45+
...updatedEvent,
3646
};
3747

38-
return HttpResponse.json(serverState.events[targetIndex]);
48+
serverState.data.events = [...events];
49+
return HttpResponse.json(events[targetIndex]);
3950
}),
4051

4152
http.delete('/api/events/:id', ({ params }) => {
53+
const events = [...serverState.data.events];
4254
const { id } = params;
4355

44-
const targetIndex = serverState.events.findIndex(x => x.id === id);
56+
const targetIndex = events.findIndex((x) => x.id === id);
4557
if (targetIndex === -1) {
4658
return HttpResponse.json({ error: 'Not Found' }, { status: 404 });
4759
}
48-
49-
serverState.events.splice(targetIndex, 1);
5060

61+
serverState.data.events = events.filter((x) => x.id !== id);
5162
return HttpResponse.json(null, { status: 204 });
5263
}),
5364
];

src/__tests__/medium.integration.spec.tsx

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
import CssBaseline from '@mui/material/CssBaseline';
22
import { ThemeProvider, createTheme } from '@mui/material/styles';
3-
import { render, screen, within, act, fireEvent, RenderResult, waitFor } from '@testing-library/react';
3+
import {
4+
render,
5+
screen,
6+
within,
7+
act,
8+
fireEvent,
9+
RenderResult,
10+
waitFor,
11+
} from '@testing-library/react';
412
import { UserEvent, userEvent } from '@testing-library/user-event';
513
import { http, HttpResponse } from 'msw';
614
import { SnackbarProvider } from 'notistack';
715
import { ReactElement } from 'react';
816

917
import App from '../App';
1018
import { server } from '../setupTests';
11-
import { Event } from '../types';
19+
import { Event, EventForm } from '../types';
1220

1321
const renderApp = () => {
1422
const theme = createTheme();
@@ -22,35 +30,97 @@ const renderApp = () => {
2230
);
2331
};
2432

25-
describe('일정 CRUD 및 기본 기능', () => {
26-
it.only('입력한 새로운 일정 정보에 맞춰 모든 필드가 이벤트 리스트에 정확히 저장된다.', async () => {
27-
renderApp();
33+
async function clearEvent() {
34+
fireEvent.change(screen.getByLabelText('제목'), { target: { value: '' } });
35+
fireEvent.change(screen.getByLabelText('날짜'), { target: { value: '' } });
36+
fireEvent.change(screen.getByLabelText('설명'), { target: { value: '' } });
37+
fireEvent.change(screen.getByLabelText('시작 시간'), { target: { value: '' } });
38+
fireEvent.change(screen.getByLabelText('종료 시간'), { target: { value: '' } });
39+
}
40+
41+
async function createEvent({ title, date, description, startTime, endTime }: Partial<EventForm>) {
42+
fireEvent.change(screen.getByLabelText('제목'), { target: { value: title } });
43+
fireEvent.change(screen.getByLabelText('날짜'), { target: { value: date } });
44+
fireEvent.change(screen.getByLabelText('설명'), { target: { value: description } });
45+
fireEvent.change(screen.getByLabelText('시작 시간'), { target: { value: startTime } });
46+
fireEvent.change(screen.getByLabelText('종료 시간'), { target: { value: endTime } });
47+
48+
await fireEvent.click(screen.getByTestId('event-submit-button'));
2849

29-
fireEvent.change(screen.getByLabelText('제목'), { target: { value : '테스트 - 새 회의'}});
30-
fireEvent.change(screen.getByLabelText('날짜'), { target: { value : '2025-08-01'}});
31-
fireEvent.change(screen.getByLabelText('설명'), { target: { value : '테스트 - 설명'}});
32-
fireEvent.change(screen.getByLabelText('시작 시간'), { target: { value : '17:00'}});
33-
fireEvent.change(screen.getByLabelText('종료 시간'), { target: { value : '18:00'}});
50+
clearEvent();
51+
}
3452

35-
await fireEvent.click(
36-
screen.getByTestId('event-submit-button')
37-
);
53+
async function updateEvent({ title, date, description, startTime, endTime }: Partial<EventForm>) {
54+
fireEvent.click(screen.getByLabelText('Edit event'));
3855

39-
await waitFor(() => {
40-
screen.getByTestId('event-list');
41-
})
56+
fireEvent.change(screen.getByLabelText('제목'), { target: { value: title } });
57+
date && fireEvent.change(screen.getByLabelText('날짜'), { target: { value: date } });
58+
description && fireEvent.change(screen.getByLabelText('설명'), { target: { value: description } });
59+
startTime && fireEvent.change(screen.getByLabelText('시작 시간'), { target: { value: startTime } });
60+
endTime && fireEvent.change(screen.getByLabelText('종료 시간'), { target: { value: endTime } });
4261

43-
const eventList = within(screen.getByTestId('event-list'));
62+
await fireEvent.click(screen.getByTestId('event-submit-button'));
63+
64+
clearEvent();
65+
}
66+
67+
describe.only('일정 CRUD 및 기본 기능', () => {
68+
it('입력한 새로운 일정 정보에 맞춰 모든 필드가 이벤트 리스트에 정확히 저장된다.', async () => {
69+
renderApp();
70+
71+
// 기존 회의 날짜로 이동
72+
fireEvent.click(screen.getByLabelText('Next'));
73+
fireEvent.click(screen.getByLabelText('Next'));
74+
75+
await createEvent({
76+
title: '테스트 - 새 회의',
77+
date: '2025-10-16',
78+
description: '테스트 - 설명',
79+
startTime: '17:00',
80+
endTime: '18:00',
81+
});
82+
83+
const eventList = within(await screen.findByTestId('event-list'));
4484
expect(eventList.getByText('테스트 - 새 회의')).toBeInTheDocument();
45-
expect(eventList.getByText('2025-08-01')).toBeInTheDocument();
85+
expect(eventList.getByText('2025-10-16')).toBeInTheDocument();
4686
expect(eventList.getByText('테스트 - 설명')).toBeInTheDocument();
4787

88+
// TODO: 구현에 몇가지 validation 을 넣어서 더 안정적으로 바꾸기
4889
// ! HINT. event를 추가 제거하고 저장하는 로직을 잘 살펴보고, 만약 그대로 구현한다면 어떤 문제가 있을 지 고민해보세요.
4990
});
5091

51-
it('기존 일정의 세부 정보를 수정하고 변경사항이 정확히 반영된다', async () => {});
92+
it('기존 일정의 세부 정보를 수정하고 변경사항이 정확히 반영된다', async () => {
93+
renderApp();
94+
95+
// 기존 회의 날짜로 이동
96+
fireEvent.click(screen.getByLabelText('Next'));
97+
fireEvent.click(screen.getByLabelText('Next'));
5298

53-
it('일정을 삭제하고 더 이상 조회되지 않는지 확인한다', async () => {});
99+
const eventList = within(await screen.findByTestId('event-list'));
100+
expect(eventList.getByText('기존 회의')).toBeInTheDocument();
101+
102+
await updateEvent({ title: '바뀐 회의' });
103+
104+
const eventListAfterUpdate = within(await screen.findByTestId('event-list'));
105+
expect(eventListAfterUpdate.getByText('바뀐 회의')).toBeInTheDocument();
106+
});
107+
108+
it('일정을 삭제하고 더 이상 조회되지 않는지 확인한다', async () => {
109+
renderApp();
110+
111+
// 기존 회의 날짜로 이동
112+
fireEvent.click(screen.getByLabelText('Next'));
113+
fireEvent.click(screen.getByLabelText('Next'));
114+
115+
const eventList = within(await screen.findByTestId('event-list'));
116+
expect(eventList.getByText('기존 회의')).toBeInTheDocument();
117+
118+
await fireEvent.click(screen.getByLabelText('Delete event'));
119+
120+
const eventListAfterDelete = within(await screen.findByTestId('event-list'));
121+
expect(await eventListAfterDelete.queryByText('기존 회의')).toBeNull();
122+
expect(await eventListAfterDelete.getByText('검색 결과가 없습니다.')).toBeInTheDocument();
123+
});
54124
});
55125

56126
describe('일정 뷰', () => {

src/setupTests.ts

Lines changed: 2 additions & 1 deletion
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, serverState } from './__mocks__/handlers';
55

66
/* msw */
77
export const server = setupServer(...handlers);
@@ -16,6 +16,7 @@ beforeEach(() => {
1616

1717
afterEach(() => {
1818
server.resetHandlers();
19+
serverState.reset();
1920
vi.clearAllMocks();
2021
});
2122

0 commit comments

Comments
 (0)