@samitouri / QOS-React / commits / cf58f296e9

Add test exercising public API to test BeforeInputEventPlugin + FallbackCompositionState (#11849)

* Add test exercising public API to test BeforeInputEventPlugin + FallbackCompositionState - I've adopted a similar approach to the existing test for BeforeInputEventPlugin - I've simulated events and then assert the event handler for onBeforeInput is fired or not fired based on the test conditions - The scenarios are tested against IE11, Webkite and Presto environment simulations - I've encorporated what I understand to be the functionality in the FallbackCompositionState test * Prettier * Linting fixes * Remove test for contenteditable in Presto - the contenteditable type is not supported in Presto powered browsers (Opera). * Remove mention of Presto as this explicit condition is no longer handled in BeforeInputEventPlugin. We still need to exercise usage of FallbackCompositionState though so let's keep a test where the env does not support Composition and Text events. * Add tests for envs with only CompositionEvent support * Remove internal tests no longer needed * Shorten test case names to satisfy lint rules * Add tests for onCompositionStart and onCompositionUpdte events The BeforeInputEventPlugin is responsible for emitting these events so we need to add tests for this. This also ensure we exercise the code path that, L207, that was not previously exercised with the public tests.

Gordon Dent committed Feb 22, 2018 at 10:27 UTC cf58f296e9024fa76a32a53d7b3572c74b608ed3
3 files changed +830 -317
packages/react-dom/src/events/__tests__/BeforeInputEventPlugin-test.internal.js deleted
-226
@@ -1,226 +0,0 @@
1 -/**
2 - * Copyright (c) 2013-present, Facebook, Inc.
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 -const React = require('react');
13 -const ReactTestUtils = require('react-dom/test-utils');
14 -
15 -const EventMapping = {
16 - compositionstart: 'topCompositionStart',
17 - compositionend: 'topCompositionEnd',
18 - keyup: 'topKeyUp',
19 - keydown: 'topKeyDown',
20 - keypress: 'topKeyPress',
21 - textInput: 'topTextInput',
22 - textinput: null, // Not defined now
23 -};
24 -
25 -describe('BeforeInputEventPlugin', function() {
26 - let ModuleCache;
27 -
28 - function simulateIE11() {
29 - document.documentMode = 11;
30 - window.CompositionEvent = {};
31 - delete window.TextEvent;
32 - }
33 -
34 - function simulateWebkit() {
35 - delete document.documentMode;
36 - window.CompositionEvent = {};
37 - window.TextEvent = {};
38 - }
39 -
40 - function initialize(simulator) {
41 - // Need to delete cached modules before executing simulator
42 - jest.resetModules();
43 -
44 - // Initialize variables in the scope of BeforeInputEventPlugin
45 - simulator();
46 -
47 - // Modules which have dependency on BeforeInputEventPlugin are stored
48 - // in ModuleCache so that we can use these modules ouside test functions.
49 - this.ReactDOM = require('react-dom');
50 -
51 - // TODO: can we express this test with only public API?
52 - this.ReactDOMComponentTree = require('../../client/ReactDOMComponentTree');
53 - this.SyntheticCompositionEvent = require('../SyntheticCompositionEvent').default;
54 - this.SyntheticInputEvent = require('../SyntheticInputEvent').default;
55 - this.BeforeInputEventPlugin = require('../BeforeInputEventPlugin').default;
56 - }
57 -
58 - function extract(node, eventType, optionalData) {
59 - let evt = document.createEvent('HTMLEvents');
60 - evt.initEvent(eventType, true, true);
61 - evt = Object.assign(evt, optionalData);
62 - return ModuleCache.BeforeInputEventPlugin.extractEvents(
63 - EventMapping[eventType],
64 - ModuleCache.ReactDOMComponentTree.getInstanceFromNode(node),
65 - evt,
66 - node,
67 - );
68 - }
69 -
70 - function setElementText(node) {
71 - return args => (node.innerHTML = args);
72 - }
73 -
74 - function accumulateEvents(node, events) {
75 - // We don't use accumulateInto module to apply partial application.
76 - return function() {
77 - const newArgs = [node].concat(Array.prototype.slice.call(arguments));
78 - const newEvents = extract.apply(this, newArgs);
79 -
80 - if (Array.isArray(newEvents)) {
81 - Array.prototype.push.apply(events, newEvents);
82 - } else if (newEvents) {
83 - events.push(newEvents);
84 - }
85 - };
86 - }
87 -
88 - function EventMismatchError(idx, message) {
89 - this.name = 'EventMismatchError';
90 - this.message = '[' + idx + '] ' + message;
91 - }
92 - EventMismatchError.prototype = Object.create(Error.prototype);
93 -
94 - function verifyEvents(actualEvents, expectedEvents) {
95 - expect(actualEvents.length).toBe(expectedEvents.length);
96 - expectedEvents.forEach(function(expected, idx) {
97 - const actual = actualEvents[idx];
98 - expect(function() {
99 - if (actual === null && expected.type === null) {
100 - // Both are null. Expected.
101 - } else if (actual === null) {
102 - throw new EventMismatchError(idx, 'Expected not to be null');
103 - } else if (
104 - expected.type === null ||
105 - !(actual instanceof expected.type)
106 - ) {
107 - throw new EventMismatchError(idx, 'Unexpected type: ' + actual);
108 - } else {
109 - // Type match.
110 - Object.keys(expected.data).forEach(function(expectedKey) {
111 - if (!(expectedKey in actual)) {
112 - throw new EventMismatchError(idx, 'KeyNotFound: ' + expectedKey);
113 - } else if (actual[expectedKey] !== expected.data[expectedKey]) {
114 - throw new EventMismatchError(
115 - idx,
116 - 'ValueMismatch: ' + actual[expectedKey],
117 - );
118 - }
119 - });
120 - }
121 - }).not.toThrow();
122 - });
123 - }
124 -
125 - // IE fires an event named `textinput` with all lowercase characters,
126 - // instead of a standard name `textInput`. As of now, React does not have
127 - // a corresponding topEvent to IE's textinput, but both events are added to
128 - // this scenario data for future use.
129 - const Test_Scenario = [
130 - // Composition test
131 - {run: accumulateEvents, arg: ['compositionstart', {data: ''}]},
132 - {run: accumulateEvents, arg: ['textInput', {data: 'A'}]},
133 - {run: accumulateEvents, arg: ['textinput', {data: 'A'}]},
134 - {run: accumulateEvents, arg: ['keyup', {keyCode: 65}]},
135 - {run: setElementText, arg: ['ABC']},
136 - {run: accumulateEvents, arg: ['textInput', {data: 'abc'}]},
137 - {run: accumulateEvents, arg: ['textinput', {data: 'abc'}]},
138 - {run: accumulateEvents, arg: ['keyup', {keyCode: 32}]},
139 - {run: setElementText, arg: ['XYZ']},
140 - {run: accumulateEvents, arg: ['textInput', {data: 'xyz'}]},
141 - {run: accumulateEvents, arg: ['textinput', {data: 'xyz'}]},
142 - {run: accumulateEvents, arg: ['keyup', {keyCode: 32}]},
143 - {run: accumulateEvents, arg: ['compositionend', {data: 'Hello'}]},
144 -
145 - // Emoji test
146 - {
147 - run: accumulateEvents,
148 - arg: ['keypress', {char: '\uD83D\uDE0A', which: 65}],
149 - },
150 - {run: accumulateEvents, arg: ['textInput', {data: '\uD83D\uDE0A'}]},
151 - ];
152 -
153 - /* Defined expected results as a factory of result data because we need
154 - lazy evaluation for event modules.
155 - Event modules are reloaded to simulate a different platform per testcase.
156 - If we define expected results as a simple dictionary here, the comparison
157 - of 'instanceof' fails after module cache is reset. */
158 -
159 - // Webkit behavior is simple. We expect SyntheticInputEvent at each
160 - // textInput, SyntheticCompositionEvent at composition, and nothing from
161 - // keyUp.
162 - const Expected_Webkit = () => [
163 - {type: ModuleCache.SyntheticCompositionEvent, data: {}},
164 - {type: ModuleCache.SyntheticInputEvent, data: {data: 'A'}},
165 - // textinput of A
166 - // keyUp of 65
167 - {type: ModuleCache.SyntheticInputEvent, data: {data: 'abc'}},
168 - // textinput of abc
169 - // keyUp of 32
170 - {type: ModuleCache.SyntheticInputEvent, data: {data: 'xyz'}},
171 - // textinput of xyz
172 - // keyUp of 32
173 - {type: ModuleCache.SyntheticCompositionEvent, data: {data: 'Hello'}},
174 - // Emoji test
175 - {type: ModuleCache.SyntheticInputEvent, data: {data: '\uD83D\uDE0A'}},
176 - ];
177 -
178 - // For IE11, we use fallback data instead of IE's textinput events.
179 - // We expect no SyntheticInputEvent from textinput. Fallback beforeInput is
180 - // expected to be triggered at compositionend with a text of the target
181 - // element, not event data.
182 - const Expected_IE11 = () => [
183 - {type: ModuleCache.SyntheticCompositionEvent, data: {}},
184 - // textInput of A
185 - // textinput of A
186 - // keyUp of 65
187 - // textInput of abc
188 - // textinput of abc
189 - // fallbackData should NOT be set at keyUp with any of END_KEYCODES
190 - // keyUp of 32
191 - // textInput of xyz
192 - // textinput of xyz
193 - // keyUp of 32
194 - // fallbackData is retrieved from the element, which is XYZ,
195 - // at a time of compositionend
196 - {type: ModuleCache.SyntheticCompositionEvent, data: {}},
197 - {type: ModuleCache.SyntheticInputEvent, data: {data: 'XYZ'}},
198 - // Emoji test
199 - {type: ModuleCache.SyntheticInputEvent, data: {data: '\uD83D\uDE0A'}},
200 - ];
201 -
202 - function TestEditableReactComponent(Emulator, Scenario, ExpectedResult) {
203 - ModuleCache = new initialize(Emulator);
204 -
205 - class EditableDiv extends React.Component {
206 - render() {
207 - return <div contentEditable="true" />;
208 - }
209 - }
210 - const rendered = ReactTestUtils.renderIntoDocument(<EditableDiv />);
211 -
212 - const node = ModuleCache.ReactDOM.findDOMNode(rendered);
213 - const events = [];
214 -
215 - Scenario.forEach(el => el.run.call(this, node, events).apply(this, el.arg));
216 - verifyEvents(events, ExpectedResult());
217 - }
218 -
219 - it('extract onBeforeInput from native textinput events', function() {
220 - TestEditableReactComponent(simulateWebkit, Test_Scenario, Expected_Webkit);
221 - });
222 -
223 - it('extract onBeforeInput from fallback objects', function() {
224 - TestEditableReactComponent(simulateIE11, Test_Scenario, Expected_IE11);
225 - });
226 -});
packages/react-dom/src/events/__tests__/BeforeInputEventPlugin-test.js new
+830
@@ -0,0 +1,830 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
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 ReactDOM;
14 +
15 +describe('BeforeInputEventPlugin', () => {
16 + let container;
17 +
18 + function loadReactDOM(envSimulator) {
19 + jest.resetModules();
20 + if (envSimulator) {
21 + envSimulator();
22 + }
23 + return require('react-dom');
24 + }
25 +
26 + function simulateIE11() {
27 + document.documentMode = 11;
28 + window.CompositionEvent = {};
29 + }
30 +
31 + function simulateWebkit() {
32 + window.CompositionEvent = {};
33 + window.TextEvent = {};
34 + }
35 +
36 + function simulateComposition() {
37 + window.CompositionEvent = {};
38 + }
39 +
40 + function simulateNoComposition() {
41 + // no composition event in Window - will use fallback
42 + }
43 +
44 + function simulateEvent(elem, type, data) {
45 + const event = new Event(type, {bubbles: true});
46 + Object.keys(data).forEach(key => {
47 + event[key] = data[key];
48 + });
49 + elem.dispatchEvent(event);
50 + }
51 +
52 + function simulateKeyboardEvent(elem, type, data) {
53 + const {char, value, ...rest} = data;
54 + const event = new KeyboardEvent(type, {
55 + bubbles: true,
56 + ...rest,
57 + });
58 + if (char) {
59 + event.char = char;
60 + }
61 + if (value) {
62 + elem.value = value;
63 + }
64 + elem.dispatchEvent(event);
65 + }
66 +
67 + function simulatePaste(elem) {
68 + const pasteEvent = new Event('paste', {
69 + bubbles: true,
70 + });
71 + pasteEvent.clipboardData = {
72 + dropEffect: null,
73 + effectAllowed: null,
74 + files: null,
75 + items: null,
76 + types: null,
77 + };
78 + elem.dispatchEvent(pasteEvent);
79 + }
80 +
81 + beforeEach(() => {
82 + React = require('react');
83 + container = document.createElement('div');
84 + document.body.appendChild(container);
85 + });
86 +
87 + afterEach(() => {
88 + delete document.documentMode;
89 + delete window.CompositionEvent;
90 + delete window.TextEvent;
91 + delete window.opera;
92 + document.body.removeChild(container);
93 + container = null;
94 + });
95 +
96 + function keyCode(char) {
97 + return char.charCodeAt(0);
98 + }
99 +
100 + const scenarios = [
101 + {
102 + eventSimulator: simulateEvent,
103 + eventSimulatorArgs: [
104 + 'compositionstart',
105 + {detail: {data: 'test'}, data: 'test'},
106 + ],
107 + },
108 + {
109 + eventSimulator: simulateEvent,
110 + eventSimulatorArgs: [
111 + 'compositionupdate',
112 + {detail: {data: 'test string'}, data: 'test string'},
113 + ],
114 + },
115 + {
116 + eventSimulator: simulateEvent,
117 + eventSimulatorArgs: [
118 + 'compositionend',
119 + {detail: {data: 'test string 3'}, data: 'test string 3'},
120 + ],
121 + },
122 + {
123 + eventSimulator: simulateEvent,
124 + eventSimulatorArgs: ['textInput', {data: 'abcß'}],
125 + },
126 + {
127 + eventSimulator: simulateKeyboardEvent,
128 + eventSimulatorArgs: ['keypress', {which: keyCode('a')}],
129 + },
130 + {
131 + eventSimulator: simulateKeyboardEvent,
132 + eventSimulatorArgs: ['keypress', {which: keyCode(' ')}, ' '],
133 + },
134 + {
135 + eventSimulator: simulateEvent,
136 + eventSimulatorArgs: ['textInput', {data: ' '}],
137 + },
138 + {
139 + eventSimulator: simulateKeyboardEvent,
140 + eventSimulatorArgs: ['keypress', {which: keyCode('a'), ctrlKey: true}],
141 + },
142 + {
143 + eventSimulator: simulateKeyboardEvent,
144 + eventSimulatorArgs: ['keypress', {which: keyCode('b'), altKey: true}],
145 + },
146 + {
147 + eventSimulator: simulateKeyboardEvent,
148 + eventSimulatorArgs: [
149 + 'keypress',
150 + {which: keyCode('c'), altKey: true, ctrlKey: true},
151 + ],
152 + },
153 + {
154 + eventSimulator: simulateKeyboardEvent,
155 + eventSimulatorArgs: [
156 + 'keypress',
157 + {which: keyCode('X'), char: '\uD83D\uDE0A'},
158 + ],
159 + },
160 + {
161 + eventSimulator: simulateEvent,
162 + eventSimulatorArgs: ['textInput', {data: '\uD83D\uDE0A'}],
163 + },
164 + {
165 + eventSimulator: simulateKeyboardEvent,
166 + eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foo'}],
167 + },
168 + {
169 + eventSimulator: simulateKeyboardEvent,
170 + eventSimulatorArgs: ['keydown', {keyCode: 9, value: 'foobar'}],
171 + },
172 + {
173 + eventSimulator: simulateKeyboardEvent,
174 + eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foofoo'}],
175 + },
176 + {
177 + eventSimulator: simulateKeyboardEvent,
178 + eventSimulatorArgs: ['keyup', {keyCode: 9, value: 'fooBARfoo'}],
179 + },
180 + {
181 + eventSimulator: simulateKeyboardEvent,
182 + eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foofoo'}],
183 + },
184 + {
185 + eventSimulator: simulateKeyboardEvent,
186 + eventSimulatorArgs: ['keypress', {keyCode: 60, value: 'Barfoofoo'}],
187 + },
188 + {
189 + eventSimulator: simulatePaste,
190 + eventSimulatorArgs: [],
191 + },
192 + ];
193 +
194 + const environments = [
195 + {
196 + emulator: simulateWebkit,
197 + assertions: [
198 + {
199 + run: ({
200 + beforeInputEvent,
201 + compositionStartEvent,
202 + spyOnBeforeInput,
203 + spyOnCompositionStart,
204 + }) => {
205 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
206 + expect(beforeInputEvent).toBeNull();
207 + expect(spyOnCompositionStart.mock.calls.length).toBe(1);
208 + expect(compositionStartEvent.type).toBe('compositionstart');
209 + expect(compositionStartEvent.data).toBe('test');
210 + },
211 + },
212 + {
213 + run: ({
214 + beforeInputEvent,
215 + compositionUpdateEvent,
216 + spyOnBeforeInput,
217 + spyOnCompositionUpdate,
218 + }) => {
219 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
220 + expect(beforeInputEvent).toBeNull();
221 + expect(spyOnCompositionUpdate.mock.calls.length).toBe(1);
222 + expect(compositionUpdateEvent.type).toBe('compositionupdate');
223 + expect(compositionUpdateEvent.data).toBe('test string');
224 + },
225 + },
226 + {
227 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
228 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
229 + expect(beforeInputEvent.type).toBe('compositionend');
230 + expect(beforeInputEvent.data).toBe('test string 3');
231 + },
232 + },
233 + {
234 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
235 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
236 + expect(beforeInputEvent.type).toBe('textInput');
237 + expect(beforeInputEvent.data).toBe('abcß');
238 + },
239 + },
240 + {
241 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
242 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
243 + expect(beforeInputEvent).toBeNull();
244 + },
245 + },
246 + {
247 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
248 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
249 + expect(beforeInputEvent.type).toBe('keypress');
250 + expect(beforeInputEvent.data).toBe(' ');
251 + },
252 + },
253 + {
254 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
255 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
256 + expect(beforeInputEvent).toBeNull();
257 + },
258 + },
259 + {
260 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
261 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
262 + expect(beforeInputEvent).toBeNull();
263 + },
264 + },
265 + {
266 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
267 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
268 + expect(beforeInputEvent).toBeNull();
269 + },
270 + },
271 + {
272 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
273 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
274 + expect(beforeInputEvent).toBeNull();
275 + },
276 + },
277 + {
278 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
279 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
280 + expect(beforeInputEvent).toBeNull();
281 + },
282 + },
283 + {
284 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
285 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
286 + expect(beforeInputEvent.type).toBe('textInput');
287 + expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
288 + },
289 + },
290 + {
291 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
292 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
293 + expect(beforeInputEvent).toBeNull();
294 + },
295 + },
296 + {
297 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
298 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
299 + expect(beforeInputEvent).toBeNull();
300 + },
301 + },
302 + {
303 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
304 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
305 + expect(beforeInputEvent).toBeNull();
306 + },
307 + },
308 + {
309 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
310 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
311 + expect(beforeInputEvent).toBeNull();
312 + },
313 + },
314 + {
315 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
316 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
317 + expect(beforeInputEvent).toBeNull();
318 + },
319 + },
320 + {
321 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
322 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
323 + expect(beforeInputEvent).toBeNull();
324 + },
325 + },
326 + {
327 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
328 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
329 + expect(beforeInputEvent).toBeNull();
330 + },
331 + },
332 + ],
333 + },
334 + {
335 + emulator: simulateIE11,
336 + assertions: [
337 + {
338 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
339 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
340 + expect(beforeInputEvent).toBeNull();
341 + },
342 + },
343 + {
344 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
345 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
346 + expect(beforeInputEvent).toBeNull();
347 + },
348 + },
349 + {
350 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
351 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
352 + expect(beforeInputEvent).toBeNull();
353 + },
354 + },
355 + {
356 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
357 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
358 + expect(beforeInputEvent).toBeNull();
359 + },
360 + },
361 + {
362 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
363 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
364 + expect(beforeInputEvent.type).toBe('keypress');
365 + expect(beforeInputEvent.data).toBe('a');
366 + },
367 + },
368 + {
369 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
370 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
371 + expect(beforeInputEvent.type).toBe('keypress');
372 + expect(beforeInputEvent.data).toBe(' ');
373 + },
374 + },
375 + {
376 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
377 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
378 + expect(beforeInputEvent).toBeNull();
379 + },
380 + },
381 + {
382 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
383 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
384 + expect(beforeInputEvent).toBeNull();
385 + },
386 + },
387 + {
388 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
389 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
390 + expect(beforeInputEvent).toBeNull();
391 + },
392 + },
393 + {
394 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
395 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
396 + expect(beforeInputEvent.type).toBe('keypress');
397 + expect(beforeInputEvent.data).toBe('c');
398 + },
399 + },
400 + {
401 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
402 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
403 + expect(beforeInputEvent.type).toBe('keypress');
404 + expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
405 + },
406 + },
407 + {
408 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
409 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
410 + expect(beforeInputEvent).toBeNull();
411 + },
412 + },
413 + {
414 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
415 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
416 + expect(beforeInputEvent).toBeNull();
417 + },
418 + },
419 + {
420 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
421 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
422 + expect(beforeInputEvent).toBeNull();
423 + },
424 + },
425 + {
426 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
427 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
428 + expect(beforeInputEvent).toBeNull();
429 + },
430 + },
431 + {
432 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
433 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
434 + expect(beforeInputEvent).toBeNull();
435 + },
436 + },
437 + {
438 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
439 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
440 + expect(beforeInputEvent).toBeNull();
441 + },
442 + },
443 + {
444 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
445 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
446 + expect(beforeInputEvent).toBeNull();
447 + },
448 + },
449 + {
450 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
451 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
452 + expect(beforeInputEvent).toBeNull();
453 + },
454 + },
455 + ],
456 + },
457 + {
458 + emulator: simulateNoComposition,
459 + assertions: [
460 + {
461 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
462 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
463 + expect(beforeInputEvent).toBeNull();
464 + },
465 + },
466 + {
467 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
468 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
469 + expect(beforeInputEvent).toBeNull();
470 + },
471 + },
472 + {
473 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
474 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
475 + expect(beforeInputEvent).toBeNull();
476 + },
477 + },
478 + {
479 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
480 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
481 + expect(beforeInputEvent).toBeNull();
482 + },
483 + },
484 + {
485 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
486 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
487 + expect(beforeInputEvent.type).toBe('keypress');
488 + expect(beforeInputEvent.data).toBe('a');
489 + },
490 + },
491 + {
492 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
493 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
494 + expect(beforeInputEvent.type).toBe('keypress');
495 + expect(beforeInputEvent.data).toBe(' ');
496 + },
497 + },
498 + {
499 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
500 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
501 + expect(beforeInputEvent).toBeNull();
502 + },
503 + },
504 + {
505 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
506 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
507 + expect(beforeInputEvent).toBeNull();
508 + },
509 + },
510 + {
511 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
512 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
513 + expect(beforeInputEvent).toBeNull();
514 + },
515 + },
516 + {
517 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
518 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
519 + expect(beforeInputEvent.type).toBe('keypress');
520 + expect(beforeInputEvent.data).toBe('c');
521 + },
522 + },
523 + {
524 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
525 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
526 + expect(beforeInputEvent.type).toBe('keypress');
527 + expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
528 + },
529 + },
530 + {
531 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
532 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
533 + expect(beforeInputEvent).toBeNull();
534 + },
535 + },
536 + {
537 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
538 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
539 + expect(beforeInputEvent).toBeNull();
540 + },
541 + },
542 + {
543 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
544 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
545 + expect(beforeInputEvent.type).toBe('keydown');
546 + expect(beforeInputEvent.data).toBe('bar');
547 + },
548 + },
549 + {
550 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
551 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
552 + expect(beforeInputEvent).toBeNull();
553 + },
554 + },
555 + {
556 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
557 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
558 + expect(beforeInputEvent.type).toBe('keyup');
559 + expect(beforeInputEvent.data).toBe('BAR');
560 + },
561 + },
562 + {
563 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
564 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
565 + expect(beforeInputEvent).toBeNull();
566 + },
567 + },
568 + {
569 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
570 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
571 + expect(beforeInputEvent.type).toBe('keypress');
572 + expect(beforeInputEvent.data).toBe('Bar');
573 + },
574 + },
575 + {
576 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
577 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
578 + expect(beforeInputEvent).toBeNull();
579 + },
580 + },
581 + ],
582 + },
583 + {
584 + emulator: simulateComposition,
585 + assertions: [
586 + {
587 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
588 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
589 + expect(beforeInputEvent).toBeNull();
590 + },
591 + },
592 + {
593 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
594 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
595 + expect(beforeInputEvent).toBeNull();
596 + },
597 + },
598 + {
599 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
600 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
601 + expect(beforeInputEvent.type).toBe('compositionend');
602 + expect(beforeInputEvent.data).toBe('test string 3');
603 + },
604 + },
605 + {
606 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
607 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
608 + expect(beforeInputEvent).toBeNull();
609 + },
610 + },
611 + {
612 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
613 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
614 + expect(beforeInputEvent.type).toBe('keypress');
615 + expect(beforeInputEvent.data).toBe('a');
616 + },
617 + },
618 + {
619 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
620 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
621 + expect(beforeInputEvent.type).toBe('keypress');
622 + expect(beforeInputEvent.data).toBe(' ');
623 + },
624 + },
625 + {
626 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
627 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
628 + expect(beforeInputEvent).toBeNull();
629 + },
630 + },
631 + {
632 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
633 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
634 + expect(beforeInputEvent).toBeNull();
635 + },
636 + },
637 + {
638 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
639 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
640 + expect(beforeInputEvent).toBeNull();
641 + },
642 + },
643 + {
644 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
645 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
646 + expect(beforeInputEvent.type).toBe('keypress');
647 + expect(beforeInputEvent.data).toBe('c');
648 + },
649 + },
650 + {
651 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
652 + expect(spyOnBeforeInput.mock.calls.length).toBe(1);
653 + expect(beforeInputEvent.type).toBe('keypress');
654 + expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
655 + },
656 + },
657 + {
658 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
659 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
660 + expect(beforeInputEvent).toBeNull();
661 + },
662 + },
663 + {
664 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
665 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
666 + expect(beforeInputEvent).toBeNull();
667 + },
668 + },
669 + {
670 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
671 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
672 + expect(beforeInputEvent).toBeNull();
673 + },
674 + },
675 + {
676 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
677 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
678 + expect(beforeInputEvent).toBeNull();
679 + },
680 + },
681 + {
682 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
683 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
684 + expect(beforeInputEvent).toBeNull();
685 + },
686 + },
687 + {
688 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
689 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
690 + expect(beforeInputEvent).toBeNull();
691 + },
692 + },
693 + {
694 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
695 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
696 + expect(beforeInputEvent).toBeNull();
697 + },
698 + },
699 + {
700 + run: ({beforeInputEvent, spyOnBeforeInput}) => {
701 + expect(spyOnBeforeInput.mock.calls.length).toBe(0);
702 + expect(beforeInputEvent).toBeNull();
703 + },
704 + },
705 + ],
706 + },
707 + ];
708 +
709 + const testInputComponent = (env, scenes) => {
710 + let beforeInputEvent;
711 + let compositionStartEvent;
712 + let compositionUpdateEvent;
713 + let spyOnBeforeInput;
714 + let spyOnCompositionStart;
715 + let spyOnCompositionUpdate;
716 + ReactDOM = loadReactDOM(env.emulator);
717 + const node = ReactDOM.render(
718 + <input
719 + type="text"
720 + onBeforeInput={({type, data}) => {
721 + spyOnBeforeInput();
722 + beforeInputEvent = {type, data};
723 + }}
724 + onCompositionStart={({type, data}) => {
725 + spyOnCompositionStart();
726 + compositionStartEvent = {type, data};
727 + }}
728 + onCompositionUpdate={({type, data}) => {
729 + spyOnCompositionUpdate();
730 + compositionUpdateEvent = {type, data};
731 + }}
732 + />,
733 + container,
734 + );
735 +
736 + scenes.forEach((s, id) => {
737 + beforeInputEvent = null;
738 + compositionStartEvent = null;
739 + compositionUpdateEvent = null;
740 + spyOnBeforeInput = jest.fn();
741 + spyOnCompositionStart = jest.fn();
742 + spyOnCompositionUpdate = jest.fn();
743 + s.eventSimulator.apply(null, [node, ...s.eventSimulatorArgs]);
744 + env.assertions[id].run({
745 + beforeInputEvent,
746 + compositionStartEvent,
747 + compositionUpdateEvent,
748 + spyOnBeforeInput,
749 + spyOnCompositionStart,
750 + spyOnCompositionUpdate,
751 + });
752 + });
753 + };
754 +
755 + const testContentEditableComponent = (env, scenes) => {
756 + let beforeInputEvent;
757 + let compositionStartEvent;
758 + let compositionUpdateEvent;
759 + let spyOnBeforeInput;
760 + let spyOnCompositionStart;
761 + let spyOnCompositionUpdate;
762 + ReactDOM = loadReactDOM(env.emulator);
763 + const node = ReactDOM.render(
764 + <div
765 + contentEditable={true}
766 + onBeforeInput={({type, data}) => {
767 + spyOnBeforeInput();
768 + beforeInputEvent = {type, data};
769 + }}
770 + onCompositionStart={({type, data}) => {
771 + spyOnCompositionStart();
772 + compositionStartEvent = {type, data};
773 + }}
774 + onCompositionUpdate={({type, data}) => {
775 + spyOnCompositionUpdate();
776 + compositionUpdateEvent = {type, data};
777 + }}
778 + />,
779 + container,
780 + );
781 +
782 + scenes.forEach((s, id) => {
783 + beforeInputEvent = null;
784 + compositionStartEvent = null;
785 + compositionUpdateEvent = null;
786 + spyOnBeforeInput = jest.fn();
787 + spyOnCompositionStart = jest.fn();
788 + spyOnCompositionUpdate = jest.fn();
789 + s.eventSimulator.apply(null, [node, ...s.eventSimulatorArgs]);
790 + env.assertions[id].run({
791 + beforeInputEvent,
792 + compositionStartEvent,
793 + compositionUpdateEvent,
794 + spyOnBeforeInput,
795 + spyOnCompositionStart,
796 + spyOnCompositionUpdate,
797 + });
798 + });
799 + };
800 +
801 + it('should extract onBeforeInput when simulating in Webkit on input[type=text]', () => {
802 + testInputComponent(environments[0], scenarios);
803 + });
804 + it('should extract onBeforeInput when simulating in Webkit on contenteditable', () => {
805 + testContentEditableComponent(environments[0], scenarios);
806 + });
807 +
808 + it('should extract onBeforeInput when simulating in IE11 on input[type=text]', () => {
809 + testInputComponent(environments[1], scenarios);
810 + });
811 + it('should extract onBeforeInput when simulating in IE11 on contenteditable', () => {
812 + testContentEditableComponent(environments[1], scenarios);
813 + });
814 +
815 + it('should extract onBeforeInput when simulating in env with no CompositionEvent on input[type=text]', () => {
816 + testInputComponent(environments[2], scenarios);
817 + });
818 +
819 + // in an environment using composition fallback onBeforeInput will not work
820 + // as expected on a contenteditable as keydown and keyup events are translated
821 + // to keypress events
822 +
823 + it('should extract onBeforeInput when simulating in env with only CompositionEvent on input[type=text]', () => {
824 + testInputComponent(environments[3], scenarios);
825 + });
826 +
827 + it('should extract onBeforeInput when simulating in env with only CompositionEvent on contenteditable', () => {
828 + testContentEditableComponent(environments[3], scenarios);
829 + });
830 +});
packages/react-dom/src/events/__tests__/FallbackCompositionState-test.js deleted
-91
@@ -1,91 +0,0 @@
1 -/**
2 - * Copyright (c) 2013-present, Facebook, Inc.
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 -describe('FallbackCompositionState', () => {
13 - let FallbackCompositionState;
14 -
15 - const TEXT = 'Hello world';
16 -
17 - beforeEach(() => {
18 - // TODO: can we express this test with only public API?
19 - FallbackCompositionState = require('../FallbackCompositionState');
20 - });
21 -
22 - function getInput() {
23 - const input = document.createElement('input');
24 - input.value = TEXT;
25 - return input;
26 - }
27 -
28 - function getTextarea() {
29 - const textarea = document.createElement('textarea');
30 - textarea.value = TEXT;
31 - return textarea;
32 - }
33 -
34 - function getContentEditable() {
35 - const editable = document.createElement('div');
36 - const inner = document.createElement('span');
37 - inner.appendChild(document.createTextNode(TEXT));
38 - editable.appendChild(inner);
39 - return editable;
40 - }
41 -
42 - function assertExtractedData(modifiedValue, expectedData) {
43 - const input = getInput();
44 - FallbackCompositionState.initialize(input);
45 - input.value = modifiedValue;
46 - expect(FallbackCompositionState.getData()).toBe(expectedData);
47 - FallbackCompositionState.reset();
48 - }
49 -
50 - it('extracts value via `getText()`', () => {
51 - FallbackCompositionState.initialize(getInput());
52 - expect(FallbackCompositionState.getText()).toBe(TEXT);
53 - FallbackCompositionState.reset();
54 -
55 - FallbackCompositionState.initialize(getTextarea());
56 - expect(FallbackCompositionState.getText()).toBe(TEXT);
57 - FallbackCompositionState.reset();
58 -
59 - FallbackCompositionState.initialize(getContentEditable());
60 - expect(FallbackCompositionState.getText()).toBe(TEXT);
61 - FallbackCompositionState.reset();
62 - });
63 -
64 - describe('Extract fallback data inserted at collapsed cursor', () => {
65 - it('extracts when inserted at start of text', () => {
66 - assertExtractedData('XXXHello world', 'XXX');
67 - });
68 -
69 - it('extracts when inserted within text', () => {
70 - assertExtractedData('Hello XXXworld', 'XXX');
71 - });
72 -
73 - it('extracts when inserted at end of text', () => {
74 - assertExtractedData('Hello worldXXX', 'XXX');
75 - });
76 - });
77 -
78 - describe('Extract fallback data for non-collapsed range', () => {
79 - it('extracts when inserted at start of text', () => {
80 - assertExtractedData('XXX world', 'XXX');
81 - });
82 -
83 - it('extracts when inserted within text', () => {
84 - assertExtractedData('HelXXXrld', 'XXX');
85 - });
86 -
87 - it('extracts when inserted at end of text', () => {
88 - assertExtractedData('Hello XXX', 'XXX');
89 - });
90 - });
91 -});