/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-refresh/src/__tests__/ReactFreshBabelPlugin-test.js
564 строки
15 KB
Andrew Clark
[Codemod] Update copyright header to Meta (#25315)
18 окт 2022, 18:19
Не верифицирован
18 окт 2022, 18:19
9cdf8a9
Код
Авторство
О чём код?
/** * 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'; const babel = require('@babel/core'); const {wrap} = require('jest-snapshot-serializer-raw'); const freshPlugin = require('react-refresh/babel'); function transform(input, options = {}) { return wrap( babel.transform(input, { babelrc: false, configFile: false, envName: options.envName, plugins: [ '@babel/syntax-jsx', '@babel/syntax-dynamic-import', [ freshPlugin, { skipEnvCheck: options.skipEnvCheck === undefined ? true : options.skipEnvCheck, // To simplify debugging tests: emitFullSignatures: true, ...options.freshOptions, }, ], ...(options.plugins || []), ], }).code, ); } describe('ReactFreshBabelPlugin', () => { it('registers top-level function declarations', () => { // Hello and Bar should be registered, handleClick shouldn't. expect( transform(` function Hello() { function handleClick() {} return <h1 onClick={handleClick}>Hi</h1>; } function Bar() { return <Hello />; } `), ).toMatchSnapshot(); }); it('registers top-level exported function declarations', () => { expect( transform(` export function Hello() { function handleClick() {} return <h1 onClick={handleClick}>Hi</h1>; } export default function Bar() { return <Hello />; } function Baz() { return <h1>OK</h1>; } const NotAComp = 'hi'; export { Baz, NotAComp }; export function sum() {} export const Bad = 42; `), ).toMatchSnapshot(); }); it('registers top-level exported named arrow functions', () => { expect( transform(` export const Hello = () => { function handleClick() {} return <h1 onClick={handleClick}>Hi</h1>; }; export let Bar = (props) => <Hello />; export default () => { // This one should be ignored. // You should name your components. return <Hello />; }; `), ).toMatchSnapshot(); }); it('uses original function declaration if it get reassigned', () => { // This should register the original version. // TODO: in the future, we may *also* register the wrapped one. expect( transform(` function Hello() { return <h1>Hi</h1>; } Hello = connect(Hello); `), ).toMatchSnapshot(); }); it('only registers pascal case functions', () => { // Should not get registered. expect( transform(` function hello() { return 2 * 2; } `), ).toMatchSnapshot(); }); it('registers top-level variable declarations with function expressions', () => { // Hello and Bar should be registered; handleClick, sum, Baz, and Qux shouldn't. expect( transform(` let Hello = function() { function handleClick() {} return <h1 onClick={handleClick}>Hi</h1>; }; const Bar = function Baz() { return <Hello />; }; function sum() {} let Baz = 10; var Qux; `), ).toMatchSnapshot(); }); it('registers top-level variable declarations with arrow functions', () => { // Hello, Bar, and Baz should be registered; handleClick and sum shouldn't. expect( transform(` let Hello = () => { const handleClick = () => {}; return <h1 onClick={handleClick}>Hi</h1>; } const Bar = () => { return <Hello />; }; var Baz = () => <div />; var sum = () => {}; `), ).toMatchSnapshot(); }); it('ignores HOC definitions', () => { // TODO: we might want to handle HOCs at usage site, however. // TODO: it would be nice if we could always avoid registering // a function that is known to return a function or other non-node. expect( transform(` let connect = () => { function Comp() { const handleClick = () => {}; return <h1 onClick={handleClick}>Hi</h1>; } return Comp; }; function withRouter() { return function Child() { const handleClick = () => {}; return <h1 onClick={handleClick}>Hi</h1>; } }; `), ).toMatchSnapshot(); }); it('ignores complex definitions', () => { expect( transform(` let A = foo ? () => { return <h1>Hi</h1>; } : null const B = (function Foo() { return <h1>Hi</h1>; })(); let C = () => () => { return <h1>Hi</h1>; }; let D = bar && (() => { return <h1>Hi</h1>; }); `), ).toMatchSnapshot(); }); it('ignores unnamed function declarations', () => { expect( transform(` export default function() {} `), ).toMatchSnapshot(); }); it('registers likely HOCs with inline functions', () => { expect( transform(` const A = forwardRef(function() { return <h1>Foo</h1>; }); const B = memo(React.forwardRef(() => { return <h1>Foo</h1>; })); export default React.memo(forwardRef((props, ref) => { return <h1>Foo</h1>; })); `), ).toMatchSnapshot(); expect( transform(` export default React.memo(forwardRef(function (props, ref) { return <h1>Foo</h1>; })); `), ).toMatchSnapshot(); expect( transform(` export default React.memo(forwardRef(function Named(props, ref) { return <h1>Foo</h1>; })); `), ).toMatchSnapshot(); }); it('ignores higher-order functions that are not HOCs', () => { expect( transform(` const throttledAlert = throttle(function() { alert('Hi'); }); const TooComplex = (function() { return hello })(() => {}); if (cond) { const Foo = thing(() => {}); } `), ).toMatchSnapshot(); }); it('registers identifiers used in JSX at definition site', () => { // When in doubt, register variables that were used in JSX. // Foo, Header, and B get registered. // A doesn't get registered because it's not declared locally. // Alias doesn't get registered because its definition is just an identifier. expect( transform(` import A from './A'; import Store from './Store'; Store.subscribe(); const Header = styled.div\`color: red\` const StyledFactory1 = styled('div')\`color: hotpink\` const StyledFactory2 = styled('div')({ color: 'hotpink' }) const StyledFactory3 = styled(A)({ color: 'hotpink' }) const FunnyFactory = funny.factory\`\`; let Alias1 = A; let Alias2 = A.Foo; const Dict = {}; function Foo() { return ( <div><A /><B /><StyledFactory1 /><StyledFactory2 /><StyledFactory3 /><Alias1 /><Alias2 /><Header /><Dict.X /></div> ); } const B = hoc(A); // This is currently registered as a false positive: const NotAComponent = wow(A); // We could avoid it but it also doesn't hurt. `), ).toMatchSnapshot(); }); it('registers identifiers used in React.createElement at definition site', () => { // When in doubt, register variables that were used in JSX. // Foo, Header, and B get registered. // A doesn't get registered because it's not declared locally. // Alias doesn't get registered because its definition is just an identifier. expect( transform(` import A from './A'; import Store from './Store'; Store.subscribe(); const Header = styled.div\`color: red\` const StyledFactory1 = styled('div')\`color: hotpink\` const StyledFactory2 = styled('div')({ color: 'hotpink' }) const StyledFactory3 = styled(A)({ color: 'hotpink' }) const FunnyFactory = funny.factory\`\`; let Alias1 = A; let Alias2 = A.Foo; const Dict = {}; function Foo() { return [ React.createElement(A), React.createElement(B), React.createElement(StyledFactory1), React.createElement(StyledFactory2), React.createElement(StyledFactory3), React.createElement(Alias1), React.createElement(Alias2), jsx(Header), React.createElement(Dict.X), ]; } React.createContext(Store); const B = hoc(A); // This is currently registered as a false positive: const NotAComponent = wow(A); // We could avoid it but it also doesn't hurt. `), ).toMatchSnapshot(); }); it('registers capitalized identifiers in HOC calls', () => { expect( transform(` function Foo() { return <h1>Hi</h1>; } export default hoc(Foo); export const A = hoc(Foo); const B = hoc(Foo); `), ).toMatchSnapshot(); }); it('generates signatures for function declarations calling hooks', () => { expect( transform(` export default function App() { const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1>{foo}</h1>; } `), ).toMatchSnapshot(); }); it('generates signatures for function expressions calling hooks', () => { // Unlike __register__, we want to sign all functions -- not just top level. // This lets us support editing HOCs better. // For function declarations, __signature__ is called on next line. // For function expressions, it wraps the expression. // In order for this to work, __signature__ returns its first argument. expect( transform(` export const A = React.memo(React.forwardRef((props, ref) => { const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1 ref={ref}>{foo}</h1>; })); export const B = React.memo(React.forwardRef(function(props, ref) { const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1 ref={ref}>{foo}</h1>; })); function hoc() { return function Inner() { const [foo, setFoo] = useState(0); React.useEffect(() => {}); return <h1 ref={ref}>{foo}</h1>; }; } export let C = hoc(); `), ).toMatchSnapshot(); }); it('includes custom hooks into the signatures', () => { expect( transform(` function useFancyState() { const [foo, setFoo] = React.useState(0); useFancyEffect(); return foo; } const useFancyEffect = () => { React.useEffect(() => {}); }; export default function App() { const bar = useFancyState(); return <h1>{bar}</h1>; } `), ).toMatchSnapshot(); }); it('includes custom hooks into the signatures when commonjs target is used', () => { // this test is passing with Babel 6 // but would fail for Babel 7 _without_ custom hook node being cloned for signature expect( transform( ` import {useFancyState} from './hooks'; export default function App() { const bar = useFancyState(); return <h1>{bar}</h1>; } `, { plugins: ['@babel/transform-modules-commonjs'], }, ), ).toMatchSnapshot(); }); it('generates valid signature for exotic ways to call Hooks', () => { expect( transform(` import FancyHook from 'fancy'; export default function App() { function useFancyState() { const [foo, setFoo] = React.useState(0); useFancyEffect(); return foo; } const bar = useFancyState(); const baz = FancyHook.useThing(); React.useState(); useThePlatform(); return <h1>{bar}{baz}</h1>; } `), ).toMatchSnapshot(); }); it('does not consider require-like methods to be HOCs', () => { // None of these were declared in this file. // It's bad to register them because that would trigger // modules to execute in an environment with inline requires. // So we expect the transform to skip all of them even though // they are used in JSX. expect( transform(` const A = require('A'); const B = foo ? require('X') : require('Y'); const C = requireCond(gk, 'C'); const D = import('D'); export default function App() { return ( <div> <A /> <B /> <C /> <D /> </div> ); } `), ).toMatchSnapshot(); }); it('can handle implicit arrow returns', () => { expect( transform(` export default () => useContext(X); export const Foo = () => useContext(X); module.exports = () => useContext(X); const Bar = () => useContext(X); const Baz = memo(() => useContext(X)); const Qux = () => (0, useContext(X)); `), ).toMatchSnapshot(); }); it('uses custom identifiers for $RefreshReg$ and $RefreshSig$', () => { expect( transform( `export default function Bar () { useContext(X) return <Foo /> };`, { freshOptions: { refreshReg: 'import.meta.refreshReg', refreshSig: 'import.meta.refreshSig', }, }, ), ).toMatchSnapshot(); }); it("respects Babel's envName option", () => { const envName = 'random'; expect(() => transform(`export default function BabelEnv () { return null };`, { envName, skipEnvCheck: false, }), ).toThrowError( 'React Refresh Babel transform should only be enabled in development environment. ' + 'Instead, the environment is: "' + envName + '". If you want to override this check, pass {skipEnvCheck: true} as plugin options.', ); }); it('does not get tripped by IIFEs', () => { expect( transform(` while (item) { (item => { useFoo(); })(item); } `), ).toMatchSnapshot(); }); it('supports typescript namespace syntax', () => { expect( transform( ` namespace Foo { export namespace Bar { export const A = () => {}; function B() {}; export const B1 = B; } export const C = () => {}; export function D() {}; namespace NotExported { export const E = () => {}; } } `, {plugins: [['@babel/plugin-syntax-typescript', {isTSX: true}]]}, ), ).toMatchSnapshot(); }); });