@samitouri / QOS-React / commits / 32f6f258ba

Remove event simulation of onChange events (#13176)

* Remove event simulation of onChange events It’s time to get rid of even more `ReactTestUtils.Simulate`s. In this PR we remove the event simulation from all onChange tests. To do this, we have to get a setter to the untracked value/checked props. All remaining `ReactTestUtils.Simulate` calls are either testing ReactTestUtils or assert that they do/don't throw. * Use input instead of change event for all but checkbox, radio, and select

Philipp Spieß committed Jul 12, 2018 at 13:11 UTC 32f6f258bad524ec3886901a5b132887f8dd7553
4 files changed +257 -223
packages/react-dom/src/__tests__/ReactDOMInput-test.js
+149 -186
@@ -15,8 +15,9 @@ describe('ReactDOMInput', () => {
15 let React;
16 let ReactDOM;
17 let ReactDOMServer;
18 - let ReactTestUtils;
18 let setUntrackedValue;
19 + let setUntrackedChecked;
20 + let container;
21
22 function dispatchEventOnNode(node, type) {
23 node.dispatchEvent(new Event(type, {bubbles: true, cancelable: true}));
@@ -29,15 +30,24 @@ describe('ReactDOMInput', () => {
30 HTMLInputElement.prototype,
31 'value',
32 ).set;
33 + setUntrackedChecked = Object.getOwnPropertyDescriptor(
34 + HTMLInputElement.prototype,
35 + 'checked',
36 + ).set;
37
38 React = require('react');
39 ReactDOM = require('react-dom');
40 ReactDOMServer = require('react-dom/server');
36 - ReactTestUtils = require('react-dom/test-utils');
41 +
42 + container = document.createElement('div');
43 + document.body.appendChild(container);
44 + });
45 +
46 + afterEach(() => {
47 + document.body.removeChild(container);
48 });
49
50 it('should properly control a value even if no event listener exists', () => {
40 - const container = document.createElement('div');
51 let node;
52
53 expect(() => {
@@ -46,19 +56,13 @@ describe('ReactDOMInput', () => {
56 'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
57 );
58
49 - document.body.appendChild(container);
50 -
51 - try {
52 - setUntrackedValue.call(node, 'giraffe');
59 + setUntrackedValue.call(node, 'giraffe');
60
54 - // This must use the native event dispatching. If we simulate, we will
55 - // bypass the lazy event attachment system so we won't actually test this.
56 - dispatchEventOnNode(node, 'change');
61 + // This must use the native event dispatching. If we simulate, we will
62 + // bypass the lazy event attachment system so we won't actually test this.
63 + dispatchEventOnNode(node, 'input');
64
58 - expect(node.value).toBe('lion');
59 - } finally {
60 - document.body.removeChild(container);
61 - }
65 + expect(node.value).toBe('lion');
66 });
67
68 it('should control a value in reentrant events', () => {
@@ -72,7 +76,7 @@ describe('ReactDOMInput', () => {
76 // Calling focus here will blur the text box which causes a native
77 // change event. Ideally we shouldn't have to fire this ourselves.
78 // Don't remove unless you've verified the fix in #8240 is still covered.
75 - dispatchEventOnNode(this.a, 'change');
79 + dispatchEventOnNode(this.a, 'input');
80 this.b.focus();
81 }
82 blur(currentValue) {
@@ -97,25 +101,19 @@ describe('ReactDOMInput', () => {
101 }
102 }
103
100 - const container = document.createElement('div');
104 const instance = ReactDOM.render(<ControlledInputs />, container);
105
103 - // We need it to be in the body to test native event dispatching.
104 - document.body.appendChild(container);
105 -
106 // Focus the field so we can later blur it.
107 // Don't remove unless you've verified the fix in #8240 is still covered.
108 instance.a.focus();
109 setUntrackedValue.call(instance.a, 'giraffe');
110 // This must use the native event dispatching. If we simulate, we will
111 // bypass the lazy event attachment system so we won't actually test this.
112 - dispatchEventOnNode(instance.a, 'change');
112 + dispatchEventOnNode(instance.a, 'input');
113 dispatchEventOnNode(instance.a, 'blur');
114
115 expect(instance.a.value).toBe('giraffe');
116 expect(instance.switchedFocus).toBe(true);
117 -
118 - document.body.removeChild(container);
117 });
118
119 it('should control values in reentrant events with different targets', () => {
@@ -150,12 +148,8 @@ describe('ReactDOMInput', () => {
148 }
149 }
150
153 - const container = document.createElement('div');
151 const instance = ReactDOM.render(<ControlledInputs />, container);
152
156 - // We need it to be in the body to test native event dispatching.
157 - document.body.appendChild(container);
158 -
153 setUntrackedValue.call(instance.a, 'giraffe');
154 // This must use the native event dispatching. If we simulate, we will
155 // bypass the lazy event attachment system so we won't actually test this.
@@ -163,18 +157,16 @@ describe('ReactDOMInput', () => {
157
158 expect(instance.a.value).toBe('lion');
159 expect(instance.b.checked).toBe(true);
166 -
167 - document.body.removeChild(container);
160 });
161
162 describe('switching text inputs between numeric and string numbers', () => {
163 it('does change the number 2 to "2.0" with no change handler', () => {
164 const stub = <input type="text" value={2} onChange={jest.fn()} />;
173 - const node = ReactTestUtils.renderIntoDocument(stub);
165 + const node = ReactDOM.render(stub, container);
166
175 - node.value = '2.0';
167 + setUntrackedValue.call(node, '2.0');
168
177 - ReactTestUtils.Simulate.change(node);
169 + dispatchEventOnNode(node, 'input');
170
171 expect(node.getAttribute('value')).toBe('2');
172 expect(node.value).toBe('2');
@@ -182,11 +174,11 @@ describe('ReactDOMInput', () => {
174
175 it('does change the string "2" to "2.0" with no change handler', () => {
176 const stub = <input type="text" value={'2'} onChange={jest.fn()} />;
185 - const node = ReactTestUtils.renderIntoDocument(stub);
177 + const node = ReactDOM.render(stub, container);
178
187 - node.value = '2.0';
179 + setUntrackedValue.call(node, '2.0');
180
189 - ReactTestUtils.Simulate.change(node);
181 + dispatchEventOnNode(node, 'input');
182
183 expect(node.getAttribute('value')).toBe('2');
184 expect(node.value).toBe('2');
@@ -207,12 +199,12 @@ describe('ReactDOMInput', () => {
199 }
200 }
201
210 - const stub = ReactTestUtils.renderIntoDocument(<Stub />);
202 + const stub = ReactDOM.render(<Stub />, container);
203 const node = ReactDOM.findDOMNode(stub);
204
213 - node.value = '2.0';
205 + setUntrackedValue.call(node, '2.0');
206
215 - ReactTestUtils.Simulate.change(node);
207 + dispatchEventOnNode(node, 'input');
208
209 expect(node.getAttribute('value')).toBe('2.0');
210 expect(node.value).toBe('2.0');
@@ -231,7 +223,7 @@ describe('ReactDOMInput', () => {
223
224 let stub;
225 expect(() => {
234 - stub = ReactTestUtils.renderIntoDocument(<Stub />);
226 + stub = ReactDOM.render(<Stub />, container);
227 }).toWarnDev(
228 'You provided a `value` prop to a form field ' +
229 'without an `onChange` handler.',
@@ -252,7 +244,7 @@ describe('ReactDOMInput', () => {
244 }
245 }
246
255 - const stub = ReactTestUtils.renderIntoDocument(<Stub />);
247 + const stub = ReactDOM.render(<Stub />, container);
248 const node = ReactDOM.findDOMNode(stub);
249 stub.setState({value: 0});
250
@@ -260,7 +252,6 @@ describe('ReactDOMInput', () => {
252 });
253
254 it('updates the value on radio buttons from "" to 0', function() {
263 - const container = document.createElement('div');
255 ReactDOM.render(
256 <input type="radio" value="" onChange={function() {}} />,
257 container,
@@ -274,7 +265,6 @@ describe('ReactDOMInput', () => {
265 });
266
267 it('updates the value on checkboxes from "" to 0', function() {
277 - const container = document.createElement('div');
268 ReactDOM.render(
269 <input type="checkbox" value="" onChange={function() {}} />,
270 container,
@@ -300,7 +290,7 @@ describe('ReactDOMInput', () => {
290 let stub;
291
292 expect(() => {
303 - stub = ReactTestUtils.renderIntoDocument(<Stub />);
293 + stub = ReactDOM.render(<Stub />, container);
294 }).toWarnDev(
295 'You provided a `value` prop to a form field ' +
296 'without an `onChange` handler.',
@@ -313,7 +303,7 @@ describe('ReactDOMInput', () => {
303
304 it('should display `defaultValue` of number 0', () => {
305 let stub = <input type="text" defaultValue={0} />;
316 - const node = ReactTestUtils.renderIntoDocument(stub);
306 + const node = ReactDOM.render(stub, container);
307
308 expect(node.getAttribute('value')).toBe('0');
309 expect(node.value).toBe('0');
@@ -326,7 +316,7 @@ describe('ReactDOMInput', () => {
316 }
317 }
318
329 - const component = ReactTestUtils.renderIntoDocument(<Test />);
319 + const component = ReactDOM.render(<Test />, container);
320 const node = ReactDOM.findDOMNode(component);
321
322 Object.defineProperty(node, 'defaultValue', {
@@ -345,21 +335,19 @@ describe('ReactDOMInput', () => {
335
336 it('should display "true" for `defaultValue` of `true`', () => {
337 let stub = <input type="text" defaultValue={true} />;
348 - const node = ReactTestUtils.renderIntoDocument(stub);
338 + const node = ReactDOM.render(stub, container);
339
340 expect(node.value).toBe('true');
341 });
342
343 it('should display "false" for `defaultValue` of `false`', () => {
344 let stub = <input type="text" defaultValue={false} />;
355 - const node = ReactTestUtils.renderIntoDocument(stub);
345 + const node = ReactDOM.render(stub, container);
346
347 expect(node.value).toBe('false');
348 });
349
350 it('should update `defaultValue` for uncontrolled input', () => {
361 - const container = document.createElement('div');
362 -
351 const node = ReactDOM.render(
352 <input type="text" defaultValue="0" />,
353 container,
@@ -374,8 +362,6 @@ describe('ReactDOMInput', () => {
362 });
363
364 it('should update `defaultValue` for uncontrolled date/time input', () => {
377 - const container = document.createElement('div');
378 -
365 const node = ReactDOM.render(
366 <input type="date" defaultValue="1980-01-01" />,
367 container,
@@ -392,7 +378,6 @@ describe('ReactDOMInput', () => {
378 });
379
380 it('should take `defaultValue` when changing to uncontrolled input', () => {
395 - const container = document.createElement('div');
381 const node = ReactDOM.render(
382 <input type="text" value="0" readOnly="true" />,
383 container,
@@ -427,7 +412,6 @@ describe('ReactDOMInput', () => {
412 });
413
414 it('should render name attribute if it is supplied', () => {
430 - const container = document.createElement('div');
415 const node = ReactDOM.render(<input type="text" name="name" />, container);
416 expect(node.name).toBe('name');
417 expect(container.firstChild.getAttribute('name')).toBe('name');
@@ -442,7 +426,6 @@ describe('ReactDOMInput', () => {
426 });
427
428 it('should not render name attribute if it is not supplied', () => {
445 - const container = document.createElement('div');
429 ReactDOM.render(<input type="text" />, container);
430 expect(container.firstChild.getAttribute('name')).toBe(null);
431 });
@@ -463,20 +446,19 @@ describe('ReactDOMInput', () => {
446 };
447
448 const stub = <input type="text" defaultValue={objToString} />;
466 - const node = ReactTestUtils.renderIntoDocument(stub);
449 + const node = ReactDOM.render(stub, container);
450
451 expect(node.value).toBe('foobar');
452 });
453
454 it('should display `value` of number 0', () => {
455 const stub = <input type="text" value={0} />;
473 - const node = ReactTestUtils.renderIntoDocument(stub);
456 + const node = ReactDOM.render(stub, container);
457
458 expect(node.value).toBe('0');
459 });
460
461 it('should allow setting `value` to `true`', () => {
479 - const container = document.createElement('div');
462 let stub = <input type="text" value="yolo" onChange={emptyFunction} />;
463 const node = ReactDOM.render(stub, container);
464
@@ -490,7 +472,6 @@ describe('ReactDOMInput', () => {
472 });
473
474 it('should allow setting `value` to `false`', () => {
493 - const container = document.createElement('div');
475 let stub = <input type="text" value="yolo" onChange={emptyFunction} />;
476 const node = ReactDOM.render(stub, container);
477
@@ -504,7 +485,6 @@ describe('ReactDOMInput', () => {
485 });
486
487 it('should allow setting `value` to `objToString`', () => {
507 - const container = document.createElement('div');
488 let stub = <input type="text" value="foo" onChange={emptyFunction} />;
489 const node = ReactDOM.render(stub, container);
490
@@ -523,7 +503,6 @@ describe('ReactDOMInput', () => {
503 });
504
505 it('should not incur unnecessary DOM mutations', () => {
526 - const container = document.createElement('div');
506 ReactDOM.render(<input value="a" onChange={() => {}} />, container);
507
508 const node = container.firstChild;
@@ -546,7 +525,6 @@ describe('ReactDOMInput', () => {
525 });
526
527 it('should not incur unnecessary DOM mutations for numeric type conversion', () => {
549 - const container = document.createElement('div');
528 ReactDOM.render(<input value="0" onChange={() => {}} />, container);
529
530 const node = container.firstChild;
@@ -566,7 +544,6 @@ describe('ReactDOMInput', () => {
544 });
545
546 it('should not incur unnecessary DOM mutations for the boolean type conversion', () => {
569 - const container = document.createElement('div');
547 ReactDOM.render(<input value="true" onChange={() => {}} />, container);
548
549 const node = container.firstChild;
@@ -587,34 +564,32 @@ describe('ReactDOMInput', () => {
564
565 it('should properly control a value of number `0`', () => {
566 const stub = <input type="text" value={0} onChange={emptyFunction} />;
590 - const node = ReactTestUtils.renderIntoDocument(stub);
567 + const node = ReactDOM.render(stub, container);
568
592 - node.value = 'giraffe';
593 - ReactTestUtils.Simulate.change(node);
569 + setUntrackedValue.call(node, 'giraffe');
570 + dispatchEventOnNode(node, 'input');
571 expect(node.value).toBe('0');
572 });
573
574 it('should properly control 0.0 for a text input', () => {
575 const stub = <input type="text" value={0} onChange={emptyFunction} />;
599 - const node = ReactTestUtils.renderIntoDocument(stub);
576 + const node = ReactDOM.render(stub, container);
577
601 - node.value = '0.0';
602 - ReactTestUtils.Simulate.change(node, {target: {value: '0.0'}});
578 + setUntrackedValue.call(node, '0.0');
579 + dispatchEventOnNode(node, 'input');
580 expect(node.value).toBe('0');
581 });
582
583 it('should properly control 0.0 for a number input', () => {
584 const stub = <input type="number" value={0} onChange={emptyFunction} />;
608 - const node = ReactTestUtils.renderIntoDocument(stub);
585 + const node = ReactDOM.render(stub, container);
586
610 - node.value = '0.0';
611 - ReactTestUtils.Simulate.change(node, {target: {value: '0.0'}});
587 + setUntrackedValue.call(node, '0.0');
588 + dispatchEventOnNode(node, 'input');
589 expect(node.value).toBe('0.0');
590 });
591
592 it('should properly transition from an empty value to 0', function() {
616 - const container = document.createElement('div');
617 -
593 ReactDOM.render(<input type="text" value="" />, container);
594 ReactDOM.render(<input type="text" value={0} />, container);
595
@@ -625,8 +600,6 @@ describe('ReactDOMInput', () => {
600 });
601
602 it('should properly transition from 0 to an empty value', function() {
628 - const container = document.createElement('div');
629 -
603 ReactDOM.render(<input type="text" value={0} />, container);
604 ReactDOM.render(<input type="text" value="" />, container);
605
@@ -637,8 +610,6 @@ describe('ReactDOMInput', () => {
610 });
611
612 it('should properly transition a text input from 0 to an empty 0.0', function() {
640 - const container = document.createElement('div');
641 -
613 ReactDOM.render(<input type="text" value={0} />, container);
614 ReactDOM.render(<input type="text" value="0.0" />, container);
615
@@ -649,8 +620,6 @@ describe('ReactDOMInput', () => {
620 });
621
622 it('should properly transition a number input from "" to 0', function() {
652 - const container = document.createElement('div');
653 -
623 ReactDOM.render(<input type="number" value="" />, container);
624 ReactDOM.render(<input type="number" value={0} />, container);
625
@@ -661,8 +630,6 @@ describe('ReactDOMInput', () => {
630 });
631
632 it('should properly transition a number input from "" to "0"', function() {
664 - const container = document.createElement('div');
665 -
633 ReactDOM.render(<input type="number" value="" />, container);
634 ReactDOM.render(<input type="number" value="0" />, container);
635
@@ -679,21 +646,18 @@ describe('ReactDOMInput', () => {
646 handled = true;
647 };
648 const stub = <input type="text" value={0} onChange={handler} />;
682 - const container = document.createElement('div');
649 const node = ReactDOM.render(stub, container);
650
651 setUntrackedValue.call(node, 'giraffe');
652
687 - ReactTestUtils.SimulateNative.input(node, {
688 - path: [node, container],
689 - });
653 + dispatchEventOnNode(node, 'input');
654
655 expect(handled).toBe(true);
656 });
657
658 it('should not set a value for submit buttons unnecessarily', () => {
659 const stub = <input type="submit" />;
696 - const node = ReactTestUtils.renderIntoDocument(stub);
660 + const node = ReactDOM.render(stub, container);
661
662 // The value shouldn't be '', or else the button will have no text; it
663 // should have the default "Submit" or "Submit Query" label. Most browsers
@@ -733,7 +697,7 @@ describe('ReactDOMInput', () => {
697 }
698 }
699
736 - const stub = ReactTestUtils.renderIntoDocument(<RadioGroup />);
700 + const stub = ReactDOM.render(<RadioGroup />, container);
701 const aNode = stub.refs.a;
702 const bNode = stub.refs.b;
703 const cNode = stub.refs.c;
@@ -746,11 +710,8 @@ describe('ReactDOMInput', () => {
710 expect(cNode.checked).toBe(true);
711 expect(cNode.hasAttribute('checked')).toBe(true);
712
749 - bNode.checked = true;
750 - // This next line isn't necessary in a proper browser environment, but
751 - // jsdom doesn't uncheck the others in a group (which makes this whole test
752 - // a little less effective)
753 - aNode.checked = false;
713 + setUntrackedChecked.call(bNode, true);
714 + expect(aNode.checked).toBe(false);
715 expect(cNode.checked).toBe(true);
716
717 // The original 'checked' attribute should be unchanged
@@ -759,7 +720,7 @@ describe('ReactDOMInput', () => {
720 expect(cNode.hasAttribute('checked')).toBe(true);
721
722 // Now let's run the actual ReactDOMInput change event handler
762 - ReactTestUtils.Simulate.change(bNode);
723 + dispatchEventOnNode(bNode, 'click');
724
725 // The original state should have been restored
726 expect(aNode.checked).toBe(true);
@@ -797,16 +758,16 @@ describe('ReactDOMInput', () => {
758 }
759 }
760
800 - const stub = ReactTestUtils.renderIntoDocument(<App />);
761 + const stub = ReactDOM.render(<App />, container);
762 const buttonNode = ReactDOM.findDOMNode(stub).childNodes[0];
763 const firstRadioNode = ReactDOM.findDOMNode(stub).childNodes[1];
764 expect(firstRadioNode.checked).toBe(false);
804 - ReactTestUtils.Simulate.click(buttonNode);
765 + dispatchEventOnNode(buttonNode, 'click');
766 expect(firstRadioNode.checked).toBe(true);
767 });
768
769 it('should control radio buttons if the tree updates during render', () => {
809 - const sharedParent = document.createElement('div');
770 + const sharedParent = container;
771 const container1 = document.createElement('div');
772 const container2 = document.createElement('div');
773
@@ -815,6 +776,15 @@ describe('ReactDOMInput', () => {
776 let aNode;
777 let bNode;
778 class ComponentA extends React.Component {
779 + state = {changed: false};
780 + handleChange = () => {
781 + this.setState({
782 + changed: true,
783 + });
784 + };
785 + componentDidUpdate() {
786 + sharedParent.appendChild(container2);
787 + }
788 componentDidMount() {
789 ReactDOM.render(<ComponentB />, container2);
790 }
@@ -825,8 +795,8 @@ describe('ReactDOMInput', () => {
795 ref={n => (aNode = n)}
796 type="radio"
797 name="fruit"
828 - checked={true}
829 - onChange={emptyFunction}
798 + checked={false}
799 + onChange={this.handleChange}
800 />
801 A
802 </div>
@@ -835,15 +805,6 @@ describe('ReactDOMInput', () => {
805 }
806
807 class ComponentB extends React.Component {
838 - state = {changed: false};
839 - handleChange = () => {
840 - this.setState({
841 - changed: true,
842 - });
843 - };
844 - componentDidUpdate() {
845 - sharedParent.appendChild(container2);
846 - }
808 render() {
809 return (
810 <div>
@@ -851,8 +812,8 @@ describe('ReactDOMInput', () => {
812 ref={n => (bNode = n)}
813 type="radio"
814 name="fruit"
854 - checked={false}
855 - onChange={this.handleChange}
815 + checked={true}
816 + onChange={emptyFunction}
817 />
818 B
819 </div>
@@ -862,31 +823,34 @@ describe('ReactDOMInput', () => {
823
824 ReactDOM.render(<ComponentA />, container1);
825
865 - expect(aNode.checked).toBe(true);
866 - expect(bNode.checked).toBe(false);
826 + expect(aNode.checked).toBe(false);
827 + expect(bNode.checked).toBe(true);
828
868 - bNode.checked = true;
829 + setUntrackedChecked.call(aNode, true);
830 // This next line isn't necessary in a proper browser environment, but
870 - // jsdom doesn't uncheck the others in a group (which makes this whole test
871 - // a little less effective)
872 - aNode.checked = false;
831 + // jsdom doesn't uncheck the others in a group (because they are not yet
832 + // sharing a parent), which makes this whole test a little less effective.
833 + setUntrackedChecked.call(bNode, false);
834
835 // Now let's run the actual ReactDOMInput change event handler
875 - ReactTestUtils.Simulate.change(bNode);
836 + dispatchEventOnNode(aNode, 'click');
837
838 // The original state should have been restored
878 - expect(aNode.checked).toBe(true);
879 - expect(bNode.checked).toBe(false);
839 + expect(aNode.checked).toBe(false);
840 + expect(bNode.checked).toBe(true);
841 });
842
843 it('should warn with value and no onChange handler and readOnly specified', () => {
883 - ReactTestUtils.renderIntoDocument(
844 + ReactDOM.render(
845 <input type="text" value="zoink" readOnly={true} />,
846 + container,
847 );
848 + ReactDOM.unmountComponentAtNode(container);
849
850 expect(() =>
888 - ReactTestUtils.renderIntoDocument(
851 + ReactDOM.render(
852 <input type="text" value="zoink" readOnly={false} />,
853 + container,
854 ),
855 ).toWarnDev(
856 'Warning: Failed prop type: You provided a `value` prop to a form ' +
@@ -898,24 +862,29 @@ describe('ReactDOMInput', () => {
862 });
863
864 it('should have a this value of undefined if bind is not used', () => {
865 + expect.assertions(1);
866 const unboundInputOnChange = function() {
867 expect(this).toBe(undefined);
868 };
869
905 - let instance = <input type="text" onChange={unboundInputOnChange} />;
906 - instance = ReactTestUtils.renderIntoDocument(instance);
870 + const stub = <input type="text" onChange={unboundInputOnChange} />;
871 + const node = ReactDOM.render(stub, container);
872
908 - ReactTestUtils.Simulate.change(instance);
873 + setUntrackedValue.call(node, 'giraffe');
874 + dispatchEventOnNode(node, 'input');
875 });
876
877 it('should warn with checked and no onChange handler with readOnly specified', () => {
912 - ReactTestUtils.renderIntoDocument(
878 + ReactDOM.render(
879 <input type="checkbox" checked="false" readOnly={true} />,
880 + container,
881 );
882 + ReactDOM.unmountComponentAtNode(container);
883
884 expect(() =>
917 - ReactTestUtils.renderIntoDocument(
885 + ReactDOM.render(
886 <input type="checkbox" checked="false" readOnly={false} />,
887 + container,
888 ),
889 ).toWarnDev(
890 'Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler. ' +
@@ -925,7 +894,6 @@ describe('ReactDOMInput', () => {
894 });
895
896 it('should update defaultValue to empty string', () => {
928 - const container = document.createElement('div');
897 ReactDOM.render(<input type="text" defaultValue={'foo'} />, container);
898 ReactDOM.render(<input type="text" defaultValue={''} />, container);
899 expect(container.firstChild.defaultValue).toBe('');
@@ -933,25 +901,27 @@ describe('ReactDOMInput', () => {
901
902 it('should warn if value is null', () => {
903 expect(() =>
936 - ReactTestUtils.renderIntoDocument(<input type="text" value={null} />),
904 + ReactDOM.render(<input type="text" value={null} />, container),
905 ).toWarnDev(
906 '`value` prop on `input` should not be null. ' +
907 'Consider using an empty string to clear the component or `undefined` ' +
908 'for uncontrolled components.',
909 );
910 + ReactDOM.unmountComponentAtNode(container);
911
943 - ReactTestUtils.renderIntoDocument(<input type="text" value={null} />);
912 + ReactDOM.render(<input type="text" value={null} />, container);
913 });
914
915 it('should warn if checked and defaultChecked props are specified', () => {
916 expect(() =>
948 - ReactTestUtils.renderIntoDocument(
917 + ReactDOM.render(
918 <input
919 type="radio"
920 checked={true}
921 defaultChecked={true}
922 readOnly={true}
923 />,
924 + container,
925 ),
926 ).toWarnDev(
927 'A component contains an input of type radio with both checked and defaultChecked props. ' +
@@ -961,21 +931,24 @@ describe('ReactDOMInput', () => {
931 'element and remove one of these props. More info: ' +
932 'https://fb.me/react-controlled-components',
933 );
934 + ReactDOM.unmountComponentAtNode(container);
935
965 - ReactTestUtils.renderIntoDocument(
936 + ReactDOM.render(
937 <input
938 type="radio"
939 checked={true}
940 defaultChecked={true}
941 readOnly={true}
942 />,
943 + container,
944 );
945 });
946
947 it('should warn if value and defaultValue props are specified', () => {
948 expect(() =>
977 - ReactTestUtils.renderIntoDocument(
949 + ReactDOM.render(
950 <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
951 + container,
952 ),
953 ).toWarnDev(
954 'A component contains an input of type text with both value and defaultValue props. ' +
@@ -985,9 +958,11 @@ describe('ReactDOMInput', () => {
958 'element and remove one of these props. More info: ' +
959 'https://fb.me/react-controlled-components',
960 );
961 + ReactDOM.unmountComponentAtNode(container);
962
989 - ReactTestUtils.renderIntoDocument(
963 + ReactDOM.render(
964 <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
965 + container,
966 );
967 });
968
@@ -995,7 +970,6 @@ describe('ReactDOMInput', () => {
970 const stub = (
971 <input type="text" value="controlled" onChange={emptyFunction} />
972 );
998 - const container = document.createElement('div');
973 ReactDOM.render(stub, container);
974 expect(() => ReactDOM.render(<input type="text" />, container)).toWarnDev(
975 'Warning: A component is changing a controlled input of type text to be uncontrolled. ' +
@@ -1010,7 +984,6 @@ describe('ReactDOMInput', () => {
984 const stub = (
985 <input type="text" value="controlled" onChange={emptyFunction} />
986 );
1013 - const container = document.createElement('div');
987 ReactDOM.render(stub, container);
988 expect(() =>
989 ReactDOM.render(<input type="text" value={null} />, container),
@@ -1029,7 +1002,6 @@ describe('ReactDOMInput', () => {
1002 const stub = (
1003 <input type="text" value="controlled" onChange={emptyFunction} />
1004 );
1032 - const container = document.createElement('div');
1005 ReactDOM.render(stub, container);
1006 expect(() =>
1007 ReactDOM.render(
@@ -1047,7 +1019,6 @@ describe('ReactDOMInput', () => {
1019
1020 it('should warn if uncontrolled input (value is undefined) switches to controlled', () => {
1021 const stub = <input type="text" />;
1050 - const container = document.createElement('div');
1022 ReactDOM.render(stub, container);
1023 expect(() =>
1024 ReactDOM.render(<input type="text" value="controlled" />, container),
@@ -1062,7 +1033,6 @@ describe('ReactDOMInput', () => {
1033
1034 it('should warn if uncontrolled input (value is null) switches to controlled', () => {
1035 const stub = <input type="text" value={null} />;
1065 - const container = document.createElement('div');
1036 expect(() => ReactDOM.render(stub, container)).toWarnDev(
1037 '`value` prop on `input` should not be null. ' +
1038 'Consider using an empty string to clear the component or `undefined` for uncontrolled components.',
@@ -1082,7 +1052,6 @@ describe('ReactDOMInput', () => {
1052 const stub = (
1053 <input type="checkbox" checked={true} onChange={emptyFunction} />
1054 );
1085 - const container = document.createElement('div');
1055 ReactDOM.render(stub, container);
1056 expect(() =>
1057 ReactDOM.render(<input type="checkbox" />, container),
@@ -1099,7 +1068,6 @@ describe('ReactDOMInput', () => {
1068 const stub = (
1069 <input type="checkbox" checked={true} onChange={emptyFunction} />
1070 );
1102 - const container = document.createElement('div');
1071 ReactDOM.render(stub, container);
1072 expect(() =>
1073 ReactDOM.render(<input type="checkbox" checked={null} />, container),
@@ -1116,7 +1084,6 @@ describe('ReactDOMInput', () => {
1084 const stub = (
1085 <input type="checkbox" checked={true} onChange={emptyFunction} />
1086 );
1119 - const container = document.createElement('div');
1087 ReactDOM.render(stub, container);
1088 expect(() =>
1089 ReactDOM.render(
@@ -1134,7 +1101,6 @@ describe('ReactDOMInput', () => {
1101
1102 it('should warn if uncontrolled checkbox (checked is undefined) switches to controlled', () => {
1103 const stub = <input type="checkbox" />;
1137 - const container = document.createElement('div');
1104 ReactDOM.render(stub, container);
1105 expect(() =>
1106 ReactDOM.render(<input type="checkbox" checked={true} />, container),
@@ -1149,7 +1115,6 @@ describe('ReactDOMInput', () => {
1115
1116 it('should warn if uncontrolled checkbox (checked is null) switches to controlled', () => {
1117 const stub = <input type="checkbox" checked={null} />;
1152 - const container = document.createElement('div');
1118 ReactDOM.render(stub, container);
1119 expect(() =>
1120 ReactDOM.render(<input type="checkbox" checked={true} />, container),
@@ -1164,7 +1129,6 @@ describe('ReactDOMInput', () => {
1129
1130 it('should warn if controlled radio switches to uncontrolled (checked is undefined)', () => {
1131 const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1167 - const container = document.createElement('div');
1132 ReactDOM.render(stub, container);
1133 expect(() => ReactDOM.render(<input type="radio" />, container)).toWarnDev(
1134 'Warning: A component is changing a controlled input of type radio to be uncontrolled. ' +
@@ -1177,7 +1141,6 @@ describe('ReactDOMInput', () => {
1141
1142 it('should warn if controlled radio switches to uncontrolled (checked is null)', () => {
1143 const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1180 - const container = document.createElement('div');
1144 ReactDOM.render(stub, container);
1145 expect(() =>
1146 ReactDOM.render(<input type="radio" checked={null} />, container),
@@ -1192,7 +1155,6 @@ describe('ReactDOMInput', () => {
1155
1156 it('should warn if controlled radio switches to uncontrolled with defaultChecked', () => {
1157 const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1195 - const container = document.createElement('div');
1158 ReactDOM.render(stub, container);
1159 expect(() =>
1160 ReactDOM.render(<input type="radio" defaultChecked={true} />, container),
@@ -1207,7 +1169,6 @@ describe('ReactDOMInput', () => {
1169
1170 it('should warn if uncontrolled radio (checked is undefined) switches to controlled', () => {
1171 const stub = <input type="radio" />;
1210 - const container = document.createElement('div');
1172 ReactDOM.render(stub, container);
1173 expect(() =>
1174 ReactDOM.render(<input type="radio" checked={true} />, container),
@@ -1222,7 +1183,6 @@ describe('ReactDOMInput', () => {
1183
1184 it('should warn if uncontrolled radio (checked is null) switches to controlled', () => {
1185 const stub = <input type="radio" checked={null} />;
1225 - const container = document.createElement('div');
1186 ReactDOM.render(stub, container);
1187 expect(() =>
1188 ReactDOM.render(<input type="radio" checked={true} />, container),
@@ -1236,7 +1196,6 @@ describe('ReactDOMInput', () => {
1196 });
1197
1198 it('should not warn if radio value changes but never becomes controlled', () => {
1239 - const container = document.createElement('div');
1199 ReactDOM.render(<input type="radio" value="value" />, container);
1200 ReactDOM.render(<input type="radio" />, container);
1201 ReactDOM.render(
@@ -1251,7 +1210,6 @@ describe('ReactDOMInput', () => {
1210 });
1211
1212 it('should not warn if radio value changes but never becomes uncontrolled', () => {
1254 - const container = document.createElement('div');
1213 ReactDOM.render(
1214 <input type="radio" checked={false} onChange={() => null} />,
1215 container,
@@ -1269,7 +1227,6 @@ describe('ReactDOMInput', () => {
1227 });
1228
1229 it('should warn if radio checked false changes to become uncontrolled', () => {
1272 - const container = document.createElement('div');
1230 ReactDOM.render(
1231 <input
1232 type="radio"
@@ -1314,7 +1271,7 @@ describe('ReactDOMInput', () => {
1271 return el;
1272 });
1273
1317 - ReactTestUtils.renderIntoDocument(
1274 + ReactDOM.render(
1275 <input
1276 value="0"
1277 onChange={() => {}}
@@ -1323,6 +1280,7 @@ describe('ReactDOMInput', () => {
1280 max="100"
1281 step="1"
1282 />,
1283 + container,
1284 );
1285 expect(log).toEqual([
1286 'set attribute type',
@@ -1336,9 +1294,7 @@ describe('ReactDOMInput', () => {
1294 });
1295
1296 it('sets value properly with type coming later in props', () => {
1339 - const input = ReactTestUtils.renderIntoDocument(
1340 - <input value="hi" type="radio" />,
1341 - );
1297 + const input = ReactDOM.render(<input value="hi" type="radio" />, container);
1298 expect(input.value).toBe('hi');
1299 });
1300
@@ -1352,7 +1308,7 @@ describe('ReactDOMInput', () => {
1308 }
1309 }
1310
1355 - const input = ReactTestUtils.renderIntoDocument(<Input />);
1311 + const input = ReactDOM.render(<Input />, container);
1312 const node = ReactDOM.findDOMNode(input);
1313
1314 // If the value is set before the type, a validation warning will raise and
@@ -1388,9 +1344,7 @@ describe('ReactDOMInput', () => {
1344 return el;
1345 });
1346
1391 - ReactTestUtils.renderIntoDocument(
1392 - <input type="date" defaultValue="1980-01-01" />,
1393 - );
1347 + ReactDOM.render(<input type="date" defaultValue="1980-01-01" />, container);
1348 expect(log).toEqual([
1349 'node.setAttribute("type", "date")',
1350 'node.value = "1980-01-01"',
@@ -1419,61 +1373,68 @@ describe('ReactDOMInput', () => {
1373
1374 it('always sets the attribute when values change on text inputs', function() {
1375 const Input = getTestInput();
1422 - const stub = ReactTestUtils.renderIntoDocument(<Input type="text" />);
1376 + const stub = ReactDOM.render(<Input type="text" />, container);
1377 const node = ReactDOM.findDOMNode(stub);
1378
1425 - ReactTestUtils.Simulate.change(node, {target: {value: '2'}});
1379 + setUntrackedValue.call(node, '2');
1380 + dispatchEventOnNode(node, 'input');
1381
1382 expect(node.getAttribute('value')).toBe('2');
1383 });
1384
1385 it('does not set the value attribute on number inputs if focused', () => {
1386 const Input = getTestInput();
1432 - const stub = ReactTestUtils.renderIntoDocument(
1387 + const stub = ReactDOM.render(
1388 <Input type="number" value="1" />,
1389 + container,
1390 );
1391 const node = ReactDOM.findDOMNode(stub);
1392
1393 node.focus();
1394
1439 - ReactTestUtils.Simulate.change(node, {target: {value: '2'}});
1395 + setUntrackedValue.call(node, '2');
1396 + dispatchEventOnNode(node, 'input');
1397
1398 expect(node.getAttribute('value')).toBe('1');
1399 });
1400
1401 it('sets the value attribute on number inputs on blur', () => {
1402 const Input = getTestInput();
1446 - const stub = ReactTestUtils.renderIntoDocument(
1403 + const stub = ReactDOM.render(
1404 <Input type="number" value="1" />,
1405 + container,
1406 );
1407 const node = ReactDOM.findDOMNode(stub);
1408
1451 - ReactTestUtils.Simulate.change(node, {target: {value: '2'}});
1452 - ReactTestUtils.SimulateNative.blur(node);
1409 + setUntrackedValue.call(node, '2');
1410 + dispatchEventOnNode(node, 'input');
1411 + dispatchEventOnNode(node, 'blur');
1412
1413 expect(node.getAttribute('value')).toBe('2');
1414 });
1415
1416 it('an uncontrolled number input will not update the value attribute on blur', () => {
1458 - const node = ReactTestUtils.renderIntoDocument(
1417 + const node = ReactDOM.render(
1418 <input type="number" defaultValue="1" />,
1419 + container,
1420 );
1421
1462 - node.value = 4;
1422 + setUntrackedValue.call(node, 4);
1423
1464 - ReactTestUtils.SimulateNative.blur(node);
1424 + dispatchEventOnNode(node, 'blur');
1425
1426 expect(node.getAttribute('value')).toBe('1');
1427 });
1428
1429 it('an uncontrolled text input will not update the value attribute on blur', () => {
1470 - const node = ReactTestUtils.renderIntoDocument(
1430 + const node = ReactDOM.render(
1431 <input type="text" defaultValue="1" />,
1432 + container,
1433 );
1434
1474 - node.value = 4;
1435 + setUntrackedValue.call(node, 4);
1436
1476 - ReactTestUtils.SimulateNative.blur(node);
1437 + dispatchEventOnNode(node, 'blur');
1438
1439 expect(node.getAttribute('value')).toBe('1');
1440 });
@@ -1483,7 +1444,12 @@ describe('ReactDOMInput', () => {
1444 let input;
1445
1446 function renderInputWithStringThenWithUndefined() {
1447 + let setValueToUndefined;
1448 class Input extends React.Component {
1449 + constructor() {
1450 + super();
1451 + setValueToUndefined = () => this.setState({value: undefined});
1452 + }
1453 state = {value: 'first'};
1454 render() {
1455 return (
@@ -1495,10 +1461,11 @@ describe('ReactDOMInput', () => {
1461 }
1462 }
1463
1498 - const stub = ReactTestUtils.renderIntoDocument(<Input />);
1464 + const stub = ReactDOM.render(<Input />, container);
1465 input = ReactDOM.findDOMNode(stub);
1500 - ReactTestUtils.Simulate.change(input, {target: {value: 'latest'}});
1501 - ReactTestUtils.Simulate.change(input, {target: {value: undefined}});
1466 + setUntrackedValue.call(input, 'latest');
1467 + dispatchEventOnNode(input, 'input');
1468 + setValueToUndefined();
1469 }
1470
1471 it('reverts the value attribute to the initial value', () => {
@@ -1522,7 +1489,12 @@ describe('ReactDOMInput', () => {
1489 let input;
1490
1491 function renderInputWithStringThenWithNull() {
1492 + let setValueToNull;
1493 class Input extends React.Component {
1494 + constructor() {
1495 + super();
1496 + setValueToNull = () => this.setState({value: null});
1497 + }
1498 state = {value: 'first'};
1499 render() {
1500 return (
@@ -1534,10 +1506,11 @@ describe('ReactDOMInput', () => {
1506 }
1507 }
1508
1537 - const stub = ReactTestUtils.renderIntoDocument(<Input />);
1509 + const stub = ReactDOM.render(<Input />, container);
1510 input = ReactDOM.findDOMNode(stub);
1539 - ReactTestUtils.Simulate.change(input, {target: {value: 'latest'}});
1540 - ReactTestUtils.Simulate.change(input, {target: {value: null}});
1511 + setUntrackedValue.call(input, 'latest');
1512 + dispatchEventOnNode(input, 'input');
1513 + setValueToNull();
1514 }
1515
1516 it('reverts the value attribute to the initial value', () => {
@@ -1565,7 +1538,6 @@ describe('ReactDOMInput', () => {
1538
1539 describe('When given a Symbol value', function() {
1540 it('treats initial Symbol value as an empty string', function() {
1568 - const container = document.createElement('div');
1541 expect(() =>
1542 ReactDOM.render(
1543 <input value={Symbol('foobar')} onChange={() => {}} />,
@@ -1579,7 +1551,6 @@ describe('ReactDOMInput', () => {
1551 });
1552
1553 it('treats updated Symbol value as an empty string', function() {
1582 - const container = document.createElement('div');
1554 ReactDOM.render(<input value="foo" onChange={() => {}} />, container);
1555 expect(() =>
1556 ReactDOM.render(
@@ -1594,7 +1565,6 @@ describe('ReactDOMInput', () => {
1565 });
1566
1567 it('treats initial Symbol defaultValue as an empty string', function() {
1597 - const container = document.createElement('div');
1568 ReactDOM.render(<input defaultValue={Symbol('foobar')} />, container);
1569 const node = container.firstChild;
1570
@@ -1604,7 +1574,6 @@ describe('ReactDOMInput', () => {
1574 });
1575
1576 it('treats updated Symbol defaultValue as an empty string', function() {
1607 - const container = document.createElement('div');
1577 ReactDOM.render(<input defaultValue="foo" />, container);
1578 ReactDOM.render(<input defaultValue={Symbol('foobar')} />, container);
1579 const node = container.firstChild;
@@ -1617,7 +1586,6 @@ describe('ReactDOMInput', () => {
1586
1587 describe('When given a function value', function() {
1588 it('treats initial function value as an empty string', function() {
1620 - const container = document.createElement('div');
1589 expect(() =>
1590 ReactDOM.render(
1591 <input value={() => {}} onChange={() => {}} />,
@@ -1631,7 +1599,6 @@ describe('ReactDOMInput', () => {
1599 });
1600
1601 it('treats updated function value as an empty string', function() {
1634 - const container = document.createElement('div');
1602 ReactDOM.render(<input value="foo" onChange={() => {}} />, container);
1603 expect(() =>
1604 ReactDOM.render(
@@ -1646,7 +1613,6 @@ describe('ReactDOMInput', () => {
1613 });
1614
1615 it('treats initial function defaultValue as an empty string', function() {
1649 - const container = document.createElement('div');
1616 ReactDOM.render(<input defaultValue={() => {}} />, container);
1617 const node = container.firstChild;
1618
@@ -1656,7 +1622,6 @@ describe('ReactDOMInput', () => {
1622 });
1623
1624 it('treats updated function defaultValue as an empty string', function() {
1659 - const container = document.createElement('div');
1625 ReactDOM.render(<input defaultValue="foo" />, container);
1626 ReactDOM.render(<input defaultValue={() => {}} />, container);
1627 const node = container.firstChild;
@@ -1673,7 +1638,6 @@ describe('ReactDOMInput', () => {
1638 // value in order to "dettach" it from defaultValue. This had the unfortunate
1639 // side-effect of assigning value="on" to radio and checkboxes
1640 it('does not add "on" in absence of value on a checkbox', function() {
1676 - const container = document.createElement('div');
1641 ReactDOM.render(
1642 <input type="checkbox" defaultChecked={true} />,
1643 container,
@@ -1685,7 +1649,6 @@ describe('ReactDOMInput', () => {
1649 });
1650
1651 it('does not add "on" in absence of value on a radio', function() {
1688 - const container = document.createElement('div');
1652 ReactDOM.render(<input type="radio" defaultChecked={true} />, container);
1653 const node = container.firstChild;
1654
packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+12 -3
@@ -615,11 +615,20 @@ describe('ReactDOMSelect', () => {
615 <option value="gorilla">A gorilla!</option>
616 </select>
617 );
618 - const node = ReactTestUtils.renderIntoDocument(stub);
618 + const container = document.createElement('div');
619 + document.body.appendChild(container);
620
620 - ReactTestUtils.Simulate.change(node);
621 + try {
622 + const node = ReactDOM.render(stub, container);
623
622 - expect(node.value).toBe('giraffe');
624 + node.dispatchEvent(
625 + new Event('change', {bubbles: true, cancelable: false}),
626 + );
627 +
628 + expect(node.value).toBe('giraffe');
629 + } finally {
630 + document.body.removeChild(container);
631 + }
632 });
633
634 it('should warn if value and defaultValue props are specified', () => {
packages/react-dom/src/__tests__/ReactDOMServerIntegrationForms-test.js
+78 -30
@@ -14,14 +14,12 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
14 let React;
15 let ReactDOM;
16 let ReactDOMServer;
17 -let ReactTestUtils;
17
18 function initModules() {
19 // Reset warning cache.
20 jest.resetModuleRegistry();
21 React = require('react');
22 ReactDOM = require('react-dom');
24 - ReactTestUtils = require('react-dom/test-utils');
23 ReactDOMServer = require('react-dom/server');
24
25 // Make them available to the helpers.
@@ -519,35 +517,63 @@ describe('ReactDOMServerIntegration', () => {
517
518 describe('user interaction with controlled inputs', function() {
519 itClientRenders('a controlled text input', async render => {
520 + const setUntrackedValue = Object.getOwnPropertyDescriptor(
521 + HTMLInputElement.prototype,
522 + 'value',
523 + ).set;
524 +
525 let changeCount = 0;
526 const e = await render(
527 <ControlledInput onChange={() => changeCount++} />,
528 );
526 - expect(changeCount).toBe(0);
527 - expect(e.value).toBe('Hello');
529 + const container = e.parentNode;
530 + document.body.appendChild(container);
531
529 - // simulate a user typing.
530 - e.value = 'Goodbye';
531 - ReactTestUtils.Simulate.change(e);
532 + try {
533 + expect(changeCount).toBe(0);
534 + expect(e.value).toBe('Hello');
535
533 - expect(changeCount).toBe(1);
534 - expect(e.value).toBe('Goodbye');
536 + // simulate a user typing.
537 + setUntrackedValue.call(e, 'Goodbye');
538 + e.dispatchEvent(
539 + new Event('input', {bubbles: true, cancelable: false}),
540 + );
541 +
542 + expect(changeCount).toBe(1);
543 + expect(e.value).toBe('Goodbye');
544 + } finally {
545 + document.body.removeChild(container);
546 + }
547 });
548
549 itClientRenders('a controlled textarea', async render => {
550 + const setUntrackedValue = Object.getOwnPropertyDescriptor(
551 + HTMLTextAreaElement.prototype,
552 + 'value',
553 + ).set;
554 +
555 let changeCount = 0;
556 const e = await render(
557 <ControlledTextArea onChange={() => changeCount++} />,
558 );
542 - expect(changeCount).toBe(0);
543 - expect(e.value).toBe('Hello');
559 + const container = e.parentNode;
560 + document.body.appendChild(container);
561
545 - // simulate a user typing.
546 - e.value = 'Goodbye';
547 - ReactTestUtils.Simulate.change(e);
562 + try {
563 + expect(changeCount).toBe(0);
564 + expect(e.value).toBe('Hello');
565
549 - expect(changeCount).toBe(1);
550 - expect(e.value).toBe('Goodbye');
566 + // simulate a user typing.
567 + setUntrackedValue.call(e, 'Goodbye');
568 + e.dispatchEvent(
569 + new Event('input', {bubbles: true, cancelable: false}),
570 + );
571 +
572 + expect(changeCount).toBe(1);
573 + expect(e.value).toBe('Goodbye');
574 + } finally {
575 + document.body.removeChild(container);
576 + }
577 });
578
579 itClientRenders('a controlled checkbox', async render => {
@@ -555,31 +581,53 @@ describe('ReactDOMServerIntegration', () => {
581 const e = await render(
582 <ControlledCheckbox onChange={() => changeCount++} />,
583 );
558 - expect(changeCount).toBe(0);
559 - expect(e.checked).toBe(true);
584 + const container = e.parentNode;
585 + document.body.appendChild(container);
586
561 - // simulate a user typing.
562 - e.checked = false;
563 - ReactTestUtils.Simulate.change(e);
587 + try {
588 + expect(changeCount).toBe(0);
589 + expect(e.checked).toBe(true);
590
565 - expect(changeCount).toBe(1);
566 - expect(e.checked).toBe(false);
591 + // simulate a user clicking.
592 + e.dispatchEvent(
593 + new Event('click', {bubbles: true, cancelable: true}),
594 + );
595 +
596 + expect(changeCount).toBe(1);
597 + expect(e.checked).toBe(false);
598 + } finally {
599 + document.body.removeChild(container);
600 + }
601 });
602
603 itClientRenders('a controlled select', async render => {
604 + const setUntrackedValue = Object.getOwnPropertyDescriptor(
605 + HTMLSelectElement.prototype,
606 + 'value',
607 + ).set;
608 +
609 let changeCount = 0;
610 const e = await render(
611 <ControlledSelect onChange={() => changeCount++} />,
612 );
574 - expect(changeCount).toBe(0);
575 - expect(e.value).toBe('Hello');
613 + const container = e.parentNode;
614 + document.body.appendChild(container);
615
577 - // simulate a user typing.
578 - e.value = 'Goodbye';
579 - ReactTestUtils.Simulate.change(e);
616 + try {
617 + expect(changeCount).toBe(0);
618 + expect(e.value).toBe('Hello');
619
581 - expect(changeCount).toBe(1);
582 - expect(e.value).toBe('Goodbye');
620 + // simulate a user typing.
621 + setUntrackedValue.call(e, 'Goodbye');
622 + e.dispatchEvent(
623 + new Event('change', {bubbles: true, cancelable: false}),
624 + );
625 +
626 + expect(changeCount).toBe(1);
627 + expect(e.value).toBe('Goodbye');
628 + } finally {
629 + document.body.removeChild(container);
630 + }
631 });
632 });
633
packages/react-dom/src/__tests__/ReactDOMTextarea-test.js
+18 -4
@@ -237,11 +237,25 @@ describe('ReactDOMTextarea', () => {
237
238 it('should properly control a value of number `0`', () => {
239 const stub = <textarea value={0} onChange={emptyFunction} />;
240 - const node = renderTextarea(stub);
240 + const setUntrackedValue = Object.getOwnPropertyDescriptor(
241 + HTMLTextAreaElement.prototype,
242 + 'value',
243 + ).set;
244
242 - node.value = 'giraffe';
243 - ReactTestUtils.Simulate.change(node);
244 - expect(node.value).toBe('0');
245 + const container = document.createElement('div');
246 + document.body.appendChild(container);
247 +
248 + try {
249 + const node = renderTextarea(stub, container);
250 +
251 + setUntrackedValue.call(node, 'giraffe');
252 + node.dispatchEvent(
253 + new Event('input', {bubbles: true, cancelable: false}),
254 + );
255 + expect(node.value).toBe('0');
256 + } finally {
257 + document.body.removeChild(container);
258 + }
259 });
260
261 it('should treat children like `defaultValue`', () => {