1616
1717import { renderHook , act , waitFor } from '@testing-library/react' ;
1818import useSaveData from './useSaveData' ;
19- import { post } from '../../../api/utils' ;
20-
21- jest . mock ( '../../../api/utils' , ( ) => ( {
22- post : jest . fn ( )
23- } ) ) ;
19+ import { HttpMethod , sendApiRequest } from '../../../api/utils' ;
20+
21+ jest . mock ( '../../../api/utils' , ( ) => {
22+ const original = jest . requireActual ( '../../../api/utils' ) ;
23+ return {
24+ ...original ,
25+ post : jest . fn ( ) ,
26+ put : jest . fn ( ) ,
27+ patch : jest . fn ( ) ,
28+ sendApiRequest : jest . fn ( )
29+ } ;
30+ } ) ;
2431
25- const mockPost = post as jest . MockedFunction < typeof post > ;
32+ const mockSendApiRequest = sendApiRequest as jest . MockedFunction < typeof sendApiRequest > ;
2633const mockUrlPrefix = 'https://api.example.com' ;
2734const mockEndpoint = '/save-endpoint' ;
2835const mockUrl = `${ mockUrlPrefix } ${ mockEndpoint } ` ;
2936const mockData = { id : 1 , product : 'Hue' } ;
3037const mockBody = { id : 1 } ;
38+ const mockRequestOptions = {
39+ ignoreSuccessErrors : true ,
40+ qsEncodeData : false ,
41+ silenceErrors : true
42+ } ;
3143
3244describe ( 'useSaveData' , ( ) => {
3345 beforeEach ( ( ) => {
3446 jest . clearAllMocks ( ) ;
35- mockPost . mockResolvedValue ( mockData ) ;
47+ mockSendApiRequest . mockResolvedValue ( mockData ) ;
3648 } ) ;
3749
3850 it ( 'should save data successfully and update state' , async ( ) => {
@@ -49,8 +61,13 @@ describe('useSaveData', () => {
4961 expect ( result . current . loading ) . toBe ( true ) ;
5062
5163 await waitFor ( ( ) => {
52- expect ( mockPost ) . toHaveBeenCalledTimes ( 1 ) ;
53- expect ( mockPost ) . toHaveBeenCalledWith ( mockUrl , mockBody , expect . any ( Object ) ) ;
64+ expect ( mockSendApiRequest ) . toHaveBeenCalledTimes ( 1 ) ;
65+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
66+ HttpMethod . POST ,
67+ mockUrl ,
68+ mockBody ,
69+ mockRequestOptions
70+ ) ;
5471 expect ( result . current . data ) . toEqual ( mockData ) ;
5572 expect ( result . current . error ) . toBeUndefined ( ) ;
5673 expect ( result . current . loading ) . toBe ( false ) ;
@@ -59,7 +76,7 @@ describe('useSaveData', () => {
5976
6077 it ( 'should handle errors and update error state' , async ( ) => {
6178 const mockError = new Error ( 'Save error' ) ;
62- mockPost . mockRejectedValue ( mockError ) ;
79+ mockSendApiRequest . mockRejectedValue ( mockError ) ;
6380
6481 const { result } = renderHook ( ( ) => useSaveData ( mockUrl ) ) ;
6582
@@ -74,7 +91,12 @@ describe('useSaveData', () => {
7491 expect ( result . current . loading ) . toBe ( true ) ;
7592
7693 await waitFor ( ( ) => {
77- expect ( mockPost ) . toHaveBeenCalledWith ( mockUrl , mockBody , expect . any ( Object ) ) ;
94+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
95+ HttpMethod . POST ,
96+ mockUrl ,
97+ mockBody ,
98+ mockRequestOptions
99+ ) ;
78100 expect ( result . current . data ) . toBeUndefined ( ) ;
79101 expect ( result . current . error ) . toEqual ( mockError ) ;
80102 expect ( result . current . loading ) . toBe ( false ) ;
@@ -91,7 +113,7 @@ describe('useSaveData', () => {
91113 expect ( result . current . data ) . toBeUndefined ( ) ;
92114 expect ( result . current . error ) . toBeUndefined ( ) ;
93115 expect ( result . current . loading ) . toBe ( false ) ;
94- expect ( mockPost ) . not . toHaveBeenCalled ( ) ;
116+ expect ( mockSendApiRequest ) . not . toHaveBeenCalled ( ) ;
95117 } ) ;
96118
97119 it ( 'should update options when props change' , async ( ) => {
@@ -110,15 +132,20 @@ describe('useSaveData', () => {
110132 expect ( result . current . loading ) . toBe ( true ) ;
111133
112134 await waitFor ( ( ) => {
113- expect ( mockPost ) . toHaveBeenCalledWith ( mockUrl , mockBody , expect . any ( Object ) ) ;
135+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
136+ HttpMethod . POST ,
137+ mockUrl ,
138+ mockBody ,
139+ mockRequestOptions
140+ ) ;
114141 expect ( result . current . data ) . toEqual ( mockData ) ;
115142 expect ( result . current . error ) . toBeUndefined ( ) ;
116143 expect ( result . current . loading ) . toBe ( false ) ;
117144 } ) ;
118145
119146 const newBody = { id : 2 } ;
120147 const newMockData = { ...mockData , id : 2 } ;
121- mockPost . mockResolvedValueOnce ( newMockData ) ;
148+ mockSendApiRequest . mockResolvedValueOnce ( newMockData ) ;
122149
123150 rerender ( { url : mockUrl } ) ;
124151
@@ -129,7 +156,12 @@ describe('useSaveData', () => {
129156 expect ( result . current . loading ) . toBe ( true ) ;
130157
131158 await waitFor ( ( ) => {
132- expect ( mockPost ) . toHaveBeenCalledWith ( mockUrl , newBody , expect . any ( Object ) ) ;
159+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
160+ HttpMethod . POST ,
161+ mockUrl ,
162+ newBody ,
163+ mockRequestOptions
164+ ) ;
133165 expect ( result . current . data ) . toEqual ( newMockData ) ;
134166 expect ( result . current . error ) . toBeUndefined ( ) ;
135167 expect ( result . current . loading ) . toBe ( false ) ;
@@ -157,7 +189,12 @@ describe('useSaveData', () => {
157189 expect ( result . current . loading ) . toBe ( true ) ;
158190
159191 await waitFor ( ( ) => {
160- expect ( mockPost ) . toHaveBeenCalledWith ( mockUrl , mockBody , expect . any ( Object ) ) ;
192+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
193+ HttpMethod . POST ,
194+ mockUrl ,
195+ mockBody ,
196+ mockRequestOptions
197+ ) ;
161198 expect ( result . current . data ) . toEqual ( mockData ) ;
162199 expect ( result . current . error ) . toBeUndefined ( ) ;
163200 expect ( result . current . loading ) . toBe ( false ) ;
@@ -168,7 +205,7 @@ describe('useSaveData', () => {
168205
169206 it ( 'should call onError callback when provided' , async ( ) => {
170207 const mockError = new Error ( 'Save error' ) ;
171- mockPost . mockRejectedValue ( mockError ) ;
208+ mockSendApiRequest . mockRejectedValue ( mockError ) ;
172209
173210 const mockOnSuccess = jest . fn ( ) ;
174211 const mockOnError = jest . fn ( ) ;
@@ -190,7 +227,12 @@ describe('useSaveData', () => {
190227 expect ( result . current . loading ) . toBe ( true ) ;
191228
192229 await waitFor ( ( ) => {
193- expect ( mockPost ) . toHaveBeenCalledWith ( mockUrl , mockBody , expect . any ( Object ) ) ;
230+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
231+ HttpMethod . POST ,
232+ mockUrl ,
233+ mockBody ,
234+ mockRequestOptions
235+ ) ;
194236 expect ( result . current . data ) . toBeUndefined ( ) ;
195237 expect ( result . current . error ) . toEqual ( mockError ) ;
196238 expect ( result . current . loading ) . toBe ( false ) ;
@@ -207,7 +249,8 @@ describe('useSaveData', () => {
207249 } ) ;
208250
209251 await waitFor ( ( ) => {
210- expect ( mockPost ) . toHaveBeenCalledWith (
252+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
253+ HttpMethod . POST ,
211254 mockUrl ,
212255 'hue data' ,
213256 expect . objectContaining ( { qsEncodeData : true } )
@@ -228,7 +271,8 @@ describe('useSaveData', () => {
228271 } ) ;
229272
230273 await waitFor ( ( ) => {
231- expect ( mockPost ) . toHaveBeenCalledWith (
274+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
275+ HttpMethod . POST ,
232276 mockUrl ,
233277 payload ,
234278 expect . objectContaining ( { qsEncodeData : false } )
@@ -249,7 +293,8 @@ describe('useSaveData', () => {
249293 } ) ;
250294
251295 await waitFor ( ( ) => {
252- expect ( mockPost ) . toHaveBeenCalledWith (
296+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
297+ HttpMethod . POST ,
253298 mockUrl ,
254299 payload ,
255300 expect . objectContaining ( { qsEncodeData : false } )
@@ -274,7 +319,8 @@ describe('useSaveData', () => {
274319 } ) ;
275320
276321 await waitFor ( ( ) => {
277- expect ( mockPost ) . toHaveBeenCalledWith (
322+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
323+ HttpMethod . POST ,
278324 mockUrl ,
279325 payload ,
280326 expect . objectContaining ( { qsEncodeData : true } )
@@ -284,4 +330,90 @@ describe('useSaveData', () => {
284330 expect ( result . current . loading ) . toBe ( false ) ;
285331 } ) ;
286332 } ) ;
333+
334+ it ( 'should use PUT method when specified in options' , async ( ) => {
335+ mockSendApiRequest . mockResolvedValue ( mockData ) ;
336+
337+ const { result } = renderHook ( ( ) => useSaveData ( mockUrl , { method : HttpMethod . PUT } ) ) ;
338+
339+ act ( ( ) => {
340+ result . current . save ( mockBody ) ;
341+ } ) ;
342+
343+ await waitFor ( ( ) => {
344+ expect ( mockSendApiRequest ) . toHaveBeenCalledTimes ( 1 ) ;
345+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
346+ HttpMethod . PUT ,
347+ mockUrl ,
348+ mockBody ,
349+ mockRequestOptions
350+ ) ;
351+ expect ( result . current . data ) . toEqual ( mockData ) ;
352+ expect ( result . current . loading ) . toBe ( false ) ;
353+ } ) ;
354+ } ) ;
355+
356+ it ( 'should use PATCH method when specified in saveOptions' , async ( ) => {
357+ mockSendApiRequest . mockResolvedValue ( mockData ) ;
358+
359+ const { result } = renderHook ( ( ) => useSaveData ( mockUrl ) ) ;
360+
361+ act ( ( ) => {
362+ result . current . save ( mockBody , { method : HttpMethod . PATCH } ) ;
363+ } ) ;
364+
365+ await waitFor ( ( ) => {
366+ expect ( mockSendApiRequest ) . toHaveBeenCalledTimes ( 1 ) ;
367+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
368+ HttpMethod . PATCH ,
369+ mockUrl ,
370+ mockBody ,
371+ mockRequestOptions
372+ ) ;
373+ expect ( result . current . data ) . toEqual ( mockData ) ;
374+ expect ( result . current . loading ) . toBe ( false ) ;
375+ } ) ;
376+ } ) ;
377+
378+ it ( 'should prioritize saveOptions method over options method' , async ( ) => {
379+ mockSendApiRequest . mockResolvedValue ( mockData ) ;
380+
381+ const { result } = renderHook ( ( ) => useSaveData ( mockUrl , { method : HttpMethod . PUT } ) ) ;
382+
383+ act ( ( ) => {
384+ result . current . save ( mockBody , { method : HttpMethod . PATCH } ) ;
385+ } ) ;
386+
387+ await waitFor ( ( ) => {
388+ expect ( mockSendApiRequest ) . toHaveBeenCalledTimes ( 1 ) ;
389+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
390+ HttpMethod . PATCH ,
391+ mockUrl ,
392+ mockBody ,
393+ mockRequestOptions
394+ ) ;
395+ expect ( result . current . data ) . toEqual ( mockData ) ;
396+ expect ( result . current . loading ) . toBe ( false ) ;
397+ } ) ;
398+ } ) ;
399+
400+ it ( 'should default to POST when no method is specified' , async ( ) => {
401+ const { result } = renderHook ( ( ) => useSaveData ( mockUrl ) ) ;
402+
403+ act ( ( ) => {
404+ result . current . save ( mockBody ) ;
405+ } ) ;
406+
407+ await waitFor ( ( ) => {
408+ expect ( mockSendApiRequest ) . toHaveBeenCalledTimes ( 1 ) ;
409+ expect ( mockSendApiRequest ) . toHaveBeenCalledWith (
410+ HttpMethod . POST ,
411+ mockUrl ,
412+ mockBody ,
413+ mockRequestOptions
414+ ) ;
415+ expect ( result . current . data ) . toEqual ( mockData ) ;
416+ expect ( result . current . loading ) . toBe ( false ) ;
417+ } ) ;
418+ } ) ;
287419} ) ;
0 commit comments