rsnext/test/unit/next-babel.test.ts
Balázs Orbán 33db463fe4
chore: upgrade jest (#56909)
### What?

Upgrade jest to its latest version

### Why?

#56899 fails because historically Jest stripped the globals in Node.js, but 28+ isn't doing that anymore. If we upgrade, we don't have to keep track of Node.js globals and when they are added. This will be useful in removing even more polyfills for things that are natively shipped in Node.js now.

### How?

Jest 29 introduced a change to the snapshot format: https://jestjs.io/blog/2022/08/25/jest-29

First, I tried setting the old compat option to not require updating snapshots, but some tests were still failing: https://dev.azure.com/nextjs/next.js/_build/results?buildId=70633&view=logs&j=8af7cf9c-43a1-584d-6f5c-57bad8880974&t=7ae70e63-3625-50f4-6764-5b3e72b4bd7a&l=273 So going through the pain now instead.
2023-10-19 17:38:24 +00:00

195 lines
6.3 KiB
TypeScript

/* eslint-env jest */
import { transformSync } from '@babel/core'
const trim = (s) => s.join('\n').trim().replace(/^\s+/gm, '')
const babel = (code, esm = false, presetOptions = {}, filename = 'noop.js') =>
transformSync(code, {
filename,
presets: [[require('next/dist/build/babel/preset'), presetOptions]],
babelrc: false,
configFile: false,
sourceType: 'module',
compact: true,
caller: {
name: 'tests',
supportsStaticESM: esm,
isDev: false,
},
} as any).code
describe('next/babel', () => {
describe('jsx-pragma', () => {
it('should transform JSX to use a local identifier in modern mode', () => {
const output = babel(`const a = () => <a href="/">home</a>;`, true)
// it should add a React import:
expect(output).toMatch(`import React from"react"`)
// it should hoist JSX factory to a module level variable:
expect(output).toMatch(`var __jsx=React.createElement`)
// it should use that factory for all JSX:
expect(output).toMatch(`__jsx("a",{href:"/"`)
expect(
babel(`const a = ()=><a href="/">home</a>`, true)
).toMatchInlineSnapshot(
`"import React from"react";var __jsx=React.createElement;var a=function a(){return __jsx("a",{href:"/"},"home");};"`
)
})
it('should transform JSX to use a local identifier in CommonJS mode', () => {
const output = babel(trim`
const a = () => <React.Fragment><a href="/">home</a></React.Fragment>;
`)
// Grab generated names from the compiled output.
// It looks something like this:
// var _react = _interopRequireDefault(require("react"));
// var __jsx = _react["default"].createElement;
// react: _react
// reactNamespace: _react["default"]
const [, react, reactNamespace] = output.match(
/(([a-z0-9_]+)(\[[^\]]*?\]|\.[a-z0-9_]+)*?)\.Fragment/i
)
expect(output).toMatch(`var ${reactNamespace}=`)
expect(output).toMatch(`require("react")`)
expect(output).toMatch(`var __jsx=${react}.createElement`)
// Fragment should use the same React namespace import:
expect(output).toMatch(`__jsx(${react}.Fragment`)
expect(output).toMatch(`__jsx("a",{href:"/"`)
expect(babel(`const a = ()=><a href="/">home</a>`)).toMatchInlineSnapshot(
`""use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");var _react=_interopRequireDefault(require("react"));var __jsx=_react["default"].createElement;var a=function a(){return __jsx("a",{href:"/"},"home");};"`
)
})
it('should support Fragment syntax', () => {
const output = babel(`const a = () => <>hello</>;`, true)
expect(output).toMatch(`React.Fragment`)
expect(babel(`const a = () => <>hello</>;`, true)).toMatchInlineSnapshot(
`"import React from"react";var __jsx=React.createElement;var a=function a(){return __jsx(React.Fragment,null,"hello");};"`
)
})
it('should support commonjs', () => {
const output = babel(
trim`
const React = require('react');
module.exports = () => <div>test2</div>;
`,
true
)
expect(output).toMatchInlineSnapshot(
`"var React=require('react');var __jsx=React.createElement;module.exports=function(){return __jsx("div",null,"test2");};"`
)
})
})
describe('optimize-hook-destructuring', () => {
it('should transform Array-destructured hook return values use object destructuring', () => {
const output = babel(
trim`
import { useState } from 'react';
const [count, setCount] = useState(0);
`,
true
)
expect(output).toMatch(trim`
var _useState=useState(0),count=_useState[0],setCount=_useState[1];
`)
expect(output).toMatchInlineSnapshot(
`"import{useState}from'react';var _useState=useState(0),count=_useState[0],setCount=_useState[1];"`
)
})
it('should be able to ignore some Array-destructured hook return values', () => {
const output = babel(
trim`
import { useState } from 'react';
const [, setCount] = useState(0);
`,
true
)
expect(output).toMatch(trim`
var _useState=useState(0),setCount=_useState[1];
`)
expect(output).toMatchInlineSnapshot(
`"import{useState}from'react';var _useState=useState(0),setCount=_useState[1];"`
)
})
})
describe('@babel/preset-typescript', () => {
describe('should allow passing options', () => {
const code = trim`
import { Tesla } from "./tesla";
import { Car } from "./car";
const benediktsDreamCar: Car = new Tesla();
`
function compileMyCar(options) {
return babel(
code,
false,
{
'preset-typescript': options,
},
'my-car.ts'
)
}
describe('when setting { onlyRemoveTypeImports: true }', () => {
it('should not elide import', () => {
const output = compileMyCar({ onlyRemoveTypeImports: true })
expect(output).toContain('require("./car")')
})
})
describe('when setting { onlyRemoveTypeImports: false }', () => {
it('should elide import', () => {
const output = compileMyCar({ onlyRemoveTypeImports: false })
expect(output).not.toContain('require("./car")')
})
})
})
})
describe('respect preset-react runtime', () => {
it('should allow forcing on automatic mode', () => {
const code = trim`const a = ()=><a href="/">home</a>`
const output = babel(code, true, {
'preset-react': {
runtime: 'automatic',
},
})
expect(output).toMatchInlineSnapshot(
`"import{jsx as _jsx}from"react/jsx-runtime";var a=function a(){return/*#__PURE__*/_jsx("a",{href:"/",children:"home"});};"`
)
})
it('should allow forcing on classic mode', () => {
const code = trim`const a = ()=><a href="/">home</a>`
const output = babel(code, true, {
'preset-react': {
runtime: 'classic',
},
})
expect(output).toMatchInlineSnapshot(
`"import React from"react";var __jsx=React.createElement;var a=function a(){return __jsx("a",{href:"/"},"home");};"`
)
})
})
})