Add regression tests for all events (#19485)
Dan Abramov committed
Jul 29, 2020 at 21:13 UTC
291db05a756dd88d0f687b3083e85a22abbf5214
1 file changed
+1505
-104
packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js
+1505
-104
@@ -14,6 +14,7 @@ describe('ReactDOMEventListener', () => {
14
let container;
15
16
beforeEach(() => {
17
+ window.TextEvent = function() {};
18
jest.resetModules();
19
React = require('react');
20
jest.isolateModules(() => {
@@ -45,6 +46,85 @@ describe('ReactDOMEventListener', () => {
46
}
47
48
describe('bubbling events', () => {
49
+ it('onAnimationEnd', () => {
50
+ testNativeBubblingEvent({
51
+ type: 'div',
52
+ reactEvent: 'onAnimationEnd',
53
+ nativeEvent: 'animationend',
54
+ dispatch(node) {
55
+ node.dispatchEvent(
56
+ new Event('animationend', {
57
+ bubbles: true,
58
+ cancelable: true,
59
+ }),
60
+ );
61
+ },
62
+ });
63
+ });
64
+
65
+ it('onAnimationIteration', () => {
66
+ testNativeBubblingEvent({
67
+ type: 'div',
68
+ reactEvent: 'onAnimationIteration',
69
+ nativeEvent: 'animationiteration',
70
+ dispatch(node) {
71
+ node.dispatchEvent(
72
+ new Event('animationiteration', {
73
+ bubbles: true,
74
+ cancelable: true,
75
+ }),
76
+ );
77
+ },
78
+ });
79
+ });
80
+
81
+ it('onAnimationStart', () => {
82
+ testNativeBubblingEvent({
83
+ type: 'div',
84
+ reactEvent: 'onAnimationStart',
85
+ nativeEvent: 'animationstart',
86
+ dispatch(node) {
87
+ node.dispatchEvent(
88
+ new Event('animationstart', {
89
+ bubbles: true,
90
+ cancelable: true,
91
+ }),
92
+ );
93
+ },
94
+ });
95
+ });
96
+
97
+ it('onAuxClick', () => {
98
+ testNativeBubblingEvent({
99
+ type: 'div',
100
+ reactEvent: 'onAuxClick',
101
+ nativeEvent: 'auxclick',
102
+ dispatch(node) {
103
+ node.dispatchEvent(
104
+ new KeyboardEvent('auxclick', {
105
+ bubbles: true,
106
+ cancelable: true,
107
+ }),
108
+ );
109
+ },
110
+ });
111
+ });
112
+
113
+ it('onBlur', () => {
114
+ testNativeBubblingEvent({
115
+ type: 'input',
116
+ reactEvent: 'onBlur',
117
+ nativeEvent: 'focusout',
118
+ dispatch(node) {
119
+ const e = new Event('focusout', {
120
+ bubbles: true,
121
+ cancelable: true,
122
+ });
123
+ node.dispatchEvent(e);
124
+ },
125
+ });
126
+ });
127
+
128
// This test will fail in legacy mode (only used in WWW)
129
// because we emulate the React 16 behavior where
130
// the click handler is attached to the document.
@@ -55,84 +135,823 @@ describe('ReactDOMEventListener', () => {
135
reactEvent: 'onClick',
136
nativeEvent: 'click',
137
dispatch(node) {
58
- node.click();
138
+ node.click();
139
+ },
140
+ });
141
+ });
142
+
143
+ it('onContextMenu', () => {
144
+ testNativeBubblingEvent({
145
+ type: 'div',
146
+ reactEvent: 'onContextMenu',
147
+ nativeEvent: 'contextmenu',
148
+ dispatch(node) {
149
+ node.dispatchEvent(
150
+ new MouseEvent('contextmenu', {
151
+ bubbles: true,
152
+ cancelable: true,
153
+ }),
154
+ );
155
+ },
156
+ });
157
+ });
158
+
159
+ it('onCopy', () => {
160
+ testNativeBubblingEvent({
161
+ type: 'div',
162
+ reactEvent: 'onCopy',
163
+ nativeEvent: 'copy',
164
+ dispatch(node) {
165
+ node.dispatchEvent(
166
+ new Event('copy', {
167
+ bubbles: true,
168
+ cancelable: true,
169
+ }),
170
+ );
171
+ },
172
+ });
173
+ });
174
+
175
+ it('onCut', () => {
176
+ testNativeBubblingEvent({
177
+ type: 'div',
178
+ reactEvent: 'onCut',
179
+ nativeEvent: 'cut',
180
+ dispatch(node) {
181
+ node.dispatchEvent(
182
+ new Event('cut', {
183
+ bubbles: true,
184
+ cancelable: true,
185
+ }),
186
+ );
187
+ },
188
+ });
189
+ });
190
+
191
+ it('onDoubleClick', () => {
192
+ testNativeBubblingEvent({
193
+ type: 'div',
194
+ reactEvent: 'onDoubleClick',
195
+ nativeEvent: 'dblclick',
196
+ dispatch(node) {
197
+ node.dispatchEvent(
198
+ new KeyboardEvent('dblclick', {
199
+ bubbles: true,
200
+ cancelable: true,
201
+ }),
202
+ );
203
+ },
204
+ });
205
+ });
206
+
207
+ it('onDrag', () => {
208
+ testNativeBubblingEvent({
209
+ type: 'div',
210
+ reactEvent: 'onDrag',
211
+ nativeEvent: 'drag',
212
+ dispatch(node) {
213
+ node.dispatchEvent(
214
+ new MouseEvent('drag', {
215
+ bubbles: true,
216
+ cancelable: true,
217
+ }),
218
+ );
219
+ },
220
+ });
221
+ });
222
+
223
+ it('onDragEnd', () => {
224
+ testNativeBubblingEvent({
225
+ type: 'div',
226
+ reactEvent: 'onDragEnd',
227
+ nativeEvent: 'dragend',
228
+ dispatch(node) {
229
+ node.dispatchEvent(
230
+ new MouseEvent('dragend', {
231
+ bubbles: true,
232
+ cancelable: true,
233
+ }),
234
+ );
235
+ },
236
+ });
237
+ });
238
+
239
+ it('onDragEnter', () => {
240
+ testNativeBubblingEvent({
241
+ type: 'div',
242
+ reactEvent: 'onDragEnter',
243
+ nativeEvent: 'dragenter',
244
+ dispatch(node) {
245
+ node.dispatchEvent(
246
+ new MouseEvent('dragenter', {
247
+ bubbles: true,
248
+ cancelable: true,
249
+ }),
250
+ );
251
+ },
252
+ });
253
+ });
254
+
255
+ it('onDragExit', () => {
256
+ testNativeBubblingEvent({
257
+ type: 'div',
258
+ reactEvent: 'onDragExit',
259
+ nativeEvent: 'dragexit',
260
+ dispatch(node) {
261
+ node.dispatchEvent(
262
+ new MouseEvent('dragexit', {
263
+ bubbles: true,
264
+ cancelable: true,
265
+ }),
266
+ );
267
+ },
268
+ });
269
+ });
270
+
271
+ it('onDragLeave', () => {
272
+ testNativeBubblingEvent({
273
+ type: 'div',
274
+ reactEvent: 'onDragLeave',
275
+ nativeEvent: 'dragleave',
276
+ dispatch(node) {
277
+ node.dispatchEvent(
278
+ new MouseEvent('dragleave', {
279
+ bubbles: true,
280
+ cancelable: true,
281
+ }),
282
+ );
283
+ },
284
+ });
285
+ });
286
+
287
+ it('onDragOver', () => {
288
+ testNativeBubblingEvent({
289
+ type: 'div',
290
+ reactEvent: 'onDragOver',
291
+ nativeEvent: 'dragover',
292
+ dispatch(node) {
293
+ node.dispatchEvent(
294
+ new MouseEvent('dragover', {
295
+ bubbles: true,
296
+ cancelable: true,
297
+ }),
298
+ );
299
+ },
300
+ });
301
+ });
302
+
303
+ it('onDragStart', () => {
304
+ testNativeBubblingEvent({
305
+ type: 'div',
306
+ reactEvent: 'onDragStart',
307
+ nativeEvent: 'dragstart',
308
+ dispatch(node) {
309
+ node.dispatchEvent(
310
+ new MouseEvent('dragstart', {
311
+ bubbles: true,
312
+ cancelable: true,
313
+ }),
314
+ );
315
+ },
316
+ });
317
+ });
318
+
319
+ it('onDrop', () => {
320
+ testNativeBubblingEvent({
321
+ type: 'div',
322
+ reactEvent: 'onDrop',
323
+ nativeEvent: 'drop',
324
+ dispatch(node) {
325
+ node.dispatchEvent(
326
+ new MouseEvent('drop', {
327
+ bubbles: true,
328
+ cancelable: true,
329
+ }),
330
+ );
331
+ },
332
+ });
333
+ });
334
+
335
+ it('onFocus', () => {
336
+ testNativeBubblingEvent({
337
+ type: 'input',
338
+ reactEvent: 'onFocus',
339
+ nativeEvent: 'focusin',
340
+ dispatch(node) {
341
+ const e = new Event('focusin', {
342
+ bubbles: true,
343
+ cancelable: true,
344
+ });
345
+ node.dispatchEvent(e);
346
+ },
347
+ });
348
+ });
349
+
350
+ // TODO: this has regressed. Fix me.
351
+ it.skip('onGotPointerCapture', () => {
352
+ testNativeBubblingEvent({
353
+ type: 'div',
354
+ reactEvent: 'onGotPointerCapture',
355
+ nativeEvent: 'gotpointercapture',
356
+ dispatch(node) {
357
+ node.dispatchEvent(
358
+ new Event('gotpointercapture', {
359
+ bubbles: true,
360
+ cancelable: true,
361
+ }),
362
+ );
363
+ },
364
+ });
365
+ });
366
+
367
+ it('onKeyDown', () => {
368
+ testNativeBubblingEvent({
369
+ type: 'input',
370
+ reactEvent: 'onKeyDown',
371
+ nativeEvent: 'keydown',
372
+ dispatch(node) {
373
+ node.dispatchEvent(
374
+ new KeyboardEvent('keydown', {
375
+ bubbles: true,
376
+ cancelable: true,
377
+ }),
378
+ );
379
+ },
380
+ });
381
+ });
382
+
383
+ it('onKeyPress', () => {
384
+ testNativeBubblingEvent({
385
+ type: 'input',
386
+ reactEvent: 'onKeyPress',
387
+ nativeEvent: 'keypress',
388
+ dispatch(node) {
389
+ node.dispatchEvent(
390
+ new KeyboardEvent('keypress', {
391
+ keyCode: 13,
392
+ bubbles: true,
393
+ cancelable: true,
394
+ }),
395
+ );
396
+ },
397
+ });
398
+ });
399
+
400
+ it('onKeyUp', () => {
401
+ testNativeBubblingEvent({
402
+ type: 'input',
403
+ reactEvent: 'onKeyUp',
404
+ nativeEvent: 'keyup',
405
+ dispatch(node) {
406
+ node.dispatchEvent(
407
+ new KeyboardEvent('keyup', {
408
+ bubbles: true,
409
+ cancelable: true,
410
+ }),
411
+ );
412
+ },
413
+ });
414
+ });
415
+
416
+ // TODO: this has regressed. Fix me.
417
+ it.skip('onLostPointerCapture', () => {
418
+ testNativeBubblingEvent({
419
+ type: 'div',
420
+ reactEvent: 'onLostPointerCapture',
421
+ nativeEvent: 'lostpointercapture',
422
+ dispatch(node) {
423
+ node.dispatchEvent(
424
+ new Event('lostpointercapture', {
425
+ bubbles: true,
426
+ cancelable: true,
427
+ }),
428
+ );
429
+ },
430
+ });
431
+ });
432
+
433
+ it('onMouseDown', () => {
434
+ testNativeBubblingEvent({
435
+ type: 'div',
436
+ reactEvent: 'onMouseDown',
437
+ nativeEvent: 'mousedown',
438
+ dispatch(node) {
439
+ node.dispatchEvent(
440
+ new MouseEvent('mousedown', {
441
+ bubbles: true,
442
+ cancelable: true,
443
+ }),
444
+ );
445
+ },
446
+ });
447
+ });
448
+
449
+ it('onMouseOut', () => {
450
+ testNativeBubblingEvent({
451
+ type: 'div',
452
+ reactEvent: 'onMouseOut',
453
+ nativeEvent: 'mouseout',
454
+ dispatch(node) {
455
+ node.dispatchEvent(
456
+ new MouseEvent('mouseout', {
457
+ bubbles: true,
458
+ cancelable: true,
459
+ }),
460
+ );
461
+ },
462
+ });
463
+ });
464
+
465
+ it('onMouseOver', () => {
466
+ testNativeBubblingEvent({
467
+ type: 'div',
468
+ reactEvent: 'onMouseOver',
469
+ nativeEvent: 'mouseover',
470
+ dispatch(node) {
471
+ node.dispatchEvent(
472
+ new MouseEvent('mouseover', {
473
+ bubbles: true,
474
+ cancelable: true,
475
+ }),
476
+ );
477
+ },
478
+ });
479
+ });
480
+
481
+ it('onMouseUp', () => {
482
+ testNativeBubblingEvent({
483
+ type: 'div',
484
+ reactEvent: 'onMouseUp',
485
+ nativeEvent: 'mouseup',
486
+ dispatch(node) {
487
+ node.dispatchEvent(
488
+ new MouseEvent('mouseup', {
489
+ bubbles: true,
490
+ cancelable: true,
491
+ }),
492
+ );
493
+ },
494
+ });
495
+ });
496
+
497
+ it('onPaste', () => {
498
+ testNativeBubblingEvent({
499
+ type: 'div',
500
+ reactEvent: 'onPaste',
501
+ nativeEvent: 'paste',
502
+ dispatch(node) {
503
+ node.dispatchEvent(
504
+ new Event('paste', {
505
+ bubbles: true,
506
+ cancelable: true,
507
+ }),
508
+ );
509
+ },
510
+ });
511
+ });
512
+
513
+ it('onPointerCancel', () => {
514
+ testNativeBubblingEvent({
515
+ type: 'div',
516
+ reactEvent: 'onPointerCancel',
517
+ nativeEvent: 'pointercancel',
518
+ dispatch(node) {
519
+ node.dispatchEvent(
520
+ new Event('pointercancel', {
521
+ bubbles: true,
522
+ cancelable: true,
523
+ }),
524
+ );
525
+ },
526
+ });
527
+ });
528
+
529
+ it('onPointerDown', () => {
530
+ testNativeBubblingEvent({
531
+ type: 'div',
532
+ reactEvent: 'onPointerDown',
533
+ nativeEvent: 'pointerdown',
534
+ dispatch(node) {
535
+ node.dispatchEvent(
536
+ new Event('pointerdown', {
537
+ bubbles: true,
538
+ cancelable: true,
539
+ }),
540
+ );
541
+ },
542
+ });
543
+ });
544
+
545
+ it('onPointerMove', () => {
546
+ testNativeBubblingEvent({
547
+ type: 'div',
548
+ reactEvent: 'onPointerMove',
549
+ nativeEvent: 'pointermove',
550
+ dispatch(node) {
551
+ node.dispatchEvent(
552
+ new Event('pointermove', {
553
+ bubbles: true,
554
+ cancelable: true,
555
+ }),
556
+ );
557
+ },
558
+ });
559
+ });
560
+
561
+ it('onPointerOut', () => {
562
+ testNativeBubblingEvent({
563
+ type: 'div',
564
+ reactEvent: 'onPointerOut',
565
+ nativeEvent: 'pointerout',
566
+ dispatch(node) {
567
+ node.dispatchEvent(
568
+ new Event('pointerout', {
569
+ bubbles: true,
570
+ cancelable: true,
571
+ }),
572
+ );
573
+ },
574
+ });
575
+ });
576
+
577
+ it('onPointerOver', () => {
578
+ testNativeBubblingEvent({
579
+ type: 'div',
580
+ reactEvent: 'onPointerOver',
581
+ nativeEvent: 'pointerover',
582
+ dispatch(node) {
583
+ node.dispatchEvent(
584
+ new Event('pointerover', {
585
+ bubbles: true,
586
+ cancelable: true,
587
+ }),
588
+ );
589
+ },
590
+ });
591
+ });
592
+
593
+ it('onPointerUp', () => {
594
+ testNativeBubblingEvent({
595
+ type: 'div',
596
+ reactEvent: 'onPointerUp',
597
+ nativeEvent: 'pointerup',
598
+ dispatch(node) {
599
+ node.dispatchEvent(
600
+ new Event('pointerup', {
601
+ bubbles: true,
602
+ cancelable: true,
603
+ }),
604
+ );
605
+ },
606
+ });
607
+ });
608
+
609
+ it('onReset', () => {
610
+ testNativeBubblingEvent({
611
+ type: 'form',
612
+ reactEvent: 'onReset',
613
+ nativeEvent: 'reset',
614
+ dispatch(node) {
615
+ const e = new Event('reset', {
616
+ bubbles: true,
617
+ cancelable: true,
618
+ });
619
+ node.dispatchEvent(e);
620
+ },
621
+ });
622
+ });
623
+
624
+ it('onSubmit', () => {
625
+ testNativeBubblingEvent({
626
+ type: 'form',
627
+ reactEvent: 'onSubmit',
628
+ nativeEvent: 'submit',
629
+ dispatch(node) {
630
+ const e = new Event('submit', {
631
+ bubbles: true,
632
+ cancelable: true,
633
+ });
634
+ node.dispatchEvent(e);
635
+ },
636
+ });
637
+ });
638
+
639
+ it('onTouchCancel', () => {
640
+ testNativeBubblingEvent({
641
+ type: 'div',
642
+ reactEvent: 'onTouchCancel',
643
+ nativeEvent: 'touchcancel',
644
+ dispatch(node) {
645
+ node.dispatchEvent(
646
+ new Event('touchcancel', {
647
+ bubbles: true,
648
+ cancelable: true,
649
+ }),
650
+ );
651
+ },
652
+ });
653
+ });
654
+
655
+ it('onTouchEnd', () => {
656
+ testNativeBubblingEvent({
657
+ type: 'div',
658
+ reactEvent: 'onTouchEnd',
659
+ nativeEvent: 'touchend',
660
+ dispatch(node) {
661
+ node.dispatchEvent(
662
+ new Event('touchend', {
663
+ bubbles: true,
664
+ cancelable: true,
665
+ }),
666
+ );
667
+ },
668
+ });
669
+ });
670
+
671
+ it('onTouchMove', () => {
672
+ testNativeBubblingEvent({
673
+ type: 'div',
674
+ reactEvent: 'onTouchMove',
675
+ nativeEvent: 'touchmove',
676
+ dispatch(node) {
677
+ node.dispatchEvent(
678
+ new Event('touchmove', {
679
+ bubbles: true,
680
+ cancelable: true,
681
+ }),
682
+ );
683
+ },
684
+ });
685
+ });
686
+
687
+ it('onTouchStart', () => {
688
+ testNativeBubblingEvent({
689
+ type: 'div',
690
+ reactEvent: 'onTouchStart',
691
+ nativeEvent: 'touchstart',
692
+ dispatch(node) {
693
+ node.dispatchEvent(
694
+ new Event('touchstart', {
695
+ bubbles: true,
696
+ cancelable: true,
697
+ }),
698
+ );
699
+ },
700
+ });
701
+ });
702
+
703
+ it('onTransitionEnd', () => {
704
+ testNativeBubblingEvent({
705
+ type: 'div',
706
+ reactEvent: 'onTransitionEnd',
707
+ nativeEvent: 'transitionend',
708
+ dispatch(node) {
709
+ node.dispatchEvent(
710
+ new Event('transitionend', {
711
+ bubbles: true,
712
+ cancelable: true,
713
+ }),
714
+ );
715
+ },
716
+ });
717
+ });
718
+
719
+ it('onWheel', () => {
720
+ testNativeBubblingEvent({
721
+ type: 'div',
722
+ reactEvent: 'onWheel',
723
+ nativeEvent: 'wheel',
724
+ dispatch(node) {
725
+ node.dispatchEvent(
726
+ new Event('wheel', {
727
+ bubbles: true,
728
+ cancelable: true,
729
+ }),
730
+ );
731
+ },
732
+ });
733
+ });
734
+ });
735
+
736
+ describe('non-bubbling events that bubble in React', () => {
737
+ it('onAbort', () => {
738
+ testEmulatedBubblingEvent({
739
+ type: 'video',
740
+ reactEvent: 'onAbort',
741
+ nativeEvent: 'abort',
742
+ dispatch(node) {
743
+ const e = new Event('abort', {
744
+ bubbles: false,
745
+ cancelable: true,
746
+ });
747
+ node.dispatchEvent(e);
748
+ },
749
+ });
750
+ });
751
+
752
+ it('onCancel', () => {
753
+ testEmulatedBubblingEvent({
754
+ type: 'dialog',
755
+ reactEvent: 'onCancel',
756
+ nativeEvent: 'cancel',
757
+ dispatch(node) {
758
+ const e = new Event('cancel', {
759
+ bubbles: false,
760
+ cancelable: true,
761
+ });
762
+ node.dispatchEvent(e);
763
+ },
764
+ });
765
+ });
766
+
767
+ it('onCanPlay', () => {
768
+ testEmulatedBubblingEvent({
769
+ type: 'video',
770
+ reactEvent: 'onCanPlay',
771
+ nativeEvent: 'canplay',
772
+ dispatch(node) {
773
+ const e = new Event('canplay', {
774
+ bubbles: false,
775
+ cancelable: true,
776
+ });
777
+ node.dispatchEvent(e);
778
+ },
779
+ });
780
+ });
781
+
782
+ it('onCanPlayThrough', () => {
783
+ testEmulatedBubblingEvent({
784
+ type: 'video',
785
+ reactEvent: 'onCanPlayThrough',
786
+ nativeEvent: 'canplaythrough',
787
+ dispatch(node) {
788
+ const e = new Event('canplaythrough', {
789
+ bubbles: false,
790
+ cancelable: true,
791
+ });
792
+ node.dispatchEvent(e);
793
+ },
794
+ });
795
+ });
796
+
797
+ it('onClose', () => {
798
+ testEmulatedBubblingEvent({
799
+ type: 'dialog',
800
+ reactEvent: 'onClose',
801
+ nativeEvent: 'close',
802
+ dispatch(node) {
803
+ const e = new Event('close', {
804
+ bubbles: false,
805
+ cancelable: true,
806
+ });
807
+ node.dispatchEvent(e);
808
+ },
809
+ });
810
+ });
811
+
812
+ it('onDurationChange', () => {
813
+ testEmulatedBubblingEvent({
814
+ type: 'video',
815
+ reactEvent: 'onDurationChange',
816
+ nativeEvent: 'durationchange',
817
+ dispatch(node) {
818
+ const e = new Event('durationchange', {
819
+ bubbles: false,
820
+ cancelable: true,
821
+ });
822
+ node.dispatchEvent(e);
823
+ },
824
+ });
825
+ });
826
+
827
+ it('onEmptied', () => {
828
+ testEmulatedBubblingEvent({
829
+ type: 'video',
830
+ reactEvent: 'onEmptied',
831
+ nativeEvent: 'emptied',
832
+ dispatch(node) {
833
+ const e = new Event('emptied', {
834
+ bubbles: false,
835
+ cancelable: true,
836
+ });
837
+ node.dispatchEvent(e);
838
+ },
839
+ });
840
+ });
841
+
842
+ it('onEncrypted', () => {
843
+ testEmulatedBubblingEvent({
844
+ type: 'video',
845
+ reactEvent: 'onEncrypted',
846
+ nativeEvent: 'encrypted',
847
+ dispatch(node) {
848
+ const e = new Event('encrypted', {
849
+ bubbles: false,
850
+ cancelable: true,
851
+ });
852
+ node.dispatchEvent(e);
853
+ },
854
+ });
855
+ });
856
+
857
+ it('onEnded', () => {
858
+ testEmulatedBubblingEvent({
859
+ type: 'video',
860
+ reactEvent: 'onEnded',
861
+ nativeEvent: 'ended',
862
+ dispatch(node) {
863
+ const e = new Event('ended', {
864
+ bubbles: false,
865
+ cancelable: true,
866
+ });
867
+ node.dispatchEvent(e);
868
},
869
});
870
});
871
63
- it('onKeyPress', () => {
64
- testNativeBubblingEvent({
872
+ it('onError', () => {
873
+ testEmulatedBubblingEvent({
874
+ type: 'img',
875
+ reactEvent: 'onError',
876
+ nativeEvent: 'error',
877
+ dispatch(node) {
878
+ const e = new Event('error', {
879
+ bubbles: false,
880
+ cancelable: true,
881
+ });
882
+ node.dispatchEvent(e);
883
+ },
884
+ });
885
+ });
886
+
887
+ it('onInvalid', () => {
888
+ testEmulatedBubblingEvent({
889
type: 'input',
66
- reactEvent: 'onKeyPress',
67
- nativeEvent: 'keypress',
890
+ reactEvent: 'onInvalid',
891
+ nativeEvent: 'invalid',
892
dispatch(node) {
69
- node.dispatchEvent(
70
- new KeyboardEvent('keypress', {
71
- keyCode: 13,
72
- bubbles: true,
73
- cancelable: true,
74
- }),
75
- );
893
+ const e = new Event('invalid', {
894
+ bubbles: false,
895
+ cancelable: true,
896
+ });
897
+ node.dispatchEvent(e);
898
},
899
});
900
});
901
80
- it('onMouseDown', () => {
81
- testNativeBubblingEvent({
82
- type: 'button',
83
- reactEvent: 'onMouseDown',
84
- nativeEvent: 'mousedown',
902
+ it('onLoad', () => {
903
+ testEmulatedBubblingEvent({
904
+ type: 'img',
905
+ reactEvent: 'onLoad',
906
+ nativeEvent: 'load',
907
dispatch(node) {
86
- node.dispatchEvent(
87
- new MouseEvent('mousedown', {
88
- bubbles: true,
89
- cancelable: true,
90
- }),
91
- );
908
+ const e = new Event('load', {
909
+ bubbles: false,
910
+ cancelable: true,
911
+ });
912
+ node.dispatchEvent(e);
913
},
914
});
915
});
916
96
- it('onTouchStart', () => {
97
- testNativeBubblingEvent({
98
- type: 'div',
99
- reactEvent: 'onTouchStart',
100
- nativeEvent: 'touchstart',
917
+ it('onLoadedData', () => {
918
+ testEmulatedBubblingEvent({
919
+ type: 'video',
920
+ reactEvent: 'onLoadedData',
921
+ nativeEvent: 'loadeddata',
922
dispatch(node) {
102
- node.dispatchEvent(
103
- new Event('touchstart', {
104
- bubbles: true,
105
- cancelable: true,
106
- }),
107
- );
923
+ const e = new Event('loadeddata', {
924
+ bubbles: false,
925
+ cancelable: true,
926
+ });
927
+ node.dispatchEvent(e);
928
},
929
});
930
});
931
112
- it('onWheel', () => {
113
- testNativeBubblingEvent({
114
- type: 'div',
115
- reactEvent: 'onWheel',
116
- nativeEvent: 'wheel',
932
+ it('onLoadedMetadata', () => {
933
+ testEmulatedBubblingEvent({
934
+ type: 'video',
935
+ reactEvent: 'onLoadedMetadata',
936
+ nativeEvent: 'loadedmetadata',
937
dispatch(node) {
118
- node.dispatchEvent(
119
- new Event('wheel', {
120
- bubbles: true,
121
- cancelable: true,
122
- }),
123
- );
938
+ const e = new Event('loadedmetadata', {
939
+ bubbles: false,
940
+ cancelable: true,
941
+ });
942
+ node.dispatchEvent(e);
943
},
944
});
945
});
946
128
- it('onSubmit', () => {
129
- testNativeBubblingEvent({
130
- type: 'form',
131
- reactEvent: 'onSubmit',
132
- nativeEvent: 'submit',
947
+ it('onLoadStart', () => {
948
+ testEmulatedBubblingEvent({
949
+ type: 'video',
950
+ reactEvent: 'onLoadStart',
951
+ nativeEvent: 'loadstart',
952
dispatch(node) {
134
- const e = new Event('submit', {
135
- bubbles: true,
953
+ const e = new Event('loadstart', {
954
+ bubbles: false,
955
cancelable: true,
956
});
957
node.dispatchEvent(e);
@@ -140,14 +959,14 @@ describe('ReactDOMEventListener', () => {
959
});
960
});
961
143
- it('onReset', () => {
144
- testNativeBubblingEvent({
145
- type: 'form',
146
- reactEvent: 'onReset',
147
- nativeEvent: 'reset',
962
+ it('onPause', () => {
963
+ testEmulatedBubblingEvent({
964
+ type: 'video',
965
+ reactEvent: 'onPause',
966
+ nativeEvent: 'pause',
967
dispatch(node) {
149
- const e = new Event('reset', {
150
- bubbles: true,
968
+ const e = new Event('pause', {
969
+ bubbles: false,
970
cancelable: true,
971
});
972
node.dispatchEvent(e);
@@ -155,14 +974,14 @@ describe('ReactDOMEventListener', () => {
974
});
975
});
976
158
- it('onFocus', () => {
159
- testNativeBubblingEvent({
160
- type: 'input',
161
- reactEvent: 'onFocus',
162
- nativeEvent: 'focusin',
977
+ it('onPlay', () => {
978
+ testEmulatedBubblingEvent({
979
+ type: 'video',
980
+ reactEvent: 'onPlay',
981
+ nativeEvent: 'play',
982
dispatch(node) {
164
- const e = new Event('focusin', {
165
- bubbles: true,
983
+ const e = new Event('play', {
984
+ bubbles: false,
985
cancelable: true,
986
});
987
node.dispatchEvent(e);
@@ -170,30 +989,28 @@ describe('ReactDOMEventListener', () => {
989
});
990
});
991
173
- it('onBlur', () => {
174
- testNativeBubblingEvent({
175
- type: 'input',
176
- reactEvent: 'onBlur',
177
- nativeEvent: 'focusout',
992
+ it('onPlaying', () => {
993
+ testEmulatedBubblingEvent({
994
+ type: 'video',
995
+ reactEvent: 'onPlaying',
996
+ nativeEvent: 'playing',
997
dispatch(node) {
179
- const e = new Event('focusout', {
180
- bubbles: true,
998
+ const e = new Event('playing', {
999
+ bubbles: false,
1000
cancelable: true,
1001
});
1002
node.dispatchEvent(e);
1003
},
1004
});
1005
});
187
- });
1006
189
- describe('non-bubbling events that bubble in React', () => {
190
- it('onInvalid', () => {
1007
+ it('onProgress', () => {
1008
testEmulatedBubblingEvent({
192
- type: 'input',
193
- reactEvent: 'onInvalid',
194
- nativeEvent: 'invalid',
1009
+ type: 'video',
1010
+ reactEvent: 'onProgress',
1011
+ nativeEvent: 'progress',
1012
dispatch(node) {
196
- const e = new Event('invalid', {
1013
+ const e = new Event('progress', {
1014
bubbles: false,
1015
cancelable: true,
1016
});
@@ -202,13 +1019,13 @@ describe('ReactDOMEventListener', () => {
1019
});
1020
});
1021
205
- it('onLoad', () => {
1022
+ it('onRateChange', () => {
1023
testEmulatedBubblingEvent({
207
- type: 'img',
208
- reactEvent: 'onLoad',
209
- nativeEvent: 'load',
1024
+ type: 'video',
1025
+ reactEvent: 'onRateChange',
1026
+ nativeEvent: 'ratechange',
1027
dispatch(node) {
211
- const e = new Event('load', {
1028
+ const e = new Event('ratechange', {
1029
bubbles: false,
1030
cancelable: true,
1031
});
@@ -217,13 +1034,13 @@ describe('ReactDOMEventListener', () => {
1034
});
1035
});
1036
220
- it('onError', () => {
1037
+ it('onSeeked', () => {
1038
testEmulatedBubblingEvent({
222
- type: 'img',
223
- reactEvent: 'onError',
224
- nativeEvent: 'error',
1039
+ type: 'video',
1040
+ reactEvent: 'onSeeked',
1041
+ nativeEvent: 'seeked',
1042
dispatch(node) {
226
- const e = new Event('error', {
1043
+ const e = new Event('seeked', {
1044
bubbles: false,
1045
cancelable: true,
1046
});
@@ -232,13 +1049,13 @@ describe('ReactDOMEventListener', () => {
1049
});
1050
});
1051
235
- it('onClose', () => {
1052
+ it('onSeeking', () => {
1053
testEmulatedBubblingEvent({
237
- type: 'dialog',
238
- reactEvent: 'onClose',
239
- nativeEvent: 'close',
1054
+ type: 'video',
1055
+ reactEvent: 'onSeeking',
1056
+ nativeEvent: 'seeking',
1057
dispatch(node) {
241
- const e = new Event('close', {
1058
+ const e = new Event('seeking', {
1059
bubbles: false,
1060
cancelable: true,
1061
});
@@ -247,13 +1064,13 @@ describe('ReactDOMEventListener', () => {
1064
});
1065
});
1066
250
- it('onCancel', () => {
1067
+ it('onStalled', () => {
1068
testEmulatedBubblingEvent({
252
- type: 'dialog',
253
- reactEvent: 'onCancel',
254
- nativeEvent: 'cancel',
1069
+ type: 'video',
1070
+ reactEvent: 'onStalled',
1071
+ nativeEvent: 'stalled',
1072
dispatch(node) {
256
- const e = new Event('cancel', {
1073
+ const e = new Event('stalled', {
1074
bubbles: false,
1075
cancelable: true,
1076
});
@@ -262,13 +1079,73 @@ describe('ReactDOMEventListener', () => {
1079
});
1080
});
1081
265
- it('onPlay', () => {
1082
+ it('onSuspend', () => {
1083
testEmulatedBubblingEvent({
1084
type: 'video',
268
- reactEvent: 'onPlay',
269
- nativeEvent: 'play',
1085
+ reactEvent: 'onSuspend',
1086
+ nativeEvent: 'suspend',
1087
dispatch(node) {
271
- const e = new Event('play', {
1088
+ const e = new Event('suspend', {
1089
+ bubbles: false,
1090
+ cancelable: true,
1091
+ });
1092
+ node.dispatchEvent(e);
1093
+ },
1094
+ });
1095
+ });
1096
+
1097
+ it('onTimeUpdate', () => {
1098
+ testEmulatedBubblingEvent({
1099
+ type: 'video',
1100
+ reactEvent: 'onTimeUpdate',
1101
+ nativeEvent: 'timeupdate',
1102
+ dispatch(node) {
1103
+ const e = new Event('timeupdate', {
1104
+ bubbles: false,
1105
+ cancelable: true,
1106
+ });
1107
+ node.dispatchEvent(e);
1108
+ },
1109
+ });
1110
+ });
1111
+
1112
+ it('onToggle', () => {
1113
+ testEmulatedBubblingEvent({
1114
+ type: 'details',
1115
+ reactEvent: 'onToggle',
1116
+ nativeEvent: 'toggle',
1117
+ dispatch(node) {
1118
+ const e = new Event('toggle', {
1119
+ bubbles: false,
1120
+ cancelable: true,
1121
+ });
1122
+ node.dispatchEvent(e);
1123
+ },
1124
+ });
1125
+ });
1126
+
1127
+ it('onVolumeChange', () => {
1128
+ testEmulatedBubblingEvent({
1129
+ type: 'video',
1130
+ reactEvent: 'onVolumeChange',
1131
+ nativeEvent: 'volumechange',
1132
+ dispatch(node) {
1133
+ const e = new Event('volumechange', {
1134
+ bubbles: false,
1135
+ cancelable: true,
1136
+ });
1137
+ node.dispatchEvent(e);
1138
+ },
1139
+ });
1140
+ });
1141
+
1142
+ it('onWaiting', () => {
1143
+ testEmulatedBubblingEvent({
1144
+ type: 'video',
1145
+ reactEvent: 'onWaiting',
1146
+ nativeEvent: 'waiting',
1147
+ dispatch(node) {
1148
+ const e = new Event('waiting', {
1149
bubbles: false,
1150
cancelable: true,
1151
});
@@ -301,6 +1178,530 @@ describe('ReactDOMEventListener', () => {
1178
});
1179
});
1180
1181
+ // The tests for these events are currently very limited
1182
+ // because they are fully synthetic, and so they don't
1183
+ // work very well across different roots. For now, we'll
1184
+ // just document the current state in these tests.
1185
+ describe('enter/leave events', () => {
1186
+ it('onMouseEnter and onMouseLeave', () => {
1187
+ const log = [];
1188
+ const targetRef = React.createRef();
1189
+ render(
1190
+ <Fixture
1191
+ type="div"
1192
+ targetRef={targetRef}
1193
+ targetProps={{
1194
+ onMouseEnter: e => {
1195
+ log.push('---- inner enter');
1196
+ },
1197
+ onMouseLeave: e => {
1198
+ log.push('---- inner leave');
1199
+ },
1200
+ }}
1201
+ parentProps={{
1202
+ onMouseEnter: e => {
1203
+ log.push('--- inner parent enter');
1204
+ },
1205
+ onMouseLeave: e => {
1206
+ log.push('--- inner parent leave');
1207
+ },
1208
+ }}
1209
+ outerProps={{
1210
+ onMouseEnter: e => {
1211
+ log.push('-- outer enter');
1212
+ },
1213
+ onMouseLeave: e => {
1214
+ log.push('-- outer leave');
1215
+ },
1216
+ }}
1217
+ outerParentProps={{
1218
+ onMouseEnter: e => {
1219
+ log.push('- outer parent enter');
1220
+ },
1221
+ onMouseLeave: e => {
1222
+ log.push('- outer parent leave');
1223
+ },
1224
+ }}
1225
+ />,
1226
+ );
1227
+ expect(log.length).toBe(0);
1228
+ targetRef.current.dispatchEvent(
1229
+ new MouseEvent('mouseover', {
1230
+ bubbles: true,
1231
+ cancelable: true,
1232
+ relatedTarget: null,
1233
+ }),
1234
+ );
1235
+ // This order isn't ideal because each root
1236
+ // has a separate traversal.
1237
+ expect(log).toEqual(unindent`
1238
+ --- inner parent enter
1239
+ ---- inner enter
1240
+ - outer parent enter
1241
+ -- outer enter
1242
+ `);
1243
+ log.length = 0;
1244
+ targetRef.current.dispatchEvent(
1245
+ new MouseEvent('mouseout', {
1246
+ bubbles: true,
1247
+ cancelable: true,
1248
+ relatedTarget: document.body,
1249
+ }),
1250
+ );
1251
+ expect(log).toEqual(unindent`
1252
+ ---- inner leave
1253
+ --- inner parent leave
1254
+ -- outer leave
1255
+ - outer parent leave
1256
+ `);
1257
+ });
1258
+
1259
+ it('onPointerEnter and onPointerLeave', () => {
1260
+ const log = [];
1261
+ const targetRef = React.createRef();
1262
+ render(
1263
+ <Fixture
1264
+ type="div"
1265
+ targetRef={targetRef}
1266
+ targetProps={{
1267
+ onPointerEnter: e => {
1268
+ log.push('---- inner enter');
1269
+ },
1270
+ onPointerLeave: e => {
1271
+ log.push('---- inner leave');
1272
+ },
1273
+ }}
1274
+ parentProps={{
1275
+ onPointerEnter: e => {
1276
+ log.push('--- inner parent enter');
1277
+ },
1278
+ onPointerLeave: e => {
1279
+ log.push('--- inner parent leave');
1280
+ },
1281
+ }}
1282
+ outerProps={{
1283
+ onPointerEnter: e => {
1284
+ log.push('-- outer enter');
1285
+ },
1286
+ onPointerLeave: e => {
1287
+ log.push('-- outer leave');
1288
+ },
1289
+ }}
1290
+ outerParentProps={{
1291
+ onPointerEnter: e => {
1292
+ log.push('- outer parent enter');
1293
+ },
1294
+ onPointerLeave: e => {
1295
+ log.push('- outer parent leave');
1296
+ },
1297
+ }}
1298
+ />,
1299
+ );
1300
+ expect(log.length).toBe(0);
1301
+ targetRef.current.dispatchEvent(
1302
+ new Event('pointerover', {
1303
+ bubbles: true,
1304
+ cancelable: true,
1305
+ relatedTarget: null,
1306
+ }),
1307
+ );
1308
+ // This order isn't ideal because each root
1309
+ // has a separate traversal.
1310
+ expect(log).toEqual(unindent`
1311
+ --- inner parent enter
1312
+ ---- inner enter
1313
+ - outer parent enter
1314
+ -- outer enter
1315
+ `);
1316
+ log.length = 0;
1317
+ targetRef.current.dispatchEvent(
1318
+ new Event('pointerout', {
1319
+ bubbles: true,
1320
+ cancelable: true,
1321
+ relatedTarget: document.body,
1322
+ }),
1323
+ );
1324
+ expect(log).toEqual(unindent`
1325
+ ---- inner leave
1326
+ --- inner parent leave
1327
+ -- outer leave
1328
+ - outer parent leave
1329
+ `);
1330
+ });
1331
+ });
1332
+
1333
+ const setUntrackedValue = Object.getOwnPropertyDescriptor(
1334
+ HTMLInputElement.prototype,
1335
+ 'value',
1336
+ ).set;
1337
+
1338
+ // The tests for these events are currently very limited
1339
+ // because they are fully synthetic, and so they don't
1340
+ // work very well across different roots. For now, we'll
1341
+ // just document the current state in these tests.
1342
+ describe('polyfilled events', () => {
1343
+ it('onBeforeInput', () => {
1344
+ const log = [];
1345
+ const targetRef = React.createRef();
1346
+ render(
1347
+ <Fixture
1348
+ type="input"
1349
+ targetRef={targetRef}
1350
+ targetProps={{
1351
+ onBeforeInput: e => {
1352
+ log.push('---- inner');
1353
+ },
1354
+ onBeforeInputCapture: e => {
1355
+ log.push('---- inner capture');
1356
+ },
1357
+ }}
1358
+ parentProps={{
1359
+ onBeforeInput: e => {
1360
+ log.push('--- inner parent');
1361
+ },
1362
+ onBeforeInputCapture: e => {
1363
+ log.push('--- inner parent capture');
1364
+ },
1365
+ }}
1366
+ outerProps={{
1367
+ onBeforeInput: e => {
1368
+ log.push('-- outer');
1369
+ },
1370
+ onBeforeInputCapture: e => {
1371
+ log.push('-- outer capture');
1372
+ },
1373
+ }}
1374
+ outerParentProps={{
1375
+ onBeforeInput: e => {
1376
+ log.push('- outer parent');
1377
+ },
1378
+ onBeforeInputCapture: e => {
1379
+ log.push('- outer parent capture');
1380
+ },
1381
+ }}
1382
+ />,
1383
+ );
1384
+ expect(log.length).toBe(0);
1385
+ const e = new Event('textInput', {
1386
+ bubbles: true,
1387
+ });
1388
+ e.data = 'abcd';
1389
+ targetRef.current.dispatchEvent(e);
1390
+ // Since this is a polyfilled event,
1391
+ // the capture and bubble phases are
1392
+ // emulated, and don't align between roots.
1393
+ expect(log).toEqual(unindent`
1394
+ --- inner parent capture
1395
+ ---- inner capture
1396
+ ---- inner
1397
+ --- inner parent
1398
+ - outer parent capture
1399
+ -- outer capture
1400
+ -- outer
1401
+ - outer parent
1402
+ `);
1403
+ });
1404
+
1405
+ it('onChange', () => {
1406
+ const log = [];
1407
+ const targetRef = React.createRef();
1408
+ render(
1409
+ <Fixture
1410
+ type="input"
1411
+ targetRef={targetRef}
1412
+ targetProps={{
1413
+ onChange: e => {
1414
+ log.push('---- inner');
1415
+ },
1416
+ onChangeCapture: e => {
1417
+ log.push('---- inner capture');
1418
+ },
1419
+ }}
1420
+ parentProps={{
1421
+ onChange: e => {
1422
+ log.push('--- inner parent');
1423
+ },
1424
+ onChangeCapture: e => {
1425
+ log.push('--- inner parent capture');
1426
+ },
1427
+ }}
1428
+ outerProps={{
1429
+ onChange: e => {
1430
+ log.push('-- outer');
1431
+ },
1432
+ onChangeCapture: e => {
1433
+ log.push('-- outer capture');
1434
+ },
1435
+ }}
1436
+ outerParentProps={{
1437
+ onChange: e => {
1438
+ log.push('- outer parent');
1439
+ },
1440
+ onChangeCapture: e => {
1441
+ log.push('- outer parent capture');
1442
+ },
1443
+ }}
1444
+ />,
1445
+ );
1446
+ expect(log.length).toBe(0);
1447
+ setUntrackedValue.call(targetRef.current, 'hello');
1448
+ targetRef.current.dispatchEvent(
1449
+ new Event('input', {
1450
+ bubbles: true,
1451
+ }),
1452
+ );
1453
+ // The outer React doesn't receive the event at all
1454
+ // because it is not responsible for this input.
1455
+ expect(log).toEqual(unindent`
1456
+ --- inner parent capture
1457
+ ---- inner capture
1458
+ ---- inner
1459
+ --- inner parent
1460
+ `);
1461
+ });
1462
+
1463
+ it('onCompositionStart', () => {
1464
+ const log = [];
1465
+ const targetRef = React.createRef();
1466
+ render(
1467
+ <Fixture
1468
+ type="input"
1469
+ targetRef={targetRef}
1470
+ targetProps={{
1471
+ onCompositionStart: e => {
1472
+ log.push('---- inner');
1473
+ },
1474
+ onCompositionStartCapture: e => {
1475
+ log.push('---- inner capture');
1476
+ },
1477
+ }}
1478
+ parentProps={{
1479
+ onCompositionStart: e => {
1480
+ log.push('--- inner parent');
1481
+ },
1482
+ onCompositionStartCapture: e => {
1483
+ log.push('--- inner parent capture');
1484
+ },
1485
+ }}
1486
+ outerProps={{
1487
+ onCompositionStart: e => {
1488
+ log.push('-- outer');
1489
+ },
1490
+ onCompositionStartCapture: e => {
1491
+ log.push('-- outer capture');
1492
+ },
1493
+ }}
1494
+ outerParentProps={{
1495
+ onCompositionStart: e => {
1496
+ log.push('- outer parent');
1497
+ },
1498
+ onCompositionStartCapture: e => {
1499
+ log.push('- outer parent capture');
1500
+ },
1501
+ }}
1502
+ />,
1503
+ );
1504
+ expect(log.length).toBe(0);
1505
+ const e = new Event('compositionstart', {
1506
+ bubbles: true,
1507
+ });
1508
+ targetRef.current.dispatchEvent(e);
1509
+ // Since this is a polyfilled event,
1510
+ // the capture and bubble phases are
1511
+ // emulated, and don't align between roots.
1512
+ expect(log).toEqual(unindent`
1513
+ --- inner parent capture
1514
+ ---- inner capture
1515
+ ---- inner
1516
+ --- inner parent
1517
+ - outer parent capture
1518
+ -- outer capture
1519
+ -- outer
1520
+ - outer parent
1521
+ `);
1522
+ });
1523
+
1524
+ it('onCompositionEnd', () => {
1525
+ const log = [];
1526
+ const targetRef = React.createRef();
1527
+ render(
1528
+ <Fixture
1529
+ type="input"
1530
+ targetRef={targetRef}
1531
+ targetProps={{
1532
+ onCompositionEnd: e => {
1533
+ log.push('---- inner');
1534
+ },
1535
+ onCompositionEndCapture: e => {
1536
+ log.push('---- inner capture');
1537
+ },
1538
+ }}
1539
+ parentProps={{
1540
+ onCompositionEnd: e => {
1541
+ log.push('--- inner parent');
1542
+ },
1543
+ onCompositionEndCapture: e => {
1544
+ log.push('--- inner parent capture');
1545
+ },
1546
+ }}
1547
+ outerProps={{
1548
+ onCompositionEnd: e => {
1549
+ log.push('-- outer');
1550
+ },
1551
+ onCompositionEndCapture: e => {
1552
+ log.push('-- outer capture');
1553
+ },
1554
+ }}
1555
+ outerParentProps={{
1556
+ onCompositionEnd: e => {
1557
+ log.push('- outer parent');
1558
+ },
1559
+ onCompositionEndCapture: e => {
1560
+ log.push('- outer parent capture');
1561
+ },
1562
+ }}
1563
+ />,
1564
+ );
1565
+ expect(log.length).toBe(0);
1566
+ const e = new Event('compositionend', {
1567
+ bubbles: true,
1568
+ });
1569
+ targetRef.current.dispatchEvent(e);
1570
+ // Since this is a polyfilled event,
1571
+ // the capture and bubble phases are
1572
+ // emulated, and don't align between roots.
1573
+ expect(log).toEqual(unindent`
1574
+ --- inner parent capture
1575
+ ---- inner capture
1576
+ ---- inner
1577
+ --- inner parent
1578
+ - outer parent capture
1579
+ -- outer capture
1580
+ -- outer
1581
+ - outer parent
1582
+ `);
1583
+ });
1584
+
1585
+ it('onCompositionUpdate', () => {
1586
+ const log = [];
1587
+ const targetRef = React.createRef();
1588
+ render(
1589
+ <Fixture
1590
+ type="input"
1591
+ targetRef={targetRef}
1592
+ targetProps={{
1593
+ onCompositionUpdate: e => {
1594
+ log.push('---- inner');
1595
+ },
1596
+ onCompositionUpdateCapture: e => {
1597
+ log.push('---- inner capture');
1598
+ },
1599
+ }}
1600
+ parentProps={{
1601
+ onCompositionUpdate: e => {
1602
+ log.push('--- inner parent');
1603
+ },
1604
+ onCompositionUpdateCapture: e => {
1605
+ log.push('--- inner parent capture');
1606
+ },
1607
+ }}
1608
+ outerProps={{
1609
+ onCompositionUpdate: e => {
1610
+ log.push('-- outer');
1611
+ },
1612
+ onCompositionUpdateCapture: e => {
1613
+ log.push('-- outer capture');
1614
+ },
1615
+ }}
1616
+ outerParentProps={{
1617
+ onCompositionUpdate: e => {
1618
+ log.push('- outer parent');
1619
+ },
1620
+ onCompositionUpdateCapture: e => {
1621
+ log.push('- outer parent capture');
1622
+ },
1623
+ }}
1624
+ />,
1625
+ );
1626
+ expect(log.length).toBe(0);
1627
+ const e = new Event('compositionupdate', {
1628
+ bubbles: true,
1629
+ });
1630
+ targetRef.current.dispatchEvent(e);
1631
+ // Since this is a polyfilled event,
1632
+ // the capture and bubble phases are
1633
+ // emulated, and don't align between roots.
1634
+ expect(log).toEqual(unindent`
1635
+ --- inner parent capture
1636
+ ---- inner capture
1637
+ ---- inner
1638
+ --- inner parent
1639
+ - outer parent capture
1640
+ -- outer capture
1641
+ -- outer
1642
+ - outer parent
1643
+ `);
1644
+ });
1645
+
1646
+ it('onSelect', () => {
1647
+ const log = [];
1648
+ const targetRef = React.createRef();
1649
+ render(
1650
+ <Fixture
1651
+ type="input"
1652
+ targetRef={targetRef}
1653
+ targetProps={{
1654
+ onSelect: e => {
1655
+ log.push('---- inner');
1656
+ },
1657
+ onSelectCapture: e => {
1658
+ log.push('---- inner capture');
1659
+ },
1660
+ }}
1661
+ parentProps={{
1662
+ onSelect: e => {
1663
+ log.push('--- inner parent');
1664
+ },
1665
+ onSelectCapture: e => {
1666
+ log.push('--- inner parent capture');
1667
+ },
1668
+ }}
1669
+ outerProps={{
1670
+ onSelect: e => {
1671
+ log.push('-- outer');
1672
+ },
1673
+ onSelectCapture: e => {
1674
+ log.push('-- outer capture');
1675
+ },
1676
+ }}
1677
+ outerParentProps={{
1678
+ onSelect: e => {
1679
+ log.push('- outer parent');
1680
+ },
1681
+ onSelectCapture: e => {
1682
+ log.push('- outer parent capture');
1683
+ },
1684
+ }}
1685
+ />,
1686
+ );
1687
+ expect(log.length).toBe(0);
1688
+ targetRef.current.focus();
1689
+ targetRef.current.dispatchEvent(
1690
+ new Event('keydown', {
1691
+ bubbles: true,
1692
+ }),
1693
+ );
1694
+ // The outer React doesn't receive the event at all
1695
+ // because it is not responsible for this input.
1696
+ expect(log).toEqual(unindent`
1697
+ --- inner parent capture
1698
+ ---- inner capture
1699
+ ---- inner
1700
+ --- inner parent
1701
+ `);
1702
+ });
1703
+ });
1704
+
1705
// Events that bubble in React and in the browser.
1706
// React delegates them to the root.
1707
function testNativeBubblingEvent(config) {