Skip to content

Commit 3867af9

Browse files
committed
refactor: 통합 테스트 폴더구조 재구성
1 parent 4145f4b commit 3867af9

File tree

8 files changed

+325
-188
lines changed

8 files changed

+325
-188
lines changed

src/__tests__/medium.integration.spec.tsx

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import CssBaseline from '@mui/material/CssBaseline';
22
import { ThemeProvider, createTheme } from '@mui/material/styles';
3-
import { render, screen, within, act, waitFor } from '@testing-library/react';
3+
import { render, screen, within, act } from '@testing-library/react';
44
import { UserEvent, userEvent } from '@testing-library/user-event';
55
import { http, HttpResponse } from 'msw';
66
import { SnackbarProvider } from 'notistack';
@@ -340,70 +340,3 @@ it('notificationTime을 10으로 하면 지정 시간 10분 전 알람 텍스트
340340

341341
expect(screen.getByText('10분 후 기존 회의 일정이 시작됩니다.')).toBeInTheDocument();
342342
});
343-
344-
describe('반복 UI 및 표시', () => {
345-
it('반복 일정 체크 시 반복 유형/종료일 입력 UI가 노출된다', async () => {
346-
const { user } = setup(<App />);
347-
348-
const repeatToggle = await screen.findByLabelText('반복 일정');
349-
await user.click(repeatToggle);
350-
351-
expect(await screen.findByRole('combobox', { name: '반복 유형' })).toBeInTheDocument();
352-
expect(screen.getByLabelText('반복 종료일')).toBeInTheDocument();
353-
});
354-
355-
it('반복 일정을 저장하면 리스트에 반복 아이콘(↻)이 표시된다 (Red)', async () => {
356-
setupMockHandlerCreation();
357-
358-
const { user } = setup(<App />);
359-
360-
await user.click(screen.getAllByText('일정 추가')[0]);
361-
362-
await user.type(screen.getByLabelText('제목'), '반복 회의');
363-
await user.type(screen.getByLabelText('날짜'), '2025-10-15');
364-
await user.type(screen.getByLabelText('시작 시간'), '09:00');
365-
await user.type(screen.getByLabelText('종료 시간'), '10:00');
366-
await user.type(screen.getByLabelText('설명'), '반복 테스트');
367-
await user.type(screen.getByLabelText('위치'), '회의실 C');
368-
369-
await user.click(screen.getByLabelText('카테고리'));
370-
await user.click(within(screen.getByLabelText('카테고리')).getByRole('combobox'));
371-
await user.click(screen.getByRole('option', { name: '업무-option' }));
372-
373-
const repeatToggle = await screen.findByLabelText('반복 일정');
374-
await user.click(repeatToggle);
375-
await user.click(await screen.findByRole('combobox', { name: '반복 유형' }));
376-
await user.click(screen.getByRole('option', { name: '매주' }));
377-
378-
await user.click(screen.getByTestId('event-submit-button'));
379-
380-
const eventList = within(screen.getByTestId('event-list'));
381-
expect(eventList.getByLabelText('반복 일정 아이콘')).toBeInTheDocument();
382-
});
383-
384-
it('반복 일정을 단일 수정으로 저장하면 반복 아이콘이 사라진다', async () => {
385-
setupMockHandlerUpdating();
386-
387-
const { user } = setup(<App />);
388-
389-
// 먼저 기존 이벤트를 반복 이벤트로 변경하여 아이콘이 보이도록 만든다
390-
await user.click((await screen.findAllByLabelText('Edit event'))[0]);
391-
const repeatToggle = await screen.findByLabelText('반복 일정');
392-
await user.click(repeatToggle);
393-
await user.click(await screen.findByRole('combobox', { name: '반복 유형' }));
394-
await user.click(screen.getByRole('option', { name: '매주' }));
395-
await user.click(screen.getByTestId('event-submit-button'));
396-
397-
const list = within(screen.getByTestId('event-list'));
398-
expect(await list.findByLabelText('반복 일정 아이콘')).toBeInTheDocument();
399-
400-
await user.click((await screen.findAllByLabelText('Edit event'))[0]);
401-
const repeatToggle2 = await screen.findByLabelText('반복 일정');
402-
await user.click(repeatToggle2); // 체크 해제
403-
await user.click(screen.getByTestId('event-submit-button'));
404-
405-
await waitFor(() => {
406-
expect(list.queryByLabelText('반복 일정 아이콘')).not.toBeInTheDocument();
407-
});
408-
});
409-
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { CssBaseline } from '@mui/material';
2+
import { createTheme, ThemeProvider } from '@mui/material/styles';
3+
import { screen, within, waitFor, render } from '@testing-library/react';
4+
import { userEvent } from '@testing-library/user-event';
5+
import { SnackbarProvider } from 'notistack';
6+
import { ReactElement } from 'react';
7+
8+
import {
9+
setupMockHandlerCreation,
10+
setupMockHandlerUpdating,
11+
} from '../../../__mocks__/handlersUtils';
12+
import App from '../../../App';
13+
14+
const theme = createTheme();
15+
16+
const setup = (element: ReactElement) => {
17+
const user = userEvent.setup();
18+
19+
return {
20+
...render(
21+
<ThemeProvider theme={theme}>
22+
<CssBaseline />
23+
<SnackbarProvider>{element}</SnackbarProvider>
24+
</ThemeProvider>
25+
),
26+
user,
27+
};
28+
};
29+
30+
describe('반복 UI 및 표시', () => {
31+
it('반복 일정 체크 시 반복 유형/종료일 입력 UI가 노출된다', async () => {
32+
const { user } = setup(<App />);
33+
34+
const repeatToggle = await screen.findByLabelText('반복 일정');
35+
await user.click(repeatToggle);
36+
37+
expect(await screen.findByRole('combobox', { name: '반복 유형' })).toBeInTheDocument();
38+
expect(screen.getByLabelText('반복 종료일')).toBeInTheDocument();
39+
});
40+
41+
it('반복 일정을 저장하면 리스트에 반복 아이콘(↻)이 표시된다 (Red)', async () => {
42+
setupMockHandlerCreation();
43+
44+
const { user } = setup(<App />);
45+
46+
await user.click(screen.getAllByText('일정 추가')[0]);
47+
48+
await user.type(screen.getByLabelText('제목'), '반복 회의');
49+
await user.type(screen.getByLabelText('날짜'), '2025-10-15');
50+
await user.type(screen.getByLabelText('시작 시간'), '09:00');
51+
await user.type(screen.getByLabelText('종료 시간'), '10:00');
52+
await user.type(screen.getByLabelText('설명'), '반복 테스트');
53+
await user.type(screen.getByLabelText('위치'), '회의실 C');
54+
55+
await user.click(screen.getByLabelText('카테고리'));
56+
await user.click(within(screen.getByLabelText('카테고리')).getByRole('combobox'));
57+
await user.click(screen.getByRole('option', { name: '업무-option' }));
58+
59+
const repeatToggle = await screen.findByLabelText('반복 일정');
60+
await user.click(repeatToggle);
61+
await user.click(await screen.findByRole('combobox', { name: '반복 유형' }));
62+
await user.click(screen.getByRole('option', { name: '매주' }));
63+
64+
await user.click(screen.getByTestId('event-submit-button'));
65+
66+
const eventList = within(screen.getByTestId('event-list'));
67+
expect(eventList.getByLabelText('반복 일정 아이콘')).toBeInTheDocument();
68+
});
69+
70+
it('반복 일정을 단일 수정으로 저장하면 반복 아이콘이 사라진다', async () => {
71+
setupMockHandlerUpdating();
72+
73+
const { user } = setup(<App />);
74+
75+
// 먼저 기존 이벤트를 반복 이벤트로 변경하여 아이콘이 보이도록 만든다
76+
await user.click((await screen.findAllByLabelText('Edit event'))[0]);
77+
const repeatToggle = await screen.findByLabelText('반복 일정');
78+
await user.click(repeatToggle);
79+
await user.click(await screen.findByRole('combobox', { name: '반복 유형' }));
80+
await user.click(screen.getByRole('option', { name: '매주' }));
81+
await user.click(screen.getByTestId('event-submit-button'));
82+
83+
const list = within(screen.getByTestId('event-list'));
84+
expect(await list.findByLabelText('반복 일정 아이콘')).toBeInTheDocument();
85+
86+
await user.click((await screen.findAllByLabelText('Edit event'))[0]);
87+
const repeatToggle2 = await screen.findByLabelText('반복 일정');
88+
await user.click(repeatToggle2); // 체크 해제
89+
await user.click(screen.getByTestId('event-submit-button'));
90+
91+
await waitFor(() => {
92+
expect(list.queryByLabelText('반복 일정 아이콘')).not.toBeInTheDocument();
93+
});
94+
});
95+
});

src/__tests__/repeat/unit/actions.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { getFilteredEvents } from '../../../utils/eventUtils.ts';
2+
import { expandEventsToNextOccurrences, generateInstances } from '../../../utils/repeat/actions.ts';
23
import {
3-
expandEventsToNextOccurrences,
44
getNextDailyOccurrence,
5-
} from '../../../utils/repeat/actions.ts';
6-
import { getNextWeeklyOccurrence } from '../../../utils/repeat/actions.ts';
7-
import { getNextYearlyOccurrence } from '../../../utils/repeat/actions.ts';
8-
import { generateInstances } from '../../../utils/repeat/actions.ts';
5+
getNextWeeklyOccurrence,
6+
getNextYearlyOccurrence,
7+
} from '../../../utils/repeat/helpers.ts';
98
import { makeEvent } from '../../utils.ts';
109

1110
describe('getNextDailyOccurrence', () => {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { GLOBAL_REPEAT_CAP } from '../../../utils/repeat/constants';
2+
import {
3+
dateStringToUtcDateOnly,
4+
toDateStringUTC,
5+
clampEndDate,
6+
daysInMonthUTC,
7+
addMonthsUntilHasDay,
8+
getNextMonthlyOccurrence,
9+
getNextYearlyOccurrence,
10+
getNextWeeklyOccurrence,
11+
getNextDailyOccurrence,
12+
} from '../../../utils/repeat/helpers';
13+
14+
describe('helpers - date edge cases', () => {
15+
it('clampEndDate는 더 이른 날짜를 반환해야 한다', () => {
16+
const end = dateStringToUtcDateOnly('2025-01-10');
17+
const cap = GLOBAL_REPEAT_CAP;
18+
const clamped = clampEndDate(end, cap);
19+
expect(toDateStringUTC(clamped)).toBe('2025-01-10');
20+
});
21+
22+
it('daysInMonthUTC는 2월 윤년/평년과 30/31일을 정확히 반환해야 한다', () => {
23+
expect(daysInMonthUTC(2024, 1)).toBe(29);
24+
expect(daysInMonthUTC(2025, 1)).toBe(28);
25+
expect(daysInMonthUTC(2025, 3)).toBe(30); // April
26+
expect(daysInMonthUTC(2025, 0)).toBe(31); // Jan
27+
});
28+
29+
it('addMonthsUntilHasDay는 1/31에서 2월을 건너뛰고 3/31을 반환해야 한다', () => {
30+
const base = dateStringToUtcDateOnly('2025-01-31');
31+
const next = addMonthsUntilHasDay(base, 1, 31);
32+
expect(toDateStringUTC(next)).toBe('2025-03-31');
33+
});
34+
35+
it('getNextMonthlyOccurrence: 1/31 시작, from=2월 → 3/31', () => {
36+
const base = dateStringToUtcDateOnly('2025-01-31');
37+
const from = dateStringToUtcDateOnly('2025-02-10');
38+
const next = getNextMonthlyOccurrence(base, from, 1);
39+
expect(toDateStringUTC(next)).toBe('2025-03-31');
40+
});
41+
42+
it('getNextYearlyOccurrence: 2/29 시작, from=평년 → 다음 윤년', () => {
43+
const base = dateStringToUtcDateOnly('2024-02-29');
44+
const from = dateStringToUtcDateOnly('2025-02-28');
45+
const next = getNextYearlyOccurrence(base, from, 1);
46+
expect(toDateStringUTC(next)).toBe('2028-02-29');
47+
});
48+
49+
it('getNextWeeklyOccurrence: 수요일 시작, from=다음주 월요일 → 다음 수요일', () => {
50+
const base = dateStringToUtcDateOnly('2025-01-01');
51+
const from = dateStringToUtcDateOnly('2025-01-06');
52+
const next = getNextWeeklyOccurrence(base, from, 1);
53+
expect(toDateStringUTC(next)).toBe('2025-01-08');
54+
});
55+
56+
it('getNextDailyOccurrence: from이 더 뒤면 interval에 맞게 다음 날짜', () => {
57+
const base = dateStringToUtcDateOnly('2025-01-01');
58+
const from = dateStringToUtcDateOnly('2025-01-03');
59+
const next = getNextDailyOccurrence(base, from, 1);
60+
expect(toDateStringUTC(next)).toBe('2025-01-03');
61+
});
62+
});

0 commit comments

Comments
 (0)