11import { describe , it , expect , vi , beforeEach } from 'vitest'
2- import { renderHook , waitFor } from '@testing-library/react'
2+ import { renderHook , waitFor , act } from '@testing-library/react'
33import { useStaffs , useCreateStaff , useSyncPending } from './hooks'
44import { QueryClient , QueryClientProvider } from '@tanstack/react-query'
55import * as staffsService from '@/services/staffs'
@@ -11,6 +11,8 @@ vi.mock('@/services/staffs', () => ({
1111 listStaffs : vi . fn ( ) ,
1212 createStaff : vi . fn ( ) ,
1313 pushStaffToFirebase : vi . fn ( ) ,
14+ updateStaff : vi . fn ( ) ,
15+ deleteStaff : vi . fn ( ) ,
1416} ) )
1517
1618vi . mock ( '@/services/local-storage' , ( ) => ( {
@@ -20,12 +22,15 @@ vi.mock('@/services/local-storage', () => ({
2022const createWrapper = ( ) => {
2123 const queryClient = new QueryClient ( {
2224 defaultOptions : {
23- queries : { retry : false } ,
25+ queries : { retry : false , gcTime : Infinity , staleTime : Infinity } ,
2426 } ,
2527 } )
26- return ( { children } : { children : React . ReactNode } ) => (
27- < QueryClientProvider client = { queryClient } > { children } </ QueryClientProvider >
28- )
28+ return {
29+ queryClient,
30+ wrapper : ( { children } : { children : React . ReactNode } ) => (
31+ < QueryClientProvider client = { queryClient } > { children } </ QueryClientProvider >
32+ )
33+ }
2934}
3035
3136describe ( 'Staff Hooks' , ( ) => {
@@ -34,50 +39,66 @@ describe('Staff Hooks', () => {
3439 } )
3540
3641 describe ( 'useStaffs' , ( ) => {
37- it ( 'deve chamar listStaffs e retornar dados' , async ( ) => {
42+ it ( 'deve retornar dados do serviço ' , async ( ) => {
3843 const mockData = [ { id : '1' , name : 'Test' } ]
3944 vi . mocked ( staffsService . listStaffs ) . mockResolvedValue ( mockData as any )
4045
41- const { result } = renderHook ( ( ) => useStaffs ( ) , { wrapper : createWrapper ( ) } )
46+ const { wrapper } = createWrapper ( )
47+ const { result } = renderHook ( ( ) => useStaffs ( ) , { wrapper } )
4248
4349 await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) )
4450 expect ( result . current . data ) . toEqual ( mockData )
45- expect ( staffsService . listStaffs ) . toHaveBeenCalled ( )
4651 } )
4752 } )
4853
49- describe ( 'useCreateStaff' , ( ) => {
50- it ( 'deve chamar createStaff ao mutar' , async ( ) => {
51- vi . mocked ( staffsService . createStaff ) . mockResolvedValue ( { synced : true } )
54+ describe ( 'useCreateStaff (Optimistic Updates)' , ( ) => {
55+ it ( 'deve injetar colaborador no cache instantaneamente' , async ( ) => {
56+ // Mock com delay para capturar o estado otimista
57+ vi . mocked ( staffsService . createStaff ) . mockImplementation ( ( ) =>
58+ new Promise ( resolve => setTimeout ( ( ) => resolve ( { synced : true } ) , 100 ) )
59+ )
5260
53- const { result } = renderHook ( ( ) => useCreateStaff ( ) , { wrapper : createWrapper ( ) } )
61+ const { queryClient, wrapper } = createWrapper ( )
62+ queryClient . setQueryData ( [ 'staffs' ] , [ ] )
63+
64+ const { result } = renderHook ( ( ) => useCreateStaff ( ) , { wrapper } )
65+ const newStaff = { name : 'Optimistic User' , email : 'opt@test.com' , department : 'TI' , status : 'ACTIVE' }
5466
55- result . current . mutate ( { name : 'New' , email : 'a@a.com' } as any )
67+ await act ( async ( ) => {
68+ result . current . mutate ( newStaff as any )
69+ } )
5670
57- await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) )
58- expect ( staffsService . createStaff ) . toHaveBeenCalled ( )
71+ const cached = queryClient . getQueryData < any [ ] > ( [ 'staffs' ] )
72+ expect ( cached ) . toBeDefined ( )
73+ expect ( cached ?. length ) . toBe ( 1 )
74+ expect ( cached ! [ 0 ] ) . toMatchObject ( { name : 'Optimistic User' , _pendingSync : true } )
5975 } )
60- } )
6176
62- describe ( 'useSyncPending' , ( ) => {
63- it ( 'deve retornar a contagem correta de pendentes' , ( ) => {
64- vi . mocked ( localStorageService . getPendingStaffs ) . mockReturnValue ( [ { } , { } ] as any )
65-
66- const { result } = renderHook ( ( ) => useSyncPending ( ) , { wrapper : createWrapper ( ) } )
77+ it ( 'deve restaurar cache em caso de erro' , async ( ) => {
78+ vi . mocked ( staffsService . createStaff ) . mockRejectedValue ( new Error ( 'Fail' ) )
6779
68- expect ( result . current . pendingCount ) . toBe ( 2 )
69- } )
80+ const { queryClient, wrapper } = createWrapper ( )
81+ const previous = [ { id : 'old' , name : 'Old' } ]
82+ queryClient . setQueryData ( [ 'staffs' ] , previous )
7083
71- it ( 'deve tentar sincronizar cada item pendente' , async ( ) => {
72- const mockPending = [ { email : '1@a.com' } , { email : '2@a.com' } ]
73- vi . mocked ( localStorageService . getPendingStaffs ) . mockReturnValue ( mockPending as any )
74- vi . mocked ( staffsService . pushStaffToFirebase ) . mockResolvedValue ( true )
75-
76- const { result } = renderHook ( ( ) => useSyncPending ( ) , { wrapper : createWrapper ( ) } )
84+ const { result } = renderHook ( ( ) => useCreateStaff ( ) , { wrapper } )
7785
78- await result . current . sync ( )
86+ await act ( async ( ) => {
87+ try {
88+ await result . current . mutateAsync ( { name : 'Error' } as any )
89+ } catch ( e ) { }
90+ } )
91+
92+ expect ( queryClient . getQueryData ( [ 'staffs' ] ) ) . toEqual ( previous )
93+ } )
94+ } )
7995
80- expect ( staffsService . pushStaffToFirebase ) . toHaveBeenCalledTimes ( 2 )
96+ describe ( 'useSyncPending' , ( ) => {
97+ it ( 'deve contar pendentes corretamente' , ( ) => {
98+ vi . mocked ( localStorageService . getPendingStaffs ) . mockReturnValue ( [ { } , { } ] as any )
99+ const { wrapper } = createWrapper ( )
100+ const { result } = renderHook ( ( ) => useSyncPending ( ) , { wrapper } )
101+ expect ( result . current . pendingCount ) . toBe ( 2 )
81102 } )
82103 } )
83104} )
0 commit comments