Repository: facebook/react
Pull request: Convert ChangeEventPlugin to createRoot


Changed files:
filename: packages/react-dom/src/events/plugins/__tests__/ChangeEventPlugin-test.js
patch: @@ -83,92 +83,106 @@ describe('ChangeEventPlugin', () => {
   // keep track of the "current" value and only fire events when it changes.
   // See https://github.com/facebook/react/pull/5746.
 
-  it('should consider initial text value to be current', () => {
+  it('should consider initial text value to be current', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <input type="text" onChange={cb} defaultValue="foo" />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="text" onChange={cb} defaultValue="foo" />);
+    });
+
+    const node = container.firstChild;
     node.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
     node.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
 
     // There should be no React change events because the value stayed the same.
     expect(called).toBe(0);
   });
 
-  it('should consider initial text value to be current (capture)', () => {
+  it('should consider initial text value to be current (capture)', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <input type="text" onChangeCapture={cb} defaultValue="foo" />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(
+        <input type="text" onChangeCapture={cb} defaultValue="foo" />,
+      );
+    });
+
+    const node = container.firstChild;
     node.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
     node.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
 
     // There should be no React change events because the value stayed the same.
     expect(called).toBe(0);
   });
 
-  it('should not invoke a change event for textarea same value', () => {
+  it('should not invoke a change event for textarea same value', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <textarea onChange={cb} defaultValue="initial" />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<textarea onChange={cb} defaultValue="initial" />);
+    });
+
+    const node = container.firstChild;
     node.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
     node.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
     // There should be no React change events because the value stayed the same.
     expect(called).toBe(0);
   });
 
-  it('should not invoke a change event for textarea same value (capture)', () => {
+  it('should not invoke a change event for textarea same value (capture)', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <textarea onChangeCapture={cb} defaultValue="initial" />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<textarea onChangeCapture={cb} defaultValue="initial" />);
+    });
+
+    const node = container.firstChild;
     node.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
     node.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
     // There should be no React change events because the value stayed the same.
     expect(called).toBe(0);
   });
 
-  it('should consider initial checkbox checked=true to be current', () => {
+  it('should consider initial checkbox checked=true to be current', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <input type="checkbox" onChange={cb} defaultChecked={true} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(
+        <input type="checkbox" onChange={cb} defaultChecked={true} />,
+      );
+    });
+
+    const node = container.firstChild;
 
     // Secretly, set `checked` to false, so that dispatching the `click` will
     // make it `true` again. Thus, at the time of the event, React should not
@@ -181,18 +195,22 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(0);
   });
 
-  it('should consider initial checkbox checked=false to be current', () => {
+  it('should consider initial checkbox checked=false to be current', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <input type="checkbox" onChange={cb} defaultChecked={false} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(
+        <input type="checkbox" onChange={cb} defaultChecked={false} />,
+      );
+    });
+
+    const node = container.firstChild;
 
     // Secretly, set `checked` to true, so that dispatching the `click` will
     // make it `false` again. Thus, at the time of the event, React should not
@@ -205,18 +223,20 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(0);
   });
 
-  it('should fire change for checkbox input', () => {
+  it('should fire change for checkbox input', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const node = ReactDOM.render(
-      <input type="checkbox" onChange={cb} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="checkbox" onChange={cb} />);
+    });
+
+    const node = container.firstChild;
 
     expect(node.checked).toBe(false);
     node.dispatchEvent(
@@ -235,18 +255,20 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(2);
   });
 
-  it('should not fire change setting the value programmatically', () => {
+  it('should not fire change setting the value programmatically', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const input = ReactDOM.render(
-      <input type="text" defaultValue="foo" onChange={cb} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="text" defaultValue="foo" onChange={cb} />);
+    });
+
+    const input = container.firstChild;
 
     // Set it programmatically.
     input.value = 'bar';
@@ -271,18 +293,20 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(1);
   });
 
