11import { MemoryRouter } from 'react-router-dom' ;
22import { Form , Field } from 'react-final-form' ;
3+ import { act } from 'react' ;
34
4- import { waitFor } from '@folio/jest-config-stripes/testing-library/react' ;
5+ import { waitFor , screen } from '@folio/jest-config-stripes/testing-library/react' ;
56import { StripesContext , useStripes } from '@folio/stripes/core' ;
67
78import FileUploaderField from './FileUploaderField' ;
@@ -60,33 +61,30 @@ describe('FileUploaderField', () => {
6061
6162 describe ( 'Validation error timing' , ( ) => {
6263 it ( 'should not show validation error when untouched' , ( ) => {
63- const { container } = renderComponent ( stripes , {
64+ renderComponent ( stripes , {
6465 touched : false ,
6566 error : 'Required' ,
6667 } ) ;
6768
68- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
69- expect ( errorElement ) . not . toBeInTheDocument ( ) ;
69+ expect ( screen . queryByText ( 'Required' ) ) . not . toBeInTheDocument ( ) ;
7070 } ) ;
7171
7272 it ( 'should show validation error when touched' , ( ) => {
73- const { container } = renderComponent ( stripes , {
73+ renderComponent ( stripes , {
7474 touched : true ,
7575 error : 'Required' ,
7676 } ) ;
7777
78- const errorElement = container . querySelector ( '.errorMessage' ) ;
79- expect ( errorElement ) . not . toHaveAttribute ( 'hidden' ) ;
78+ expect ( screen . getByText ( 'Required' ) ) . toBeInTheDocument ( ) ;
8079 } ) ;
8180
8281 it ( 'should show validation error when submit failed' , ( ) => {
83- const { container } = renderComponent ( stripes , {
82+ renderComponent ( stripes , {
8483 submitFailed : true ,
8584 error : 'Required' ,
8685 } ) ;
8786
88- const errorElement = container . querySelector ( '.errorMessage' ) ;
89- expect ( errorElement ) . not . toHaveAttribute ( 'hidden' ) ;
87+ expect ( screen . getByText ( 'Required' ) ) . toBeInTheDocument ( ) ;
9088 } ) ;
9189 } ) ;
9290
@@ -103,7 +101,9 @@ describe('FileUploaderField', () => {
103101 text : ( ) => Promise . resolve ( 'file-id-123' ) ,
104102 } ) ;
105103
106- await mockOnDrop ( [ smallFile ] ) ;
104+ await act ( async ( ) => {
105+ await mockOnDrop ( [ smallFile ] ) ;
106+ } ) ;
107107
108108 expect ( mockOnUploadFile ) . toHaveBeenCalledWith ( smallFile ) ;
109109 await waitFor ( ( ) => {
@@ -122,7 +122,9 @@ describe('FileUploaderField', () => {
122122 text : ( ) => Promise . resolve ( 'file-id-456' ) ,
123123 } ) ;
124124
125- await mockOnDrop ( [ maxFile ] ) ;
125+ await act ( async ( ) => {
126+ await mockOnDrop ( [ maxFile ] ) ;
127+ } ) ;
126128
127129 expect ( mockOnUploadFile ) . toHaveBeenCalledWith ( maxFile ) ;
128130 await waitFor ( ( ) => {
@@ -131,85 +133,92 @@ describe('FileUploaderField', () => {
131133 } ) ;
132134
133135 it ( 'should reject files over the limit' , async ( ) => {
134- const { container } = renderComponent ( stripes ) ;
136+ renderComponent ( stripes ) ;
135137
136138 const largeFile = new File ( [ 'test' ] , 'large.txt' ) ;
137139 Object . defineProperty ( largeFile , 'size' , { value : MAX_FILE_SIZE_BYTES + 1 } ) ;
138140
139- await mockOnDrop ( [ largeFile ] ) ;
141+ await act ( async ( ) => {
142+ await mockOnDrop ( [ largeFile ] ) ;
143+ } ) ;
140144
141145 expect ( mockOnUploadFile ) . not . toHaveBeenCalled ( ) ;
142146
143147 await waitFor ( ( ) => {
144- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
145- expect ( errorElement ) . toBeInTheDocument ( ) ;
148+ expect ( screen . getByText ( / e x c e e d s t h e m a x i m u m a l l o w e d s i z e / i) ) . toBeInTheDocument ( ) ;
146149 } ) ;
147150 } ) ;
148151 } ) ;
149152
150153 describe ( 'Upload error handling' , ( ) => {
151154 it ( 'should handle 413 with valid backend message' , async ( ) => {
152- const { container } = renderComponent ( stripes ) ;
155+ renderComponent ( stripes ) ;
153156
154157 const file = new File ( [ 'test' ] , 'test.txt' ) ;
155158
156159 mockOnUploadFile . mockResolvedValue ( {
157160 ok : false ,
158161 status : 413 ,
162+ headers : new Headers ( { 'Content-Length' : '25' } ) ,
159163 text : ( ) => Promise . resolve ( 'File size limit exceeded' ) ,
160164 } ) ;
161165
162- await mockOnDrop ( [ file ] ) ;
166+ await act ( async ( ) => {
167+ await mockOnDrop ( [ file ] ) ;
168+ } ) ;
163169
164170 await waitFor ( ( ) => {
165- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
166- expect ( errorElement ) . toBeInTheDocument ( ) ;
171+ expect ( screen . getByText ( / S e r v e r r e j e c t e d t h e f i l e / i) ) . toBeInTheDocument ( ) ;
167172 } ) ;
168173 } ) ;
169174
170175 it ( 'should sanitize HTML in backend messages' , async ( ) => {
171- const { container } = renderComponent ( stripes ) ;
176+ renderComponent ( stripes ) ;
172177
173178 const file = new File ( [ 'test' ] , 'test.txt' ) ;
174179
175180 mockOnUploadFile . mockResolvedValue ( {
176181 ok : false ,
177182 status : 413 ,
183+ headers : new Headers ( { 'Content-Length' : '29' } ) ,
178184 text : ( ) => Promise . resolve ( '<script>alert("xss")</script>' ) ,
179185 } ) ;
180186
181- await mockOnDrop ( [ file ] ) ;
187+ await act ( async ( ) => {
188+ await mockOnDrop ( [ file ] ) ;
189+ } ) ;
182190
183191 await waitFor ( ( ) => {
184- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
185- expect ( errorElement ) . toBeInTheDocument ( ) ;
186- expect ( errorElement . textContent ) . not . toContain ( '<script>' ) ;
192+ expect ( screen . queryByText ( / < s c r i p t > / ) ) . not . toBeInTheDocument ( ) ;
193+ expect ( screen . getByText ( / e x c e e d s t h e m a x i m u m a l l o w e d s i z e / i) ) . toBeInTheDocument ( ) ;
187194 } ) ;
188195 } ) ;
189196
190197 it ( 'should reject overly long backend messages' , async ( ) => {
191- const { container } = renderComponent ( stripes ) ;
198+ renderComponent ( stripes ) ;
192199
193200 const file = new File ( [ 'test' ] , 'test.txt' ) ;
194201 const longMessage = 'a' . repeat ( 250 ) ;
195202
196203 mockOnUploadFile . mockResolvedValue ( {
197204 ok : false ,
198205 status : 413 ,
206+ headers : new Headers ( { 'Content-Length' : '250' } ) ,
199207 text : ( ) => Promise . resolve ( longMessage ) ,
200208 } ) ;
201209
202- await mockOnDrop ( [ file ] ) ;
210+ await act ( async ( ) => {
211+ await mockOnDrop ( [ file ] ) ;
212+ } ) ;
203213
204214 await waitFor ( ( ) => {
205- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
206- expect ( errorElement ) . toBeInTheDocument ( ) ;
207- expect ( errorElement . textContent ) . not . toContain ( longMessage ) ;
215+ expect ( screen . queryByText ( longMessage ) ) . not . toBeInTheDocument ( ) ;
216+ expect ( screen . getByText ( / e x c e e d s t h e m a x i m u m a l l o w e d s i z e / i) ) . toBeInTheDocument ( ) ;
208217 } ) ;
209218 } ) ;
210219
211220 it ( 'should handle network errors' , async ( ) => {
212- const { container } = renderComponent ( stripes ) ;
221+ renderComponent ( stripes ) ;
213222
214223 const file = new File ( [ 'test' ] , 'test.txt' ) ;
215224
@@ -218,45 +227,49 @@ describe('FileUploaderField', () => {
218227 status : 500 ,
219228 } ) ;
220229
221- await mockOnDrop ( [ file ] ) ;
230+ await act ( async ( ) => {
231+ await mockOnDrop ( [ file ] ) ;
232+ } ) ;
222233
223234 await waitFor ( ( ) => {
224- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
225- expect ( errorElement ) . toBeInTheDocument ( ) ;
235+ expect ( screen . getByText ( / A n e r r o r o c c u r r e d d u r i n g u p l o a d / i) ) . toBeInTheDocument ( ) ;
226236 } ) ;
227237 } ) ;
228238
229239 it ( 'should handle rejected promises' , async ( ) => {
230- const { container } = renderComponent ( stripes ) ;
240+ renderComponent ( stripes ) ;
231241
232242 const file = new File ( [ 'test' ] , 'test.txt' ) ;
233243
234244 mockOnUploadFile . mockRejectedValue ( new Error ( 'Network failure' ) ) ;
235245
236- await mockOnDrop ( [ file ] ) ;
246+ await act ( async ( ) => {
247+ await mockOnDrop ( [ file ] ) ;
248+ } ) ;
237249
238250 await waitFor ( ( ) => {
239- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
240- expect ( errorElement ) . toBeInTheDocument ( ) ;
251+ expect ( screen . getByText ( 'Network failure' ) ) . toBeInTheDocument ( ) ;
241252 } ) ;
242253 } ) ;
243254
244255 it ( 'should handle response.text() failure' , async ( ) => {
245- const { container } = renderComponent ( stripes ) ;
256+ renderComponent ( stripes ) ;
246257
247258 const file = new File ( [ 'test' ] , 'test.txt' ) ;
248259
249260 mockOnUploadFile . mockResolvedValue ( {
250261 ok : false ,
251262 status : 413 ,
263+ headers : new Headers ( { 'Content-Length' : '11' } ) ,
252264 text : ( ) => Promise . reject ( new Error ( 'Read failed' ) ) ,
253265 } ) ;
254266
255- await mockOnDrop ( [ file ] ) ;
267+ await act ( async ( ) => {
268+ await mockOnDrop ( [ file ] ) ;
269+ } ) ;
256270
257271 await waitFor ( ( ) => {
258- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
259- expect ( errorElement ) . toBeInTheDocument ( ) ;
272+ expect ( screen . getByText ( / e x c e e d s t h e m a x i m u m a l l o w e d s i z e / i) ) . toBeInTheDocument ( ) ;
260273 } ) ;
261274 } ) ;
262275 } ) ;
@@ -268,7 +281,9 @@ describe('FileUploaderField', () => {
268281 const file1 = new File ( [ 'test1' ] , 'test1.txt' ) ;
269282 const file2 = new File ( [ 'test2' ] , 'test2.txt' ) ;
270283
271- await mockOnDrop ( [ file1 , file2 ] ) ;
284+ await act ( async ( ) => {
285+ await mockOnDrop ( [ file1 , file2 ] ) ;
286+ } ) ;
272287
273288 expect ( mockOnUploadFile ) . not . toHaveBeenCalled ( ) ;
274289 } ) ;
@@ -284,23 +299,26 @@ describe('FileUploaderField', () => {
284299 text : ( ) => Promise . resolve ( 'empty-file-id' ) ,
285300 } ) ;
286301
287- await mockOnDrop ( [ file ] ) ;
302+ await act ( async ( ) => {
303+ await mockOnDrop ( [ file ] ) ;
304+ } ) ;
288305
289306 expect ( mockOnUploadFile ) . toHaveBeenCalledWith ( file ) ;
290307 } ) ;
291308
292309 it ( 'should clear errors on successful upload' , async ( ) => {
293- const { container } = renderComponent ( stripes ) ;
310+ renderComponent ( stripes ) ;
294311
295312 // First, trigger an error
296313 const largeFile = new File ( [ 'test' ] , 'large.txt' ) ;
297314 Object . defineProperty ( largeFile , 'size' , { value : MAX_FILE_SIZE_BYTES + 1 } ) ;
298315
299- await mockOnDrop ( [ largeFile ] ) ;
316+ await act ( async ( ) => {
317+ await mockOnDrop ( [ largeFile ] ) ;
318+ } ) ;
300319
301320 await waitFor ( ( ) => {
302- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
303- expect ( errorElement ) . toBeInTheDocument ( ) ;
321+ expect ( screen . getByText ( / e x c e e d s t h e m a x i m u m a l l o w e d s i z e / i) ) . toBeInTheDocument ( ) ;
304322 } ) ;
305323
306324 // Now upload a valid file
@@ -312,11 +330,12 @@ describe('FileUploaderField', () => {
312330 text : ( ) => Promise . resolve ( 'valid-file-id' ) ,
313331 } ) ;
314332
315- await mockOnDrop ( [ validFile ] ) ;
333+ await act ( async ( ) => {
334+ await mockOnDrop ( [ validFile ] ) ;
335+ } ) ;
316336
317337 await waitFor ( ( ) => {
318- const errorElement = container . querySelector ( '.errorMessage:not([hidden])' ) ;
319- expect ( errorElement ) . not . toBeInTheDocument ( ) ;
338+ expect ( screen . queryByText ( / e x c e e d s t h e m a x i m u m a l l o w e d s i z e / i) ) . not . toBeInTheDocument ( ) ;
320339 } ) ;
321340 } ) ;
322341 } ) ;
0 commit comments