main
js 861 lines 27.8 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails react-core
8 */
9
10 'use strict';
11
12 let React;
13 let ReactDOMClient;
14 let act;
15
16 describe('BeforeInputEventPlugin', () => {
17 let container;
18
19 function loadReactDOMClientAndAct(envSimulator) {
20 jest.resetModules();
21 if (envSimulator) {
22 envSimulator();
23 }
24 return {
25 ReactDOMClient: require('react-dom/client'),
26 act: require('internal-test-utils').act,
27 };
28 }
29
30 function simulateIE11() {
31 document.documentMode = 11;
32 window.CompositionEvent = {};
33 }
34
35 function simulateWebkit() {
36 window.CompositionEvent = {};
37 window.TextEvent = {};
38 }
39
40 function simulateComposition() {
41 window.CompositionEvent = {};
42 }
43
44 function simulateNoComposition() {
45 // no composition event in Window - will use fallback
46 }
47
48 function simulateEvent(elem, type, data) {
49 const event = new Event(type, {bubbles: true});
50 Object.assign(event, data);
51 elem.dispatchEvent(event);
52 }
53
54 function simulateKeyboardEvent(elem, type, data) {
55 const {char, value, ...rest} = data;
56 const event = new KeyboardEvent(type, {
57 bubbles: true,
58 ...rest,
59 });
60 if (char) {
61 event.char = char;
62 }
63 if (value) {
64 elem.value = value;
65 }
66 elem.dispatchEvent(event);
67 }
68
69 function simulatePaste(elem) {
70 const pasteEvent = new Event('paste', {
71 bubbles: true,
72 });
73 pasteEvent.clipboardData = {
74 dropEffect: null,
75 effectAllowed: null,
76 files: null,
77 items: null,
78 types: null,
79 };
80 elem.dispatchEvent(pasteEvent);
81 }
82
83 beforeEach(() => {
84 React = require('react');
85 container = document.createElement('div');
86 document.body.appendChild(container);
87 });
88
89 afterEach(() => {
90 delete document.documentMode;
91 delete window.CompositionEvent;
92 delete window.TextEvent;
93 delete window.opera;
94 document.body.removeChild(container);
95 container = null;
96 });
97
98 function keyCode(char) {
99 return char.charCodeAt(0);
100 }
101
102 const scenarios = [
103 {
104 eventSimulator: simulateEvent,
105 eventSimulatorArgs: [
106 'compositionstart',
107 {detail: {data: 'test'}, data: 'test'},
108 ],
109 },
110 {
111 eventSimulator: simulateEvent,
112 eventSimulatorArgs: [
113 'compositionupdate',
114 {detail: {data: 'test string'}, data: 'test string'},
115 ],
116 },
117 {
118 eventSimulator: simulateEvent,
119 eventSimulatorArgs: [
120 'compositionend',
121 {detail: {data: 'test string 3'}, data: 'test string 3'},
122 ],
123 },
124 {
125 eventSimulator: simulateEvent,
126 eventSimulatorArgs: ['textInput', {data: 'abcß'}],
127 },
128 {
129 eventSimulator: simulateKeyboardEvent,
130 eventSimulatorArgs: ['keypress', {which: keyCode('a')}],
131 },
132 {
133 eventSimulator: simulateKeyboardEvent,
134 eventSimulatorArgs: ['keypress', {which: keyCode(' ')}, ' '],
135 },
136 {
137 eventSimulator: simulateEvent,
138 eventSimulatorArgs: ['textInput', {data: ' '}],
139 },
140 {
141 eventSimulator: simulateKeyboardEvent,
142 eventSimulatorArgs: ['keypress', {which: keyCode('a'), ctrlKey: true}],
143 },
144 {
145 eventSimulator: simulateKeyboardEvent,
146 eventSimulatorArgs: ['keypress', {which: keyCode('b'), altKey: true}],
147 },
148 {
149 eventSimulator: simulateKeyboardEvent,
150 eventSimulatorArgs: [
151 'keypress',
152 {which: keyCode('c'), altKey: true, ctrlKey: true},
153 ],
154 },
155 {
156 eventSimulator: simulateKeyboardEvent,
157 eventSimulatorArgs: [
158 'keypress',
159 {which: keyCode('X'), char: '\uD83D\uDE0A'},
160 ],
161 },
162 {
163 eventSimulator: simulateEvent,
164 eventSimulatorArgs: ['textInput', {data: '\uD83D\uDE0A'}],
165 },
166 {
167 eventSimulator: simulateKeyboardEvent,
168 eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foo'}],
169 },
170 {
171 eventSimulator: simulateKeyboardEvent,
172 eventSimulatorArgs: ['keydown', {keyCode: 9, value: 'foobar'}],
173 },
174 {
175 eventSimulator: simulateKeyboardEvent,
176 eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foofoo'}],
177 },
178 {
179 eventSimulator: simulateKeyboardEvent,
180 eventSimulatorArgs: ['keyup', {keyCode: 9, value: 'fooBARfoo'}],
181 },
182 {
183 eventSimulator: simulateKeyboardEvent,
184 eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foofoo'}],
185 },
186 {
187 eventSimulator: simulateKeyboardEvent,
188 eventSimulatorArgs: ['keypress', {keyCode: 60, value: 'Barfoofoo'}],
189 },
190 {
191 eventSimulator: simulatePaste,
192 eventSimulatorArgs: [],
193 },
194 ];
195
196 const environments = [
197 {
198 emulator: simulateWebkit,
199 assertions: [
200 {
201 run: ({
202 beforeInputEvent,
203 compositionStartEvent,
204 spyOnBeforeInput,
205 spyOnCompositionStart,
206 }) => {
207 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
208 expect(beforeInputEvent).toBeNull();
209 expect(spyOnCompositionStart).toHaveBeenCalledTimes(1);
210 expect(compositionStartEvent.type).toBe('compositionstart');
211 expect(compositionStartEvent.data).toBe('test');
212 },
213 },
214 {
215 run: ({
216 beforeInputEvent,
217 compositionUpdateEvent,
218 spyOnBeforeInput,
219 spyOnCompositionUpdate,
220 }) => {
221 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
222 expect(beforeInputEvent).toBeNull();
223 expect(spyOnCompositionUpdate).toHaveBeenCalledTimes(1);
224 expect(compositionUpdateEvent.type).toBe('compositionupdate');
225 expect(compositionUpdateEvent.data).toBe('test string');
226 },
227 },
228 {
229 run: ({beforeInputEvent, spyOnBeforeInput}) => {
230 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
231 expect(beforeInputEvent.nativeEvent.type).toBe('compositionend');
232 expect(beforeInputEvent.type).toBe('beforeinput');
233 expect(beforeInputEvent.data).toBe('test string 3');
234 },
235 },
236 {
237 run: ({beforeInputEvent, spyOnBeforeInput}) => {
238 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
239 expect(beforeInputEvent.nativeEvent.type).toBe('textInput');
240 expect(beforeInputEvent.type).toBe('beforeinput');
241 expect(beforeInputEvent.data).toBe('abcß');
242 },
243 },
244 {
245 run: ({beforeInputEvent, spyOnBeforeInput}) => {
246 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
247 expect(beforeInputEvent).toBeNull();
248 },
249 },
250 {
251 run: ({beforeInputEvent, spyOnBeforeInput}) => {
252 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
253 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
254 expect(beforeInputEvent.type).toBe('beforeinput');
255 expect(beforeInputEvent.data).toBe(' ');
256 },
257 },
258 {
259 run: ({beforeInputEvent, spyOnBeforeInput}) => {
260 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
261 expect(beforeInputEvent).toBeNull();
262 },
263 },
264 {
265 run: ({beforeInputEvent, spyOnBeforeInput}) => {
266 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
267 expect(beforeInputEvent).toBeNull();
268 },
269 },
270 {
271 run: ({beforeInputEvent, spyOnBeforeInput}) => {
272 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
273 expect(beforeInputEvent).toBeNull();
274 },
275 },
276 {
277 run: ({beforeInputEvent, spyOnBeforeInput}) => {
278 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
279 expect(beforeInputEvent).toBeNull();
280 },
281 },
282 {
283 run: ({beforeInputEvent, spyOnBeforeInput}) => {
284 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
285 expect(beforeInputEvent).toBeNull();
286 },
287 },
288 {
289 run: ({beforeInputEvent, spyOnBeforeInput}) => {
290 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
291 expect(beforeInputEvent.nativeEvent.type).toBe('textInput');
292 expect(beforeInputEvent.type).toBe('beforeinput');
293 expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
294 },
295 },
296 {
297 run: ({beforeInputEvent, spyOnBeforeInput}) => {
298 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
299 expect(beforeInputEvent).toBeNull();
300 },
301 },
302 {
303 run: ({beforeInputEvent, spyOnBeforeInput}) => {
304 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
305 expect(beforeInputEvent).toBeNull();
306 },
307 },
308 {
309 run: ({beforeInputEvent, spyOnBeforeInput}) => {
310 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
311 expect(beforeInputEvent).toBeNull();
312 },
313 },
314 {
315 run: ({beforeInputEvent, spyOnBeforeInput}) => {
316 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
317 expect(beforeInputEvent).toBeNull();
318 },
319 },
320 {
321 run: ({beforeInputEvent, spyOnBeforeInput}) => {
322 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
323 expect(beforeInputEvent).toBeNull();
324 },
325 },
326 {
327 run: ({beforeInputEvent, spyOnBeforeInput}) => {
328 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
329 expect(beforeInputEvent).toBeNull();
330 },
331 },
332 {
333 run: ({beforeInputEvent, spyOnBeforeInput}) => {
334 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
335 expect(beforeInputEvent).toBeNull();
336 },
337 },
338 ],
339 },
340 {
341 emulator: simulateIE11,
342 assertions: [
343 {
344 run: ({beforeInputEvent, spyOnBeforeInput}) => {
345 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
346 expect(beforeInputEvent).toBeNull();
347 },
348 },
349 {
350 run: ({beforeInputEvent, spyOnBeforeInput}) => {
351 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
352 expect(beforeInputEvent).toBeNull();
353 },
354 },
355 {
356 run: ({beforeInputEvent, spyOnBeforeInput}) => {
357 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
358 expect(beforeInputEvent).toBeNull();
359 },
360 },
361 {
362 run: ({beforeInputEvent, spyOnBeforeInput}) => {
363 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
364 expect(beforeInputEvent).toBeNull();
365 },
366 },
367 {
368 run: ({beforeInputEvent, spyOnBeforeInput}) => {
369 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
370 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
371 expect(beforeInputEvent.type).toBe('beforeinput');
372 expect(beforeInputEvent.data).toBe('a');
373 },
374 },
375 {
376 run: ({beforeInputEvent, spyOnBeforeInput}) => {
377 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
378 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
379 expect(beforeInputEvent.type).toBe('beforeinput');
380 expect(beforeInputEvent.data).toBe(' ');
381 },
382 },
383 {
384 run: ({beforeInputEvent, spyOnBeforeInput}) => {
385 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
386 expect(beforeInputEvent).toBeNull();
387 },
388 },
389 {
390 run: ({beforeInputEvent, spyOnBeforeInput}) => {
391 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
392 expect(beforeInputEvent).toBeNull();
393 },
394 },
395 {
396 run: ({beforeInputEvent, spyOnBeforeInput}) => {
397 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
398 expect(beforeInputEvent).toBeNull();
399 },
400 },
401 {
402 run: ({beforeInputEvent, spyOnBeforeInput}) => {
403 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
404 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
405 expect(beforeInputEvent.type).toBe('beforeinput');
406 expect(beforeInputEvent.data).toBe('c');
407 },
408 },
409 {
410 run: ({beforeInputEvent, spyOnBeforeInput}) => {
411 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
412 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
413 expect(beforeInputEvent.type).toBe('beforeinput');
414 expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
415 },
416 },
417 {
418 run: ({beforeInputEvent, spyOnBeforeInput}) => {
419 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
420 expect(beforeInputEvent).toBeNull();
421 },
422 },
423 {
424 run: ({beforeInputEvent, spyOnBeforeInput}) => {
425 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
426 expect(beforeInputEvent).toBeNull();
427 },
428 },
429 {
430 run: ({beforeInputEvent, spyOnBeforeInput}) => {
431 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
432 expect(beforeInputEvent).toBeNull();
433 },
434 },
435 {
436 run: ({beforeInputEvent, spyOnBeforeInput}) => {
437 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
438 expect(beforeInputEvent).toBeNull();
439 },
440 },
441 {
442 run: ({beforeInputEvent, spyOnBeforeInput}) => {
443 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
444 expect(beforeInputEvent).toBeNull();
445 },
446 },
447 {
448 run: ({beforeInputEvent, spyOnBeforeInput}) => {
449 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
450 expect(beforeInputEvent).toBeNull();
451 },
452 },
453 {
454 run: ({beforeInputEvent, spyOnBeforeInput}) => {
455 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
456 expect(beforeInputEvent).toBeNull();
457 },
458 },
459 {
460 run: ({beforeInputEvent, spyOnBeforeInput}) => {
461 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
462 expect(beforeInputEvent).toBeNull();
463 },
464 },
465 ],
466 },
467 {
468 emulator: simulateNoComposition,
469 assertions: [
470 {
471 run: ({beforeInputEvent, spyOnBeforeInput}) => {
472 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
473 expect(beforeInputEvent).toBeNull();
474 },
475 },
476 {
477 run: ({beforeInputEvent, spyOnBeforeInput}) => {
478 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
479 expect(beforeInputEvent).toBeNull();
480 },
481 },
482 {
483 run: ({beforeInputEvent, spyOnBeforeInput}) => {
484 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
485 expect(beforeInputEvent).toBeNull();
486 },
487 },
488 {
489 run: ({beforeInputEvent, spyOnBeforeInput}) => {
490 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
491 expect(beforeInputEvent).toBeNull();
492 },
493 },
494 {
495 run: ({beforeInputEvent, spyOnBeforeInput}) => {
496 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
497 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
498 expect(beforeInputEvent.type).toBe('beforeinput');
499 expect(beforeInputEvent.data).toBe('a');
500 },
501 },
502 {
503 run: ({beforeInputEvent, spyOnBeforeInput}) => {
504 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
505 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
506 expect(beforeInputEvent.type).toBe('beforeinput');
507 expect(beforeInputEvent.data).toBe(' ');
508 },
509 },
510 {
511 run: ({beforeInputEvent, spyOnBeforeInput}) => {
512 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
513 expect(beforeInputEvent).toBeNull();
514 },
515 },
516 {
517 run: ({beforeInputEvent, spyOnBeforeInput}) => {
518 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
519 expect(beforeInputEvent).toBeNull();
520 },
521 },
522 {
523 run: ({beforeInputEvent, spyOnBeforeInput}) => {
524 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
525 expect(beforeInputEvent).toBeNull();
526 },
527 },
528 {
529 run: ({beforeInputEvent, spyOnBeforeInput}) => {
530 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
531 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
532 expect(beforeInputEvent.type).toBe('beforeinput');
533 expect(beforeInputEvent.data).toBe('c');
534 },
535 },
536 {
537 run: ({beforeInputEvent, spyOnBeforeInput}) => {
538 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
539 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
540 expect(beforeInputEvent.type).toBe('beforeinput');
541 expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
542 },
543 },
544 {
545 run: ({beforeInputEvent, spyOnBeforeInput}) => {
546 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
547 expect(beforeInputEvent).toBeNull();
548 },
549 },
550 {
551 run: ({beforeInputEvent, spyOnBeforeInput}) => {
552 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
553 expect(beforeInputEvent).toBeNull();
554 },
555 },
556 {
557 run: ({beforeInputEvent, spyOnBeforeInput}) => {
558 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
559 expect(beforeInputEvent.nativeEvent.type).toBe('keydown');
560 expect(beforeInputEvent.type).toBe('beforeinput');
561 expect(beforeInputEvent.data).toBe('bar');
562 },
563 },
564 {
565 run: ({beforeInputEvent, spyOnBeforeInput}) => {
566 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
567 expect(beforeInputEvent).toBeNull();
568 },
569 },
570 {
571 run: ({beforeInputEvent, spyOnBeforeInput}) => {
572 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
573 expect(beforeInputEvent.nativeEvent.type).toBe('keyup');
574 expect(beforeInputEvent.type).toBe('beforeinput');
575 expect(beforeInputEvent.data).toBe('BAR');
576 },
577 },
578 {
579 run: ({beforeInputEvent, spyOnBeforeInput}) => {
580 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
581 expect(beforeInputEvent).toBeNull();
582 },
583 },
584 {
585 run: ({beforeInputEvent, spyOnBeforeInput}) => {
586 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
587 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
588 expect(beforeInputEvent.type).toBe('beforeinput');
589 expect(beforeInputEvent.data).toBe('Bar');
590 },
591 },
592 {
593 run: ({beforeInputEvent, spyOnBeforeInput}) => {
594 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
595 expect(beforeInputEvent).toBeNull();
596 },
597 },
598 ],
599 },
600 {
601 emulator: simulateComposition,
602 assertions: [
603 {
604 run: ({beforeInputEvent, spyOnBeforeInput}) => {
605 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
606 expect(beforeInputEvent).toBeNull();
607 },
608 },
609 {
610 run: ({beforeInputEvent, spyOnBeforeInput}) => {
611 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
612 expect(beforeInputEvent).toBeNull();
613 },
614 },
615 {
616 run: ({beforeInputEvent, spyOnBeforeInput}) => {
617 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
618 expect(beforeInputEvent.nativeEvent.type).toBe('compositionend');
619 expect(beforeInputEvent.type).toBe('beforeinput');
620 expect(beforeInputEvent.data).toBe('test string 3');
621 },
622 },
623 {
624 run: ({beforeInputEvent, spyOnBeforeInput}) => {
625 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
626 expect(beforeInputEvent).toBeNull();
627 },
628 },
629 {
630 run: ({beforeInputEvent, spyOnBeforeInput}) => {
631 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
632 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
633 expect(beforeInputEvent.type).toBe('beforeinput');
634 expect(beforeInputEvent.data).toBe('a');
635 },
636 },
637 {
638 run: ({beforeInputEvent, spyOnBeforeInput}) => {
639 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
640 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
641 expect(beforeInputEvent.type).toBe('beforeinput');
642 expect(beforeInputEvent.data).toBe(' ');
643 },
644 },
645 {
646 run: ({beforeInputEvent, spyOnBeforeInput}) => {
647 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
648 expect(beforeInputEvent).toBeNull();
649 },
650 },
651 {
652 run: ({beforeInputEvent, spyOnBeforeInput}) => {
653 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
654 expect(beforeInputEvent).toBeNull();
655 },
656 },
657 {
658 run: ({beforeInputEvent, spyOnBeforeInput}) => {
659 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
660 expect(beforeInputEvent).toBeNull();
661 },
662 },
663 {
664 run: ({beforeInputEvent, spyOnBeforeInput}) => {
665 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
666 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
667 expect(beforeInputEvent.type).toBe('beforeinput');
668 expect(beforeInputEvent.data).toBe('c');
669 },
670 },
671 {
672 run: ({beforeInputEvent, spyOnBeforeInput}) => {
673 expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
674 expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
675 expect(beforeInputEvent.type).toBe('beforeinput');
676 expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
677 },
678 },
679 {
680 run: ({beforeInputEvent, spyOnBeforeInput}) => {
681 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
682 expect(beforeInputEvent).toBeNull();
683 },
684 },
685 {
686 run: ({beforeInputEvent, spyOnBeforeInput}) => {
687 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
688 expect(beforeInputEvent).toBeNull();
689 },
690 },
691 {
692 run: ({beforeInputEvent, spyOnBeforeInput}) => {
693 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
694 expect(beforeInputEvent).toBeNull();
695 },
696 },
697 {
698 run: ({beforeInputEvent, spyOnBeforeInput}) => {
699 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
700 expect(beforeInputEvent).toBeNull();
701 },
702 },
703 {
704 run: ({beforeInputEvent, spyOnBeforeInput}) => {
705 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
706 expect(beforeInputEvent).toBeNull();
707 },
708 },
709 {
710 run: ({beforeInputEvent, spyOnBeforeInput}) => {
711 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
712 expect(beforeInputEvent).toBeNull();
713 },
714 },
715 {
716 run: ({beforeInputEvent, spyOnBeforeInput}) => {
717 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
718 expect(beforeInputEvent).toBeNull();
719 },
720 },
721 {
722 run: ({beforeInputEvent, spyOnBeforeInput}) => {
723 expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
724 expect(beforeInputEvent).toBeNull();
725 },
726 },
727 ],
728 },
729 ];
730
731 const testInputComponent = async (env, scenes) => {
732 let beforeInputEvent;
733 let compositionStartEvent;
734 let compositionUpdateEvent;
735 let spyOnBeforeInput;
736 let spyOnCompositionStart;
737 let spyOnCompositionUpdate;
738 ({ReactDOMClient, act} = loadReactDOMClientAndAct(env.emulator));
739 const root = ReactDOMClient.createRoot(container);
740 await act(() => {
741 root.render(
742 <input
743 type="text"
744 onBeforeInput={e => {
745 spyOnBeforeInput();
746 beforeInputEvent = e;
747 }}
748 onCompositionStart={e => {
749 spyOnCompositionStart();
750 compositionStartEvent = e;
751 }}
752 onCompositionUpdate={e => {
753 spyOnCompositionUpdate();
754 compositionUpdateEvent = e;
755 }}
756 />,
757 );
758 });
759
760 const node = container.firstChild;
761
762 scenes.forEach((s, id) => {
763 beforeInputEvent = null;
764 compositionStartEvent = null;
765 compositionUpdateEvent = null;
766 spyOnBeforeInput = jest.fn();
767 spyOnCompositionStart = jest.fn();
768 spyOnCompositionUpdate = jest.fn();
769 s.eventSimulator.apply(null, [node, ...s.eventSimulatorArgs]);
770 env.assertions[id].run({
771 beforeInputEvent,
772 compositionStartEvent,
773 compositionUpdateEvent,
774 spyOnBeforeInput,
775 spyOnCompositionStart,
776 spyOnCompositionUpdate,
777 });
778 });
779 };
780
781 const testContentEditableComponent = async (env, scenes) => {
782 let beforeInputEvent;
783 let compositionStartEvent;
784 let compositionUpdateEvent;
785 let spyOnBeforeInput;
786 let spyOnCompositionStart;
787 let spyOnCompositionUpdate;
788 ({ReactDOMClient, act} = loadReactDOMClientAndAct(env.emulator));
789 const root = ReactDOMClient.createRoot(container);
790
791 await act(() => {
792 root.render(
793 <div
794 contentEditable={true}
795 onBeforeInput={e => {
796 spyOnBeforeInput();
797 beforeInputEvent = e;
798 }}
799 onCompositionStart={e => {
800 spyOnCompositionStart();
801 compositionStartEvent = e;
802 }}
803 onCompositionUpdate={e => {
804 spyOnCompositionUpdate();
805 compositionUpdateEvent = e;
806 }}
807 />,
808 );
809 });
810
811 const node = container.firstChild;
812
813 scenes.forEach((s, id) => {
814 beforeInputEvent = null;
815 compositionStartEvent = null;
816 compositionUpdateEvent = null;
817 spyOnBeforeInput = jest.fn();
818 spyOnCompositionStart = jest.fn();
819 spyOnCompositionUpdate = jest.fn();
820 s.eventSimulator.apply(null, [node, ...s.eventSimulatorArgs]);
821 env.assertions[id].run({
822 beforeInputEvent,
823 compositionStartEvent,
824 compositionUpdateEvent,
825 spyOnBeforeInput,
826 spyOnCompositionStart,
827 spyOnCompositionUpdate,
828 });
829 });
830 };
831
832 it('should extract onBeforeInput when simulating in Webkit on input[type=text]', async () => {
833 await testInputComponent(environments[0], scenarios);
834 });
835 it('should extract onBeforeInput when simulating in Webkit on contenteditable', async () => {
836 await testContentEditableComponent(environments[0], scenarios);
837 });
838
839 it('should extract onBeforeInput when simulating in IE11 on input[type=text]', async () => {
840 await testInputComponent(environments[1], scenarios);
841 });
842 it('should extract onBeforeInput when simulating in IE11 on contenteditable', async () => {
843 await testContentEditableComponent(environments[1], scenarios);
844 });
845
846 it('should extract onBeforeInput when simulating in env with no CompositionEvent on input[type=text]', async () => {
847 await testInputComponent(environments[2], scenarios);
848 });
849
850 // in an environment using composition fallback onBeforeInput will not work
851 // as expected on a contenteditable as keydown and keyup events are translated
852 // to keypress events
853
854 it('should extract onBeforeInput when simulating in env with only CompositionEvent on input[type=text]', async () => {
855 await testInputComponent(environments[3], scenarios);
856 });
857
858 it('should extract onBeforeInput when simulating in env with only CompositionEvent on contenteditable', async () => {
859 await testContentEditableComponent(environments[3], scenarios);
860 });
861 });