react-dropzone 12.1.0 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.spec.js CHANGED
@@ -1,12 +1,6 @@
1
1
  /* eslint react/prop-types: 0, jsx-a11y/label-has-for: 0 */
2
2
  import React, { createRef } from "react";
3
- import {
4
- act,
5
- cleanup,
6
- fireEvent,
7
- render,
8
- waitFor,
9
- } from "@testing-library/react";
3
+ import { act, cleanup, fireEvent, render } from "@testing-library/react";
10
4
  import { renderHook } from "@testing-library/react-hooks";
11
5
  import { fromEvent } from "file-selector";
12
6
  import * as utils from "./utils";
@@ -41,7 +35,9 @@ describe("useDropzone() hook", () => {
41
35
  });
42
36
 
43
37
  it("sets {accept} prop on the <input>", () => {
44
- const accept = "image/jpeg";
38
+ const accept = {
39
+ "image/jpeg": [],
40
+ };
45
41
  const { container } = render(
46
42
  <Dropzone accept={accept}>
47
43
  {({ getRootProps, getInputProps }) => (
@@ -52,14 +48,19 @@ describe("useDropzone() hook", () => {
52
48
  </Dropzone>
53
49
  );
54
50
 
55
- const input = container.querySelector("input");
56
-
57
- expect(input).toHaveAttribute("accept", accept);
51
+ expect(container.querySelector("input")).toHaveAttribute(
52
+ "accept",
53
+ "image/jpeg"
54
+ );
58
55
  });
59
56
 
60
57
  it("updates {multiple} prop on the <input> when it changes", () => {
61
58
  const { container, rerender } = render(
62
- <Dropzone accept="image/jpeg">
59
+ <Dropzone
60
+ accept={{
61
+ "image/jpeg": [],
62
+ }}
63
+ >
63
64
  {({ getRootProps, getInputProps }) => (
64
65
  <div {...getRootProps()}>
65
66
  <input {...getInputProps()} />
@@ -74,7 +75,11 @@ describe("useDropzone() hook", () => {
74
75
  );
75
76
 
76
77
  rerender(
77
- <Dropzone accept="image/png">
78
+ <Dropzone
79
+ accept={{
80
+ "image/png": [],
81
+ }}
82
+ >
78
83
  {({ getRootProps, getInputProps }) => (
79
84
  <div {...getRootProps()}>
80
85
  <input {...getInputProps()} />
@@ -100,8 +105,7 @@ describe("useDropzone() hook", () => {
100
105
  </Dropzone>
101
106
  );
102
107
 
103
- const input = container.querySelector("input");
104
- expect(input).toHaveAttribute("multiple");
108
+ expect(container.querySelector("input")).toHaveAttribute("multiple");
105
109
  });
106
110
 
107
111
  it("updates {multiple} prop on the <input> when it changes", () => {
@@ -142,8 +146,7 @@ describe("useDropzone() hook", () => {
142
146
  </Dropzone>
143
147
  );
144
148
 
145
- const input = container.querySelector("input");
146
- expect(input).toHaveAttribute("name", name);
149
+ expect(container.querySelector("input")).toHaveAttribute("name", name);
147
150
  });
148
151
 
149
152
  it("sets any props passed to the root props getter on the root node", () => {
@@ -158,9 +161,10 @@ describe("useDropzone() hook", () => {
158
161
  </Dropzone>
159
162
  );
160
163
 
161
- const dropzone = container.querySelector("div");
162
-
163
- expect(dropzone).toHaveAttribute("aria-label", ariaLabel);
164
+ expect(container.querySelector("div")).toHaveAttribute(
165
+ "aria-label",
166
+ ariaLabel
167
+ );
164
168
  });
165
169
 
166
170
  it("runs the custom callback handlers provided to the root props getter", async () => {
@@ -177,7 +181,7 @@ describe("useDropzone() hook", () => {
177
181
  onDrop: jest.fn(),
178
182
  };
179
183
 
180
- const ui = (
184
+ const { container } = render(
181
185
  <Dropzone>
182
186
  {({ getRootProps, getInputProps }) => (
183
187
  <div {...getRootProps(rootProps)}>
@@ -187,8 +191,6 @@ describe("useDropzone() hook", () => {
187
191
  </Dropzone>
188
192
  );
189
193
 
190
- const { container, rerender } = render(ui);
191
-
192
194
  const dropzone = container.querySelector("div");
193
195
 
194
196
  fireEvent.click(dropzone);
@@ -202,8 +204,7 @@ describe("useDropzone() hook", () => {
202
204
  fireEvent.blur(dropzone);
203
205
  expect(rootProps.onBlur).toHaveBeenCalled();
204
206
 
205
- fireEvent.dragEnter(dropzone, event);
206
- await flushPromises(rerender, ui);
207
+ await act(() => fireEvent.dragEnter(dropzone, event));
207
208
  expect(rootProps.onDragEnter).toHaveBeenCalled();
208
209
 
209
210
  fireEvent.dragOver(dropzone, event);
@@ -212,8 +213,7 @@ describe("useDropzone() hook", () => {
212
213
  fireEvent.dragLeave(dropzone, event);
213
214
  expect(rootProps.onDragLeave).toHaveBeenCalled();
214
215
 
215
- fireEvent.drop(dropzone, event);
216
- await flushPromises(rerender, ui);
216
+ await act(() => fireEvent.drop(dropzone, event));
217
217
  expect(rootProps.onDrop).toHaveBeenCalled();
218
218
  });
219
219
 
@@ -223,7 +223,7 @@ describe("useDropzone() hook", () => {
223
223
  onChange: jest.fn(),
224
224
  };
225
225
 
226
- const ui = (
226
+ const { container } = render(
227
227
  <Dropzone>
228
228
  {({ getRootProps, getInputProps }) => (
229
229
  <div {...getRootProps()}>
@@ -233,20 +233,16 @@ describe("useDropzone() hook", () => {
233
233
  </Dropzone>
234
234
  );
235
235
 
236
- const { container, rerender } = render(ui);
237
-
238
236
  const input = container.querySelector("input");
239
237
 
240
238
  fireEvent.click(input);
241
- await flushPromises(rerender, ui);
242
239
  expect(inputProps.onClick).toHaveBeenCalled();
243
240
 
244
- fireEvent.change(input);
245
- await flushPromises(rerender, ui);
241
+ await act(async () => fireEvent.change(input, { target: { files: [] } }));
246
242
  expect(inputProps.onChange).toHaveBeenCalled();
247
243
  });
248
244
 
249
- it("runs no callback handlers if {disabled} is true", () => {
245
+ it("runs no callback handlers if {disabled} is true", async () => {
250
246
  const event = createDtWithFiles(files);
251
247
 
252
248
  const rootProps = {
@@ -288,7 +284,7 @@ describe("useDropzone() hook", () => {
288
284
  fireEvent.blur(dropzone);
289
285
  expect(rootProps.onBlur).not.toHaveBeenCalled();
290
286
 
291
- fireEvent.dragEnter(dropzone, event);
287
+ await act(() => fireEvent.dragEnter(dropzone, event));
292
288
  expect(rootProps.onDragEnter).not.toHaveBeenCalled();
293
289
 
294
290
  fireEvent.dragOver(dropzone, event);
@@ -297,7 +293,7 @@ describe("useDropzone() hook", () => {
297
293
  fireEvent.dragLeave(dropzone, event);
298
294
  expect(rootProps.onDragLeave).not.toHaveBeenCalled();
299
295
 
300
- fireEvent.drop(dropzone, event);
296
+ await act(() => fireEvent.drop(dropzone, event));
301
297
  expect(rootProps.onDrop).not.toHaveBeenCalled();
302
298
 
303
299
  const input = container.querySelector("input");
@@ -305,7 +301,7 @@ describe("useDropzone() hook", () => {
305
301
  fireEvent.click(input);
306
302
  expect(inputProps.onClick).not.toHaveBeenCalled();
307
303
 
308
- fireEvent.change(input);
304
+ await act(() => fireEvent.change(input));
309
305
  expect(inputProps.onChange).not.toHaveBeenCalled();
310
306
  });
311
307
 
@@ -525,8 +521,8 @@ describe("useDropzone() hook", () => {
525
521
  const dropzone = container.querySelector("#dropzone");
526
522
 
527
523
  const fn = async () => {
528
- fireDrop(dropzone, data);
529
- await flushPromises(rerender, ui);
524
+ await act(() => fireEvent.drop(dropzone, data));
525
+ rerender(ui);
530
526
  done();
531
527
  };
532
528
 
@@ -551,13 +547,10 @@ describe("useDropzone() hook", () => {
551
547
 
552
548
  const dropzone = container.querySelector("label");
553
549
 
554
- const event = new Event("click", { bubbles: true, cancelable: true });
555
-
556
- fireEvent(dropzone, event);
550
+ fireEvent.click(dropzone, { bubbles: true, cancelable: true });
557
551
 
558
- const ref = activeRef.current;
559
- expect(ref).not.toBeNull();
560
- expect(dropzone).toContainElement(ref);
552
+ expect(activeRef.current).not.toBeNull();
553
+ expect(dropzone).toContainElement(activeRef.current);
561
554
  expect(onClickSpy).toHaveBeenCalledTimes(1);
562
555
  });
563
556
  });
@@ -664,12 +657,11 @@ describe("useDropzone() hook", () => {
664
657
  )}
665
658
  </Dropzone>
666
659
  );
667
- const dropzone = container.querySelector("div");
668
660
 
669
661
  const dropEvt = new Event("drop", { bubbles: true });
670
662
  const dropEvtPreventDefaultSpy = jest.spyOn(dropEvt, "preventDefault");
671
663
 
672
- fireEvent(dropzone, dropEvt);
664
+ fireEvent(container.querySelector("div"), dropEvt);
673
665
  // A call is from the onDrop handler for the dropzone,
674
666
  // but there should be no more than 1
675
667
  expect(dropEvtPreventDefaultSpy).toHaveBeenCalled();
@@ -721,7 +713,7 @@ describe("useDropzone() hook", () => {
721
713
  onDrop: jest.fn(),
722
714
  };
723
715
 
724
- const ui = (
716
+ const { container } = render(
725
717
  <Dropzone {...parentProps}>
726
718
  {({ getRootProps, getInputProps }) => (
727
719
  <div {...getRootProps()}>
@@ -732,25 +724,21 @@ describe("useDropzone() hook", () => {
732
724
  </Dropzone>
733
725
  );
734
726
 
735
- const { container, rerender } = render(ui);
736
-
737
727
  const innerDropzone = container.querySelector("#inner-dropzone");
738
728
 
739
- fireDragEnter(innerDropzone, data);
740
- await flushPromises(rerender, ui);
729
+ await act(() => fireEvent.dragEnter(innerDropzone, data));
741
730
  expect(innerProps.onDragEnter).toHaveBeenCalled();
742
731
  expect(parentProps.onDragEnter).toHaveBeenCalled();
743
732
 
744
- fireDragOver(innerDropzone, data);
733
+ fireEvent.dragOver(innerDropzone, data);
745
734
  expect(innerProps.onDragOver).toHaveBeenCalled();
746
735
  expect(parentProps.onDragOver).toHaveBeenCalled();
747
736
 
748
- fireDragLeave(innerDropzone, data);
737
+ fireEvent.dragLeave(innerDropzone, data);
749
738
  expect(innerProps.onDragLeave).toHaveBeenCalled();
750
739
  expect(parentProps.onDragLeave).toHaveBeenCalled();
751
740
 
752
- fireDrop(innerDropzone, data);
753
- await flushPromises(rerender, ui);
741
+ await act(() => fireEvent.drop(innerDropzone, data));
754
742
  expect(innerProps.onDrop).toHaveBeenCalled();
755
743
  expect(parentProps.onDrop).toHaveBeenCalled();
756
744
  });
@@ -787,7 +775,7 @@ describe("useDropzone() hook", () => {
787
775
  onDrop: jest.fn(),
788
776
  };
789
777
 
790
- const ui = (
778
+ const { container } = render(
791
779
  <Dropzone {...parentProps}>
792
780
  {({ getRootProps, getInputProps }) => (
793
781
  <div {...getRootProps()}>
@@ -798,25 +786,21 @@ describe("useDropzone() hook", () => {
798
786
  </Dropzone>
799
787
  );
800
788
 
801
- const { container, rerender } = render(ui);
802
-
803
789
  const innerDropzone = container.querySelector("#inner-dropzone");
804
790
 
805
- fireDragEnter(innerDropzone, data);
806
- await flushPromises(rerender, ui);
791
+ await act(() => fireEvent.dragEnter(innerDropzone, data));
807
792
  expect(innerProps.onDragEnter).toHaveBeenCalled();
808
793
  expect(parentProps.onDragEnter).not.toHaveBeenCalled();
809
794
 
810
- fireDragOver(innerDropzone, data);
795
+ fireEvent.dragOver(innerDropzone, data);
811
796
  expect(innerProps.onDragOver).toHaveBeenCalled();
812
797
  expect(parentProps.onDragOver).not.toHaveBeenCalled();
813
798
 
814
- fireDragLeave(innerDropzone, data);
799
+ fireEvent.dragLeave(innerDropzone, data);
815
800
  expect(innerProps.onDragLeave).toHaveBeenCalled();
816
801
  expect(parentProps.onDragLeave).not.toHaveBeenCalled();
817
802
 
818
- fireDrop(innerDropzone, data);
819
- await flushPromises(rerender, ui);
803
+ await act(() => fireEvent.drop(innerDropzone, data));
820
804
  expect(innerProps.onDrop).toHaveBeenCalled();
821
805
  expect(parentProps.onDrop).not.toHaveBeenCalled();
822
806
  });
@@ -846,7 +830,7 @@ describe("useDropzone() hook", () => {
846
830
  onDrop: jest.fn(),
847
831
  };
848
832
 
849
- const ui = (
833
+ const { container } = render(
850
834
  <Dropzone {...parentProps}>
851
835
  {({ getRootProps, getInputProps }) => (
852
836
  <div id="outer-dropzone" {...getRootProps()}>
@@ -857,30 +841,25 @@ describe("useDropzone() hook", () => {
857
841
  </Dropzone>
858
842
  );
859
843
 
860
- const { container, rerender } = render(ui);
861
-
862
844
  const outerDropzone = container.querySelector("#outer-dropzone");
863
845
  const innerDropzone = container.querySelector("#inner-dropzone");
864
846
 
865
847
  // Sets drag targets on the outer dropzone
866
- fireDragEnter(outerDropzone, data);
867
- await flushPromises(rerender, ui);
848
+ await act(() => fireEvent.dragEnter(outerDropzone, data));
868
849
 
869
- fireDragEnter(innerDropzone, data);
870
- await flushPromises(rerender, ui);
850
+ await act(() => fireEvent.dragEnter(innerDropzone, data));
871
851
  expect(innerProps.onDragEnter).toHaveBeenCalled();
872
852
  expect(parentProps.onDragEnter).toHaveBeenCalledTimes(1);
873
853
 
874
- fireDragOver(innerDropzone, data);
854
+ fireEvent.dragOver(innerDropzone, data);
875
855
  expect(innerProps.onDragOver).toHaveBeenCalled();
876
856
  expect(parentProps.onDragOver).not.toHaveBeenCalled();
877
857
 
878
- fireDragLeave(innerDropzone, data);
858
+ fireEvent.dragLeave(innerDropzone, data);
879
859
  expect(innerProps.onDragLeave).toHaveBeenCalled();
880
860
  expect(parentProps.onDragLeave).not.toHaveBeenCalled();
881
861
 
882
- fireDrop(innerDropzone, data);
883
- await flushPromises(rerender, ui);
862
+ await act(() => fireEvent.drop(innerDropzone, data));
884
863
  expect(innerProps.onDrop).toHaveBeenCalled();
885
864
  expect(parentProps.onDrop).not.toHaveBeenCalled();
886
865
  });
@@ -898,7 +877,8 @@ describe("useDropzone() hook", () => {
898
877
  );
899
878
 
900
879
  const parentDragLeave = jest.fn();
901
- const ui = (
880
+
881
+ const { container } = render(
902
882
  <Dropzone onDragLeave={parentDragLeave}>
903
883
  {({ getRootProps, getInputProps }) => (
904
884
  <div id="parent-dropzone" {...getRootProps()}>
@@ -909,18 +889,14 @@ describe("useDropzone() hook", () => {
909
889
  </Dropzone>
910
890
  );
911
891
 
912
- const { container, rerender } = render(ui);
913
-
914
892
  const parentDropzone = container.querySelector("#parent-dropzone");
915
893
 
916
- fireDragEnter(parentDropzone, data);
917
- await flushPromises(rerender, ui);
894
+ await act(() => fireEvent.dragEnter(parentDropzone, data));
918
895
 
919
896
  const innerDropzone = container.querySelector("#inner-dropzone");
920
- fireDragEnter(innerDropzone, data);
921
- await flushPromises(rerender, ui);
897
+ await act(() => fireEvent.dragEnter(innerDropzone, data));
922
898
 
923
- fireDragLeave(innerDropzone, data);
899
+ fireEvent.dragLeave(innerDropzone, data);
924
900
  expect(innerDragLeave).toHaveBeenCalled();
925
901
  expect(parentDragLeave).not.toHaveBeenCalled();
926
902
  });
@@ -940,7 +916,7 @@ describe("useDropzone() hook", () => {
940
916
  onDrop: jest.fn(),
941
917
  };
942
918
 
943
- const ui = (
919
+ const { container } = render(
944
920
  <Dropzone {...props}>
945
921
  {({ getRootProps, getInputProps }) => (
946
922
  <div {...getRootProps()}>
@@ -949,24 +925,54 @@ describe("useDropzone() hook", () => {
949
925
  )}
950
926
  </Dropzone>
951
927
  );
952
- const { container, rerender } = render(ui);
953
928
  const dropzone = container.querySelector("div");
954
929
 
955
- fireDragEnter(dropzone, data);
956
- await flushPromises(rerender, ui);
930
+ await act(() => fireEvent.dragEnter(dropzone, data));
957
931
  expect(props.onDragEnter).toHaveBeenCalled();
958
932
 
959
- fireDragOver(dropzone, data);
933
+ fireEvent.dragOver(dropzone, data);
960
934
  expect(props.onDragOver).toHaveBeenCalled();
961
935
 
962
- fireDragLeave(dropzone, data);
936
+ fireEvent.dragLeave(dropzone, data);
963
937
  expect(props.onDragLeave).toHaveBeenCalled();
964
938
 
965
- fireDrop(dropzone, data);
966
- await flushPromises(rerender, ui);
939
+ await act(() => fireEvent.drop(dropzone, data));
967
940
  expect(props.onDrop).toHaveBeenCalled();
941
+ expect(props.getFilesFromEvent).toHaveBeenCalledTimes(2);
942
+ });
943
+
944
+ it("calls {onError} when getFilesFromEvent() rejects", async () => {
945
+ const data = createDtWithFiles(files);
946
+
947
+ const props = {
948
+ getFilesFromEvent: jest
949
+ .fn()
950
+ .mockImplementation(() => Promise.reject("oops :(")),
951
+ onDragEnter: jest.fn(),
952
+ onDrop: jest.fn(),
953
+ onError: jest.fn(),
954
+ };
955
+
956
+ const ui = (
957
+ <Dropzone {...props}>
958
+ {({ getRootProps, getInputProps }) => (
959
+ <div {...getRootProps()}>
960
+ <input {...getInputProps()} />
961
+ </div>
962
+ )}
963
+ </Dropzone>
964
+ );
965
+ const { container } = render(ui);
966
+ const dropzone = container.querySelector("div");
967
+
968
+ await act(() => fireEvent.dragEnter(dropzone, data));
969
+ expect(props.onDragEnter).not.toHaveBeenCalled();
970
+
971
+ await act(() => fireEvent.drop(dropzone, data));
972
+ expect(props.onDrop).not.toHaveBeenCalled();
968
973
 
969
974
  expect(props.getFilesFromEvent).toHaveBeenCalledTimes(2);
975
+ expect(props.onError).toHaveBeenCalledTimes(2);
970
976
  });
971
977
  });
972
978
 
@@ -1217,9 +1223,8 @@ describe("useDropzone() hook", () => {
1217
1223
  const dropzone = container.querySelector("div");
1218
1224
 
1219
1225
  fireEvent.click(dropzone);
1220
- const ref = activeRef.current;
1221
- expect(ref).not.toBeNull();
1222
- expect(dropzone).toContainElement(ref);
1226
+ expect(activeRef.current).not.toBeNull();
1227
+ expect(dropzone).toContainElement(activeRef.current);
1223
1228
  expect(onClickSpy).toHaveBeenCalled();
1224
1229
  });
1225
1230
 
@@ -1237,9 +1242,7 @@ describe("useDropzone() hook", () => {
1237
1242
  </Dropzone>
1238
1243
  );
1239
1244
 
1240
- const dropzone = container.querySelector("div");
1241
-
1242
- fireEvent.click(dropzone);
1245
+ fireEvent.click(container.querySelector("div"));
1243
1246
  expect(onClickSpy).not.toHaveBeenCalled();
1244
1247
  });
1245
1248
 
@@ -1255,9 +1258,7 @@ describe("useDropzone() hook", () => {
1255
1258
  </Dropzone>
1256
1259
  );
1257
1260
 
1258
- const dropzone = container.querySelector("div");
1259
-
1260
- fireEvent.click(dropzone);
1261
+ fireEvent.click(container.querySelector("div"));
1261
1262
  expect(onClickSpy).not.toHaveBeenCalled();
1262
1263
  });
1263
1264
 
@@ -1307,13 +1308,10 @@ describe("useDropzone() hook", () => {
1307
1308
  </Dropzone>
1308
1309
  );
1309
1310
 
1310
- const dropzone = container.querySelector("div");
1311
- const btn = container.querySelector("button");
1312
-
1313
- fireEvent.click(dropzone);
1311
+ fireEvent.click(container.querySelector("div"));
1314
1312
  expect(inputClickSpy).not.toHaveBeenCalled();
1315
1313
 
1316
- fireEvent.click(btn);
1314
+ fireEvent.click(container.querySelector("button"));
1317
1315
  expect(btnClickSpy).toHaveBeenCalled();
1318
1316
  });
1319
1317
 
@@ -1325,7 +1323,7 @@ describe("useDropzone() hook", () => {
1325
1323
  .mockReturnValueOnce(true);
1326
1324
  const onClickSpy = jest.spyOn(HTMLInputElement.prototype, "click");
1327
1325
 
1328
- const ui = (
1326
+ const { container } = render(
1329
1327
  <Dropzone>
1330
1328
  {({ getRootProps, getInputProps }) => (
1331
1329
  <div {...getRootProps()}>
@@ -1335,40 +1333,35 @@ describe("useDropzone() hook", () => {
1335
1333
  </Dropzone>
1336
1334
  );
1337
1335
 
1338
- const { container } = render(ui);
1339
-
1340
- const dropzone = container.querySelector("div");
1341
-
1342
- fireEvent.click(dropzone);
1343
- drainTimers();
1336
+ fireEvent.click(container.querySelector("div"));
1337
+ drainPendingTimers();
1344
1338
 
1345
1339
  expect(onClickSpy).toHaveBeenCalled();
1346
1340
  jest.useRealTimers();
1347
1341
  isIeOrEdgeSpy.mockClear();
1348
1342
  });
1349
1343
 
1350
- it("should not use showOpenFilePicker() if supported and {useFsAccessApi} is not true", async () => {
1344
+ it("should not use showOpenFilePicker() if supported and {useFsAccessApi} is not true", () => {
1351
1345
  jest.useFakeTimers();
1352
1346
 
1353
1347
  const activeRef = createRef();
1354
1348
  const active = <span ref={activeRef}>I am active</span>;
1355
1349
  const onClickSpy = jest.spyOn(HTMLInputElement.prototype, "click");
1356
1350
 
1357
- const handlers = files.map((f) => createFileSystemFileHandle(f));
1358
- const showOpenFilePickerMock = jest
1359
- .fn()
1360
- .mockReturnValue(Promise.resolve(handlers));
1351
+ const showOpenFilePickerMock = jest.fn();
1361
1352
 
1362
1353
  window.showOpenFilePicker = showOpenFilePickerMock;
1363
1354
 
1364
1355
  const onDropSpy = jest.fn();
1365
1356
  const onFileDialogOpenSpy = jest.fn();
1366
1357
 
1367
- const ui = (
1358
+ const { container } = render(
1368
1359
  <Dropzone
1369
1360
  onDrop={onDropSpy}
1370
1361
  onFileDialogOpen={onFileDialogOpenSpy}
1371
- accept="application/pdf"
1362
+ accept={{
1363
+ "application/pdf": [],
1364
+ }}
1372
1365
  multiple
1373
1366
  useFsAccessApi={false}
1374
1367
  >
@@ -1381,39 +1374,34 @@ describe("useDropzone() hook", () => {
1381
1374
  </Dropzone>
1382
1375
  );
1383
1376
 
1384
- const { container, rerender } = render(ui);
1385
-
1386
1377
  const dropzone = container.querySelector("div");
1387
1378
 
1388
1379
  fireEvent.click(dropzone);
1389
1380
 
1390
- await flushPromises(rerender, ui);
1391
-
1392
- dispatchEvt(document.body, "focus");
1393
- drainTimers();
1394
-
1381
+ expect(showOpenFilePickerMock).not.toHaveBeenCalled();
1382
+ expect(onClickSpy).toHaveBeenCalled();
1395
1383
  expect(onFileDialogOpenSpy).toHaveBeenCalled();
1384
+ expect(activeRef.current).not.toBeNull();
1385
+ expect(dropzone).toContainElement(activeRef.current);
1386
+
1387
+ focusWindow();
1388
+ drainPendingTimers();
1396
1389
 
1397
1390
  expect(activeRef.current).toBeNull();
1398
1391
  expect(dropzone).not.toContainElement(activeRef.current);
1399
- expect(onClickSpy).toHaveBeenCalled();
1400
- expect(showOpenFilePickerMock).not.toHaveBeenCalled();
1401
1392
  expect(onDropSpy).not.toHaveBeenCalled();
1402
1393
 
1403
1394
  jest.useRealTimers();
1404
1395
  });
1405
1396
 
1406
- it("should not use showOpenFilePicker() if supported and {isSecureContext} is not true", async () => {
1397
+ it("should not use showOpenFilePicker() if supported and {isSecureContext} is not true", () => {
1407
1398
  jest.useFakeTimers();
1408
1399
 
1409
1400
  const activeRef = createRef();
1410
1401
  const active = <span ref={activeRef}>I am active</span>;
1411
1402
  const onClickSpy = jest.spyOn(HTMLInputElement.prototype, "click");
1412
1403
 
1413
- const handlers = files.map((f) => createFileSystemFileHandle(f));
1414
- const showOpenFilePickerMock = jest
1415
- .fn()
1416
- .mockReturnValue(Promise.resolve(handlers));
1404
+ const showOpenFilePickerMock = jest.fn();
1417
1405
 
1418
1406
  window.showOpenFilePicker = showOpenFilePickerMock;
1419
1407
  window.isSecureContext = false;
@@ -1421,11 +1409,13 @@ describe("useDropzone() hook", () => {
1421
1409
  const onDropSpy = jest.fn();
1422
1410
  const onFileDialogOpenSpy = jest.fn();
1423
1411
 
1424
- const ui = (
1412
+ const { container } = render(
1425
1413
  <Dropzone
1426
1414
  onDrop={onDropSpy}
1427
1415
  onFileDialogOpen={onFileDialogOpenSpy}
1428
- accept="application/pdf"
1416
+ accept={{
1417
+ "application/pdf": [],
1418
+ }}
1429
1419
  multiple
1430
1420
  >
1431
1421
  {({ getRootProps, getInputProps, isFileDialogActive }) => (
@@ -1437,23 +1427,21 @@ describe("useDropzone() hook", () => {
1437
1427
  </Dropzone>
1438
1428
  );
1439
1429
 
1440
- const { container, rerender } = render(ui);
1441
-
1442
1430
  const dropzone = container.querySelector("div");
1443
1431
 
1444
1432
  fireEvent.click(dropzone);
1445
1433
 
1446
- await flushPromises(rerender, ui);
1447
-
1448
- dispatchEvt(document.body, "focus");
1449
- drainTimers();
1450
-
1434
+ expect(showOpenFilePickerMock).not.toHaveBeenCalled();
1435
+ expect(onClickSpy).toHaveBeenCalled();
1451
1436
  expect(onFileDialogOpenSpy).toHaveBeenCalled();
1437
+ expect(activeRef.current).not.toBeNull();
1438
+ expect(dropzone).toContainElement(activeRef.current);
1439
+
1440
+ focusWindow();
1441
+ drainPendingTimers();
1452
1442
 
1453
1443
  expect(activeRef.current).toBeNull();
1454
1444
  expect(dropzone).not.toContainElement(activeRef.current);
1455
- expect(onClickSpy).toHaveBeenCalled();
1456
- expect(showOpenFilePickerMock).not.toHaveBeenCalled();
1457
1445
  expect(onDropSpy).not.toHaveBeenCalled();
1458
1446
 
1459
1447
  jest.useRealTimers();
@@ -1477,11 +1465,13 @@ describe("useDropzone() hook", () => {
1477
1465
  const onDropSpy = jest.fn();
1478
1466
  const onFileDialogOpenSpy = jest.fn();
1479
1467
 
1480
- const ui = (
1468
+ const { container } = render(
1481
1469
  <Dropzone
1482
1470
  onDrop={onDropSpy}
1483
1471
  onFileDialogOpen={onFileDialogOpenSpy}
1484
- accept="application/pdf"
1472
+ accept={{
1473
+ "application/pdf": [],
1474
+ }}
1485
1475
  multiple
1486
1476
  useFsAccessApi
1487
1477
  >
@@ -1494,33 +1484,29 @@ describe("useDropzone() hook", () => {
1494
1484
  </Dropzone>
1495
1485
  );
1496
1486
 
1497
- const { container, rerender } = render(ui);
1498
-
1499
1487
  const dropzone = container.querySelector("div");
1500
1488
 
1501
1489
  fireEvent.click(dropzone);
1502
1490
 
1503
- await flushPromises(rerender, ui);
1504
-
1505
- expect(activeRef.current).not.toBeNull();
1506
- expect(dropzone).toContainElement(activeRef.current);
1507
- expect(onFileDialogOpenSpy).toHaveBeenCalled();
1508
-
1509
- thenable.done(handlers);
1510
- await flushPromises(rerender, ui);
1511
-
1512
- expect(activeRef.current).toBeNull(); // We expect the dialog to be closed at this point
1513
- expect(dropzone).not.toContainElement(activeRef.current);
1514
- expect(onClickSpy).not.toHaveBeenCalled();
1515
1491
  expect(showOpenFilePickerMock).toHaveBeenCalledWith({
1516
1492
  multiple: true,
1517
1493
  types: [
1518
1494
  {
1519
- description: "everything",
1520
1495
  accept: { "application/pdf": [] },
1521
1496
  },
1522
1497
  ],
1523
1498
  });
1499
+ expect(onClickSpy).not.toHaveBeenCalled();
1500
+ expect(onFileDialogOpenSpy).toHaveBeenCalled();
1501
+
1502
+ expect(activeRef.current).not.toBeNull();
1503
+ expect(dropzone).toContainElement(activeRef.current);
1504
+
1505
+ await act(() => thenable.done(handlers));
1506
+
1507
+ expect(activeRef.current).toBeNull();
1508
+ expect(dropzone).not.toContainElement(activeRef.current);
1509
+
1524
1510
  expect(onDropSpy).toHaveBeenCalledWith(files, [], null);
1525
1511
  });
1526
1512
 
@@ -1529,16 +1515,17 @@ describe("useDropzone() hook", () => {
1529
1515
  const active = <span ref={activeRef}>I am active</span>;
1530
1516
 
1531
1517
  const handlers = files.map((f) => createFileSystemFileHandle(f));
1518
+ const thenable = createThenable();
1532
1519
  const showOpenFilePickerMock = jest
1533
1520
  .fn()
1534
- .mockReturnValue(Promise.resolve(handlers));
1521
+ .mockReturnValue(thenable.promise);
1535
1522
 
1536
1523
  window.showOpenFilePicker = showOpenFilePickerMock;
1537
1524
 
1538
1525
  const onDropSpy = jest.fn();
1539
1526
  const onFileDialogOpenSpy = jest.fn();
1540
1527
 
1541
- const ui = (
1528
+ const { container } = render(
1542
1529
  <Dropzone
1543
1530
  onDrop={onDropSpy}
1544
1531
  onFileDialogOpen={onFileDialogOpenSpy}
@@ -1550,36 +1537,35 @@ describe("useDropzone() hook", () => {
1550
1537
  </Dropzone>
1551
1538
  );
1552
1539
 
1553
- const { container, rerender } = render(ui);
1554
-
1555
1540
  const dropzone = container.querySelector("div");
1556
1541
 
1557
1542
  fireEvent.click(dropzone);
1558
- await flushPromises(rerender, ui);
1559
- const ref = activeRef.current;
1560
- expect(ref).toBeNull(); // We expect the dialog to be closed at this point
1561
- expect(dropzone).not.toContainElement(ref);
1543
+
1562
1544
  expect(showOpenFilePickerMock).toHaveBeenCalled();
1563
- expect(onDropSpy).toHaveBeenCalledWith(files, [], null);
1564
1545
  expect(onFileDialogOpenSpy).toHaveBeenCalled();
1546
+
1547
+ await act(() => thenable.done(handlers));
1548
+
1549
+ expect(activeRef.current).toBeNull();
1550
+ expect(dropzone).not.toContainElement(activeRef.current);
1551
+ expect(onDropSpy).toHaveBeenCalledWith(files, [], null);
1565
1552
  });
1566
1553
 
1567
1554
  test("if showOpenFilePicker() is supported and {useFsAccessApi} is true, and the user cancels it should call onFileDialogCancel", async () => {
1568
1555
  const activeRef = createRef();
1569
1556
  const active = <span ref={activeRef}>I am active</span>;
1570
1557
 
1558
+ const thenable = createThenable();
1571
1559
  const showOpenFilePickerMock = jest
1572
1560
  .fn()
1573
- .mockReturnValue(
1574
- Promise.reject(new DOMException("user aborted request", "AbortError"))
1575
- );
1561
+ .mockReturnValue(thenable.promise);
1576
1562
 
1577
1563
  window.showOpenFilePicker = showOpenFilePickerMock;
1578
1564
 
1579
1565
  const onDropSpy = jest.fn();
1580
1566
  const onFileDialogCancelSpy = jest.fn();
1581
1567
 
1582
- const ui = (
1568
+ const { container } = render(
1583
1569
  <Dropzone
1584
1570
  onDrop={onDropSpy}
1585
1571
  onFileDialogCancel={onFileDialogCancelSpy}
@@ -1591,18 +1577,20 @@ describe("useDropzone() hook", () => {
1591
1577
  </Dropzone>
1592
1578
  );
1593
1579
 
1594
- const { container, rerender } = render(ui);
1595
-
1596
1580
  const dropzone = container.querySelector("div");
1597
1581
 
1598
1582
  fireEvent.click(dropzone);
1599
- await flushPromises(rerender, ui);
1600
- const ref = activeRef.current;
1601
- expect(ref).toBeNull(); // We expect the dialog to be closed at this point
1602
- expect(dropzone).not.toContainElement(ref);
1583
+
1603
1584
  expect(showOpenFilePickerMock).toHaveBeenCalled();
1604
- expect(onDropSpy).not.toHaveBeenCalled();
1585
+
1586
+ await act(() =>
1587
+ thenable.cancel(new DOMException("user aborted request", "AbortError"))
1588
+ );
1589
+
1590
+ expect(activeRef.current).toBeNull();
1591
+ expect(dropzone).not.toContainElement(activeRef.current);
1605
1592
  expect(onFileDialogCancelSpy).toHaveBeenCalled();
1593
+ expect(onDropSpy).not.toHaveBeenCalled();
1606
1594
  });
1607
1595
 
1608
1596
  test("window focus evt is not bound if showOpenFilePicker() is supported and {useFsAccessApi} is true", async () => {
@@ -1619,44 +1607,32 @@ describe("useDropzone() hook", () => {
1619
1607
 
1620
1608
  window.showOpenFilePicker = showOpenFilePickerMock;
1621
1609
 
1622
- const ui = (
1623
- <Dropzone
1624
- onFileDialogCancel={onFileDialogCancelSpy}
1625
- noClick
1626
- useFsAccessApi
1627
- >
1628
- {({ getRootProps, isFileDialogActive, open }) => (
1629
- <div {...getRootProps()}>
1630
- {isFileDialogActive && active}
1631
- <button type="button" onClick={open}>
1632
- Open
1633
- </button>
1634
- </div>
1610
+ const { container } = render(
1611
+ <Dropzone onFileDialogCancel={onFileDialogCancelSpy} useFsAccessApi>
1612
+ {({ getRootProps, isFileDialogActive }) => (
1613
+ <div {...getRootProps()}>{isFileDialogActive && active}</div>
1635
1614
  )}
1636
1615
  </Dropzone>
1637
1616
  );
1638
1617
 
1639
- const { container, rerender } = render(ui);
1640
-
1641
1618
  const dropzone = container.querySelector("div");
1642
- const btn = container.querySelector("button");
1643
1619
 
1644
- btn.click();
1645
- drainTimers();
1646
- await flushPromises(rerender, ui);
1620
+ fireEvent.click(dropzone);
1647
1621
 
1648
- const ref = activeRef.current;
1649
- expect(ref).not.toBeNull();
1650
- expect(dropzone).toContainElement(ref);
1622
+ expect(activeRef.current).not.toBeNull();
1623
+ expect(dropzone).toContainElement(activeRef.current);
1651
1624
 
1652
- thenable.cancel(new DOMException("user aborted request", "AbortError"));
1625
+ await act(() =>
1626
+ thenable.cancel(new DOMException("user aborted request", "AbortError"))
1627
+ );
1653
1628
 
1654
- dispatchEvt(document.body, "focus");
1655
- drainTimers();
1656
- await flushPromises(rerender, ui);
1629
+ // Try to focus window and run timers
1630
+ focusWindow();
1631
+ drainPendingTimers();
1657
1632
 
1633
+ expect(activeRef.current).toBeNull();
1634
+ expect(dropzone).not.toContainElement(activeRef.current);
1658
1635
  expect(onFileDialogCancelSpy).toHaveBeenCalledTimes(1);
1659
- expect(dropzone).not.toContainElement(ref);
1660
1636
 
1661
1637
  jest.useRealTimers();
1662
1638
  });
@@ -1676,11 +1652,13 @@ describe("useDropzone() hook", () => {
1676
1652
  const onDropSpy = jest.fn();
1677
1653
  const onFileDialogOpenSpy = jest.fn();
1678
1654
 
1679
- const ui = (
1655
+ const { container } = render(
1680
1656
  <Dropzone
1681
1657
  onDrop={onDropSpy}
1682
1658
  onFileDialogOpen={onFileDialogOpenSpy}
1683
- accept="application/pdf"
1659
+ accept={{
1660
+ "application/pdf": [],
1661
+ }}
1684
1662
  multiple
1685
1663
  useFsAccessApi
1686
1664
  >
@@ -1693,22 +1671,19 @@ describe("useDropzone() hook", () => {
1693
1671
  </Dropzone>
1694
1672
  );
1695
1673
 
1696
- const { container, rerender } = render(ui);
1697
-
1698
1674
  const dropzone = container.querySelector("div");
1699
1675
 
1700
1676
  fireEvent.click(dropzone);
1701
1677
 
1702
- await flushPromises(rerender, ui);
1703
-
1704
1678
  expect(activeRef.current).not.toBeNull();
1705
1679
  expect(dropzone).toContainElement(activeRef.current);
1706
1680
  expect(onFileDialogOpenSpy).toHaveBeenCalled();
1707
1681
 
1708
- thenable.cancel(
1709
- new DOMException("Cannot use this API cross-origin", "SecurityError")
1682
+ await act(() =>
1683
+ thenable.cancel(
1684
+ new DOMException("Cannot use this API cross-origin", "SecurityError")
1685
+ )
1710
1686
  );
1711
- await flushPromises(rerender, ui);
1712
1687
 
1713
1688
  expect(activeRef.current).not.toBeNull();
1714
1689
  expect(dropzone).toContainElement(activeRef.current);
@@ -1729,7 +1704,7 @@ describe("useDropzone() hook", () => {
1729
1704
 
1730
1705
  window.showOpenFilePicker = showOpenFilePickerMock;
1731
1706
 
1732
- const ui = (
1707
+ const { container } = render(
1733
1708
  <Dropzone onFileDialogCancel={onFileDialogCancelSpy}>
1734
1709
  {({ getRootProps, getInputProps, isFileDialogActive }) => (
1735
1710
  <div {...getRootProps()}>
@@ -1740,43 +1715,54 @@ describe("useDropzone() hook", () => {
1740
1715
  </Dropzone>
1741
1716
  );
1742
1717
 
1743
- const { container, rerender } = render(ui);
1744
-
1745
1718
  const dropzone = container.querySelector("div");
1746
1719
 
1747
1720
  fireEvent.click(dropzone);
1748
1721
 
1749
- drainTimers();
1750
- await flushPromises(rerender, ui);
1751
-
1752
- const ref = activeRef.current;
1753
- expect(ref).not.toBeNull();
1754
- expect(dropzone).toContainElement(ref);
1722
+ expect(activeRef.current).not.toBeNull();
1723
+ expect(dropzone).toContainElement(activeRef.current);
1755
1724
 
1756
- thenable.cancel(
1757
- new DOMException("Cannot use this API cross-origin", "SecurityError")
1725
+ await act(() =>
1726
+ thenable.cancel(
1727
+ new DOMException("Cannot use this API cross-origin", "SecurityError")
1728
+ )
1758
1729
  );
1759
1730
 
1760
- await flushPromises(rerender, ui);
1761
-
1762
- dispatchEvt(document.body, "focus");
1763
- drainTimers();
1764
- await flushPromises(rerender, ui);
1731
+ focusWindow();
1732
+ drainPendingTimers();
1765
1733
 
1766
1734
  expect(onFileDialogCancelSpy).toHaveBeenCalled();
1767
- expect(dropzone).not.toContainElement(ref);
1735
+ expect(dropzone).not.toContainElement(activeRef.current);
1768
1736
 
1769
1737
  jest.useRealTimers();
1770
1738
  });
1771
- });
1772
1739
 
1773
- describe("onKeyDown", () => {
1774
- it("triggers the click event on the input if the SPACE/ENTER keys are pressed", () => {
1740
+ test("showOpenFilePicker() should call {onError} when an unexpected error occurs", async () => {
1775
1741
  const activeRef = createRef();
1776
1742
  const active = <span ref={activeRef}>I am active</span>;
1777
- const onClickSpy = jest.spyOn(HTMLInputElement.prototype, "click");
1778
- const { container } = render(
1779
- <Dropzone>
1743
+
1744
+ const thenable = createThenable();
1745
+ const showOpenFilePickerMock = jest
1746
+ .fn()
1747
+ .mockReturnValue(thenable.promise);
1748
+
1749
+ window.showOpenFilePicker = showOpenFilePickerMock;
1750
+
1751
+ const onErrorSpy = jest.fn();
1752
+ const onDropSpy = jest.fn();
1753
+ const onFileDialogOpenSpy = jest.fn();
1754
+
1755
+ const ui = (
1756
+ <Dropzone
1757
+ onError={onErrorSpy}
1758
+ onDrop={onDropSpy}
1759
+ onFileDialogOpen={onFileDialogOpenSpy}
1760
+ accept={{
1761
+ "application/pdf": [],
1762
+ }}
1763
+ multiple
1764
+ useFsAccessApi
1765
+ >
1780
1766
  {({ getRootProps, getInputProps, isFileDialogActive }) => (
1781
1767
  <div {...getRootProps()}>
1782
1768
  <input {...getInputProps()} />
@@ -1786,11 +1772,45 @@ describe("useDropzone() hook", () => {
1786
1772
  </Dropzone>
1787
1773
  );
1788
1774
 
1775
+ const { container } = render(ui);
1776
+
1789
1777
  const dropzone = container.querySelector("div");
1790
1778
 
1791
- fireEvent.keyDown(dropzone, {
1792
- keyCode: 32, // Space
1793
- });
1779
+ fireEvent.click(dropzone);
1780
+
1781
+ expect(activeRef.current).not.toBeNull();
1782
+ expect(dropzone).toContainElement(activeRef.current);
1783
+ expect(onFileDialogOpenSpy).toHaveBeenCalled();
1784
+
1785
+ const err = new Error("oops :(");
1786
+ await act(() => thenable.cancel(err));
1787
+ expect(activeRef.current).not.toBeNull();
1788
+ expect(dropzone).toContainElement(activeRef.current);
1789
+ expect(onErrorSpy).toHaveBeenCalledWith(err);
1790
+ });
1791
+ });
1792
+
1793
+ describe("onKeyDown", () => {
1794
+ it("triggers the click event on the input if the SPACE/ENTER keys are pressed", () => {
1795
+ const activeRef = createRef();
1796
+ const active = <span ref={activeRef}>I am active</span>;
1797
+ const onClickSpy = jest.spyOn(HTMLInputElement.prototype, "click");
1798
+ const { container } = render(
1799
+ <Dropzone>
1800
+ {({ getRootProps, getInputProps, isFileDialogActive }) => (
1801
+ <div {...getRootProps()}>
1802
+ <input {...getInputProps()} />
1803
+ {isFileDialogActive && active}
1804
+ </div>
1805
+ )}
1806
+ </Dropzone>
1807
+ );
1808
+
1809
+ const dropzone = container.querySelector("div");
1810
+
1811
+ fireEvent.keyDown(dropzone, {
1812
+ keyCode: 32, // Space
1813
+ });
1794
1814
 
1795
1815
  fireEvent.keyDown(dropzone, {
1796
1816
  keyCode: 13, // Enter
@@ -1961,7 +1981,7 @@ describe("useDropzone() hook", () => {
1961
1981
  onDrop: jest.fn(),
1962
1982
  };
1963
1983
 
1964
- const ui = (
1984
+ const { container } = render(
1965
1985
  <Dropzone {...props}>
1966
1986
  {({ getRootProps, getInputProps }) => (
1967
1987
  <div {...getRootProps()}>
@@ -1970,21 +1990,18 @@ describe("useDropzone() hook", () => {
1970
1990
  )}
1971
1991
  </Dropzone>
1972
1992
  );
1973
- const { container, rerender } = render(ui);
1974
1993
  const dropzone = container.querySelector("div");
1975
1994
 
1976
- fireDragEnter(dropzone, data);
1977
- await flushPromises(rerender, ui);
1995
+ await act(() => fireEvent.dragEnter(dropzone, data));
1978
1996
  expect(props.onDragEnter).toHaveBeenCalled();
1979
1997
 
1980
- fireDragOver(dropzone, data);
1998
+ fireEvent.dragOver(dropzone, data);
1981
1999
  expect(props.onDragOver).toHaveBeenCalled();
1982
2000
 
1983
- fireDragLeave(dropzone, data);
2001
+ fireEvent.dragLeave(dropzone, data);
1984
2002
  expect(props.onDragLeave).toHaveBeenCalled();
1985
2003
 
1986
- fireDrop(dropzone, data);
1987
- await flushPromises(rerender, ui);
2004
+ await act(() => fireEvent.drop(dropzone, data));
1988
2005
  expect(props.onDrop).toHaveBeenCalled();
1989
2006
  });
1990
2007
 
@@ -2000,7 +2017,7 @@ describe("useDropzone() hook", () => {
2000
2017
  onDropRejected: jest.fn(),
2001
2018
  };
2002
2019
 
2003
- const ui = (
2020
+ const { container } = render(
2004
2021
  <Dropzone {...props}>
2005
2022
  {({ getRootProps, getInputProps }) => (
2006
2023
  <div {...getRootProps()}>
@@ -2009,22 +2026,19 @@ describe("useDropzone() hook", () => {
2009
2026
  )}
2010
2027
  </Dropzone>
2011
2028
  );
2012
- const { container, rerender } = render(ui);
2013
2029
  const dropzone = container.querySelector("div");
2014
2030
 
2015
- fireDragEnter(dropzone, emptyData);
2016
- await flushPromises(rerender, ui);
2031
+ await act(() => fireEvent.dragEnter(dropzone, emptyData));
2017
2032
  expect(props.onDragEnter).toHaveBeenCalled();
2018
2033
 
2019
- fireDragOver(dropzone, emptyData);
2034
+ fireEvent.dragOver(dropzone, emptyData);
2020
2035
  expect(props.onDragOver).toHaveBeenCalled();
2021
2036
 
2022
- fireDragLeave(dropzone, emptyData);
2037
+ fireEvent.dragLeave(dropzone, emptyData);
2023
2038
  expect(props.onDragLeave).toHaveBeenCalled();
2024
2039
 
2025
2040
  const data = createDtWithFiles(files);
2026
- fireDrop(dropzone, data);
2027
- await flushPromises(rerender, ui);
2041
+ await act(() => fireEvent.drop(dropzone, data));
2028
2042
  expect(props.onDrop).toHaveBeenCalled();
2029
2043
  expect(props.onDropAccepted).toHaveBeenCalledWith(
2030
2044
  files,
@@ -2050,7 +2064,7 @@ describe("useDropzone() hook", () => {
2050
2064
  onDropRejected: jest.fn(),
2051
2065
  };
2052
2066
 
2053
- const ui = (
2067
+ const { container } = render(
2054
2068
  <Dropzone {...props}>
2055
2069
  {({ getRootProps, getInputProps }) => (
2056
2070
  <div {...getRootProps()}>
@@ -2059,21 +2073,18 @@ describe("useDropzone() hook", () => {
2059
2073
  )}
2060
2074
  </Dropzone>
2061
2075
  );
2062
- const { container, rerender } = render(ui);
2063
2076
  const dropzone = container.querySelector("div");
2064
2077
 
2065
- fireDragEnter(dropzone, data);
2066
- await flushPromises(rerender, ui);
2078
+ await act(() => fireEvent.dragEnter(dropzone, data));
2067
2079
  expect(props.onDragEnter).not.toHaveBeenCalled();
2068
2080
 
2069
- fireDragOver(dropzone, data);
2081
+ fireEvent.dragOver(dropzone, data);
2070
2082
  expect(props.onDragOver).not.toHaveBeenCalled();
2071
2083
 
2072
- fireDragLeave(dropzone, data);
2084
+ fireEvent.dragLeave(dropzone, data);
2073
2085
  expect(props.onDragLeave).not.toHaveBeenCalled();
2074
2086
 
2075
- fireDrop(dropzone, data);
2076
- await flushPromises(rerender, ui);
2087
+ await act(() => fireEvent.drop(dropzone, data));
2077
2088
  expect(props.onDrop).not.toHaveBeenCalled();
2078
2089
  expect(props.onDropAccepted).not.toHaveBeenCalled();
2079
2090
  expect(props.onDropRejected).not.toHaveBeenCalled();
@@ -2091,7 +2102,7 @@ describe("useDropzone() hook", () => {
2091
2102
  onDropRejected: jest.fn(),
2092
2103
  };
2093
2104
 
2094
- const ui = (
2105
+ const { container } = render(
2095
2106
  <Dropzone {...props} noDrag>
2096
2107
  {({ getRootProps, getInputProps }) => (
2097
2108
  <div {...getRootProps()}>
@@ -2100,21 +2111,18 @@ describe("useDropzone() hook", () => {
2100
2111
  )}
2101
2112
  </Dropzone>
2102
2113
  );
2103
- const { container, rerender } = render(ui);
2104
2114
  const dropzone = container.querySelector("div");
2105
2115
 
2106
- fireDragEnter(dropzone, data);
2107
- await flushPromises(rerender, ui);
2116
+ await act(() => fireEvent.dragEnter(dropzone, data));
2108
2117
  expect(props.onDragEnter).not.toHaveBeenCalled();
2109
2118
 
2110
- fireDragOver(dropzone, data);
2119
+ fireEvent.dragOver(dropzone, data);
2111
2120
  expect(props.onDragOver).not.toHaveBeenCalled();
2112
2121
 
2113
- fireDragLeave(dropzone, data);
2122
+ fireEvent.dragLeave(dropzone, data);
2114
2123
  expect(props.onDragLeave).not.toHaveBeenCalled();
2115
2124
 
2116
- fireDrop(dropzone, data);
2117
- await flushPromises(rerender, ui);
2125
+ await act(() => fireEvent.drop(dropzone, data));
2118
2126
  expect(props.onDrop).not.toHaveBeenCalled();
2119
2127
  expect(props.onDropAccepted).not.toHaveBeenCalled();
2120
2128
  expect(props.onDropRejected).not.toHaveBeenCalled();
@@ -2130,7 +2138,7 @@ describe("useDropzone() hook", () => {
2130
2138
  onDrop: jest.fn(),
2131
2139
  };
2132
2140
 
2133
- const noDragUi = (
2141
+ const { container, rerender } = render(
2134
2142
  <Dropzone {...props} noDrag>
2135
2143
  {({ getRootProps, getInputProps }) => (
2136
2144
  <div {...getRootProps()}>
@@ -2139,24 +2147,21 @@ describe("useDropzone() hook", () => {
2139
2147
  )}
2140
2148
  </Dropzone>
2141
2149
  );
2142
- const { container, rerender } = render(noDragUi);
2143
2150
  const dropzone = container.querySelector("div");
2144
2151
 
2145
- fireDragEnter(dropzone, data);
2146
- await flushPromises(rerender, noDragUi);
2152
+ await act(() => fireEvent.dragEnter(dropzone, data));
2147
2153
  expect(props.onDragEnter).not.toHaveBeenCalled();
2148
2154
 
2149
- fireDragOver(dropzone, data);
2155
+ fireEvent.dragOver(dropzone, data);
2150
2156
  expect(props.onDragOver).not.toHaveBeenCalled();
2151
2157
 
2152
- fireDragLeave(dropzone, data);
2158
+ fireEvent.dragLeave(dropzone, data);
2153
2159
  expect(props.onDragLeave).not.toHaveBeenCalled();
2154
2160
 
2155
- fireDrop(dropzone, data);
2156
- await flushPromises(rerender, noDragUi);
2161
+ await act(() => fireEvent.drop(dropzone, data));
2157
2162
  expect(props.onDrop).not.toHaveBeenCalled();
2158
2163
 
2159
- const ui = (
2164
+ rerender(
2160
2165
  <Dropzone {...props} noDrag={false}>
2161
2166
  {({ getRootProps, getInputProps }) => (
2162
2167
  <div {...getRootProps()}>
@@ -2165,25 +2170,22 @@ describe("useDropzone() hook", () => {
2165
2170
  )}
2166
2171
  </Dropzone>
2167
2172
  );
2168
- rerender(ui);
2169
2173
 
2170
- fireDragEnter(dropzone, data);
2171
- await flushPromises(rerender, ui);
2174
+ await act(() => fireEvent.dragEnter(dropzone, data));
2172
2175
  expect(props.onDragEnter).toHaveBeenCalled();
2173
2176
 
2174
- fireDragOver(dropzone, data);
2177
+ fireEvent.dragOver(dropzone, data);
2175
2178
  expect(props.onDragOver).toHaveBeenCalled();
2176
2179
 
2177
- fireDragLeave(dropzone, data);
2180
+ fireEvent.dragLeave(dropzone, data);
2178
2181
  expect(props.onDragLeave).toHaveBeenCalled();
2179
2182
 
2180
- fireDrop(dropzone, data);
2181
- await flushPromises(rerender, ui);
2183
+ await act(() => fireEvent.drop(dropzone, data));
2182
2184
  expect(props.onDrop).toHaveBeenCalled();
2183
2185
  });
2184
2186
 
2185
2187
  it("sets {isDragActive} and {isDragAccept} if some files are accepted on dragenter", async () => {
2186
- const ui = (
2188
+ const { container } = render(
2187
2189
  <Dropzone>
2188
2190
  {({
2189
2191
  getRootProps,
@@ -2201,12 +2203,9 @@ describe("useDropzone() hook", () => {
2201
2203
  )}
2202
2204
  </Dropzone>
2203
2205
  );
2204
- const { container, rerender } = render(ui);
2205
2206
  const dropzone = container.querySelector("div");
2206
2207
 
2207
- const data = createDtWithFiles(files);
2208
- fireDragEnter(dropzone, data);
2209
- await flushPromises(rerender, ui);
2208
+ await act(() => fireEvent.dragEnter(dropzone, createDtWithFiles(files)));
2210
2209
 
2211
2210
  expect(dropzone).toHaveTextContent("dragActive");
2212
2211
  expect(dropzone).toHaveTextContent("dragAccept");
@@ -2214,8 +2213,12 @@ describe("useDropzone() hook", () => {
2214
2213
  });
2215
2214
 
2216
2215
  it("sets {isDragActive} and {isDragReject} of some files are not accepted on dragenter", async () => {
2217
- const ui = (
2218
- <Dropzone accept="image/*">
2216
+ const { container } = render(
2217
+ <Dropzone
2218
+ accept={{
2219
+ "image/*": [],
2220
+ }}
2221
+ >
2219
2222
  {({
2220
2223
  getRootProps,
2221
2224
  getInputProps,
@@ -2232,12 +2235,11 @@ describe("useDropzone() hook", () => {
2232
2235
  )}
2233
2236
  </Dropzone>
2234
2237
  );
2235
- const { container, rerender } = render(ui);
2236
2238
  const dropzone = container.querySelector("div");
2237
2239
 
2238
- const data = createDtWithFiles([...files, ...images]);
2239
- fireDragEnter(dropzone, data);
2240
- await flushPromises(rerender, ui);
2240
+ await act(() =>
2241
+ fireEvent.dragEnter(dropzone, createDtWithFiles([...files, ...images]))
2242
+ );
2241
2243
 
2242
2244
  expect(dropzone).toHaveTextContent("dragActive");
2243
2245
  expect(dropzone).not.toHaveTextContent("dragAccept");
@@ -2245,7 +2247,7 @@ describe("useDropzone() hook", () => {
2245
2247
  });
2246
2248
 
2247
2249
  it("sets {isDragReject} if some files are too large", async () => {
2248
- const ui = (
2250
+ const { container } = render(
2249
2251
  <Dropzone maxSize={0}>
2250
2252
  {({ getRootProps, getInputProps, isDragAccept, isDragReject }) => (
2251
2253
  <div {...getRootProps()}>
@@ -2256,20 +2258,22 @@ describe("useDropzone() hook", () => {
2256
2258
  )}
2257
2259
  </Dropzone>
2258
2260
  );
2259
- const { container, rerender } = render(ui);
2260
2261
  const dropzone = container.querySelector("div");
2261
2262
 
2262
- const data = createDtWithFiles(files);
2263
- fireDragEnter(dropzone, data);
2264
- await flushPromises(rerender, ui);
2263
+ await act(() => fireEvent.dragEnter(dropzone, createDtWithFiles(files)));
2265
2264
 
2266
2265
  expect(dropzone).not.toHaveTextContent("dragAccept");
2267
2266
  expect(dropzone).toHaveTextContent("dragReject");
2268
2267
  });
2269
2268
 
2270
2269
  it("sets {isDragActive, isDragAccept, isDragReject} if any files are rejected and {multiple} is false on dragenter", async () => {
2271
- const ui = (
2272
- <Dropzone accept="image/*" multiple={false}>
2270
+ const { container } = render(
2271
+ <Dropzone
2272
+ accept={{
2273
+ "image/*": [],
2274
+ }}
2275
+ multiple={false}
2276
+ >
2273
2277
  {({
2274
2278
  getRootProps,
2275
2279
  getInputProps,
@@ -2286,12 +2290,9 @@ describe("useDropzone() hook", () => {
2286
2290
  )}
2287
2291
  </Dropzone>
2288
2292
  );
2289
- const { container, rerender } = render(ui);
2290
2293
  const dropzone = container.querySelector("div");
2291
2294
 
2292
- const data = createDtWithFiles(images);
2293
- fireDragEnter(dropzone, data);
2294
- await flushPromises(rerender, ui);
2295
+ await act(() => fireEvent.dragEnter(dropzone, createDtWithFiles(images)));
2295
2296
 
2296
2297
  expect(dropzone).toHaveTextContent("dragActive");
2297
2298
  expect(dropzone).not.toHaveTextContent("dragAccept");
@@ -2300,7 +2301,8 @@ describe("useDropzone() hook", () => {
2300
2301
 
2301
2302
  it("keeps {isDragActive} if dragleave is triggered for some arbitrary node", async () => {
2302
2303
  const { container: overlayContainer } = render(<div />);
2303
- const ui = (
2304
+
2305
+ const { container } = render(
2304
2306
  <Dropzone>
2305
2307
  {({
2306
2308
  getRootProps,
@@ -2318,80 +2320,25 @@ describe("useDropzone() hook", () => {
2318
2320
  )}
2319
2321
  </Dropzone>
2320
2322
  );
2321
- const { container, rerender } = render(ui);
2322
2323
  const dropzone = container.querySelector("div");
2323
2324
 
2324
- const data = createDtWithFiles(files);
2325
- fireDragEnter(dropzone, data);
2326
- await flushPromises(rerender, ui);
2325
+ await act(() => fireEvent.dragEnter(dropzone, createDtWithFiles(files)));
2327
2326
 
2328
- const event = new Event("dragleave", { bubbles: true });
2329
- Object.defineProperty(event, "target", {
2330
- value: overlayContainer.querySelector("div"),
2331
- writable: false,
2327
+ fireEvent.dragLeave(dropzone, {
2328
+ bubbles: true,
2329
+ target: overlayContainer.querySelector("div"),
2332
2330
  });
2333
- fireEvent(dropzone, event);
2334
-
2335
- expect(dropzone).toHaveTextContent("dragActive");
2336
- });
2337
-
2338
- it("updates {isDragActive} if {accept} changes mid-drag", async () => {
2339
- const ui = (
2340
- <Dropzone accept="image/*">
2341
- {({
2342
- getRootProps,
2343
- getInputProps,
2344
- isDragActive,
2345
- isDragAccept,
2346
- isDragReject,
2347
- }) => (
2348
- <div {...getRootProps()}>
2349
- <input {...getInputProps()} />
2350
- {isDragActive && "dragActive"}
2351
- {isDragAccept && "dragAccept"}
2352
- {isDragReject && "dragReject"}
2353
- </div>
2354
- )}
2355
- </Dropzone>
2356
- );
2357
- const { container, rerender } = render(ui);
2358
- const dropzone = container.querySelector("div");
2359
-
2360
- const data = createDtWithFiles(images);
2361
- fireDragEnter(dropzone, data);
2362
- await flushPromises(rerender, ui);
2363
-
2364
- expect(dropzone).toHaveTextContent("dragActive");
2365
- expect(dropzone).toHaveTextContent("dragAccept");
2366
- expect(dropzone).not.toHaveTextContent("dragReject");
2367
-
2368
- rerender(
2369
- <Dropzone accept="text/*">
2370
- {({
2371
- getRootProps,
2372
- getInputProps,
2373
- isDragActive,
2374
- isDragAccept,
2375
- isDragReject,
2376
- }) => (
2377
- <div {...getRootProps()}>
2378
- <input {...getInputProps()} />
2379
- {isDragActive && "dragActive"}
2380
- {isDragAccept && "dragAccept"}
2381
- {isDragReject && "dragReject"}
2382
- </div>
2383
- )}
2384
- </Dropzone>
2385
- );
2386
2331
 
2387
2332
  expect(dropzone).toHaveTextContent("dragActive");
2388
- expect(dropzone).not.toHaveTextContent("dragAccept");
2389
- expect(dropzone).toHaveTextContent("dragReject");
2390
2333
  });
2391
2334
 
2392
2335
  it("resets {isDragActive, isDragAccept, isDragReject} on dragleave", async () => {
2393
- const ui = (
2394
- <Dropzone accept="image/*">
2336
+ const { container } = render(
2337
+ <Dropzone
2338
+ accept={{
2339
+ "image/*": [],
2340
+ }}
2341
+ >
2395
2342
  {({
2396
2343
  getRootProps,
2397
2344
  getInputProps,
@@ -2415,29 +2362,27 @@ describe("useDropzone() hook", () => {
2415
2362
  )}
2416
2363
  </Dropzone>
2417
2364
  );
2418
- const { container, rerender } = render(ui);
2419
2365
  const dropzone = container.querySelector("div");
2420
2366
 
2421
2367
  const data = createDtWithFiles(images);
2422
2368
 
2423
- fireDragEnter(container.querySelector("#child"), data);
2424
- fireDragEnter(dropzone, data);
2425
- await flushPromises(rerender, ui);
2426
- fireDragEnter(dropzone, data);
2427
- await flushPromises(rerender, ui);
2369
+ await act(() =>
2370
+ fireEvent.dragEnter(container.querySelector("#child"), data)
2371
+ );
2372
+ await act(() => fireEvent.dragEnter(dropzone, data));
2373
+
2374
+ await act(() => fireEvent.dragEnter(dropzone, data));
2428
2375
 
2429
2376
  expect(dropzone).toHaveTextContent("dragActive");
2430
2377
  expect(dropzone).toHaveTextContent("dragAccept");
2431
2378
  expect(dropzone).not.toHaveTextContent("dragReject");
2432
2379
 
2433
- fireDragLeave(dropzone, data);
2434
- await flushPromises(rerender, ui);
2380
+ fireEvent.dragLeave(dropzone, data);
2435
2381
  expect(dropzone).toHaveTextContent("dragActive");
2436
2382
  expect(dropzone).toHaveTextContent("dragAccept");
2437
2383
  expect(dropzone).not.toHaveTextContent("dragReject");
2438
2384
 
2439
- fireDragLeave(dropzone, data);
2440
- await flushPromises(rerender, ui);
2385
+ fireEvent.dragLeave(dropzone, data);
2441
2386
  expect(dropzone).not.toHaveTextContent("dragActive");
2442
2387
  expect(dropzone).not.toHaveTextContent("dragAccept");
2443
2388
  expect(dropzone).not.toHaveTextContent("dragReject");
@@ -2452,7 +2397,7 @@ describe("useDropzone() hook", () => {
2452
2397
  test("callback is invoked when <input> change event occurs", async () => {
2453
2398
  const onDropSpy = jest.fn();
2454
2399
 
2455
- const ui = (
2400
+ const { container } = render(
2456
2401
  <Dropzone onDrop={onDropSpy}>
2457
2402
  {({ getRootProps, getInputProps }) => (
2458
2403
  <div {...getRootProps()}>
@@ -2461,13 +2406,12 @@ describe("useDropzone() hook", () => {
2461
2406
  )}
2462
2407
  </Dropzone>
2463
2408
  );
2464
- const { container, rerender } = render(ui);
2465
- const input = container.querySelector("input");
2466
2409
 
2467
- Object.defineProperty(input, "files", { value: files });
2468
-
2469
- dispatchEvt(input, "change");
2470
- await flushPromises(rerender, ui);
2410
+ await act(async () =>
2411
+ fireEvent.change(container.querySelector("input"), {
2412
+ target: { files },
2413
+ })
2414
+ );
2471
2415
 
2472
2416
  expect(onDropSpy).toHaveBeenCalledWith(files, [], expect.anything());
2473
2417
  });
@@ -2506,6 +2450,7 @@ describe("useDropzone() hook", () => {
2506
2450
  node.querySelectorAll(`[data-type="rejected"]`);
2507
2451
  const getRejectedFilesErrors = (node) =>
2508
2452
  node.querySelectorAll(`[data-type="error"]`);
2453
+
2509
2454
  const matchToFiles = (fileList, files) =>
2510
2455
  Array.from(fileList).every(
2511
2456
  (item) => !!files.find((file) => file.name === item.textContent)
@@ -2513,8 +2458,12 @@ describe("useDropzone() hook", () => {
2513
2458
  const matchToErrorCode = (errorList, code) =>
2514
2459
  Array.from(errorList).every((item) => item.textContent === code);
2515
2460
 
2516
- const ui = (
2517
- <Dropzone accept="image/*">
2461
+ const { container } = render(
2462
+ <Dropzone
2463
+ accept={{
2464
+ "image/*": [],
2465
+ }}
2466
+ >
2518
2467
  {({ getRootProps, getInputProps, acceptedFiles, fileRejections }) => (
2519
2468
  <div {...getRootProps()}>
2520
2469
  <input {...getInputProps()} />
@@ -2524,17 +2473,16 @@ describe("useDropzone() hook", () => {
2524
2473
  )}
2525
2474
  </Dropzone>
2526
2475
  );
2527
- const { container, rerender } = render(ui);
2528
2476
  const dropzone = container.querySelector("div");
2529
2477
 
2530
- fireDrop(dropzone, createDtWithFiles(images));
2531
- await flushPromises(rerender, ui);
2478
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2479
+
2532
2480
  const acceptedFileList = getAcceptedFiles(dropzone);
2533
2481
  expect(acceptedFileList).toHaveLength(images.length);
2534
2482
  expect(matchToFiles(acceptedFileList, images)).toBe(true);
2535
2483
 
2536
- fireDrop(dropzone, createDtWithFiles(files));
2537
- await flushPromises(rerender, ui);
2484
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
2485
+
2538
2486
  const rejectedFileList = getRejectedFiles(dropzone);
2539
2487
  expect(rejectedFileList).toHaveLength(files.length);
2540
2488
  expect(matchToFiles(rejectedFileList, files)).toBe(true);
@@ -2546,7 +2494,7 @@ describe("useDropzone() hook", () => {
2546
2494
  });
2547
2495
 
2548
2496
  it("resets {isDragActive, isDragAccept, isDragReject}", async () => {
2549
- const ui = (
2497
+ const { container } = render(
2550
2498
  <Dropzone>
2551
2499
  {({
2552
2500
  getRootProps,
@@ -2564,20 +2512,18 @@ describe("useDropzone() hook", () => {
2564
2512
  )}
2565
2513
  </Dropzone>
2566
2514
  );
2567
- const { container, rerender } = render(ui);
2568
2515
  const dropzone = container.querySelector("div");
2569
2516
 
2570
2517
  const data = createDtWithFiles(files);
2571
2518
 
2572
- fireDragEnter(dropzone, data);
2573
- await flushPromises(rerender, ui);
2519
+ await act(() => fireEvent.dragEnter(dropzone, data));
2574
2520
 
2575
2521
  expect(dropzone).toHaveTextContent("dragActive");
2576
2522
  expect(dropzone).toHaveTextContent("dragAccept");
2577
2523
  expect(dropzone).not.toHaveTextContent("dragReject");
2578
2524
 
2579
- fireDrop(dropzone, data);
2580
- await flushPromises(rerender, ui);
2525
+ await act(() => fireEvent.drop(dropzone, data));
2526
+
2581
2527
  expect(dropzone).not.toHaveTextContent("dragActive");
2582
2528
  expect(dropzone).not.toHaveTextContent("dragAccept");
2583
2529
  expect(dropzone).not.toHaveTextContent("dragReject");
@@ -2585,8 +2531,15 @@ describe("useDropzone() hook", () => {
2585
2531
 
2586
2532
  it("rejects all files if {multiple} is false and {accept} criteria is not met", async () => {
2587
2533
  const onDropSpy = jest.fn();
2588
- const ui = (
2589
- <Dropzone accept="image/*" onDrop={onDropSpy} multiple={false}>
2534
+
2535
+ const { container } = render(
2536
+ <Dropzone
2537
+ accept={{
2538
+ "image/*": [],
2539
+ }}
2540
+ onDrop={onDropSpy}
2541
+ multiple={false}
2542
+ >
2590
2543
  {({ getRootProps, getInputProps }) => (
2591
2544
  <div {...getRootProps()}>
2592
2545
  <input {...getInputProps()} />
@@ -2594,11 +2547,10 @@ describe("useDropzone() hook", () => {
2594
2547
  )}
2595
2548
  </Dropzone>
2596
2549
  );
2597
- const { container, rerender } = render(ui);
2598
2550
  const dropzone = container.querySelector("div");
2599
2551
 
2600
- fireDrop(dropzone, createDtWithFiles(files));
2601
- await flushPromises(rerender, ui);
2552
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
2553
+
2602
2554
  expect(onDropSpy).toHaveBeenCalledWith(
2603
2555
  [],
2604
2556
  [
@@ -2618,8 +2570,15 @@ describe("useDropzone() hook", () => {
2618
2570
 
2619
2571
  it("rejects all files if {multiple} is false and {accept} criteria is met", async () => {
2620
2572
  const onDropSpy = jest.fn();
2621
- const ui = (
2622
- <Dropzone accept="image/*" onDrop={onDropSpy} multiple={false}>
2573
+
2574
+ const { container } = render(
2575
+ <Dropzone
2576
+ accept={{
2577
+ "image/*": [],
2578
+ }}
2579
+ onDrop={onDropSpy}
2580
+ multiple={false}
2581
+ >
2623
2582
  {({ getRootProps, getInputProps }) => (
2624
2583
  <div {...getRootProps()}>
2625
2584
  <input {...getInputProps()} />
@@ -2627,11 +2586,10 @@ describe("useDropzone() hook", () => {
2627
2586
  )}
2628
2587
  </Dropzone>
2629
2588
  );
2630
- const { container, rerender } = render(ui);
2631
2589
  const dropzone = container.querySelector("div");
2632
2590
 
2633
- fireDrop(dropzone, createDtWithFiles(images));
2634
- await flushPromises(rerender, ui);
2591
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2592
+
2635
2593
  expect(onDropSpy).toHaveBeenCalledWith(
2636
2594
  [],
2637
2595
  [
@@ -2661,9 +2619,12 @@ describe("useDropzone() hook", () => {
2661
2619
  it("rejects all files if {multiple} is true and maxFiles is less than files and {accept} criteria is met", async () => {
2662
2620
  const onDropSpy = jest.fn();
2663
2621
  const onDropRejectedSpy = jest.fn();
2664
- const ui = (
2622
+
2623
+ const { container } = render(
2665
2624
  <Dropzone
2666
- accept="image/*"
2625
+ accept={{
2626
+ "image/*": [],
2627
+ }}
2667
2628
  onDrop={onDropSpy}
2668
2629
  onDropRejected={onDropRejectedSpy}
2669
2630
  multiple={true}
@@ -2678,13 +2639,14 @@ describe("useDropzone() hook", () => {
2678
2639
  )}
2679
2640
  </Dropzone>
2680
2641
  );
2681
- const { container, rerender } = render(ui);
2682
2642
  const dropzone = container.querySelector("div");
2683
- fireDrop(dropzone, createDtWithFiles(images));
2684
- await flushPromises(rerender, ui);
2643
+
2644
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2645
+
2685
2646
  expect(onDropRejectedSpy).toHaveBeenCalled();
2686
- fireDragEnter(dropzone, createDtWithFiles(images));
2687
- await flushPromises(rerender, ui);
2647
+
2648
+ await act(() => fireEvent.dragEnter(dropzone, createDtWithFiles(images)));
2649
+
2688
2650
  expect(dropzone).toHaveTextContent("dragReject");
2689
2651
  expect(dropzone).not.toHaveTextContent("dragAccept");
2690
2652
  expect(onDropSpy).toHaveBeenCalledWith(
@@ -2718,7 +2680,9 @@ describe("useDropzone() hook", () => {
2718
2680
  const onDropRejectedSpy = jest.fn();
2719
2681
  const ui = (maxFiles) => (
2720
2682
  <Dropzone
2721
- accept="image/*"
2683
+ accept={{
2684
+ "image/*": [],
2685
+ }}
2722
2686
  onDrop={onDropSpy}
2723
2687
  multiple={true}
2724
2688
  maxFiles={maxFiles}
@@ -2734,15 +2698,17 @@ describe("useDropzone() hook", () => {
2734
2698
  const { container, rerender } = render(ui(3));
2735
2699
  const dropzone = container.querySelector("div");
2736
2700
 
2737
- fireDrop(dropzone, createDtWithFiles(images));
2738
- await flushPromises(rerender, ui(3));
2701
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2702
+ rerender(ui(3));
2703
+
2739
2704
  expect(onDropRejectedSpy).not.toHaveBeenCalled();
2740
2705
  expect(onDropSpy).toHaveBeenCalledWith(images, [], expect.anything());
2741
2706
 
2742
2707
  rerender(ui(1));
2743
2708
 
2744
- fireDrop(dropzone, createDtWithFiles(images));
2745
- await flushPromises(rerender, ui(1));
2709
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2710
+ rerender(ui(1));
2711
+
2746
2712
  expect(onDropRejectedSpy).toHaveBeenCalledWith(
2747
2713
  expect.arrayContaining(
2748
2714
  images.map((image) =>
@@ -2756,9 +2722,12 @@ describe("useDropzone() hook", () => {
2756
2722
  it("accepts multiple files if {multiple} is true and {accept} criteria is met", async () => {
2757
2723
  const onDropSpy = jest.fn();
2758
2724
  const onDropRejectedSpy = jest.fn();
2759
- const ui = (
2725
+
2726
+ const { container } = render(
2760
2727
  <Dropzone
2761
- accept="image/*"
2728
+ accept={{
2729
+ "image/*": [],
2730
+ }}
2762
2731
  onDrop={onDropSpy}
2763
2732
  multiple={true}
2764
2733
  maxFiles={3}
@@ -2771,19 +2740,29 @@ describe("useDropzone() hook", () => {
2771
2740
  )}
2772
2741
  </Dropzone>
2773
2742
  );
2774
- const { container, rerender } = render(ui);
2775
- const dropzone = container.querySelector("div");
2776
2743
 
2777
- fireDrop(dropzone, createDtWithFiles(images));
2778
- await flushPromises(rerender, ui);
2744
+ await act(() =>
2745
+ fireEvent.drop(
2746
+ container.querySelector("div"),
2747
+ createDtWithFiles(images)
2748
+ )
2749
+ );
2750
+
2779
2751
  expect(onDropRejectedSpy).not.toHaveBeenCalled();
2780
2752
  expect(onDropSpy).toHaveBeenCalledWith(images, [], expect.anything());
2781
2753
  });
2782
2754
 
2783
2755
  it("accepts a single files if {multiple} is false and {accept} criteria is met", async () => {
2784
2756
  const onDropSpy = jest.fn();
2785
- const ui = (
2786
- <Dropzone accept="image/*" onDrop={onDropSpy} multiple={false}>
2757
+
2758
+ const { container } = render(
2759
+ <Dropzone
2760
+ accept={{
2761
+ "image/*": [],
2762
+ }}
2763
+ onDrop={onDropSpy}
2764
+ multiple={false}
2765
+ >
2787
2766
  {({ getRootProps, getInputProps }) => (
2788
2767
  <div {...getRootProps()}>
2789
2768
  <input {...getInputProps()} />
@@ -2791,18 +2770,22 @@ describe("useDropzone() hook", () => {
2791
2770
  )}
2792
2771
  </Dropzone>
2793
2772
  );
2794
- const { container, rerender } = render(ui);
2795
- const dropzone = container.querySelector("div");
2796
2773
 
2797
2774
  const [image] = images;
2798
- fireDrop(dropzone, createDtWithFiles([image]));
2799
- await flushPromises(rerender, ui);
2775
+ await act(() =>
2776
+ fireEvent.drop(
2777
+ container.querySelector("div"),
2778
+ createDtWithFiles([image])
2779
+ )
2780
+ );
2781
+
2800
2782
  expect(onDropSpy).toHaveBeenCalledWith([image], [], expect.anything());
2801
2783
  });
2802
2784
 
2803
2785
  it("accepts all files if {multiple} is true", async () => {
2804
2786
  const onDropSpy = jest.fn();
2805
- const ui = (
2787
+
2788
+ const { container } = render(
2806
2789
  <Dropzone onDrop={onDropSpy}>
2807
2790
  {({ getRootProps, getInputProps }) => (
2808
2791
  <div {...getRootProps()}>
@@ -2811,11 +2794,11 @@ describe("useDropzone() hook", () => {
2811
2794
  )}
2812
2795
  </Dropzone>
2813
2796
  );
2814
- const { container, rerender } = render(ui);
2815
- const dropzone = container.querySelector("div");
2816
2797
 
2817
- fireDrop(dropzone, createDtWithFiles(files));
2818
- await flushPromises(rerender, ui);
2798
+ await act(() =>
2799
+ fireEvent.drop(container.querySelector("div"), createDtWithFiles(files))
2800
+ );
2801
+
2819
2802
  expect(onDropSpy).toHaveBeenCalledWith(files, [], expect.anything());
2820
2803
  });
2821
2804
 
@@ -2824,38 +2807,40 @@ describe("useDropzone() hook", () => {
2824
2807
  const activeRef = createRef();
2825
2808
  const active = <span ref={activeRef}>I am active</span>;
2826
2809
 
2827
- const ui = (
2810
+ const { container } = render(
2828
2811
  <Dropzone onDrop={onDropSpy}>
2829
- {({ getRootProps, getInputProps, isFileDialogActive, open }) => (
2812
+ {({ getRootProps, getInputProps, isFileDialogActive }) => (
2830
2813
  <div {...getRootProps()}>
2831
2814
  <input {...getInputProps()} />
2832
2815
  {isFileDialogActive && active}
2833
- <button type="button" onClick={open}>
2834
- Open
2835
- </button>
2836
2816
  </div>
2837
2817
  )}
2838
2818
  </Dropzone>
2839
2819
  );
2840
- const { container, rerender } = render(ui);
2820
+
2841
2821
  const dropzone = container.querySelector("div");
2842
- const btn = container.querySelector("button");
2843
2822
 
2844
- btn.click();
2823
+ fireEvent.click(dropzone);
2845
2824
 
2846
- const ref = activeRef.current;
2847
- expect(dropzone).toContainElement(ref);
2825
+ expect(activeRef.current).not.toBeNull();
2826
+ expect(dropzone).toContainElement(activeRef.current);
2848
2827
 
2849
- fireDrop(dropzone, createDtWithFiles(files));
2850
- await flushPromises(rerender, ui);
2828
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
2851
2829
 
2852
- expect(dropzone).not.toContainElement(ref);
2830
+ expect(activeRef.current).toBeNull();
2831
+ expect(dropzone).not.toContainElement(activeRef.current);
2853
2832
  });
2854
2833
 
2855
2834
  it("gets invoked with both accepted/rejected files", async () => {
2856
2835
  const onDropSpy = jest.fn();
2857
- const ui = (
2858
- <Dropzone accept="image/*" onDrop={onDropSpy}>
2836
+
2837
+ const { container } = render(
2838
+ <Dropzone
2839
+ accept={{
2840
+ "image/*": [],
2841
+ }}
2842
+ onDrop={onDropSpy}
2843
+ >
2859
2844
  {({ getRootProps, getInputProps }) => (
2860
2845
  <div {...getRootProps()}>
2861
2846
  <input {...getInputProps()} />
@@ -2863,11 +2848,10 @@ describe("useDropzone() hook", () => {
2863
2848
  )}
2864
2849
  </Dropzone>
2865
2850
  );
2866
- const { container, rerender } = render(ui);
2867
2851
  const dropzone = container.querySelector("div");
2868
2852
 
2869
- fireDrop(dropzone, createDtWithFiles(files));
2870
- await flushPromises(rerender, ui);
2853
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
2854
+
2871
2855
  expect(onDropSpy).toHaveBeenCalledWith(
2872
2856
  [],
2873
2857
  [
@@ -2885,13 +2869,15 @@ describe("useDropzone() hook", () => {
2885
2869
  );
2886
2870
  onDropSpy.mockClear();
2887
2871
 
2888
- fireDrop(dropzone, createDtWithFiles(images));
2889
- await flushPromises(rerender, ui);
2872
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2873
+
2890
2874
  expect(onDropSpy).toHaveBeenCalledWith(images, [], expect.anything());
2891
2875
  onDropSpy.mockClear();
2892
2876
 
2893
- fireDrop(dropzone, createDtWithFiles([...files, ...images]));
2894
- await flushPromises(rerender, ui);
2877
+ await act(() =>
2878
+ fireEvent.drop(dropzone, createDtWithFiles([...files, ...images]))
2879
+ );
2880
+
2895
2881
  expect(onDropSpy).toHaveBeenCalledWith(
2896
2882
  images,
2897
2883
  [
@@ -2911,8 +2897,14 @@ describe("useDropzone() hook", () => {
2911
2897
 
2912
2898
  test("onDropAccepted callback is invoked if some files are accepted", async () => {
2913
2899
  const onDropAcceptedSpy = jest.fn();
2914
- const ui = (
2915
- <Dropzone accept="image/*" onDropAccepted={onDropAcceptedSpy}>
2900
+
2901
+ const { container } = render(
2902
+ <Dropzone
2903
+ accept={{
2904
+ "image/*": [],
2905
+ }}
2906
+ onDropAccepted={onDropAcceptedSpy}
2907
+ >
2916
2908
  {({ getRootProps, getInputProps }) => (
2917
2909
  <div {...getRootProps()}>
2918
2910
  <input {...getInputProps()} />
@@ -2920,28 +2912,34 @@ describe("useDropzone() hook", () => {
2920
2912
  )}
2921
2913
  </Dropzone>
2922
2914
  );
2923
- const { container, rerender } = render(ui);
2924
2915
  const dropzone = container.querySelector("div");
2925
2916
 
2926
- fireDrop(dropzone, createDtWithFiles(files));
2927
- await flushPromises(rerender, ui);
2917
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
2928
2918
  expect(onDropAcceptedSpy).not.toHaveBeenCalled();
2929
2919
  onDropAcceptedSpy.mockClear();
2930
2920
 
2931
- fireDrop(dropzone, createDtWithFiles(images));
2932
- await flushPromises(rerender, ui);
2921
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2922
+
2933
2923
  expect(onDropAcceptedSpy).toHaveBeenCalledWith(images, expect.anything());
2934
2924
  onDropAcceptedSpy.mockClear();
2935
2925
 
2936
- fireDrop(dropzone, createDtWithFiles([...files, ...images]));
2937
- await flushPromises(rerender, ui);
2926
+ await act(() =>
2927
+ fireEvent.drop(dropzone, createDtWithFiles([...files, ...images]))
2928
+ );
2929
+
2938
2930
  expect(onDropAcceptedSpy).toHaveBeenCalledWith(images, expect.anything());
2939
2931
  });
2940
2932
 
2941
2933
  test("onDropRejected callback is invoked if some files are rejected", async () => {
2942
2934
  const onDropRejectedSpy = jest.fn();
2943
- const ui = (
2944
- <Dropzone accept="image/*" onDropRejected={onDropRejectedSpy}>
2935
+
2936
+ const { container } = render(
2937
+ <Dropzone
2938
+ accept={{
2939
+ "image/*": [],
2940
+ }}
2941
+ onDropRejected={onDropRejectedSpy}
2942
+ >
2945
2943
  {({ getRootProps, getInputProps }) => (
2946
2944
  <div {...getRootProps()}>
2947
2945
  <input {...getInputProps()} />
@@ -2949,11 +2947,10 @@ describe("useDropzone() hook", () => {
2949
2947
  )}
2950
2948
  </Dropzone>
2951
2949
  );
2952
- const { container, rerender } = render(ui);
2953
2950
  const dropzone = container.querySelector("div");
2954
2951
 
2955
- fireDrop(dropzone, createDtWithFiles(files));
2956
- await flushPromises(rerender, ui);
2952
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
2953
+
2957
2954
  expect(onDropRejectedSpy).toHaveBeenCalledWith(
2958
2955
  [
2959
2956
  {
@@ -2970,13 +2967,15 @@ describe("useDropzone() hook", () => {
2970
2967
  );
2971
2968
  onDropRejectedSpy.mockClear();
2972
2969
 
2973
- fireDrop(dropzone, createDtWithFiles(images));
2974
- await flushPromises(rerender, ui);
2970
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(images)));
2971
+
2975
2972
  expect(onDropRejectedSpy).not.toHaveBeenCalled();
2976
2973
  onDropRejectedSpy.mockClear();
2977
2974
 
2978
- fireDrop(dropzone, createDtWithFiles([...files, ...images]));
2979
- await flushPromises(rerender, ui);
2975
+ await act(() =>
2976
+ fireEvent.drop(dropzone, createDtWithFiles([...files, ...images]))
2977
+ );
2978
+
2980
2979
  expect(onDropRejectedSpy).toHaveBeenCalledWith(
2981
2980
  [
2982
2981
  {
@@ -2995,8 +2994,14 @@ describe("useDropzone() hook", () => {
2995
2994
 
2996
2995
  it("accepts a dropped image when Firefox provides a bogus file type", async () => {
2997
2996
  const onDropSpy = jest.fn();
2998
- const ui = (
2999
- <Dropzone accept="image/*" onDrop={onDropSpy}>
2997
+
2998
+ const { container } = render(
2999
+ <Dropzone
3000
+ accept={{
3001
+ "image/*": [],
3002
+ }}
3003
+ onDrop={onDropSpy}
3004
+ >
3000
3005
  {({ getRootProps, getInputProps }) => (
3001
3006
  <div {...getRootProps()}>
3002
3007
  <input {...getInputProps()} />
@@ -3004,19 +3009,22 @@ describe("useDropzone() hook", () => {
3004
3009
  )}
3005
3010
  </Dropzone>
3006
3011
  );
3007
- const { container, rerender } = render(ui);
3008
- const dropzone = container.querySelector("div");
3009
3012
 
3010
3013
  const images = [createFile("bogus.gif", 1234, "application/x-moz-file")];
3011
- fireDrop(dropzone, createDtWithFiles(images));
3012
- await flushPromises(rerender, ui);
3014
+ await act(() =>
3015
+ fireEvent.drop(
3016
+ container.querySelector("div"),
3017
+ createDtWithFiles(images)
3018
+ )
3019
+ );
3013
3020
 
3014
3021
  expect(onDropSpy).toHaveBeenCalledWith(images, [], expect.anything());
3015
3022
  });
3016
3023
 
3017
3024
  it("filters files according to {maxSize}", async () => {
3018
3025
  const onDropSpy = jest.fn();
3019
- const ui = (
3026
+
3027
+ const { container } = render(
3020
3028
  <Dropzone onDrop={onDropSpy} maxSize={1111}>
3021
3029
  {({ getRootProps, getInputProps }) => (
3022
3030
  <div {...getRootProps()}>
@@ -3025,11 +3033,13 @@ describe("useDropzone() hook", () => {
3025
3033
  )}
3026
3034
  </Dropzone>
3027
3035
  );
3028
- const { container, rerender } = render(ui);
3029
- const dropzone = container.querySelector("div");
3030
3036
 
3031
- fireDrop(dropzone, createDtWithFiles(images));
3032
- await flushPromises(rerender, ui);
3037
+ await act(() =>
3038
+ fireEvent.drop(
3039
+ container.querySelector("div"),
3040
+ createDtWithFiles(images)
3041
+ )
3042
+ );
3033
3043
 
3034
3044
  expect(onDropSpy).toHaveBeenCalledWith(
3035
3045
  [],
@@ -3059,7 +3069,8 @@ describe("useDropzone() hook", () => {
3059
3069
 
3060
3070
  it("filters files according to {minSize}", async () => {
3061
3071
  const onDropSpy = jest.fn();
3062
- const ui = (
3072
+
3073
+ const { container } = render(
3063
3074
  <Dropzone onDrop={onDropSpy} minSize={1112}>
3064
3075
  {({ getRootProps, getInputProps }) => (
3065
3076
  <div {...getRootProps()}>
@@ -3068,11 +3079,9 @@ describe("useDropzone() hook", () => {
3068
3079
  )}
3069
3080
  </Dropzone>
3070
3081
  );
3071
- const { container, rerender } = render(ui);
3072
3082
  const dropzone = container.querySelector("div");
3073
3083
 
3074
- fireDrop(dropzone, createDtWithFiles(files));
3075
- await flushPromises(rerender, ui);
3084
+ await act(() => fireEvent.drop(dropzone, createDtWithFiles(files)));
3076
3085
 
3077
3086
  expect(onDropSpy).toHaveBeenCalledWith(
3078
3087
  [],
@@ -3114,8 +3123,8 @@ describe("useDropzone() hook", () => {
3114
3123
  </Dropzone>
3115
3124
  );
3116
3125
 
3117
- dispatchEvt(document.body, "focus");
3118
- drainTimers();
3126
+ focusWindow();
3127
+ drainPendingTimers();
3119
3128
 
3120
3129
  expect(onFileDialogCancelSpy).not.toHaveBeenCalled();
3121
3130
  });
@@ -3127,91 +3136,78 @@ describe("useDropzone() hook", () => {
3127
3136
 
3128
3137
  const { container } = render(
3129
3138
  <Dropzone onFileDialogCancel={onFileDialogCancelSpy}>
3130
- {({ getRootProps, getInputProps, isFileDialogActive, open }) => (
3139
+ {({ getRootProps, getInputProps, isFileDialogActive }) => (
3131
3140
  <div {...getRootProps()}>
3132
3141
  <input {...getInputProps()} />
3133
3142
  {isFileDialogActive && active}
3134
- <button type="button" onClick={open}>
3135
- Open
3136
- </button>
3137
3143
  </div>
3138
3144
  )}
3139
3145
  </Dropzone>
3140
3146
  );
3141
3147
 
3142
3148
  const dropzone = container.querySelector("div");
3143
- const btn = container.querySelector("button");
3144
3149
 
3145
- btn.click();
3146
- drainTimers();
3147
- const ref = activeRef.current;
3148
- expect(ref).not.toBeNull();
3149
- expect(dropzone).toContainElement(ref);
3150
+ fireEvent.click(dropzone);
3151
+
3152
+ expect(activeRef.current).not.toBeNull();
3153
+ expect(dropzone).toContainElement(activeRef.current);
3150
3154
 
3151
- dispatchEvt(document.body, "focus");
3152
- drainTimers();
3155
+ focusWindow();
3156
+ drainPendingTimers();
3153
3157
 
3154
3158
  expect(onFileDialogCancelSpy).toHaveBeenCalled();
3155
- expect(dropzone).not.toContainElement(ref);
3159
+ expect(dropzone).not.toContainElement(activeRef.current);
3156
3160
  });
3157
3161
 
3158
3162
  it("is not invoked if <input> is not rendered", () => {
3159
3163
  const onFileDialogCancelSpy = jest.fn();
3160
3164
  const { container, rerender } = render(
3161
3165
  <Dropzone onFileDialogCancel={onFileDialogCancelSpy}>
3162
- {({ getRootProps, getInputProps, open }) => (
3166
+ {({ getRootProps, getInputProps }) => (
3163
3167
  <div {...getRootProps()}>
3164
3168
  <input {...getInputProps()} />
3165
- <button type="button" onClick={open}>
3166
- Open
3167
- </button>
3168
3169
  </div>
3169
3170
  )}
3170
3171
  </Dropzone>
3171
3172
  );
3172
3173
 
3173
- const btn = container.querySelector("button");
3174
- btn.click();
3175
- drainTimers();
3174
+ fireEvent.click(container.querySelector("div"));
3176
3175
 
3176
+ // Remove the input, then on window focus nothing should happen because we check if the input ref is set
3177
3177
  rerender(
3178
3178
  <Dropzone onFileDialogCancel={onFileDialogCancelSpy}>
3179
3179
  {({ getRootProps }) => <div {...getRootProps()} />}
3180
3180
  </Dropzone>
3181
3181
  );
3182
- drainTimers();
3183
3182
 
3184
- dispatchEvt(document.body, "focus");
3185
- drainTimers();
3183
+ focusWindow();
3184
+ drainPendingTimers();
3186
3185
 
3187
3186
  expect(onFileDialogCancelSpy).not.toHaveBeenCalled();
3188
3187
  });
3189
3188
 
3190
- it("is not invoked if files were selected", () => {
3189
+ it("is not invoked if files were selected", async () => {
3191
3190
  const onFileDialogCancelSpy = jest.fn();
3192
3191
 
3193
3192
  const { container } = render(
3194
3193
  <Dropzone onFileDialogCancel={onFileDialogCancelSpy}>
3195
- {({ getRootProps, getInputProps, open }) => (
3194
+ {({ getRootProps, getInputProps }) => (
3196
3195
  <div {...getRootProps()}>
3197
3196
  <input {...getInputProps()} />
3198
- <button type="button" onClick={open}>
3199
- Open
3200
- </button>
3201
3197
  </div>
3202
3198
  )}
3203
3199
  </Dropzone>
3204
3200
  );
3205
3201
 
3206
- const input = container.querySelector("input");
3207
- Object.defineProperty(input, "files", { value: files });
3208
-
3209
- const btn = container.querySelector("button");
3210
- btn.click();
3211
- drainTimers();
3202
+ await act(async () =>
3203
+ fireEvent.change(container.querySelector("input"), {
3204
+ target: { files },
3205
+ })
3206
+ );
3207
+ fireEvent.click(container.querySelector("div"));
3212
3208
 
3213
- dispatchEvt(document.body, "focus");
3214
- drainTimers();
3209
+ focusWindow();
3210
+ drainPendingTimers();
3215
3211
 
3216
3212
  expect(onFileDialogCancelSpy).not.toHaveBeenCalled();
3217
3213
  });
@@ -3219,24 +3215,19 @@ describe("useDropzone() hook", () => {
3219
3215
  it("does not throw if callback is not provided", () => {
3220
3216
  const { container } = render(
3221
3217
  <Dropzone onFileDialogCancel={null}>
3222
- {({ getRootProps, getInputProps, open }) => (
3218
+ {({ getRootProps, getInputProps }) => (
3223
3219
  <div {...getRootProps()}>
3224
3220
  <input {...getInputProps()} />
3225
- <button type="button" onClick={open}>
3226
- Open
3227
- </button>
3228
3221
  </div>
3229
3222
  )}
3230
3223
  </Dropzone>
3231
3224
  );
3232
3225
 
3233
- const btn = container.querySelector("button");
3234
- btn.click();
3235
- drainTimers();
3226
+ fireEvent.click(container.querySelector("div"));
3236
3227
 
3237
3228
  const fn = () => {
3238
- dispatchEvt(document.body, "focus");
3239
- drainTimers();
3229
+ focusWindow();
3230
+ drainPendingTimers();
3240
3231
  };
3241
3232
  expect(fn).not.toThrow();
3242
3233
  });
@@ -3255,8 +3246,7 @@ describe("useDropzone() hook", () => {
3255
3246
  </Dropzone>
3256
3247
  );
3257
3248
 
3258
- const dropzone = container.querySelector("div");
3259
- fireEvent.click(dropzone);
3249
+ fireEvent.click(container.querySelector("div"));
3260
3250
 
3261
3251
  expect(onFileDialogOpenSpy).toHaveBeenCalled();
3262
3252
  });
@@ -3276,8 +3266,7 @@ describe("useDropzone() hook", () => {
3276
3266
  </Dropzone>
3277
3267
  );
3278
3268
 
3279
- const btn = container.querySelector("button");
3280
- btn.click();
3269
+ fireEvent.click(container.querySelector("button"));
3281
3270
 
3282
3271
  expect(onFileDialogOpenSpy).toHaveBeenCalled();
3283
3272
  });
@@ -3299,9 +3288,7 @@ describe("useDropzone() hook", () => {
3299
3288
  </Dropzone>
3300
3289
  );
3301
3290
 
3302
- const btn = container.querySelector("button");
3303
-
3304
- btn.click();
3291
+ fireEvent.click(container.querySelector("button"));
3305
3292
 
3306
3293
  expect(onClickSpy).toHaveBeenCalled();
3307
3294
  });
@@ -3323,14 +3310,12 @@ describe("useDropzone() hook", () => {
3323
3310
  </Dropzone>
3324
3311
  );
3325
3312
 
3326
- const dropzone = container.querySelector("div");
3327
- const btn = container.querySelector("button");
3313
+ fireEvent.click(container.querySelector("button"));
3328
3314
 
3329
- btn.click();
3330
-
3331
- const ref = activeRef.current;
3332
- expect(ref).not.toBeNull();
3333
- expect(dropzone).toContainElement(ref);
3315
+ expect(activeRef.current).not.toBeNull();
3316
+ expect(container.querySelector("div")).toContainElement(
3317
+ activeRef.current
3318
+ );
3334
3319
  });
3335
3320
 
3336
3321
  it("does nothing if {disabled} is true", () => {
@@ -3348,9 +3333,7 @@ describe("useDropzone() hook", () => {
3348
3333
  </Dropzone>
3349
3334
  );
3350
3335
 
3351
- const btn = container.querySelector("button");
3352
-
3353
- btn.click();
3336
+ fireEvent.click(container.querySelector("button"));
3354
3337
 
3355
3338
  expect(onClickSpy).not.toHaveBeenCalled();
3356
3339
  });
@@ -3368,9 +3351,7 @@ describe("useDropzone() hook", () => {
3368
3351
  </Dropzone>
3369
3352
  );
3370
3353
 
3371
- const btn = container.querySelector("button");
3372
-
3373
- const fn = () => btn.click();
3354
+ const fn = () => fireEvent.click(container.querySelector("button"));
3374
3355
  expect(fn).not.toThrow();
3375
3356
  });
3376
3357
  });
@@ -3386,21 +3367,18 @@ describe("useDropzone() hook", () => {
3386
3367
 
3387
3368
  const onDropSpy = jest.fn();
3388
3369
 
3389
- const ui = (
3370
+ const { container } = render(
3390
3371
  <Dropzone validator={validator} onDrop={onDropSpy} multiple={true}>
3391
- {({ getRootProps, getInputProps }) => (
3392
- <div {...getRootProps()}>
3393
- <input {...getInputProps()} />
3394
- </div>
3395
- )}
3372
+ {({ getRootProps }) => <div {...getRootProps()} />}
3396
3373
  </Dropzone>
3397
3374
  );
3398
3375
 
3399
- const { container, rerender } = render(ui);
3400
- const dropzone = container.querySelector("div");
3401
-
3402
- fireDrop(dropzone, createDtWithFiles(images));
3403
- await flushPromises(rerender, ui);
3376
+ await act(() =>
3377
+ fireEvent.drop(
3378
+ container.querySelector("div"),
3379
+ createDtWithFiles(images)
3380
+ )
3381
+ );
3404
3382
 
3405
3383
  expect(onDropSpy).toHaveBeenCalledWith(
3406
3384
  [images[0]],
@@ -3427,9 +3405,11 @@ describe("useDropzone() hook", () => {
3427
3405
  {({ getRootProps }) => <div id="root" {...getRootProps()} />}
3428
3406
  </Dropzone>
3429
3407
  );
3430
- const root = container.querySelector("#root");
3431
3408
 
3432
- expect(root).toHaveAttribute("role", "button");
3409
+ expect(container.querySelector("#root")).toHaveAttribute(
3410
+ "role",
3411
+ "presentation"
3412
+ );
3433
3413
  });
3434
3414
 
3435
3415
  test("users can override the default role attribute on the root", () => {
@@ -3440,21 +3420,37 @@ describe("useDropzone() hook", () => {
3440
3420
  )}
3441
3421
  </Dropzone>
3442
3422
  );
3443
- const root = container.querySelector("#root");
3444
3423
 
3445
- expect(root).toHaveAttribute("role", "generic");
3424
+ expect(container.querySelector("#root")).toHaveAttribute(
3425
+ "role",
3426
+ "generic"
3427
+ );
3446
3428
  });
3447
3429
  });
3448
3430
  });
3449
3431
 
3450
- async function flushPromises(rerender, ui) {
3451
- await act(() => waitFor(() => rerender(ui)));
3432
+ /**
3433
+ * drainPendingTimers just runs pending timers wrapped in act() to avoid
3434
+ * getting warnings from react about side effects that happen async.
3435
+ *
3436
+ * This can be used whenever a setTimeout(), setInterval() or async operation is used
3437
+ * which triggers a state update.
3438
+ */
3439
+ function drainPendingTimers() {
3440
+ return act(() => jest.runOnlyPendingTimers());
3452
3441
  }
3453
3442
 
3454
- function drainTimers() {
3455
- act(() => jest.runAllTimers());
3443
+ /**
3444
+ * focusWindow triggers focus on the window
3445
+ */
3446
+ function focusWindow() {
3447
+ return fireEvent.focus(document.body, { bubbles: true });
3456
3448
  }
3457
3449
 
3450
+ /**
3451
+ * createDtWithFiles creates a mock data transfer object that can be used for drop events
3452
+ * @param {File[]} files
3453
+ */
3458
3454
  function createDtWithFiles(files = []) {
3459
3455
  return {
3460
3456
  dataTransfer: {
@@ -3470,36 +3466,20 @@ function createDtWithFiles(files = []) {
3470
3466
  };
3471
3467
  }
3472
3468
 
3473
- function fireDragEnter(node, data) {
3474
- dispatchEvt(node, "dragenter", data);
3475
- }
3476
-
3477
- function fireDragOver(node, data) {
3478
- dispatchEvt(node, "dragover", data);
3479
- }
3480
-
3481
- function fireDragLeave(node, data) {
3482
- dispatchEvt(node, "dragleave", data);
3483
- }
3484
-
3485
- function fireDrop(node, data) {
3486
- dispatchEvt(node, "drop", data);
3487
- }
3488
-
3489
- // Using fireEvent.* doesn't work for our use case,
3490
- // we cannot set the event props
3491
- function dispatchEvt(node, type, data) {
3492
- const event = new Event(type, { bubbles: true });
3493
- if (data) {
3494
- Object.assign(event, data);
3495
- }
3496
- fireEvent(node, event);
3497
- }
3498
-
3469
+ /**
3470
+ * createFileSystemFileHandle creates a mock [FileSystemFileHandle](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle)
3471
+ * @param {File} file
3472
+ */
3499
3473
  function createFileSystemFileHandle(file) {
3500
3474
  return { getFile: () => Promise.resolve(file) };
3501
3475
  }
3502
3476
 
3477
+ /**
3478
+ * createFile creates a mock File object
3479
+ * @param {string} name
3480
+ * @param {number} size
3481
+ * @param {string} type
3482
+ */
3503
3483
  function createFile(name, size, type) {
3504
3484
  const file = new File([], name, { type });
3505
3485
  Object.defineProperty(file, "size", {
@@ -3510,6 +3490,9 @@ function createFile(name, size, type) {
3510
3490
  return file;
3511
3491
  }
3512
3492
 
3493
+ /**
3494
+ * createThenable creates a Promise that can be controlled from outside its inner scope
3495
+ */
3513
3496
  function createThenable() {
3514
3497
  let done, cancel;
3515
3498