| 1 | /** @flow */ |
| 2 | |
| 3 | // This test harness mounts each test app as a separate root to test multi-root applications. |
| 4 | |
| 5 | import semver from 'semver'; |
| 6 | |
| 7 | import {createElement} from 'react'; |
| 8 | import {createRoot} from 'react-dom/client'; |
| 9 | |
| 10 | import DeeplyNestedComponents from './DeeplyNestedComponents'; |
| 11 | import Iframe from './Iframe'; |
| 12 | import EditableProps from './EditableProps'; |
| 13 | import ElementTypes from './ElementTypes'; |
| 14 | import Hydration from './Hydration'; |
| 15 | import InspectableElements from './InspectableElements'; |
| 16 | import ReactNativeWeb from './ReactNativeWeb'; |
| 17 | import ToDoList from './ToDoList'; |
| 18 | import Toggle from './Toggle'; |
| 19 | import ErrorBoundaries from './ErrorBoundaries'; |
| 20 | import PartiallyStrictApp from './PartiallyStrictApp'; |
| 21 | import Segments from './Segments'; |
| 22 | import SuspenseTree from './SuspenseTree'; |
| 23 | import SearchableTable from './SearchableTable'; |
| 24 | import ActivityTree from './ActivityTree'; |
| 25 | import TraceUpdatesTest from './TraceUpdatesTest'; |
| 26 | import {ignoreErrors, ignoreLogs, ignoreWarnings} from './console'; |
| 27 | |
| 28 | import './styles.css'; |
| 29 | |
| 30 | // DevTools intentionally tests compatibility with certain legacy APIs. |
| 31 | // Suppress their error messages in the local dev shell, |
| 32 | // because they might mask other more serious error messages. |
| 33 | ignoreErrors([ |
| 34 | 'Warning: Legacy context API', |
| 35 | 'Warning: Unsafe lifecycle methods', |
| 36 | 'Warning: %s is deprecated in StrictMode.', // findDOMNode |
| 37 | 'Warning: ReactDOM.render was removed in React 19', |
| 38 | 'Warning: react-test-renderer is deprecated', |
| 39 | // Ignore prefixed and not prefixed since I don't know which |
| 40 | // React versions are being tested by this code. |
| 41 | 'Legacy context API', |
| 42 | 'Unsafe lifecycle methods', |
| 43 | '%s is deprecated in StrictMode.', // findDOMNode |
| 44 | 'ReactDOM.render was removed in React 19', |
| 45 | 'react-test-renderer is deprecated', |
| 46 | ]); |
| 47 | ignoreWarnings([ |
| 48 | 'Warning: componentWillReceiveProps has been renamed', |
| 49 | 'componentWillReceiveProps has been renamed', |
| 50 | ]); |
| 51 | ignoreLogs([]); |
| 52 | |
| 53 | const unmountFunctions: Array<() => void | boolean> = []; |
| 54 | |
| 55 | function createContainer() { |
| 56 | const container = document.createElement('div'); |
| 57 | |
| 58 | (document.body as any as HTMLBodyElement).appendChild(container); |
| 59 | |
| 60 | return container; |
| 61 | } |
| 62 | |
| 63 | function mountApp(App: () => React$Node) { |
| 64 | const container = createContainer(); |
| 65 | |
| 66 | const root = createRoot(container); |
| 67 | root.render(createElement(App)); |
| 68 | |
| 69 | unmountFunctions.push(() => root.unmount()); |
| 70 | } |
| 71 | |
| 72 | // $FlowFixMe[missing-local-annot] |
| 73 | function mountStrictApp(App) { |
| 74 | function StrictRoot() { |
| 75 | return createElement(App); |
| 76 | } |
| 77 | |
| 78 | const container = createContainer(); |
| 79 | |
| 80 | const root = createRoot(container, {unstable_strictMode: true}); |
| 81 | root.render(createElement(StrictRoot)); |
| 82 | |
| 83 | unmountFunctions.push(() => root.unmount()); |
| 84 | } |
| 85 | |
| 86 | function mountLegacyApp(App: () => React$Node) { |
| 87 | // $FlowFixMe[prop-missing]: These are removed in 19. |
| 88 | const {render, unmountComponentAtNode} = require('react-dom'); |
| 89 | |
| 90 | function LegacyRender() { |
| 91 | return createElement(App); |
| 92 | } |
| 93 | |
| 94 | const container = createContainer(); |
| 95 | |
| 96 | // $FlowFixMe[not-a-function]: These are removed in 19. |
| 97 | render(createElement(LegacyRender), container); |
| 98 | |
| 99 | // $FlowFixMe[not-a-function]: These are removed in 19. |
| 100 | unmountFunctions.push(() => unmountComponentAtNode(container)); |
| 101 | } |
| 102 | |
| 103 | const shouldRenderLegacy = semver.lte( |
| 104 | process.env.E2E_APP_REACT_VERSION, |
| 105 | '18.2.0', |
| 106 | ); |
| 107 | function mountTestApp() { |
| 108 | mountStrictApp(ToDoList); |
| 109 | mountApp(InspectableElements); |
| 110 | mountApp(Hydration); |
| 111 | mountApp(ElementTypes); |
| 112 | mountApp(EditableProps); |
| 113 | mountApp(ReactNativeWeb); |
| 114 | mountApp(Toggle); |
| 115 | mountApp(ErrorBoundaries); |
| 116 | mountApp(SuspenseTree); |
| 117 | mountApp(SearchableTable); |
| 118 | mountApp(DeeplyNestedComponents); |
| 119 | mountApp(Iframe); |
| 120 | mountApp(ActivityTree); |
| 121 | mountApp(TraceUpdatesTest); |
| 122 | mountApp(Segments); |
| 123 | |
| 124 | if (shouldRenderLegacy) { |
| 125 | mountLegacyApp(PartiallyStrictApp); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | function unmountTestApp() { |
| 130 | unmountFunctions.forEach(fn => fn()); |
| 131 | } |
| 132 | |
| 133 | mountTestApp(); |
| 134 | |
| 135 | window.parent.mountTestApp = mountTestApp; |
| 136 | window.parent.unmountTestApp = unmountTestApp; |