-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
82857cc
commit 80c9393
Showing
7 changed files
with
497 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { renderHook, act, waitFor } from '@testing-library/react'; | ||
import { useDeleteChat } from '@/hooks/useDeleteChat'; | ||
import { deleteDoc } from 'firebase/firestore'; | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
|
||
jest.mock('firebase/firestore', () => ({ | ||
deleteDoc: jest.fn(), | ||
doc: jest.fn(), | ||
})); | ||
|
||
const createTestQueryClient = () => { | ||
return new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
retry: false, | ||
}, | ||
}, | ||
}); | ||
}; | ||
|
||
const wrapper = ({ children }: { children: React.ReactNode }) => { | ||
return ( | ||
<QueryClientProvider client={createTestQueryClient()}> | ||
{children} | ||
</QueryClientProvider> | ||
); | ||
}; | ||
|
||
describe('useDeleteChat 테스트', () => { | ||
const mockUserId = 'user123'; | ||
const mockSessionId = 'session123'; | ||
|
||
it('성공적으로 채팅 세션을 삭제해야 한다.', async () => { | ||
(deleteDoc as jest.Mock).mockResolvedValueOnce(undefined); | ||
|
||
const { result } = renderHook(() => useDeleteChat(mockUserId), { | ||
wrapper, | ||
}); | ||
|
||
await act(async () => { | ||
result.current.mutate(mockSessionId); | ||
}); | ||
|
||
await waitFor(() => expect(result.current.isSuccess).toBe(true)); | ||
|
||
expect(deleteDoc).toHaveBeenCalledWith(expect.anything()); | ||
|
||
expect(result.current.status).toBe('success'); | ||
}); | ||
|
||
it('채팅 세션 삭제 실패 시 에러를 반환해야 한다.', async () => { | ||
const errorMessage = 'Failed to delete chat session'; | ||
(deleteDoc as jest.Mock).mockRejectedValueOnce(new Error(errorMessage)); | ||
|
||
const { result } = renderHook(() => useDeleteChat(mockUserId), { | ||
wrapper, | ||
}); | ||
|
||
await act(async () => { | ||
result.current.mutate(mockSessionId); | ||
}); | ||
|
||
await waitFor(() => expect(result.current.isError).toBe(true)); | ||
expect(result.current.error).toEqual(new Error(errorMessage)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import { renderHook, waitFor } from '@testing-library/react'; | ||
import { useFirestoreQuery } from '@/hooks/useFirestoreQuery'; | ||
import { getDocs, Query } from 'firebase/firestore'; | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
|
||
jest.mock('firebase/firestore', () => ({ | ||
getDocs: jest.fn(), | ||
Query: jest.fn(), | ||
})); | ||
|
||
const createTestQueryClient = () => { | ||
return new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
retry: false, | ||
}, | ||
}, | ||
}); | ||
}; | ||
|
||
const wrapper = ({ children }: { children: React.ReactNode }) => { | ||
return ( | ||
<QueryClientProvider client={createTestQueryClient()}> | ||
{children} | ||
</QueryClientProvider> | ||
); | ||
}; | ||
|
||
describe('useFirestoreQuery 테스트', () => { | ||
const mockQueryKey = 'testQuery'; | ||
const mockFirestoreQuery = {} as Query; | ||
|
||
it('성공적으로 Firestore 데이터를 가져와야 한다', async () => { | ||
const mockData = [ | ||
{ id: '1', name: 'Document 1' }, | ||
{ id: '2', name: 'Document 2' }, | ||
]; | ||
|
||
(getDocs as jest.Mock).mockResolvedValueOnce({ | ||
docs: mockData.map((doc) => ({ | ||
id: doc.id, | ||
data: () => ({ name: doc.name }), | ||
})), | ||
}); | ||
|
||
const { result } = renderHook( | ||
() => | ||
useFirestoreQuery<(typeof mockData)[0]>( | ||
mockQueryKey, | ||
mockFirestoreQuery | ||
), | ||
{ wrapper } | ||
); | ||
|
||
await waitFor(() => expect(result.current.isSuccess).toBe(true)); | ||
expect(result.current.data).toEqual(mockData); | ||
}); | ||
|
||
it('Firestore 호출 실패 시 에러를 반환해야 한다', async () => { | ||
const errorMessage = 'Failed to fetch data'; | ||
|
||
(getDocs as jest.Mock).mockRejectedValueOnce(new Error(errorMessage)); | ||
|
||
const { result } = renderHook( | ||
() => useFirestoreQuery<any>(mockQueryKey, mockFirestoreQuery), | ||
{ wrapper } | ||
); | ||
|
||
await waitFor(() => expect(result.current.isError).toBe(true)); | ||
expect(result.current.error).toEqual(new Error(errorMessage)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { renderHook, act } from '@testing-library/react'; | ||
import { useLocalStorage } from '@/hooks/useLocalStorage'; | ||
|
||
describe('useLocalStorage 테스트', () => { | ||
const mockKey = 'testKey'; | ||
|
||
beforeEach(() => { | ||
localStorage.clear(); | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('초기 값이 localStorage에 없을 때 기본 값을 반환해야 한다', () => { | ||
const { result } = renderHook(() => useLocalStorage(mockKey, 'initial')); | ||
|
||
const [storedValue] = result.current; | ||
|
||
expect(storedValue).toBe('initial'); | ||
expect(localStorage.getItem(mockKey)).toBe(JSON.stringify('initial')); | ||
}); | ||
|
||
it('localStorage에 값이 있을 때 해당 값을 반환해야 한다', () => { | ||
localStorage.setItem(mockKey, JSON.stringify('storedValue')); | ||
|
||
const { result } = renderHook(() => useLocalStorage(mockKey, 'initial')); | ||
|
||
const [storedValue] = result.current; | ||
|
||
expect(storedValue).toBe('storedValue'); | ||
}); | ||
|
||
it('값을 업데이트하면 localStorage에 저장되어야 한다', () => { | ||
const { result } = renderHook(() => useLocalStorage(mockKey, 'initial')); | ||
|
||
const [, setValue] = result.current; | ||
|
||
act(() => { | ||
setValue('newValue'); | ||
}); | ||
|
||
const [updatedValue] = result.current; | ||
|
||
expect(updatedValue).toBe('newValue'); | ||
expect(localStorage.getItem(mockKey)).toBe(JSON.stringify('newValue')); | ||
}); | ||
|
||
it('localStorage에서 데이터를 가져오거나 설정할 때 오류가 발생하면 기본 값을 반환해야 한다', () => { | ||
jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => { | ||
throw new Error('Failed to get item'); | ||
}); | ||
|
||
const { result } = renderHook(() => useLocalStorage(mockKey, 'initial')); | ||
|
||
const [storedValue] = result.current; | ||
|
||
expect(storedValue).toBe('initial'); | ||
}); | ||
|
||
it('setItem에서 오류가 발생하면 콘솔에 에러를 출력해야 한다', () => { | ||
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); | ||
|
||
jest.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { | ||
throw new Error('Failed to set item'); | ||
}); | ||
|
||
const { result } = renderHook(() => useLocalStorage(mockKey, 'initial')); | ||
|
||
const [, setValue] = result.current; | ||
|
||
act(() => { | ||
setValue('newValue'); | ||
}); | ||
|
||
expect(consoleErrorSpy).toHaveBeenCalledWith( | ||
'Failed to set localStorage item:', | ||
expect.any(Error) | ||
); | ||
|
||
consoleErrorSpy.mockRestore(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { renderHook, act } from '@testing-library/react'; | ||
import { useOnlineStatus } from '@/hooks/useOnlineStatus'; | ||
|
||
describe('useOnlineStatus', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('초기 온라인 상태를 설정해야 한다', () => { | ||
Object.defineProperty(navigator, 'onLine', { | ||
value: true, | ||
writable: true, | ||
}); | ||
|
||
const { result } = renderHook(() => useOnlineStatus()); | ||
expect(result.current).toBe(true); | ||
|
||
Object.defineProperty(navigator, 'onLine', { | ||
value: false, | ||
writable: true, | ||
}); | ||
|
||
const { result: offlineResult } = renderHook(() => useOnlineStatus()); | ||
expect(offlineResult.current).toBe(false); | ||
}); | ||
|
||
it('online 이벤트 발생 시 상태가 true로 변경되어야 한다', () => { | ||
const { result } = renderHook(() => useOnlineStatus()); | ||
|
||
act(() => { | ||
window.dispatchEvent(new Event('online')); | ||
}); | ||
|
||
expect(result.current).toBe(true); | ||
}); | ||
|
||
it('offline 이벤트 발생 시 상태가 false로 변경되어야 한다', () => { | ||
const { result } = renderHook(() => useOnlineStatus()); | ||
|
||
act(() => { | ||
window.dispatchEvent(new Event('offline')); | ||
}); | ||
|
||
expect(result.current).toBe(false); | ||
}); | ||
|
||
it('컴포넌트 언마운트 시 이벤트 리스너가 제거되어야 한다', () => { | ||
const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); | ||
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener'); | ||
|
||
const { unmount } = renderHook(() => useOnlineStatus()); | ||
|
||
expect(addEventListenerSpy).toHaveBeenCalledWith( | ||
'online', | ||
expect.any(Function) | ||
); | ||
expect(addEventListenerSpy).toHaveBeenCalledWith( | ||
'offline', | ||
expect.any(Function) | ||
); | ||
|
||
unmount(); | ||
|
||
expect(removeEventListenerSpy).toHaveBeenCalledWith( | ||
'online', | ||
expect.any(Function) | ||
); | ||
expect(removeEventListenerSpy).toHaveBeenCalledWith( | ||
'offline', | ||
expect.any(Function) | ||
); | ||
}); | ||
}); |
Oops, something went wrong.