11/**
22 * @jest -environment jsdom
33 */
4+ import '@testing-library/jest-dom' ;
45import { useState } from 'react' ;
5- import { render , screen , fireEvent , waitFor } from '@testing-library/react' ;
6+ import {
7+ render ,
8+ screen ,
9+ fireEvent ,
10+ waitFor ,
11+ act ,
12+ } from '@testing-library/react' ;
613import { mockFetchResponse } from '../../utils/mockFetchResponse' ;
714import { useFetcher } from '../../../src/react/index' ;
815
916describe ( 'POST body cache key update and refetch' , ( ) => {
10- it ( 'uses new body on refetch, keeps static cache key, and updates UI state ' , async ( ) => {
17+ it ( 'should not update cache key but use new body when refetch is called after body changes and retries are active ' , async ( ) => {
1118 mockFetchResponse ( '/api/user' , {
1219 ok : true ,
1320 status : 200 ,
@@ -26,6 +33,7 @@ describe('POST body cache key update and refetch', () => {
2633 retry : { retries : 5 , delay : 1000 , backoff : 2 } ,
2734 } ,
2835 ) ;
36+
2937 return (
3038 < div >
3139 < div data-testid = "result" > { data ?. echoed ?. name } </ div >
@@ -39,6 +47,8 @@ describe('POST body cache key update and refetch', () => {
3947 }
4048
4149 render ( < TestComponent /> ) ;
50+
51+ // Initial fetch with name "Alice"
4252 await waitFor ( ( ) =>
4353 expect ( screen . getByTestId ( 'result' ) . textContent ) . toBe ( 'Alice' ) ,
4454 ) ;
@@ -48,20 +58,108 @@ describe('POST body cache key update and refetch', () => {
4858 status : 200 ,
4959 body : { echoed : { name : 'Bob' } } ,
5060 } ) ;
61+
62+ // Change name to "Bob"
5163 fireEvent . click ( screen . getByText ( 'Change Name' ) ) ;
64+
65+ // Refetch with new body
5266 fireEvent . click ( screen . getByText ( 'Refetch' ) ) ;
67+
68+ // Should show loading state
5369 expect ( screen . getByTestId ( 'loading' ) . textContent ) . toBe ( 'loading' ) ;
5470 expect ( screen . getByTestId ( 'fetching' ) . textContent ) . toBe ( 'fetching' ) ;
71+
72+ // Wait for fetch to complete and check new body is used
5573 await waitFor ( ( ) =>
5674 expect ( screen . getByTestId ( 'result' ) . textContent ) . toBe ( 'Bob' ) ,
5775 ) ;
76+
77+ // Should show idle state after fetch completes
5878 expect ( screen . getByTestId ( 'loading' ) . textContent ) . toBe ( 'idle' ) ;
5979 expect ( screen . getByTestId ( 'fetching' ) . textContent ) . toBe ( 'idle' ) ;
80+
81+ // Check if the body used in fetch is updated
6082 expect ( screen . getByTestId ( 'config' ) . textContent ) . toContain (
6183 '"body":"{\\"name\\":\\"Bob\\"}"' ,
6284 ) ;
85+
86+ // Check the cache key is updated
6387 expect ( screen . getByTestId ( 'config' ) . textContent ) . toContain (
6488 '"cacheKey":"/api/user"' ,
6589 ) ;
6690 } ) ;
91+
92+ it ( 'should regenerate cache key and use updated body when POST body changes and refetch is called' , async ( ) => {
93+ const testUrl = '/api/post-body-cache-key' ;
94+ const initialBody = { value : 'first' } ;
95+ const updatedBody = { value : 'second' } ;
96+ let currentBody = initialBody ;
97+
98+ // Mock fetch to echo back the request body
99+ global . fetch = jest . fn ( ) . mockImplementation ( ( _url , config ) => {
100+ const parsedBody =
101+ config && config . body ? JSON . parse ( config . body ) : undefined ;
102+ return Promise . resolve ( {
103+ ok : true ,
104+ status : 200 ,
105+ data : parsedBody ,
106+ body : parsedBody ,
107+ json : ( ) => Promise . resolve ( parsedBody ) ,
108+ } ) ;
109+ } ) ;
110+
111+ // React state simulation
112+ let setBody : ( b : typeof initialBody ) => void = ( ) => { } ;
113+ function BodyComponent ( ) {
114+ const [ body , _setBody ] = useState ( currentBody ) ;
115+ setBody = _setBody ;
116+ const { data, refetch, isLoading } = useFetcher ( testUrl , {
117+ method : 'POST' ,
118+ body,
119+ } ) ;
120+ return (
121+ < div >
122+ < div data-testid = "data" >
123+ { data ? JSON . stringify ( data ) : 'No Data' }
124+ </ div >
125+ < div data-testid = "loading" >
126+ { isLoading ? 'Loading...' : 'Not Loading' }
127+ </ div >
128+ < button data-testid = "refetch-btn" onClick = { ( ) => refetch ( true ) } >
129+ Refetch
130+ </ button >
131+ </ div >
132+ ) ;
133+ }
134+
135+ render ( < BodyComponent /> ) ;
136+
137+ // Wait for initial fetch
138+ await waitFor ( ( ) => {
139+ expect ( screen . getByTestId ( 'data' ) ) . toHaveTextContent ( 'No Data' ) ;
140+ } ) ;
141+
142+ // Act: update the body asynchronously
143+ act ( ( ) => {
144+ currentBody = updatedBody ;
145+ setBody ( updatedBody ) ;
146+ } ) ;
147+
148+ // Refetch with new body
149+ fireEvent . click ( screen . getByTestId ( 'refetch-btn' ) ) ;
150+
151+ // Assert: data should match updated body
152+ await waitFor ( ( ) => {
153+ expect ( screen . getByTestId ( 'data' ) ) . toHaveTextContent ( 'second' ) ;
154+ } ) ;
155+
156+ // Also check that fetch was called with the updated body
157+ expect ( global . fetch ) . toHaveBeenLastCalledWith (
158+ testUrl ,
159+ expect . objectContaining ( {
160+ method : 'POST' ,
161+ body : JSON . stringify ( updatedBody ) ,
162+ } ) ,
163+ ) ;
164+ } ) ;
67165} ) ;
0 commit comments