/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /* eslint-disable quotes */ 'use strict'; const babel = require('@babel/core'); const codeFrame = require('@babel/code-frame'); const {wrap} = require('jest-snapshot-serializer-raw'); function transform(input, pluginOpts, babelOpts) { return wrap( babel.transform(input, { configFile: false, sourceType: 'module', plugins: [ '@babel/plugin-syntax-jsx', '@babel/plugin-transform-arrow-functions', ...(pluginOpts && pluginOpts.development ? [ '@babel/plugin-transform-react-jsx-source', '@babel/plugin-transform-react-jsx-self', ] : []), [ './packages/babel-plugin-react-jsx', { useBuiltIns: true, useCreateElement: false, ...pluginOpts, }, ], ], ...babelOpts, }).code ); } describe('transform react to jsx', () => { it('auto import pragma overrides regular pragma', () => { expect( transform( `/** @jsxAutoImport defaultExport */ var x =
`, { autoImport: 'namespace', importSource: 'foobar', } ) ).toMatchSnapshot(); }); it('import source pragma overrides regular pragma', () => { expect( transform( `/** @jsxImportSource baz */ var x =
`, { autoImport: 'namespace', importSource: 'foobar', } ) ).toMatchSnapshot(); }); it('multiple pragmas work', () => { expect( transform( `/** Some comment here * @jsxImportSource baz * @jsxAutoImport defaultExport */ var x =
`, { autoImport: 'namespace', importSource: 'foobar', } ) ).toMatchSnapshot(); }); it('throws error when sourceType is module and autoImport is require', () => { const code = `var x =
`; expect(() => { transform(code, { autoImport: 'require', }); }).toThrow( 'Babel `sourceType` must be set to `script` for autoImport ' + 'to use `require` syntax. See Babel `sourceType` for details.\n' + codeFrame.codeFrameColumns( code, {start: {line: 1, column: 1}, end: {line: 1, column: 28}}, {highlightCode: true} ) ); }); it('throws error when sourceType is script and autoImport is not require', () => { const code = `var x =
`; expect(() => { transform( code, { autoImport: 'namespace', }, {sourceType: 'script'} ); }).toThrow( 'Babel `sourceType` must be set to `module` for autoImport ' + 'to use `namespace` syntax. See Babel `sourceType` for details.\n' + codeFrame.codeFrameColumns( code, {start: {line: 1, column: 1}, end: {line: 1, column: 28}}, {highlightCode: true} ) ); }); it("auto import that doesn't exist should throw error", () => { const code = `var x =
`; expect(() => { transform(code, { autoImport: 'foo', }); }).toThrow( 'autoImport must be one of the following: none, require, namespace, defaultExport, namedExports\n' + codeFrame.codeFrameColumns( code, {start: {line: 1, column: 1}, end: {line: 1, column: 28}}, {highlightCode: true} ) ); }); it('auto import can specify source', () => { expect( transform(`var x =
`, { autoImport: 'namespace', importSource: 'foobar', }) ).toMatchSnapshot(); }); it('auto import require', () => { expect( transform( `var x = ( <>
);`, { autoImport: 'require', }, { sourceType: 'script', } ) ).toMatchSnapshot(); }); it('auto import namespace', () => { expect( transform( `var x = ( <>
);`, { autoImport: 'namespace', } ) ).toMatchSnapshot(); }); it('auto import default', () => { expect( transform( `var x = ( <>
);`, { autoImport: 'defaultExport', } ) ).toMatchSnapshot(); }); it('auto import named exports', () => { expect( transform( `var x = ( <>
);`, { autoImport: 'namedExports', } ) ).toMatchSnapshot(); }); it('auto import with no JSX', () => { expect( transform( `var foo = "
"`, { autoImport: 'require', }, { sourceType: 'script', } ) ).toMatchSnapshot(); }); it('complicated scope require', () => { expect( transform( ` const Bar = () => { const Foo = () => { const Component = ({thing, ..._react}) => { if (!thing) { var _react2 = "something useless"; var b = _react3(); var c = _react5(); var jsx = 1; var _jsx = 2; return
; }; return ; }; } } `, { autoImport: 'require', }, { sourceType: 'script', } ) ).toMatchSnapshot(); }); it('complicated scope named exports', () => { expect( transform( ` const Bar = () => { const Foo = () => { const Component = ({thing, ..._react}) => { if (!thing) { var _react2 = "something useless"; var b = _react3(); var jsx = 1; var _jsx = 2; return
; }; return ; }; } } `, { autoImport: 'namedExports', } ) ).toMatchSnapshot(); }); it('auto import in dev', () => { expect( transform( `var x = ( <>
);`, { autoImport: 'namedExports', development: true, } ) ).toMatchSnapshot(); }); it('auto import none', () => { expect( transform( `var x = ( <>
);`, { autoImport: 'none', } ) ).toMatchSnapshot(); }); it('auto import undefined', () => { expect( transform( `var x = ( <>
);` ) ).toMatchSnapshot(); }); it('auto import with namespaces already defined', () => { expect( transform( ` import * as _react from "foo"; const react = _react(1); const _react1 = react; const _react2 = react; var x = (
);`, { autoImport: 'namespace', } ) ).toMatchSnapshot(); }); it('auto import with react already defined', () => { expect( transform( ` import * as react from "react"; var y = react.createElement("div", {foo: 1}); var x = (
);`, { autoImport: 'namespace', } ) ).toMatchSnapshot(); }); it('fragment with no children', () => { expect(transform(`var x = <>`)).toMatchSnapshot(); }); it('fragments', () => { expect(transform(`var x = <>
`)).toMatchSnapshot(); }); it('fragments to set keys', () => { expect( transform(`var x = `) ).toMatchSnapshot(); }); it('React.fragment to set keys and source', () => { expect( transform(`var x = `, { development: true, }) ).toMatchSnapshot(); }); it('fragments in dev mode (no key and source)', () => { expect( transform(`var x = <>
`, { development: true, }) ).toMatchSnapshot(); }); it('nonStatic children', () => { expect( transform( `var x = (
{[, ]}
); `, { development: true, } ) ).toMatchSnapshot(); }); it('static children', () => { expect( transform( `var x = (
{[, ]}
); `, { development: true, } ) ).toMatchSnapshot(); }); it('uses jsxDEV instead of jsx in dev mode', () => { expect( transform(`var x = Hi`, {development: true}) ).toMatchSnapshot(); }); it('properly passes in source and self', () => { expect( transform(`var x =
;`, {development: true}) ).toMatchSnapshot(); }); it('should properly handle potentially null variables', () => { expect( transform(` var foo = null; var x =
; `) ).toMatchSnapshot(); }); it('properly handles keys', () => { expect( transform(`var x = (
);`) ).toMatchSnapshot(); }); it('uses createElement when the key comes after a spread', () => { expect( transform(`var x = (
);`) ).toMatchSnapshot(); }); it('uses jsx when the key comes before a spread', () => { expect( transform(`var x = (
);`) ).toMatchSnapshot(); }); it('should properly handle comments adjacent to children', () => { expect( transform(` var x = (
{/* A comment at the beginning */} {/* A second comment at the beginning */} {/* A nested comment */} {/* A sandwiched comment */}
{/* A comment at the end */} {/* A second comment at the end */}
); `) ).toMatchSnapshot(); }); it('adds appropriate new lines when using spread attribute', () => { expect(transform(``)).toMatchSnapshot(); }); it('arrow functions', () => { expect( transform(` var foo = function () { return () => ; }; var bar = function () { return () => ; }; `) ).toMatchSnapshot(); }); it('assignment', () => { expect( transform(`var div = `) ).toMatchSnapshot(); }); it('concatenates adjacent string literals', () => { expect( transform(` var x =
foo {"bar"} baz
buz bang
qux {null} quack
`) ).toMatchSnapshot(); }); it('should allow constructor as prop', () => { expect(transform(`;`)).toMatchSnapshot(); }); it('should allow deeper js namespacing', () => { expect( transform(`;`) ).toMatchSnapshot(); }); it('should allow elements as attributes', () => { expect(transform(`
/>`)).toMatchSnapshot(); }); it('should allow js namespacing', () => { expect(transform(`;`)).toMatchSnapshot(); }); it('should allow nested fragments', () => { expect( transform(`
< > <> Hello world <> Goodbye world
`) ).toMatchSnapshot(); }); it('should avoid wrapping in extra parens if not needed', () => { expect( transform(` var x =
; var x =
{props.children}
; var x = {props.children} ; var x = ; `) ).toMatchSnapshot(); }); it('should convert simple tags', () => { expect(transform(`var x =
;`)).toMatchSnapshot(); }); it('should convert simple text', () => { expect(transform(`var x =
text
;`)).toMatchSnapshot(); }); it('should disallow spread children', () => { let _error; const code = `
{...children}
;`; try { transform(code); } catch (error) { _error = error; } expect(_error).toEqual( new SyntaxError( 'unknown: Spread children are not supported in React.' + '\n' + codeFrame.codeFrameColumns( code, {start: {line: 1, column: 6}, end: {line: 1, column: 19}}, {highlightCode: true} ) ) ); }); it('should escape xhtml jsxattribute', () => { expect( transform(`
;
;
; `) ).toMatchSnapshot(); }); it('should escape xhtml jsxtext', () => { /* eslint-disable no-irregular-whitespace */ expect( transform(`
wow
;
wôw
;
w & w
;
w & w
;
w   w
;
this should not parse as unicode: \u00a0
;
this should parse as nbsp:  
;
this should parse as unicode: {'\u00a0 '}
;
w < w
; `) ).toMatchSnapshot(); /*eslint-enable */ }); it('should handle attributed elements', () => { expect( transform(` var HelloMessage = React.createClass({ render: function() { return
Hello {this.props.name}
; } }); React.render( Sebastian } />, mountNode); `) ).toMatchSnapshot(); }); it('should handle has own property correctly', () => { expect( transform(`testing;`) ).toMatchSnapshot(); }); it('should have correct comma in nested children', () => { expect( transform(` var x =

{foo}
{bar}

; `) ).toMatchSnapshot(); }); it('should insert commas after expressions before whitespace', () => { expect( transform(` var x =
`) ).toMatchSnapshot(); }); it('should not add quotes to identifier names', () => { expect( transform(`var e = ;`) ).toMatchSnapshot(); }); it('should not strip nbsp even couple with other whitespace', () => { expect(transform(`
 
;`)).toMatchSnapshot(); }); it('should not strip tags with a single child of nbsp', () => { expect(transform(`
 
;`)).toMatchSnapshot(); }); it('should properly handle comments between props', () => { expect( transform(` var x = (
); `) ).toMatchSnapshot(); }); it('should quote jsx attributes', () => { expect( transform(``) ).toMatchSnapshot(); }); it('should support xml namespaces if flag', () => { expect( transform('', {throwIfNamespace: false}) ).toMatchSnapshot(); }); it('should throw error namespaces if not flag', () => { let _error; const code = ``; try { transform(code); } catch (error) { _error = error; } expect(_error).toEqual( new SyntaxError( "unknown: Namespace tags are not supported by default. React's " + "JSX doesn't support namespace tags. You can turn on the " + "'throwIfNamespace' flag to bypass this warning." + '\n' + codeFrame.codeFrameColumns( code, {start: {line: 1, column: 2}, end: {line: 1, column: 9}}, {highlightCode: true} ) ) ); }); it('should transform known hyphenated tags', () => { expect(transform(``)).toMatchSnapshot(); }); it('wraps props in react spread for first spread attributes', () => { expect(transform(``)).toMatchSnapshot(); }); it('wraps props in react spread for last spread attributes', () => { expect(transform(``)).toMatchSnapshot(); }); it('wraps props in react spread for middle spread attributes', () => { expect(transform(``)).toMatchSnapshot(); }); it('useBuiltIns false uses extend instead of Object.assign', () => { expect( transform(``, {useBuiltIns: false}) ).toMatchSnapshot(); }); it('duplicate children prop should transform into sequence expression with actual children', () => { expect( transform(`2`) ).toMatchSnapshot(); }); it('duplicate children prop should transform into sequence expression with next prop', () => { expect( transform(`2`) ).toMatchSnapshot(); }); it('duplicate children props should transform into sequence expression with next prop', () => { expect( transform(`2`) ).toMatchSnapshot(); }); it('duplicate children prop should transform into sequence expression with spread', () => { expect( transform(`2`) ).toMatchSnapshot(); }); });