@samitouri / QOS-React-1 / commits / 952aa74f8e

Upgrade tests to use react/jsx-runtime (#28252)

Instead of createElement. We should have done this when we initially released jsx-runtime but better late than never. The general principle is that our tests should be written using the most up-to-date idioms that we recommend for users, except when explicitly testing an edge case or legacy behavior, like for backwards compatibility. Most of the diff is related to tweaking test output and isn't very interesting. I did have to workaround an issue related to component stacks. The component stack logic depends on shared state that lives in the React module. The problem is that most of our tests reset the React module state and re-require a fresh instance of React, React DOM, etc. However, the JSX runtime is not re-required because it's injected by the compiler as a static import. This means its copy of the shared state is no longer the same as the one used by React, causing any warning logged by the JSX runtime to not include a component stack. (This same issue also breaks string refs, but since we're removing those soon I'm not so concerned about that.) The solution I went with for now is to mock the JSX runtime with a proxy that re-requires the module on every function invocation. I don't love this but it will have to do for now. What we should really do is migrate our tests away from manually resetting the module state and use import syntax instead.

Andrew Clark committed Feb 5, 2024 at 23:07 UTC 952aa74f8e45ed35ac6bf1de10ad5ed5410deac6
18 files changed +290 -72
babel.config.js
-1
@@ -3,7 +3,6 @@
3 module.exports = {
4 plugins: [
5 '@babel/plugin-syntax-jsx',
6 - '@babel/plugin-transform-react-jsx',
6 '@babel/plugin-transform-flow-strip-types',
7 ['@babel/plugin-proposal-class-properties', {loose: true}],
8 'syntax-trailing-function-commas',
package.json
+4 -3
@@ -14,7 +14,7 @@
14 "@babel/plugin-proposal-object-rest-spread": "^7.11.0",
15 "@babel/plugin-syntax-dynamic-import": "^7.8.3",
16 "@babel/plugin-syntax-import-meta": "^7.10.4",
17 - "@babel/plugin-syntax-jsx": "^7.10.4",
17 + "@babel/plugin-syntax-jsx": "^7.23.3",
18 "@babel/plugin-syntax-typescript": "^7.14.5",
19 "@babel/plugin-transform-arrow-functions": "^7.10.4",
20 "@babel/plugin-transform-block-scoped-functions": "^7.10.4",
@@ -27,12 +27,13 @@
27 "@babel/plugin-transform-modules-commonjs": "^7.10.4",
28 "@babel/plugin-transform-object-super": "^7.10.4",
29 "@babel/plugin-transform-parameters": "^7.10.5",
30 - "@babel/plugin-transform-react-jsx-source": "^7.10.5",
30 + "@babel/plugin-transform-react-jsx": "^7.23.4",
31 + "@babel/plugin-transform-react-jsx-development": "^7.22.5",
32 "@babel/plugin-transform-shorthand-properties": "^7.10.4",
33 "@babel/plugin-transform-spread": "^7.11.0",
34 "@babel/plugin-transform-template-literals": "^7.10.5",
35 "@babel/preset-flow": "^7.10.4",
35 - "@babel/preset-react": "^7.10.4",
36 + "@babel/preset-react": "^7.23.3",
37 "@babel/traverse": "^7.11.0",
38 "@rollup/plugin-babel": "^6.0.3",
39 "@rollup/plugin-commonjs": "^24.0.1",
packages/react-dom/src/__tests__/ReactComponent-test.js
+3 -3
@@ -471,7 +471,7 @@ describe('ReactComponent', () => {
471 root.render(<X />);
472 });
473 }).toErrorDev(
474 - 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
474 + 'React.jsx: type is invalid -- expected a string (for built-in components) ' +
475 'or a class/function (for composite components) but got: undefined.',
476 ),
477 ).rejects.toThrowError(
@@ -492,7 +492,7 @@ describe('ReactComponent', () => {
492 root.render(<Y />);
493 });
494 }).toErrorDev(
495 - 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
495 + 'React.jsx: type is invalid -- expected a string (for built-in components) ' +
496 'or a class/function (for composite components) but got: null.',
497 ),
498 ).rejects.toThrowError(
@@ -528,7 +528,7 @@ describe('ReactComponent', () => {
528 root.render(<Foo />);
529 });
530 }).toErrorDev(
531 - 'React.createElement: type is invalid -- expected a string (for built-in components) ' +
531 + 'React.jsx: type is invalid -- expected a string (for built-in components) ' +
532 'or a class/function (for composite components) but got: undefined.',
533 ),
534 ).rejects.toThrowError(
packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+3 -3
@@ -1007,7 +1007,7 @@ describe('ReactDOMServerIntegration', () => {
1007 expect(() => {
1008 EmptyComponent = <EmptyComponent />;
1009 }).toErrorDev(
1010 - 'Warning: React.createElement: type is invalid -- expected a string ' +
1010 + 'Warning: React.jsx: type is invalid -- expected a string ' +
1011 '(for built-in components) or a class/function (for composite ' +
1012 'components) but got: object. You likely forgot to export your ' +
1013 "component from the file it's defined in, or you might have mixed up " +
@@ -1031,7 +1031,7 @@ describe('ReactDOMServerIntegration', () => {
1031 expect(() => {
1032 NullComponent = <NullComponent />;
1033 }).toErrorDev(
1034 - 'Warning: React.createElement: type is invalid -- expected a string ' +
1034 + 'Warning: React.jsx: type is invalid -- expected a string ' +
1035 '(for built-in components) or a class/function (for composite ' +
1036 'components) but got: null.',
1037 {withoutStack: true},
@@ -1049,7 +1049,7 @@ describe('ReactDOMServerIntegration', () => {
1049 expect(() => {
1050 UndefinedComponent = <UndefinedComponent />;
1051 }).toErrorDev(
1052 - 'Warning: React.createElement: type is invalid -- expected a string ' +
1052 + 'Warning: React.jsx: type is invalid -- expected a string ' +
1053 '(for built-in components) or a class/function (for composite ' +
1054 'components) but got: undefined. You likely forgot to export your ' +
1055 "component from the file it's defined in, or you might have mixed up " +
packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js
+8 -2
@@ -95,7 +95,10 @@ describe('ReactDeprecationWarnings', () => {
95 }
96 class Component extends React.Component {
97 render() {
98 - return <RefComponent ref="refComponent" __self={this} />;
98 + return React.createElement(RefComponent, {
99 + ref: 'refComponent',
100 + __self: this,
101 + });
102 }
103 }
104 expect(() => {
@@ -114,7 +117,10 @@ describe('ReactDeprecationWarnings', () => {
117 }
118 class Component extends React.Component {
119 render() {
117 - return <RefComponent ref="refComponent" __self={{}} />;
120 + return React.createElement(RefComponent, {
121 + ref: 'refComponent',
122 + __self: {},
123 + });
124 }
125 }
126
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+5 -5
@@ -1244,9 +1244,9 @@ describe('ReactIncrementalErrorHandling', () => {
1244 </ErrorBoundary>,
1245 );
1246 await expect(async () => await waitForAll([])).toErrorDev([
1247 - 'Warning: React.createElement: type is invalid -- expected a string',
1247 + 'Warning: React.jsx: type is invalid -- expected a string',
1248 // React retries once on error
1249 - 'Warning: React.createElement: type is invalid -- expected a string',
1249 + 'Warning: React.jsx: type is invalid -- expected a string',
1250 ]);
1251 expect(ReactNoop).toMatchRenderedOutput(
1252 <span
@@ -1295,9 +1295,9 @@ describe('ReactIncrementalErrorHandling', () => {
1295 </ErrorBoundary>,
1296 );
1297 await expect(async () => await waitForAll([])).toErrorDev([
1298 - 'Warning: React.createElement: type is invalid -- expected a string',
1298 + 'Warning: React.jsx: type is invalid -- expected a string',
1299 // React retries once on error
1300 - 'Warning: React.createElement: type is invalid -- expected a string',
1300 + 'Warning: React.jsx: type is invalid -- expected a string',
1301 ]);
1302 expect(ReactNoop).toMatchRenderedOutput(
1303 <span
@@ -1317,7 +1317,7 @@ describe('ReactIncrementalErrorHandling', () => {
1317 it('recovers from uncaught reconciler errors', async () => {
1318 const InvalidType = undefined;
1319 expect(() => ReactNoop.render(<InvalidType />)).toErrorDev(
1320 - 'Warning: React.createElement: type is invalid -- expected a string',
1320 + 'Warning: React.jsx: type is invalid -- expected a string',
1321 {withoutStack: true},
1322 );
1323 await waitForThrow(
packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js
+10 -1
@@ -86,13 +86,22 @@ describe('ReactFreshIntegration', () => {
86 // eslint-disable-next-line no-new-func
87 new Function(
88 'global',
89 + 'require',
90 'React',
91 'Scheduler',
92 'exports',
93 '$RefreshReg$',
94 '$RefreshSig$',
95 compiled,
95 - )(global, React, Scheduler, exportsObj, $RefreshReg$, $RefreshSig$);
96 + )(
97 + global,
98 + require,
99 + React,
100 + Scheduler,
101 + exportsObj,
102 + $RefreshReg$,
103 + $RefreshSig$,
104 + );
105 // Module systems will register exports as a fallback.
106 // This is useful for cases when e.g. a class is exported,
107 // and we don't want to propagate the update beyond this module.
packages/react-test-renderer/src/__tests__/ReactTestRenderer-test.internal.js
+1 -2
@@ -288,8 +288,7 @@ describe('ReactTestRenderer', () => {
288 expect(() => ReactTestRenderer.create(<Foo />)).toErrorDev(
289 'Warning: Function components cannot be given refs. Attempts ' +
290 'to access this ref will fail. ' +
291 - 'Did you mean to use React.forwardRef()?\n\n' +
292 - 'Check the render method of `Foo`.\n' +
291 + 'Did you mean to use React.forwardRef()?\n' +
292 ' in Bar (at **)\n' +
293 ' in Foo (at **)',
294 );
packages/react/src/__tests__/ReactCreateElement-test.js renamed
+24 -32
@@ -14,7 +14,9 @@ let act;
14 let React;
15 let ReactDOMClient;
16
17 -describe('ReactElement', () => {
17 +// NOTE: This module tests the old, "classic" JSX runtime, React.createElement.
18 +// Do not use JSX syntax in this module; call React.createElement directly.
19 +describe('ReactCreateElement', () => {
20 let ComponentClass;
21
22 beforeEach(() => {
@@ -24,8 +26,6 @@ describe('ReactElement', () => {
26
27 React = require('react');
28 ReactDOMClient = require('react-dom/client');
27 - // NOTE: We're explicitly not using JSX here. This is intended to test
28 - // classic JS without JSX.
29 ComponentClass = class extends React.Component {
30 render() {
31 return React.createElement('div');
@@ -48,24 +48,24 @@ describe('ReactElement', () => {
48 it('should warn when `key` is being accessed on composite element', async () => {
49 class Child extends React.Component {
50 render() {
51 - return <div>{this.props.key}</div>;
51 + return React.createElement('div', null, this.props.key);
52 }
53 }
54 class Parent extends React.Component {
55 render() {
56 - return (
57 - <div>
58 - <Child key="0" />
59 - <Child key="1" />
60 - <Child key="2" />
61 - </div>
56 + return React.createElement(
57 + 'div',
58 + null,
59 + React.createElement(Child, {key: '0'}),
60 + React.createElement(Child, {key: '1'}),
61 + React.createElement(Child, {key: '2'}),
62 );
63 }
64 }
65 const root = ReactDOMClient.createRoot(document.createElement('div'));
66 await expect(async () => {
67 await act(() => {
68 - root.render(<Parent />);
68 + root.render(React.createElement(Parent));
69 });
70 }).toErrorDev(
71 'Child: `key` is not a prop. Trying to access it will result ' +
@@ -76,7 +76,7 @@ describe('ReactElement', () => {
76 });
77
78 it('should warn when `key` is being accessed on a host element', () => {
79 - const element = <div key="3" />;
79 + const element = React.createElement('div', {key: '3'});
80 expect(() => void element.props.key).toErrorDev(
81 'div: `key` is not a prop. Trying to access it will result ' +
82 'in `undefined` being returned. If you need to access the same ' +
@@ -89,15 +89,15 @@ describe('ReactElement', () => {
89 it('should warn when `ref` is being accessed', async () => {
90 class Child extends React.Component {
91 render() {
92 - return <div> {this.props.ref} </div>;
92 + return React.createElement('div', null, this.props.ref);
93 }
94 }
95 class Parent extends React.Component {
96 render() {
97 - return (
98 - <div>
99 - <Child ref={React.createRef()} />
100 - </div>
97 + return React.createElement(
98 + 'div',
99 + null,
100 + React.createElement(Child, {ref: React.createRef()}),
101 );
102 }
103 }
@@ -105,7 +105,7 @@ describe('ReactElement', () => {
105
106 await expect(async () => {
107 await act(() => {
108 - root.render(<Parent />);
108 + root.render(React.createElement(Parent));
109 });
110 }).toErrorDev(
111 'Child: `ref` is not a prop. Trying to access it will result ' +
@@ -277,8 +277,6 @@ describe('ReactElement', () => {
277 expect(element.props.children).toEqual([1, 2, 3]);
278 });
279
280 - // NOTE: We're explicitly not using JSX here. This is intended to test
281 - // classic JS without JSX.
280 it('allows static methods to be called using the type property', () => {
281 class StaticMethodComponentClass extends React.Component {
282 render() {
@@ -291,16 +289,12 @@ describe('ReactElement', () => {
289 expect(element.type.someStaticMethod()).toBe('someReturnValue');
290 });
291
294 - // NOTE: We're explicitly not using JSX here. This is intended to test
295 - // classic JS without JSX.
292 it('is indistinguishable from a plain object', () => {
293 const element = React.createElement('div', {className: 'foo'});
294 const object = {};
295 expect(element.constructor).toBe(object.constructor);
296 });
297
302 - // NOTE: We're explicitly not using JSX here. This is intended to test
303 - // classic JS without JSX.
298 it('should use default prop value when removing a prop', async () => {
299 class Component extends React.Component {
300 render() {
@@ -325,8 +319,6 @@ describe('ReactElement', () => {
319 expect(instance.props.fruit).toBe('persimmon');
320 });
321
328 - // NOTE: We're explicitly not using JSX here. This is intended to test
329 - // classic JS without JSX.
322 it('should normalize props with default values', async () => {
323 let instance;
324 class Component extends React.Component {
@@ -354,7 +346,7 @@ describe('ReactElement', () => {
346 it('throws when changing a prop (in dev) after element creation', async () => {
347 class Outer extends React.Component {
348 render() {
357 - const el = <div className="moo" />;
349 + const el = React.createElement('div', {className: 'moo'});
350
351 if (__DEV__) {
352 expect(function () {
@@ -374,7 +366,7 @@ describe('ReactElement', () => {
366 const root = ReactDOMClient.createRoot(container);
367
368 await act(() => {
377 - root.render(<Outer color="orange" />);
369 + root.render(React.createElement(Outer, {color: 'orange'}));
370 });
371 if (__DEV__) {
372 expect(container.firstChild.className).toBe('moo');
@@ -387,7 +379,7 @@ describe('ReactElement', () => {
379 const container = document.createElement('div');
380 class Outer extends React.Component {
381 render() {
390 - const el = <div>{this.props.sound}</div>;
382 + const el = React.createElement('div', null, this.props.sound);
383
384 if (__DEV__) {
385 expect(function () {
@@ -405,7 +397,7 @@ describe('ReactElement', () => {
397 Outer.defaultProps = {sound: 'meow'};
398 const root = ReactDOMClient.createRoot(container);
399 await act(() => {
408 - root.render(<Outer />);
400 + root.render(React.createElement(Outer));
401 });
402 expect(container.firstChild.textContent).toBe('meow');
403 if (__DEV__) {
@@ -422,12 +414,12 @@ describe('ReactElement', () => {
414 test = this;
415 }
416 render() {
425 - return <div />;
417 + return React.createElement('div');
418 }
419 }
420 const root = ReactDOMClient.createRoot(document.createElement('div'));
421 await act(() => {
430 - root.render(<Test value={+undefined} />);
422 + root.render(React.createElement(Test, {value: +undefined}));
423 });
424 expect(test.props.value).toBeNaN();
425 });
packages/react/src/__tests__/ReactElementValidator-test.internal.js
+5 -2
@@ -10,7 +10,10 @@
10 'use strict';
11
12 // NOTE: We're explicitly not using JSX in this file. This is intended to test
13 -// classic JS without JSX.
13 +// classic React.createElement without JSX.
14 +// TODO: ^ the above note is a bit stale because there are tests in this file
15 +// that do use JSX syntax. We should port them to React.createElement, and also
16 +// confirm there's a corresponding test that uses JSX syntax.
17
18 let PropTypes;
19 let React;
@@ -548,7 +551,7 @@ describe('ReactElementValidator', () => {
551 expect(() => {
552 void (<Foo>{[<div />]}</Foo>);
553 }).toErrorDev(
551 - 'Warning: React.createElement: type is invalid -- expected a string ' +
554 + 'Warning: React.jsx: type is invalid -- expected a string ' +
555 '(for built-in components) or a class/function (for composite ' +
556 'components) but got: undefined. You likely forgot to export your ' +
557 "component from the file it's defined in, or you might have mixed up " +
packages/react/src/__tests__/ReactJSXElementValidator-test.js
+3 -3
@@ -220,7 +220,7 @@ describe('ReactJSXElementValidator', () => {
220 const True = true;
221 const Div = 'div';
222 expect(() => void (<Undefined />)).toErrorDev(
223 - 'Warning: React.createElement: type is invalid -- expected a string ' +
223 + 'Warning: React.jsx: type is invalid -- expected a string ' +
224 '(for built-in components) or a class/function (for composite ' +
225 'components) but got: undefined. You likely forgot to export your ' +
226 "component from the file it's defined in, or you might have mixed up " +
@@ -229,14 +229,14 @@ describe('ReactJSXElementValidator', () => {
229 {withoutStack: true},
230 );
231 expect(() => void (<Null />)).toErrorDev(
232 - 'Warning: React.createElement: type is invalid -- expected a string ' +
232 + 'Warning: React.jsx: type is invalid -- expected a string ' +
233 '(for built-in components) or a class/function (for composite ' +
234 'components) but got: null.' +
235 '\n\nCheck your code at **.',
236 {withoutStack: true},
237 );
238 expect(() => void (<True />)).toErrorDev(
239 - 'Warning: React.createElement: type is invalid -- expected a string ' +
239 + 'Warning: React.jsx: type is invalid -- expected a string ' +
240 '(for built-in components) or a class/function (for composite ' +
241 'components) but got: boolean.' +
242 '\n\nCheck your code at **.',
packages/react/src/__tests__/ReactJSXRuntime-test.js renamed
+4 -5
@@ -17,11 +17,10 @@ let JSXRuntime;
17 let JSXDEVRuntime;
18 let act;
19
20 -// NOTE: We're explicitly not using JSX here. This is intended to test
21 -// a new React.jsx api which does not have a JSX transformer yet.
22 -// A lot of these tests are pulled from ReactElement-test because
23 -// this api is meant to be backwards compatible.
24 -describe('ReactElement.jsx', () => {
20 +// NOTE: Prefer to call the JSXRuntime directly in these tests so we can be
21 +// certain that we are testing the runtime behavior, as opposed to the Babel
22 +// transform that we use in our tests configuration.
23 +describe('ReactJSXRuntime', () => {
24 beforeEach(() => {
25 jest.resetModules();
26
packages/react/src/__tests__/ReactJSXTransformIntegration-test.js renamed
+22 -1
@@ -14,7 +14,15 @@ let ReactDOMClient;
14 let ReactTestUtils;
15 let act;
16
17 -describe('ReactJSXElement', () => {
17 +// TODO: Historically this module was used to confirm that the JSX transform
18 +// produces the correct output. However, most users (and indeed our own test
19 +// suite) use a tool like Babel or TypeScript to transform JSX; unlike the
20 +// runtime, the transform is not part of React itself. So this is really just an
21 +// integration suite for the Babel transform. We might consider deleting it. We
22 +// should prefer to test the JSX runtime directly, in ReactCreateElement-test
23 +// and ReactJsxRuntime-test. In the meantime, there's lots of overlap between
24 +// those modules and this one.
25 +describe('ReactJSXTransformIntegration', () => {
26 let Component;
27
28 beforeEach(() => {
@@ -32,6 +40,19 @@ describe('ReactJSXElement', () => {
40 };
41 });
42
43 + it('sanity check: test environment is configured to compile JSX to the jsx() runtime', async () => {
44 + function App() {
45 + return <div />;
46 + }
47 + const source = App.toString();
48 + if (__DEV__) {
49 + expect(source).toContain('jsxDEV(');
50 + } else {
51 + expect(source).toContain('jsx(');
52 + }
53 + expect(source).not.toContain('React.createElement');
54 + });
55 +
56 it('returns a complete element according to spec', () => {
57 const element = <Component />;
58 expect(element.type).toBe(Component);
packages/shared/__tests__/describeComponentFrame-test.js
+17 -3
@@ -12,15 +12,18 @@
12 let React;
13 let ReactDOMClient;
14 let act;
15 +let jsxDEV;
16
17 describe('Component stack trace displaying', () => {
18 beforeEach(() => {
19 React = require('react');
19 - ReactDOMClient = require('react-dom');
20 + ReactDOMClient = require('react-dom/client');
21 act = require('internal-test-utils').act;
22 + jsxDEV = require('react/jsx-dev-runtime').jsxDEV;
23 });
24
23 - // @gate !enableComponentStackLocations || !__DEV__
25 + // @gate !enableComponentStackLocations
26 + // @gate __DEV__
27 it('should provide filenames in stack traces', async () => {
28 class Component extends React.Component {
29 render() {
@@ -98,7 +101,18 @@ describe('Component stack trace displaying', () => {
101 Component.displayName = 'Component ' + i;
102
103 await act(() => {
101 - root.render(<Component __source={{fileName, lineNumber: i}} />);
104 + root.render(
105 + // Intentionally inlining a manual jsxDEV() instead of relying on the
106 + // compiler so that we can pass a custom source location.
107 + jsxDEV(
108 + Component,
109 + {},
110 + undefined,
111 + false,
112 + {fileName, lineNumber: i},
113 + this,
114 + ),
115 + );
116 });
117
118 i++;
scripts/jest/devtools/setupEnv.js
+38
@@ -57,3 +57,41 @@ global._test_react_version_focus = (range, testName, callback) => {
57 global._test_ignore_for_react_version = (testName, callback) => {
58 test.skip(testName, callback);
59 };
60 +
61 +// Most of our tests call jest.resetModules in a beforeEach and the
62 +// re-require all the React modules. However, the JSX runtime is injected by
63 +// the compiler, so those bindings don't get updated. This causes warnings
64 +// logged by the JSX runtime to not have a component stack, because component
65 +// stack relies on the the secret internals object that lives on the React
66 +// module, which because of the resetModules call is longer the same one.
67 +//
68 +// To workaround this issue, we use a proxy that re-requires the latest
69 +// JSX Runtime from the require cache on every function invocation.
70 +//
71 +// Longer term we should migrate all our tests away from using require() and
72 +// resetModules, and use import syntax instead so this kind of thing doesn't
73 +// happen.
74 +lazyRequireFunctionExports('react/jsx-dev-runtime');
75 +
76 +// TODO: We shouldn't need to do this in the production runtime, but until
77 +// we remove string refs they also depend on the shared state object. Remove
78 +// once we remove string refs.
79 +lazyRequireFunctionExports('react/jsx-runtime');
80 +
81 +function lazyRequireFunctionExports(moduleName) {
82 + jest.mock(moduleName, () => {
83 + return new Proxy(jest.requireActual(moduleName), {
84 + get(originalModule, prop) {
85 + // If this export is a function, return a wrapper function that lazily
86 + // requires the implementation from the current module cache.
87 + if (typeof originalModule[prop] === 'function') {
88 + return function () {
89 + return jest.requireActual(moduleName)[prop].apply(this, arguments);
90 + };
91 + } else {
92 + return originalModule[prop];
93 + }
94 + },
95 + });
96 + });
97 +}
scripts/jest/preprocessor.js
+10 -6
@@ -34,12 +34,6 @@ const babelOptions = {
34 // For Node environment only. For builds, Rollup takes care of ESM.
35 require.resolve('@babel/plugin-transform-modules-commonjs'),
36
37 - // Keep stacks detailed in tests.
38 - // Don't put this in .babelrc so that we don't embed filenames
39 - // into ReactART builds that include JSX.
40 - // TODO: I have not verified that this actually works.
41 - require.resolve('@babel/plugin-transform-react-jsx-source'),
42 -
37 pathToTransformInfiniteLoops,
38 pathToTransformTestGatePragma,
39
@@ -86,6 +80,16 @@ module.exports = {
80 if (isTestFile && isInDevToolsPackages) {
81 plugins.push(pathToTransformReactVersionPragma);
82 }
83 +
84 + plugins.push([
85 + process.env.NODE_ENV === 'development'
86 + ? require.resolve('@babel/plugin-transform-react-jsx-development')
87 + : require.resolve('@babel/plugin-transform-react-jsx'),
88 + // The "automatic" runtime corresponds to react/jsx-runtime. "classic"
89 + // would be React.createElement.
90 + {runtime: 'automatic'},
91 + ]);
92 +
93 let sourceAst = hermesParser.parse(src, {babel: true});
94 return {
95 code: babel.transformFromAstSync(
scripts/jest/setupTests.js
+38
@@ -313,3 +313,41 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
313 return fn(flags);
314 };
315 }
316 +
317 +// Most of our tests call jest.resetModules in a beforeEach and the
318 +// re-require all the React modules. However, the JSX runtime is injected by
319 +// the compiler, so those bindings don't get updated. This causes warnings
320 +// logged by the JSX runtime to not have a component stack, because component
321 +// stack relies on the the secret internals object that lives on the React
322 +// module, which because of the resetModules call is longer the same one.
323 +//
324 +// To workaround this issue, we use a proxy that re-requires the latest
325 +// JSX Runtime from the require cache on every function invocation.
326 +//
327 +// Longer term we should migrate all our tests away from using require() and
328 +// resetModules, and use import syntax instead so this kind of thing doesn't
329 +// happen.
330 +lazyRequireFunctionExports('react/jsx-dev-runtime');
331 +
332 +// TODO: We shouldn't need to do this in the production runtime, but until
333 +// we remove string refs they also depend on the shared state object. Remove
334 +// once we remove string refs.
335 +lazyRequireFunctionExports('react/jsx-runtime');
336 +
337 +function lazyRequireFunctionExports(moduleName) {
338 + jest.mock(moduleName, () => {
339 + return new Proxy(jest.requireActual(moduleName), {
340 + get(originalModule, prop) {
341 + // If this export is a function, return a wrapper function that lazily
342 + // requires the implementation from the current module cache.
343 + if (typeof originalModule[prop] === 'function') {
344 + return function () {
345 + return jest.requireActual(moduleName)[prop].apply(this, arguments);
346 + };
347 + } else {
348 + return originalModule[prop];
349 + }
350 + },
351 + });
352 + });
353 +}
yarn.lock
+95
@@ -220,6 +220,13 @@
220 dependencies:
221 "@babel/types" "^7.18.6"
222
223 +"@babel/helper-annotate-as-pure@^7.22.5":
224 + version "7.22.5"
225 + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
226 + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==
227 + dependencies:
228 + "@babel/types" "^7.22.5"
229 +
230 "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4":
231 version "7.10.4"
232 resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3"
@@ -465,6 +472,13 @@
472 dependencies:
473 "@babel/types" "^7.18.6"
474
475 +"@babel/helper-module-imports@^7.22.15":
476 + version "7.22.15"
477 + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
478 + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
479 + dependencies:
480 + "@babel/types" "^7.22.15"
481 +
482 "@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0":
483 version "7.11.0"
484 resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
@@ -547,6 +561,11 @@
561 resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629"
562 integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==
563
564 +"@babel/helper-plugin-utils@^7.22.5":
565 + version "7.22.5"
566 + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
567 + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
568 +
569 "@babel/helper-regex@^7.10.4":
570 version "7.10.5"
571 resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0"
@@ -673,6 +692,11 @@
692 resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
693 integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
694
695 +"@babel/helper-string-parser@^7.23.4":
696 + version "7.23.4"
697 + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
698 + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
699 +
700 "@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.14.0":
701 version "7.14.0"
702 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
@@ -688,6 +712,11 @@
712 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
713 integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
714
715 +"@babel/helper-validator-identifier@^7.22.20":
716 + version "7.22.20"
717 + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
718 + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
719 +
720 "@babel/helper-validator-option@^7.14.5":
721 version "7.14.5"
722 resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
@@ -698,6 +727,11 @@
727 resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
728 integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
729
730 +"@babel/helper-validator-option@^7.22.15":
731 + version "7.23.5"
732 + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
733 + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
734 +
735 "@babel/helper-wrap-function@^7.10.4":
736 version "7.10.4"
737 resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
@@ -1089,6 +1123,13 @@
1123 dependencies:
1124 "@babel/helper-plugin-utils" "^7.10.4"
1125
1126 +"@babel/plugin-syntax-jsx@^7.23.3":
1127 + version "7.23.3"
1128 + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473"
1129 + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==
1130 + dependencies:
1131 + "@babel/helper-plugin-utils" "^7.22.5"
1132 +
1133 "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
1134 version "7.10.4"
1135 resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
@@ -1509,6 +1550,13 @@
1550 dependencies:
1551 "@babel/helper-plugin-utils" "^7.10.4"
1552
1553 +"@babel/plugin-transform-react-display-name@^7.23.3":
1554 + version "7.23.3"
1555 + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200"
1556 + integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==
1557 + dependencies:
1558 + "@babel/helper-plugin-utils" "^7.22.5"
1559 +
1560 "@babel/plugin-transform-react-jsx-development@^7.10.4":
1561 version "7.10.4"
1562 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.4.tgz#6ec90f244394604623880e15ebc3c34c356258ba"
@@ -1518,6 +1566,13 @@
1566 "@babel/helper-plugin-utils" "^7.10.4"
1567 "@babel/plugin-syntax-jsx" "^7.10.4"
1568
1569 +"@babel/plugin-transform-react-jsx-development@^7.22.5":
1570 + version "7.22.5"
1571 + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87"
1572 + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==
1573 + dependencies:
1574 + "@babel/plugin-transform-react-jsx" "^7.22.5"
1575 +
1576 "@babel/plugin-transform-react-jsx-self@^7.10.4":
1577 version "7.10.4"
1578 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369"
@@ -1555,6 +1610,17 @@
1610 "@babel/helper-plugin-utils" "^7.10.4"
1611 "@babel/plugin-syntax-jsx" "^7.10.4"
1612
1613 +"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.23.4":
1614 + version "7.23.4"
1615 + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312"
1616 + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==
1617 + dependencies:
1618 + "@babel/helper-annotate-as-pure" "^7.22.5"
1619 + "@babel/helper-module-imports" "^7.22.15"
1620 + "@babel/helper-plugin-utils" "^7.22.5"
1621 + "@babel/plugin-syntax-jsx" "^7.23.3"
1622 + "@babel/types" "^7.23.4"
1623 +
1624 "@babel/plugin-transform-react-pure-annotations@^7.10.4":
1625 version "7.10.4"
1626 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501"
@@ -1563,6 +1629,14 @@
1629 "@babel/helper-annotate-as-pure" "^7.10.4"
1630 "@babel/helper-plugin-utils" "^7.10.4"
1631
1632 +"@babel/plugin-transform-react-pure-annotations@^7.23.3":
1633 + version "7.23.3"
1634 + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz#fabedbdb8ee40edf5da96f3ecfc6958e3783b93c"
1635 + integrity sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==
1636 + dependencies:
1637 + "@babel/helper-annotate-as-pure" "^7.22.5"
1638 + "@babel/helper-plugin-utils" "^7.22.5"
1639 +
1640 "@babel/plugin-transform-regenerator@^7.10.4":
1641 version "7.10.4"
1642 resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63"
@@ -1775,6 +1849,18 @@
1849 "@babel/plugin-transform-react-jsx-source" "^7.10.4"
1850 "@babel/plugin-transform-react-pure-annotations" "^7.10.4"
1851
1852 +"@babel/preset-react@^7.23.3":
1853 + version "7.23.3"
1854 + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709"
1855 + integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==
1856 + dependencies:
1857 + "@babel/helper-plugin-utils" "^7.22.5"
1858 + "@babel/helper-validator-option" "^7.22.15"
1859 + "@babel/plugin-transform-react-display-name" "^7.23.3"
1860 + "@babel/plugin-transform-react-jsx" "^7.22.15"
1861 + "@babel/plugin-transform-react-jsx-development" "^7.22.5"
1862 + "@babel/plugin-transform-react-pure-annotations" "^7.23.3"
1863 +
1864 "@babel/preset-typescript@^7.14.5":
1865 version "7.16.0"
1866 resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz#b0b4f105b855fb3d631ec036cdc9d1ffd1fa5eac"
@@ -1979,6 +2065,15 @@
2065 "@babel/helper-validator-identifier" "^7.19.1"
2066 to-fast-properties "^2.0.0"
2067
2068 +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.4":
2069 + version "7.23.9"
2070 + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002"
2071 + integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==
2072 + dependencies:
2073 + "@babel/helper-string-parser" "^7.23.4"
2074 + "@babel/helper-validator-identifier" "^7.22.20"
2075 + to-fast-properties "^2.0.0"
2076 +
2077 "@bcoe/v8-coverage@^0.2.3":
2078 version "0.2.3"
2079 resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"