Repository: facebook/react
Pull request: Remove ReactDOM.render tests in ReactDOMConsoleErrorReporting-test

Each it block here was duplicated to cover ReactDOM.render and ReactDOMClient.createRoot. Here we delete the ReactDOM.render coverage.

Changed files:
filename: packages/react-dom/src/__tests__/ReactDOMConsoleErrorReporting-test.js
patch: @@ -10,7 +10,6 @@
 describe('ReactDOMConsoleErrorReporting', () => {
   let act;
   let React;
-  let ReactDOM;
   let ReactDOMClient;
 
   let ErrorBoundary;
@@ -23,7 +22,6 @@ describe('ReactDOMConsoleErrorReporting', () => {
     jest.resetModules();
     act = require('internal-test-utils').act;
     React = require('react');
-    ReactDOM = require('react-dom');
     ReactDOMClient = require('react-dom/client');
 
     const InternalTestUtils = require('internal-test-utils');
@@ -607,537 +605,4 @@ describe('ReactDOMConsoleErrorReporting', () => {
       }
     });
   });
-
-  describe('ReactDOM.render', () => {
-    it('logs errors during event handlers', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        return (
-          <button
-            onClick={() => {
-              throw Error('Boom');
-            }}>
-            click me
-          </button>
-        );
-      }
-
-      await act(() => {
-        ReactDOM.render(<Foo />, container);
-      });
-
-      await act(() => {
-        container.firstChild.dispatchEvent(
-          new MouseEvent('click', {
-            bubbles: true,
-          }),
-        );
-      });
-
-      if (__DEV__) {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported because we're in a browser click event:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-          [
-            // This one is jsdom-only. Real browser deduplicates it.
-            // (In DEV, we have a nested event due to guarded callback.)
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported because we're in a browser click event:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // This one is jsdom-only. Real browser deduplicates it.
-            // (In DEV, we have a nested event due to guarded callback.)
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-        ]);
-      } else {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported because we're in a browser click event:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported because we're in a browser click event:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-
-    it('logs render errors without an error boundary', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        throw Error('Boom');
-      }
-
-      expect(() => {
-        ReactDOM.render(<Foo />, container);
-      }).toThrow('Boom');
-
-      if (__DEV__) {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported due to guarded callback:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported due to the guarded callback:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // Addendum by React:
-            expect.stringContaining(
-              'The above error occurred in the <Foo> component',
-            ),
-          ],
-        ]);
-      } else {
-        // The top-level error was caught with try/catch, and there's no guarded callback,
-        // so in production we don't see an error event.
-        expect(windowOnError.mock.calls).toEqual([]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported by React with no extra message:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-
-    it('logs render errors with an error boundary', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        throw Error('Boom');
-      }
-
-      await act(() => {
-        ReactDOM.render(
-          <ErrorBoundary>
-            <Foo />
-          </ErrorBoundary>,
-          container,
-        );
-      });
-
-      if (__DEV__) {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported due to guarded callback:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported by jsdom due to the guarded callback:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // Addendum by React:
-            expect.stringContaining(
-              'The above error occurred in the <Foo> component',
-            ),
-          ],
-        ]);
-      } else {
-        // The top-level error was caught with try/catch, and there's no guarded callback,
-        // so in production we don't see an error event.
-        expect(windowOnError.mock.calls).toEqual([]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported by React with no extra message:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-
-    it('logs layout effect errors without an error boundary', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        React.useLayoutEffect(() => {
-          throw Error('Boom');
-        }, []);
-        return null;
-      }
-
-      expect(() => {
-        ReactDOM.render(<Foo />, container);
-      }).toThrow('Boom');
-
-      if (__DEV__) {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported due to guarded callback:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported due to the guarded callback:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // Addendum by React:
-            expect.stringContaining(
-              'The above error occurred in the <Foo> component',
-            ),
-          ],
-        ]);
-      } else {
-        // The top-level error was caught with try/catch, and there's no guarded callback,
-        // so in production we don't see an error event.
-        expect(windowOnError.mock.calls).toEqual([]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported by React with no extra message:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-
-    it('logs layout effect errors with an error boundary', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        React.useLayoutEffect(() => {
-          throw Error('Boom');
-        }, []);
-        return null;
-      }
-
-      await act(() => {
-        ReactDOM.render(
-          <ErrorBoundary>
-            <Foo />
-          </ErrorBoundary>,
-          container,
-        );
-      });
-
-      if (__DEV__) {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported due to guarded callback:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported by jsdom due to the guarded callback:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // Addendum by React:
-            expect.stringContaining(
-              'The above error occurred in the <Foo> component',
-            ),
-          ],
-        ]);
-      } else {
-        // The top-level error was caught with try/catch, and there's no guarded callback,
-        // so in production we don't see an error event.
-        expect(windowOnError.mock.calls).toEqual([]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported by React with no extra message:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-
-    it('logs passive effect errors without an error boundary', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        React.useEffect(() => {
-          throw Error('Boom');
-        }, []);
-        return null;
-      }
-
-      await act(async () => {
-        ReactDOM.render(<Foo />, container);
-        await waitForThrow('Boom');
-      });
-
-      if (__DEV__) {
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            // Reported due to guarded callback:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported due to the guarded callback:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // Addendum by React:
-            expect.stringContaining(
-              'The above error occurred in the <Foo> component',
-            ),
-          ],
-        ]);
-      } else {
-        // The top-level error was caught with try/catch, and there's no guarded callback,
-        // so in production we don't see an error event.
-        expect(windowOnError.mock.calls).toEqual([]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported by React with no extra message:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-
-    it('logs passive effect errors with an error boundary', async () => {
-      spyOnDevAndProd(console, 'error');
-
-      function Foo() {
-        React.useEffect(() => {
-          throw Error('Boom');
-        }, []);
-        return null;
-      }
-
-      await act(() => {
-        ReactDOM.render(
-          <ErrorBoundary>
-            <Foo />
-          </ErrorBoundary>,
-          container,
-        );
-      });
-
-      if (__DEV__) {
-        // Reported due to guarded callback:
-        expect(windowOnError.mock.calls).toEqual([
-          [
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-          [
-            // Reported by jsdom due to the guarded callback:
-            expect.objectContaining({
-              detail: expect.objectContaining({
-                message: 'Boom',
-              }),
-              type: 'unhandled exception',
-            }),
-          ],
-          [
-            // Addendum by React:
-            expect.stringContaining(
-              'The above error occurred in the <Foo> component',
-            ),
-          ],
-        ]);
-      } else {
-        // The top-level error was caught with try/catch, and there's no guarded callback,
-        // so in production we don't see an error event.
-        expect(windowOnError.mock.calls).toEqual([]);
-        expect(console.error.mock.calls).toEqual([
-          [
-            // Reported by React with no extra message:
-            expect.objectContaining({
-              message: 'Boom',
-            }),
-          ],
-        ]);
-      }
-
-      // Check next render doesn't throw.
-      windowOnError.mockReset();
-      console.error.mockReset();
-      await act(() => {
-        ReactDOM.render(<NoError />, container);
-      });
-      expect(container.textContent).toBe('OK');
-      expect(windowOnError.mock.calls).toEqual([]);
-      if (__DEV__) {
-        expect(console.error.mock.calls).toEqual([
-          [expect.stringContaining('ReactDOM.render is no longer supported')],
-        ]);
-      }
-    });
-  });
 });

