/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react/src/__tests__/ReactJSXRuntime-test.js
388 строк
12 KB
Ricky
[flags] remove enableOwnerStacks (#32426)
04 мар 2025, 20:34
Не верифицирован
04 мар 2025, 20:34
e0fe347
Код
Авторство
О чём код?
/** * 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; let assertConsoleErrorDev; // 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. describe('ReactJSXRuntime', () => { beforeEach(() => { jest.resetModules(); React = require('react'); JSXRuntime = require('react/jsx-runtime'); JSXDEVRuntime = require('react/jsx-dev-runtime'); ReactDOMClient = require('react-dom/client'); ({act, assertConsoleErrorDev} = require('internal-test-utils')); }); it('allows static methods to be called using the type property', () => { class StaticMethodComponentClass extends React.Component { render() { return JSXRuntime.jsx('div', {}); } } StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue'; const element = JSXRuntime.jsx(StaticMethodComponentClass, {}); expect(element.type.someStaticMethod()).toBe('someReturnValue'); }); it('is indistinguishable from a plain object', () => { const element = JSXRuntime.jsx('div', {className: 'foo'}); const object = {}; expect(element.constructor).toBe(object.constructor); }); it('should use default prop value when removing a prop', async () => { class Component extends React.Component { render() { return JSXRuntime.jsx('span', {children: [this.props.fruit]}); } } Component.defaultProps = {fruit: 'persimmon'}; const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Component, {fruit: 'mango'})); }); expect(container.firstChild.textContent).toBe('mango'); await act(() => { root.render(JSXRuntime.jsx(Component, {})); }); expect(container.firstChild.textContent).toBe('persimmon'); }); it('should normalize props with default values', async () => { class Component extends React.Component { render() { return JSXRuntime.jsx('span', {children: this.props.prop}); } } Component.defaultProps = {prop: 'testKey'}; let container = document.createElement('div'); let root = ReactDOMClient.createRoot(container); let instance; await act(() => { root.render( JSXRuntime.jsx(Component, {ref: current => (instance = current)}), ); }); expect(instance.props.prop).toBe('testKey'); container = document.createElement('div'); root = ReactDOMClient.createRoot(container); let inst2; await act(() => { root.render( JSXRuntime.jsx(Component, { prop: null, ref: current => (inst2 = current), }), ); }); expect(inst2.props.prop).toBe(null); }); it('throws when changing a prop (in dev) after element creation', async () => { class Outer extends React.Component { render() { const el = JSXRuntime.jsx('div', {className: 'moo'}); if (__DEV__) { expect(function () { el.props.className = 'quack'; }).toThrow(); expect(el.props.className).toBe('moo'); } else { el.props.className = 'quack'; expect(el.props.className).toBe('quack'); } return el; } } const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Outer, {color: 'orange'})); }); const outer = container.firstChild; if (__DEV__) { expect(outer.className).toBe('moo'); } else { expect(outer.className).toBe('quack'); } }); it('throws when adding a prop (in dev) after element creation', async () => { const container = document.createElement('div'); class Outer extends React.Component { render() { const el = JSXRuntime.jsx('div', {children: this.props.sound}); if (__DEV__) { expect(function () { el.props.className = 'quack'; }).toThrow(); expect(el.props.className).toBe(undefined); } else { el.props.className = 'quack'; expect(el.props.className).toBe('quack'); } return el; } } Outer.defaultProps = {sound: 'meow'}; const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Outer, {})); }); expect(container.firstChild.textContent).toBe('meow'); if (__DEV__) { expect(container.firstChild.className).toBe(''); } else { expect(container.firstChild.className).toBe('quack'); } }); it('does not warn for NaN props', async () => { class Test extends React.Component { render() { return JSXRuntime.jsx('div', {}); } } const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); let test; await act(() => { root.render( JSXRuntime.jsx(Test, { value: +undefined, ref: current => (test = current), }), ); }); expect(test.props.value).toBeNaN(); }); it('should warn when `key` is being accessed on composite element', async () => { const container = document.createElement('div'); class Child extends React.Component { render() { return JSXRuntime.jsx('div', {children: this.props.key}); } } class Parent extends React.Component { render() { return JSXRuntime.jsxs('div', { children: [ JSXRuntime.jsx(Child, {}, '0'), JSXRuntime.jsx(Child, {}, '1'), JSXRuntime.jsx(Child, {}, '2'), ], }); } } const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Parent, {})); }); assertConsoleErrorDev([ '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)\n' + ' in Parent (at **)', ]); }); it('warns when a jsxs is passed something that is not an array', async () => { const container = document.createElement('div'); const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsxs('div', {children: 'foo'}, null)); }); assertConsoleErrorDev( [ 'React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.', ], {withoutStack: true}, ); }); it('should warn when `key` is being accessed on a host element', () => { const element = JSXRuntime.jsxs('div', {}, '3'); void element.props.key; assertConsoleErrorDev( [ '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)', ], {withoutStack: true}, ); }); it('should warn when unkeyed children are passed to jsx', async () => { const container = document.createElement('div'); class Child extends React.Component { render() { return JSXRuntime.jsx('div', {}); } } class Parent extends React.Component { render() { return JSXRuntime.jsx('div', { children: [ JSXRuntime.jsx(Child, {}), JSXRuntime.jsx(Child, {}), JSXRuntime.jsx(Child, {}), ], }); } } const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Parent, {})); }); assertConsoleErrorDev([ '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 Parent (at **)', ]); }); it('should warn when keys are passed as part of props', async () => { const container = document.createElement('div'); class Child extends React.Component { render() { return JSXRuntime.jsx('div', {}); } } class Parent extends React.Component { render() { return JSXRuntime.jsx('div', { children: [JSXRuntime.jsx(Child, {key: '0', prop: 'hi'})], }); } } const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Parent, {})); }); assertConsoleErrorDev([ '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} />\n' + ' in Parent (at **)', ]); }); it('should not warn when unkeyed children are passed to jsxs', async () => { const container = document.createElement('div'); class Child extends React.Component { render() { return JSXRuntime.jsx('div', {}); } } class Parent extends React.Component { render() { return JSXRuntime.jsxs('div', { children: [ JSXRuntime.jsx(Child, {}), JSXRuntime.jsx(Child, {}), JSXRuntime.jsx(Child, {}), ], }); } } const root = ReactDOMClient.createRoot(container); await act(() => { root.render(JSXRuntime.jsx(Parent, {})); }); // Test shouldn't throw any errors. expect(true).toBe(true); }); it('does not call lazy initializers eagerly', () => { let didCall = false; const Lazy = React.lazy(() => { didCall = true; return {then() {}}; }); if (__DEV__) { JSXDEVRuntime.jsxDEV(Lazy, {}); } else { JSXRuntime.jsx(Lazy, {}); } expect(didCall).toBe(false); }); it('does not clone props object if key and ref is not spread', async () => { const config = { foo: 'foo', bar: 'bar', }; const element = __DEV__ ? JSXDEVRuntime.jsxDEV('div', config) : JSXRuntime.jsx('div', config); expect(Object.is(element.props, config)).toBe(true); 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', }; const elementWithSpreadKey = __DEV__ ? JSXDEVRuntime.jsxDEV('div', configWithKey) : JSXRuntime.jsx('div', configWithKey); assertConsoleErrorDev( [ 'A props object containing a "key" prop is being spread into JSX:\n' + ' let props = {key: someKey, foo: ..., bar: ...};\n' + ' <div {...props} />\n' + 'React keys must be passed directly to JSX without using spread:\n' + ' let props = {foo: ..., bar: ...};\n' + ' <div key={someKey} {...props} />', ], {withoutStack: true}, ); expect(elementWithSpreadKey.props).not.toBe(configWithKey); }); });