@samitouri / QOS-React-2 / commits / 291db05a75

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) {