filename: packages/react-dom/src/__tests__/ReactDOMConsoleErrorReportingLegacy-test.js
patch: @@ -0,0 +1,589 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+describe('ReactDOMConsoleErrorReporting', () => {
+  let act;
+  let React;
+  let ReactDOM;
+
+  let ErrorBoundary;
+  let NoError;
+  let container;
+  let windowOnError;
+  let waitForThrow;
+
+  beforeEach(() => {
+    jest.resetModules();
+    act = require('internal-test-utils').act;
+    React = require('react');
+    ReactDOM = require('react-dom');
+
+    const InternalTestUtils = require('internal-test-utils');
+    waitForThrow = InternalTestUtils.waitForThrow;
+
+    ErrorBoundary = class extends React.Component {
+      state = {error: null};
+      static getDerivedStateFromError(error) {
+        return {error};
+      }
+      render() {
+        if (this.state.error) {
+          return <h1>Caught: {this.state.error.message}</h1>;
+        }
+        return this.props.children;
+      }
+    };
+    NoError = function () {
+      return <h1>OK</h1>;
+    };
+    container = document.createElement('div');
+    document.body.appendChild(container);
+    windowOnError = jest.fn();
+    window.addEventListener('error', windowOnError);
+  });
+
+  afterEach(() => {
+    document.body.removeChild(container);
+    window.removeEventListener('error', windowOnError);
+    jest.restoreAllMocks();
+  });
+
+  describe('ReactDOM.render', () => {
+    it('logs errors during event handlers', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        return (
+          <button
+            onClick={() => {
+              throw Error('Boom');
+            }}>
+            click me
+          </button>
+        );
+      }
+
+      await act(() => {
+        ReactDOM.render(<Foo />, container);
+      });
+
+      await act(() => {
+        container.firstChild.dispatchEvent(
+          new MouseEvent('click', {
+            bubbles: true,
+          }),
+        );
+      });
+
+      if (__DEV__) {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported because we're in a browser click event:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+          [
+            // This one is jsdom-only. Real browser deduplicates it.
+            // (In DEV, we have a nested event due to guarded callback.)
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported because we're in a browser click event:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // This one is jsdom-only. Real browser deduplicates it.
+            // (In DEV, we have a nested event due to guarded callback.)
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+        ]);
+      } else {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported because we're in a browser click event:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported because we're in a browser click event:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+
+    it('logs render errors without an error boundary', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        throw Error('Boom');
+      }
+
+      expect(() => {
+        ReactDOM.render(<Foo />, container);
+      }).toThrow('Boom');
+
+      if (__DEV__) {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported due to guarded callback:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported due to the guarded callback:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // Addendum by React:
+            expect.stringContaining(
+              'The above error occurred in the <Foo> component',
+            ),
+          ],
+        ]);
+      } else {
+        // The top-level error was caught with try/catch, and there's no guarded callback,
+        // so in production we don't see an error event.
+        expect(windowOnError.mock.calls).toEqual([]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported by React with no extra message:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+
+    it('logs render errors with an error boundary', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        throw Error('Boom');
+      }
+
+      await act(() => {
+        ReactDOM.render(
+          <ErrorBoundary>
+            <Foo />
+          </ErrorBoundary>,
+          container,
+        );
+      });
+
+      if (__DEV__) {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported due to guarded callback:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported by jsdom due to the guarded callback:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // Addendum by React:
+            expect.stringContaining(
+              'The above error occurred in the <Foo> component',
+            ),
+          ],
+        ]);
+      } else {
+        // The top-level error was caught with try/catch, and there's no guarded callback,
+        // so in production we don't see an error event.
+        expect(windowOnError.mock.calls).toEqual([]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported by React with no extra message:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+
+    it('logs layout effect errors without an error boundary', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        React.useLayoutEffect(() => {
+          throw Error('Boom');
+        }, []);
+        return null;
+      }
+
+      expect(() => {
+        ReactDOM.render(<Foo />, container);
+      }).toThrow('Boom');
+
+      if (__DEV__) {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported due to guarded callback:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported due to the guarded callback:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // Addendum by React:
+            expect.stringContaining(
+              'The above error occurred in the <Foo> component',
+            ),
+          ],
+        ]);
+      } else {
+        // The top-level error was caught with try/catch, and there's no guarded callback,
+        // so in production we don't see an error event.
+        expect(windowOnError.mock.calls).toEqual([]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported by React with no extra message:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+
+    it('logs layout effect errors with an error boundary', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        React.useLayoutEffect(() => {
+          throw Error('Boom');
+        }, []);
+        return null;
+      }
+
+      await act(() => {
+        ReactDOM.render(
+          <ErrorBoundary>
+            <Foo />
+          </ErrorBoundary>,
+          container,
+        );
+      });
+
+      if (__DEV__) {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported due to guarded callback:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported by jsdom due to the guarded callback:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // Addendum by React:
+            expect.stringContaining(
+              'The above error occurred in the <Foo> component',
+            ),
+          ],
+        ]);
+      } else {
+        // The top-level error was caught with try/catch, and there's no guarded callback,
+        // so in production we don't see an error event.
+        expect(windowOnError.mock.calls).toEqual([]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported by React with no extra message:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+
+    it('logs passive effect errors without an error boundary', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        React.useEffect(() => {
+          throw Error('Boom');
+        }, []);
+        return null;
+      }
+
+      await act(async () => {
+        ReactDOM.render(<Foo />, container);
+        await waitForThrow('Boom');
+      });
+
+      if (__DEV__) {
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            // Reported due to guarded callback:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported due to the guarded callback:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // Addendum by React:
+            expect.stringContaining(
+              'The above error occurred in the <Foo> component',
+            ),
+          ],
+        ]);
+      } else {
+        // The top-level error was caught with try/catch, and there's no guarded callback,
+        // so in production we don't see an error event.
+        expect(windowOnError.mock.calls).toEqual([]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported by React with no extra message:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+
+    it('logs passive effect errors with an error boundary', async () => {
+      spyOnDevAndProd(console, 'error');
+
+      function Foo() {
+        React.useEffect(() => {
+          throw Error('Boom');
+        }, []);
+        return null;
+      }
+
+      await act(() => {
+        ReactDOM.render(
+          <ErrorBoundary>
+            <Foo />
+          </ErrorBoundary>,
+          container,
+        );
+      });
+
+      if (__DEV__) {
+        // Reported due to guarded callback:
+        expect(windowOnError.mock.calls).toEqual([
+          [
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+          [
+            // Reported by jsdom due to the guarded callback:
+            expect.objectContaining({
+              detail: expect.objectContaining({
+                message: 'Boom',
+              }),
+              type: 'unhandled exception',
+            }),
+          ],
+          [
+            // Addendum by React:
+            expect.stringContaining(
+              'The above error occurred in the <Foo> component',
+            ),
+          ],
+        ]);
+      } else {
+        // The top-level error was caught with try/catch, and there's no guarded callback,
+        // so in production we don't see an error event.
+        expect(windowOnError.mock.calls).toEqual([]);
+        expect(console.error.mock.calls).toEqual([
+          [
+            // Reported by React with no extra message:
+            expect.objectContaining({
+              message: 'Boom',
+            }),
+          ],
+        ]);
+      }
+
+      // Check next render doesn't throw.
+      windowOnError.mockReset();
+      console.error.mockReset();
+      await act(() => {
+        ReactDOM.render(<NoError />, container);
+      });
+      expect(container.textContent).toBe('OK');
+      expect(windowOnError.mock.calls).toEqual([]);
+      if (__DEV__) {
+        expect(console.error.mock.calls).toEqual([
+          [expect.stringContaining('ReactDOM.render is no longer supported')],
+        ]);
+      }
+    });
+  });
+});