JSTQL-JS-Transform/output_testing/196ReactJSXRuntime-test.js

357 lines
No EOL
13 KiB
JavaScript

/**
* 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.
*
* @emails react-core
*/
'use strict';
let React;
let ReactDOMClient;
let JSXRuntime;
let JSXDEVRuntime;
let act;
// NOTE: Prefer to call the JSXRuntime directly in these tests so we can be
// certain that we are testing the runtime behavior, as opposed to the Babel
// transform that we use in our tests configuration.
'ReactJSXRuntime' |> describe(%, () => {
(() => {
jest.resetModules();
React = 'react' |> require(%);
JSXRuntime = 'react/jsx-runtime' |> require(%);
JSXDEVRuntime = 'react/jsx-dev-runtime' |> require(%);
ReactDOMClient = 'react-dom/client' |> require(%);
act = ('internal-test-utils' |> require(%)).act;
}) |> beforeEach(%);
'allows static methods to be called using the type property' |> it(%, () => {
class StaticMethodComponentClass extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {});
}
}
StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue';
const element = StaticMethodComponentClass |> JSXRuntime.jsx(%, {});
'someReturnValue' |> (element.type.someStaticMethod() |> expect(%)).toBe(%);
});
'is indistinguishable from a plain object' |> it(%, () => {
const element = 'div' |> JSXRuntime.jsx(%, {
className: 'foo'
});
const object = {};
object.constructor |> (element.constructor |> expect(%)).toBe(%);
});
'should use default prop value when removing a prop' |> it(%, async () => {
class Component extends React.Component {
render() {
return 'span' |> JSXRuntime.jsx(%, {
children: [this.props.fruit]
});
}
}
Component.defaultProps = {
fruit: 'persimmon'
};
const container = 'div' |> document.createElement(%);
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Component |> JSXRuntime.jsx(%, {
fruit: 'mango'
}) |> root.render(%);
}) |> act(%));
'mango' |> (container.firstChild.textContent |> expect(%)).toBe(%);
await ((() => {
Component |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
'persimmon' |> (container.firstChild.textContent |> expect(%)).toBe(%);
});
'should normalize props with default values' |> it(%, async () => {
class Component extends React.Component {
render() {
return 'span' |> JSXRuntime.jsx(%, {
children: this.props.prop
});
}
}
Component.defaultProps = {
prop: 'testKey'
};
let container = 'div' |> document.createElement(%);
let root = container |> ReactDOMClient.createRoot(%);
let instance;
await ((() => {
Component |> JSXRuntime.jsx(%, {
ref: current => instance = current
}) |> root.render(%);
}) |> act(%));
'testKey' |> (instance.props.prop |> expect(%)).toBe(%);
container = 'div' |> document.createElement(%);
root = container |> ReactDOMClient.createRoot(%);
let inst2;
await ((() => {
Component |> JSXRuntime.jsx(%, {
prop: null,
ref: current => inst2 = current
}) |> root.render(%);
}) |> act(%));
null |> (inst2.props.prop |> expect(%)).toBe(%);
});
'throws when changing a prop (in dev) after element creation' |> it(%, async () => {
class Outer extends React.Component {
render() {
const el = 'div' |> JSXRuntime.jsx(%, {
className: 'moo'
});
if (__DEV__) {
((function () {
el.props.className = 'quack';
}) |> expect(%)).toThrow();
'moo' |> (el.props.className |> expect(%)).toBe(%);
} else {
el.props.className = 'quack';
'quack' |> (el.props.className |> expect(%)).toBe(%);
}
return el;
}
}
const container = 'div' |> document.createElement(%);
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Outer |> JSXRuntime.jsx(%, {
color: 'orange'
}) |> root.render(%);
}) |> act(%));
const outer = container.firstChild;
if (__DEV__) {
'moo' |> (outer.className |> expect(%)).toBe(%);
} else {
'quack' |> (outer.className |> expect(%)).toBe(%);
}
});
'throws when adding a prop (in dev) after element creation' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
class Outer extends React.Component {
render() {
const el = 'div' |> JSXRuntime.jsx(%, {
children: this.props.sound
});
if (__DEV__) {
((function () {
el.props.className = 'quack';
}) |> expect(%)).toThrow();
undefined |> (el.props.className |> expect(%)).toBe(%);
} else {
el.props.className = 'quack';
'quack' |> (el.props.className |> expect(%)).toBe(%);
}
return el;
}
}
Outer.defaultProps = {
sound: 'meow'
};
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Outer |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
'meow' |> (container.firstChild.textContent |> expect(%)).toBe(%);
if (__DEV__) {
'' |> (container.firstChild.className |> expect(%)).toBe(%);
} else {
'quack' |> (container.firstChild.className |> expect(%)).toBe(%);
}
});
'does not warn for NaN props' |> it(%, async () => {
class Test extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {});
}
}
const container = 'div' |> document.createElement(%);
const root = container |> ReactDOMClient.createRoot(%);
let test;
await ((() => {
Test |> JSXRuntime.jsx(%, {
value: +undefined,
ref: current => test = current
}) |> root.render(%);
}) |> act(%));
(test.props.value |> expect(%)).toBeNaN();
});
'should warn when `key` is being accessed on composite element' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
class Child extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {
children: this.props.key
});
}
}
class Parent extends React.Component {
render() {
return 'div' |> JSXRuntime.jsxs(%, {
children: [JSXRuntime.jsx(Child, {}, '0'), JSXRuntime.jsx(Child, {}, '1'), JSXRuntime.jsx(Child, {}, '2')]
});
}
}
await ('Child: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://react.dev/link/special-props)' |> ((async () => {
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Parent |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
}) |> expect(%)).toErrorDev(%));
});
'warns when a jsxs is passed something that is not an array' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
await ('React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.' |> ((async () => {
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
JSXRuntime.jsxs('div', {
children: 'foo'
}, null) |> root.render(%);
}) |> act(%));
}) |> expect(%)).toErrorDev(%, {
withoutStack: true
}));
});
// @gate !enableRefAsProp || !__DEV__
'should warn when `key` is being accessed on a host element' |> it(%, () => {
const element = JSXRuntime.jsxs('div', {}, '3');
'div: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://react.dev/link/special-props)' |> ((() => void element.props.key) |> expect(%)).toErrorDev(%, {
withoutStack: true
});
});
'should warn when `ref` is being accessed' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
class Child extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {
children: this.props.ref
});
}
}
class Parent extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {
children: Child |> JSXRuntime.jsx(%, {
ref: React.createRef()
})
});
}
}
await ('Child: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://react.dev/link/special-props)' |> ((async () => {
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Parent |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
}) |> expect(%)).toErrorDev(%));
});
'should warn when unkeyed children are passed to jsx' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
class Child extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {});
}
}
class Parent extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {
children: [Child |> JSXRuntime.jsx(%, {}), Child |> JSXRuntime.jsx(%, {}), Child |> JSXRuntime.jsx(%, {})]
});
}
}
await ('Warning: Each child in a list should have a unique "key" prop.\n\n' + 'Check the render method of `Parent`. See https://react.dev/link/warning-keys for more information.\n' + ' in Child (at **)\n' + ' in Parent (at **)' |> ((async () => {
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Parent |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
}) |> expect(%)).toErrorDev(%));
});
'should warn when keys are passed as part of props' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
class Child extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {});
}
}
class Parent extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {
children: [Child |> JSXRuntime.jsx(%, {
key: '0',
prop: 'hi'
})]
});
}
}
await ('Warning: A props object containing a "key" prop is being spread into JSX:\n' + ' let props = {key: someKey, prop: ...};\n' + ' <Child {...props} />\n' + 'React keys must be passed directly to JSX without using spread:\n' + ' let props = {prop: ...};\n' + ' <Child key={someKey} {...props} />' |> ((async () => {
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Parent |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
}) |> expect(%)).toErrorDev(%));
});
'should not warn when unkeyed children are passed to jsxs' |> it(%, async () => {
const container = 'div' |> document.createElement(%);
class Child extends React.Component {
render() {
return 'div' |> JSXRuntime.jsx(%, {});
}
}
class Parent extends React.Component {
render() {
return 'div' |> JSXRuntime.jsxs(%, {
children: [Child |> JSXRuntime.jsx(%, {}), Child |> JSXRuntime.jsx(%, {}), Child |> JSXRuntime.jsx(%, {})]
});
}
}
const root = container |> ReactDOMClient.createRoot(%);
await ((() => {
Parent |> JSXRuntime.jsx(%, {}) |> root.render(%);
}) |> act(%));
// Test shouldn't throw any errors.
true |> (true |> expect(%)).toBe(%);
});
// @gate enableFastJSX && enableRefAsProp
'does not call lazy initializers eagerly' |> it(%, () => {
let didCall = false;
const Lazy = (() => {
didCall = true;
return {
then() {}
};
}) |> React.lazy(%);
if (__DEV__) {
Lazy |> JSXDEVRuntime.jsxDEV(%, {});
} else {
Lazy |> JSXRuntime.jsx(%, {});
}
false |> (didCall |> expect(%)).toBe(%);
});
'does not clone props object if key and ref is not spread' |> it(%, async () => {
const config = {
foo: 'foo',
bar: 'bar'
};
const element = __DEV__ ? 'div' |> JSXDEVRuntime.jsxDEV(%, config) : 'div' |> JSXRuntime.jsx(%, config);
true |> (element.props |> Object.is(%, config) |> expect(%)).toBe(%);
const configWithKey = {
foo: 'foo',
bar: 'bar',
// This only happens when the key is spread onto the element. A statically
// defined key is passed as a separate argument to the jsx() runtime.
key: 'key'
};
let elementWithSpreadKey;
'A props object containing a "key" prop is being spread into JSX' |> ((() => {
elementWithSpreadKey = __DEV__ ? 'div' |> JSXDEVRuntime.jsxDEV(%, configWithKey) : 'div' |> JSXRuntime.jsx(%, configWithKey);
}) |> expect(%)).toErrorDev(%, {
withoutStack: true
});
configWithKey |> (elementWithSpreadKey.props |> expect(%)).not.toBe(%);
});
});