import { transform } from './transform';
// These tests cover both functionality from
// @compiled/babel-plugin and @compiled/babel-plugin-strip-runtime
// and how they interact with:
//
// * JSX pragma comments /** @jsx jsx */ and
// /** @jsxImportSource @compiled/react */, and
// * @babel/preset-react's JSX pragma-related config options
// (discussed in
// https://babeljs.io/docs/babel-plugin-transform-react-jsx
// and https://babeljs.io/docs/babel-preset-react)
describe('Compiled setup with classic runtime', () => {
describe('with JSX pragma', () => {
describe('if only Compiled is used in file', () => {
it('converts JSX elements to React.createElement', () => {
const code = `
/** @jsx jsx */
import { css, jsx } from '@compiled/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const codeWithRenamedImport = `
/** @jsx myJsx */
import { css, jsx as myJsx } from '@compiled/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
for (const c of [code, codeWithRenamedImport]) {
const actual = transform(c, {
run: 'both',
runtime: 'classic',
});
expect(actual).toContain("import * as React from 'react';");
// All components should look like this:
// const Component = () => /*#__PURE__*/ React.createElement(...)
expect(actual).toMatch(
/Component = \(\) =>\n(\s)*\/\*#__PURE__\*\/ React.createElement/m
);
expect(actual).toMatch(
/Component2 = \(\) =>\n(\s)*\/\*#__PURE__\*\/ React.createElement/m
);
// All traces of JSX pragma or JSX imports are removed
expect(actual).not.toContain('jsx');
}
});
});
describe('if only Emotion is used in file', () => {
it('is not processed by Compiled', () => {
const code = `
/** @jsx jsx */
import { css, jsx } from '@emotion/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const actual = transform(code, {
run: 'both',
runtime: 'classic',
});
expect(actual).toMatchInlineSnapshot(`
"/** @jsx jsx */
import { css, jsx } from '@emotion/react';
const Component = () =>
jsx(
'div',
{
css: {
fontSize: 12,
color: 'blue',
},
},
'hello world 2'
);
const Component2 = () =>
jsx(
'div',
{
css: css({
fontSize: 12,
color: 'pink',
}),
},
'hello world 2'
);
"
`);
});
it('is not processed by Compiled (with renamed JSX pragma)', () => {
// Uses renamed import `myJsx`
const code = `
/** @jsx myJsx */
import { css, jsx as myJsx } from '@emotion/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const actual = transform(code, {
run: 'both',
runtime: 'classic',
});
expect(actual).toMatchInlineSnapshot(`
"/** @jsx myJsx */
import { css, jsx as myJsx } from '@emotion/react';
const Component = () =>
myJsx(
'div',
{
css: {
fontSize: 12,
color: 'blue',
},
},
'hello world 2'
);
const Component2 = () =>
myJsx(
'div',
{
css: css({
fontSize: 12,
color: 'pink',
}),
},
'hello world 2'
);
"
`);
});
});
describe('if both Compiled and Emotion are used in file', () => {
it('throws error', () => {
const code = `
/** @jsx jsx */
import { css } from '@compiled/react';
import { jsx } from '@emotion/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
expect(() =>
transform(code, {
run: 'both',
runtime: 'classic',
})
).toThrow(/Found a `jsx` function call/);
});
});
});
describe('without JSX pragma', () => {
it('throws error if pragma is set in babel config', () => {
const code = `
/** @jsx jsx */
import { css, jsx } from '@compiled/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const codeWithRenamedImport = `
/** @jsx myJsx */
import { css, jsx as myJsx } from '@compiled/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
for (const c of [code, codeWithRenamedImport]) {
expect(() =>
transform(c, {
run: 'both',
runtime: 'classic',
babelJSXPragma: 'jsx',
})
).toThrow(/Found a `jsx` function call/);
}
});
});
});
describe('Compiled setup with automatic runtime', () => {
describe('with JSX pragma', () => {
describe('if Compiled is used in file', () => {
it('imports JSX runtime from React, not Compiled', () => {
const code = `
/** @jsxImportSource @compiled/react */
import { css } from '@compiled/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const actual = transform(code, {
run: 'both',
runtime: 'automatic',
});
expect(actual).toContain("'react/jsx-runtime'");
expect(actual).not.toContain("'@compiled/react/jsx-runtime'");
// jsx function calls from React (and not from Compiled/Emotion/etc)
// have "PURE" comments added beforehand by @babel/preset-react.
// Make sure this is there so Webpack or Parcel can use tree-shaking.
expect(actual).toContain('/*#__PURE__*/ _jsx');
});
});
describe('if Emotion is used in file', () => {
it('does not process file', () => {
const code = `
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const actual = transform(code, {
run: 'both',
runtime: 'automatic',
});
expect(actual).toContain("'@emotion/react/jsx-runtime'");
expect(actual).not.toContain("'@compiled/react/jsx-runtime'");
// If any of the below statements are present, that means we accidentally
// got rid of the /** @jsxImportSource @emotion/react */ and the default React
// JSX runtime is being used instead of Emotion -- whoops!
expect(actual).not.toContain("'react/jsx-runtime'");
expect(actual).not.toContain('/*#__PURE__*/ _jsx');
});
});
});
describe('without JSX pragma', () => {
it('imports JSX from Compiled if pragma is set in babel config', () => {
const code = `
import { css, jsx } from '@compiled/react';
const Component = () => (
hello world 2
);
const Component2 = () => (
hello world 2
);
`;
const actual = transform(code, {
run: 'both',
runtime: 'automatic',
babelJSXImportSource: '@compiled/react',
});
expect(actual).toContain("'@compiled/react/jsx-runtime'");
expect(actual).not.toContain("'react/jsx-runtime'");
});
});
});