-  it('should not distinguish equal string and number values', () => {
+  it('should not distinguish equal string and number values', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const input = ReactDOM.render(
-      <input type="text" defaultValue="42" onChange={cb} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="text" defaultValue="42" onChange={cb} />);
+    });
+
+    const input = container.firstChild;
 
     // When we set `value` as a property, React updates the "current" value
     // that it tracks internally. The "current" value is later used to determine
@@ -296,18 +320,22 @@ describe('ChangeEventPlugin', () => {
   });
 
   // See a similar input test above for a detailed description of why.
-  it('should not fire change when setting checked programmatically', () => {
+  it('should not fire change when setting checked programmatically', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const input = ReactDOM.render(
-      <input type="checkbox" onChange={cb} defaultChecked={false} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(
+        <input type="checkbox" onChange={cb} defaultChecked={false} />,
+      );
+    });
+
+    const input = container.firstChild;
 
     // Set the value, updating the "current" value that React tracks to true.
     input.checked = true;
@@ -326,32 +354,41 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(1);
   });
 
-  it('should unmount', () => {
-    const input = ReactDOM.render(<input />, container);
+  it('should unmount', async () => {
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input />);
+    });
+
+    const input = container.firstChild;
 
-    ReactDOM.unmountComponentAtNode(container);
+    await act(() => {
+      root.unmount();
+    });
   });
 
-  it('should only fire change for checked radio button once', () => {
+  it('should only fire change for checked radio button once', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const input = ReactDOM.render(
-      <input type="radio" onChange={cb} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="radio" onChange={cb} />);
+    });
+
+    const input = container.firstChild;
 
     setUntrackedChecked.call(input, true);
     input.dispatchEvent(new Event('click', {bubbles: true, cancelable: true}));
     input.dispatchEvent(new Event('click', {bubbles: true, cancelable: true}));
     expect(called).toBe(1);
   });
 
-  it('should track radio button cousins in a group', () => {
+  it('should track radio button cousins in a group', async () => {
     let called1 = 0;
     let called2 = 0;
 
@@ -365,13 +402,17 @@ describe('ChangeEventPlugin', () => {
       expect(e.type).toBe('change');
     }
 
-    const div = ReactDOM.render(
-      <div>
-        <input type="radio" name="group" onChange={cb1} />
-        <input type="radio" name="group" onChange={cb2} />
-      </div>,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(
+        <div>
+          <input type="radio" name="group" onChange={cb1} />
+          <input type="radio" name="group" onChange={cb2} />
+        </div>,
+      );
+    });
+
+    const div = container.firstChild;
     const option1 = div.childNodes[0];
     const option2 = div.childNodes[1];
 
@@ -455,18 +496,20 @@ describe('ChangeEventPlugin', () => {
     });
   });
 
-  it('should listen for both change and input events when supported', () => {
+  it('should listen for both change and input events when supported', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const input = ReactDOM.render(
-      <input type="range" onChange={cb} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="range" onChange={cb} />);
+    });
+
+    const input = container.firstChild;
 
     setUntrackedValue.call(input, 10);
     input.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
@@ -477,18 +520,20 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(2);
   });
 
-  it('should only fire events when the value changes for range inputs', () => {
+  it('should only fire events when the value changes for range inputs', async () => {
     let called = 0;
 
     function cb(e) {
       called++;
       expect(e.type).toBe('change');
     }
 
-    const input = ReactDOM.render(
-      <input type="range" onChange={cb} />,
-      container,
-    );
+    const root = ReactDOMClient.createRoot(container);
+    await act(() => {
+      root.render(<input type="range" onChange={cb} />);
+    });
+
+    const input = container.firstChild;
     setUntrackedValue.call(input, '40');
     input.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
     input.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
@@ -500,7 +545,7 @@ describe('ChangeEventPlugin', () => {
     expect(called).toBe(2);
   });
 
-  it('does not crash for nodes with custom value property', () => {
+  it('does not crash for nodes with custom value property', async () => {
     let originalCreateElement;
     // https://github.com/facebook/react/issues/10196
     try {
@@ -514,16 +559,25 @@ describe('ChangeEventPlugin', () => {
         return node;
       };
       const div = document.createElement('div');
+      const root = ReactDOMClient.createRoot(div);
       // Mount
-      const node = ReactDOM.render(<input type="text" />, div);
+      await act(() => {
+        root.render(<input type="text" />);
+      });
+      const node = div.firstChild;
       // Update
-      ReactDOM.render(<input type="text" />, div);
+      await act(() => {
+        root.render(<input type="text" />);
+      });
+
       // Change
       node.dispatchEvent(
         new Event('change', {bubbles: true, cancelable: true}),
       );
       // Unmount
-      ReactDOM.unmountComponentAtNode(div);
+      await act(() => {
+        root.unmount();
+      });
     } finally {
       document.createElement = originalCreateElement;
     }