import { renderHook, act } from '@testing-library/react-native' import { useAnnouncements } from './use-announcements' import { root } from '@repo/core' import { AnnouncementController } from '@repo/sdk' import { handleError } from './use-error' jest.mock('@repo/core', () => ({ root: { get: jest.fn(), }, })) jest.mock('./use-error', () => ({ handleError: jest.fn(async (cb) => { try { const data = await cb() return { data, error: null } } catch (e) { return { data: null, error: e } } }), })) describe('useAnnouncements', () => { beforeEach(() => { jest.clearAllMocks() }) afterEach(() => { jest.restoreAllMocks() }) describe('initial state', () => { it('should return initial state', () => { const mockAnnouncementController = { list: jest.fn(), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) expect(result.current.data).toBeUndefined() expect(result.current.announcements).toEqual([]) expect(result.current.loading).toBe(false) expect(result.current.loadingMore).toBe(false) expect(result.current.error).toBeNull() expect(result.current.hasMore).toBe(true) }) }) describe('execute function', () => { it('should load announcements successfully', async () => { const mockData = { announcements: [ { id: '1', title: 'Announcement 1', content: 'Content 1', createdAt: new Date() }, { id: '2', title: 'Announcement 2', content: 'Content 2', createdAt: new Date() }, ], total: 2, unreadCount: 0, page: 1, limit: 20, totalPages: 1, } const mockAnnouncementController = { list: jest.fn().mockResolvedValue(mockData), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) await act(async () => { await result.current.execute() }) expect(result.current.data).toEqual(mockData) expect(result.current.announcements).toEqual(mockData.announcements) expect(result.current.error).toBeNull() expect(result.current.loading).toBe(false) }) it('should handle API errors', async () => { const mockError = { status: 500, statusText: 'Internal Server Error', message: 'Failed to load announcements', } const mockAnnouncementController = { list: jest.fn().mockRejectedValue(mockError), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) await act(async () => { await result.current.execute() }) expect(result.current.error).toEqual(mockError) expect(result.current.data).toBeUndefined() }) it('should set loading state during fetch', async () => { let resolveFetch: (value: any) => void const fetchPromise = new Promise((resolve) => { resolveFetch = resolve }) const mockAnnouncementController = { list: jest.fn().mockReturnValue(fetchPromise), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) act(() => { result.current.execute() }) expect(result.current.loading).toBe(true) await act(async () => { resolveFetch!({ announcements: [], total: 0, unreadCount: 0, page: 1, limit: 20, totalPages: 1 }) await fetchPromise }) expect(result.current.loading).toBe(false) }) }) describe('loadMore function', () => { it('should load more announcements and append to existing', async () => { const mockFirstPage = { announcements: [{ id: '1', title: 'Announcement 1', content: 'Content 1', createdAt: new Date() }], total: 3, unreadCount: 1, page: 1, limit: 20, totalPages: 2, } const mockSecondPage = { announcements: [{ id: '2', title: 'Announcement 2', content: 'Content 2', createdAt: new Date() }], total: 3, unreadCount: 1, page: 2, limit: 20, totalPages: 2, } const mockAnnouncementController = { list: jest.fn() .mockResolvedValueOnce(mockFirstPage) .mockResolvedValueOnce(mockSecondPage), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) await act(async () => { await result.current.execute() }) expect(result.current.announcements).toHaveLength(1) expect(result.current.hasMore).toBe(true) await act(async () => { await result.current.loadMore() }) expect(result.current.announcements).toHaveLength(2) expect(result.current.hasMore).toBe(false) }) it('should not load more if already loading', async () => { let resolveFetch: (value: any) => void const fetchPromise = new Promise((resolve) => { resolveFetch = resolve }) const mockAnnouncementController = { list: jest.fn().mockReturnValue(fetchPromise), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) act(() => { result.current.execute() }) act(() => { result.current.loadMore() }) await act(async () => { resolveFetch!({ announcements: [], total: 0, unreadCount: 0, page: 1, limit: 20, totalPages: 1 }) await fetchPromise }) expect(mockAnnouncementController.list).toHaveBeenCalledTimes(1) }) it('should not load more if no more pages', async () => { const mockData = { announcements: [{ id: '1', title: 'Announcement 1', content: 'Content 1', createdAt: new Date() }], total: 1, unreadCount: 0, page: 1, limit: 20, totalPages: 1, } const mockAnnouncementController = { list: jest.fn().mockResolvedValue(mockData), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) await act(async () => { await result.current.execute() }) expect(result.current.hasMore).toBe(false) await act(async () => { await result.current.loadMore() }) expect(mockAnnouncementController.list).toHaveBeenCalledTimes(1) }) }) describe('refetch function', () => { it('should reset and reload announcements', async () => { const mockFirstData = { announcements: [{ id: '1', title: 'Announcement 1', content: 'Content 1', createdAt: new Date() }], total: 2, unreadCount: 1, page: 1, limit: 20, totalPages: 2, } const mockSecondData = { announcements: [{ id: '2', title: 'Announcement 2', content: 'Content 2', createdAt: new Date() }], total: 2, unreadCount: 1, page: 1, limit: 20, totalPages: 2, } const mockAnnouncementController = { list: jest.fn() .mockResolvedValueOnce(mockFirstData) .mockResolvedValueOnce(mockSecondData), } ;(root.get as jest.Mock).mockReturnValue(mockAnnouncementController) const { result } = renderHook(() => useAnnouncements()) await act(async () => { await result.current.execute() }) expect(result.current.hasMore).toBe(true) await act(async () => { await result.current.refetch() }) expect(mockAnnouncementController.list).toHaveBeenCalledTimes(2) expect(result.current.hasMore).toBe(true) }) }) })