rsnext/test/unit/next-babel-loader-prod.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

325 lines
12 KiB
TypeScript

/* eslint-env jest */
import os from 'os'
import path from 'path'
import { Span } from 'next/dist/trace'
import loader from 'next/dist/build/babel/loader'
const dir = path.resolve(os.tmpdir())
const babel = async (code: string, queryOpts = {} as any) => {
const { isServer = false, resourcePath = `index.js` } = queryOpts
let isAsync = false
return new Promise<string>((resolve, reject) => {
function callback(err, content) {
if (err) {
reject(err)
} else {
resolve(content.replace(/\n/g, ''))
}
}
const options = {
// loader opts
cwd: dir,
isServer,
distDir: path.resolve(dir, '.next'),
pagesDir:
'pagesDir' in queryOpts
? queryOpts.pagesDir
: path.resolve(dir, 'pages'),
cache: false,
hasReactRefresh: false,
}
const res = loader.bind({
resourcePath,
async() {
isAsync = true
return callback
},
callback,
emitWarning() {},
query: options,
getOptions: function () {
return options
},
currentTraceSpan: new Span({ name: 'test' }),
})(code, null)
if (!isAsync) {
resolve(res)
}
})
}
describe('next-babel-loader', () => {
describe('replace constants', () => {
it('should replace typeof window expression nested', async () => {
const code = await babel('function a(){console.log(typeof window)}')
expect(code).toMatchInlineSnapshot(
`"function a() { console.log("object");}"`
)
})
it('should replace typeof window expression top level (client)', async () => {
const code = await babel('typeof window;')
expect(code).toMatchInlineSnapshot(`""object";"`)
})
it('should replace typeof window expression top level (server)', async () => {
const code = await babel('typeof window;', { isServer: true })
expect(code).toMatchInlineSnapshot(`""undefined";"`)
})
it('should replace typeof window in === expression nested', async () => {
const code = await babel(
`function a(){console.log(typeof window === 'undefined')}`
)
expect(code).toMatchInlineSnapshot(
`"function a() { console.log(false);}"`
)
})
it('should replace typeof window expression top level', async () => {
const code = await babel(`typeof window === 'undefined';`)
expect(code).toMatchInlineSnapshot(`"false;"`)
})
it('should replace typeof window in === expression top level', async () => {
const code = await babel(`typeof window === 'object';`)
expect(code).toMatchInlineSnapshot(`"true;"`)
})
it('should replace typeof window in !== expression top level', async () => {
const code = await babel(`typeof window !== 'undefined';`)
expect(code).toMatchInlineSnapshot(`"true;"`)
})
it('should replace typeof window expression !== object top level', async () => {
const code = await babel(`typeof window !== 'object';`)
expect(code).toMatchInlineSnapshot(`"false;"`)
})
it('should replace typeof window expression top level serverside', async () => {
const code = await babel(`typeof window !== 'undefined';`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`"false;"`)
})
it('should replace typeof window expression !== object top level serverside', async () => {
const code = await babel(`typeof window !== 'object';`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`"true;"`)
})
it('should replace process.browser (1)', async () => {
const code = await babel(`process.browser`, {
isServer: false,
})
expect(code).toMatchInlineSnapshot(`"true;"`)
})
it('should replace process.browser (2)', async () => {
const code = await babel(`process.browser`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`"false;"`)
})
it('should replace process.browser (3)', async () => {
const code = await babel(`process.browser == false`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`"true;"`)
})
it('should replace process.browser (4)', async () => {
const code = await babel(`if (process.browser === false) {}`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`"if (true) {}"`)
})
it('should replace process.browser (5)', async () => {
const code = await babel(`if (process.browser) {}`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`"if (false) {}"`)
})
it('should replace NODE_ENV on client (prod)', async () => {
const code = await babel(`process.env.NODE_ENV`, {
isServer: false,
})
expect(code).toMatchInlineSnapshot(`""production";"`)
})
it('should replace NODE_ENV on server', async () => {
const code = await babel(`process.env.NODE_ENV`, {
isServer: true,
})
expect(code).toMatchInlineSnapshot(`""production";"`)
})
it('should replace NODE_ENV in === statement (prod)', async () => {
const code = await babel(`if (process.env.NODE_ENV === 'production') {}`)
expect(code).toMatchInlineSnapshot(`"if (true) {}"`)
})
it('should replace NODE_ENV in !== statement (prod)', async () => {
const code = await babel(`if (process.env.NODE_ENV !== 'production') {}`)
expect(code).toMatchInlineSnapshot(`"if (false) {}"`)
})
it('should handle no pagesDir', async () => {
const code = await babel(
`
import dynamic from 'next/dynamic'
const Comp = dynamic(() => import('comp'))
export default function Page(props) {
return <Comp />
}
`,
{
pagesDir: undefined,
}
)
expect(
code.replace(/modules: \[".*?"/, 'modules:["/path/to/page"')
).toMatchInlineSnapshot(
`"var _jsxFileName = "index.js";import React from "react";var __jsx = React.createElement;import dynamic from 'next/dynamic';const Comp = dynamic(() => import('comp'), { loadableGenerated: { webpack: () => [require.resolveWeak('comp')] }});export default function Page(props) { return __jsx(Comp, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 7, columnNumber: 18 } });}"`
)
})
it('should not drop unused exports by default', async () => {
const code = await babel(
// effectful
`import"core-js";` +
// basic
`import{foo,bar}from"a";import baz from"b";` +
// complex
`import * as React from "react";` +
`import baz2,{yeet}from"c";` +
`import baz3,{cats}from"d";` +
`import{c,d}from"e";` +
`import{e as ee,f as ff}from"f";`
)
expect(code).toMatchInlineSnapshot(
`"import "core-js";import { foo, bar } from "a";import baz from "b";import * as React from "react";import baz2, { yeet } from "c";import baz3, { cats } from "d";import { c, d } from "e";import { e as ee, f as ff } from "f";"`
)
})
const pageFile = path.resolve(dir, 'pages', 'index.js')
it('should not drop unused exports by default in a page', async () => {
const code = await babel(
// effectful
`import"core-js";` +
// basic
`import{foo,bar}from"a";import baz from"b";` +
// complex
`import*as React from"react";` +
`import baz2,{yeet}from"c";` +
`import baz3,{cats}from"d";` +
`import{c,d}from"e";` +
`import{e as ee,f as ff}from"f";`,
{ resourcePath: pageFile }
)
expect(code).toMatchInlineSnapshot(
`"import "core-js";import { foo, bar } from "a";import baz from "b";import * as React from "react";import baz2, { yeet } from "c";import baz3, { cats } from "d";import { c, d } from "e";import { e as ee, f as ff } from "f";"`
)
})
it('should drop unused exports in a modern-apis page', async () => {
const code = await babel(
// effectful
`import"core-js";` +
// basic
`import{foo,bar}from"a";import baz from"b";` +
// complex
`import*as React from"react";` +
`import baz2,{yeet}from"c";` +
`import baz3,{cats}from"d";` +
`import{c,d}from"e";` +
`import{e as ee,f as ff}from"f";` +
`` +
`export function getStaticProps() {foo;bar;baz;cats;baz2;ff; return { props: {} } }`,
{ resourcePath: pageFile }
)
expect(code).toMatchInlineSnapshot(
`"import "core-js";import * as React from "react";import { yeet } from "c";import baz3 from "d";import { c, d } from "e";import { e as ee } from "f";"`
)
})
it('should keep used exports in a modern-apis page (server)', async () => {
const code = await babel(
// effectful
`import"core-js";` +
// basic
`import{foo,bar}from"a";import baz from"b";import ooo from"ooo";` +
// complex
`import*as React from"react";` +
`import baz2,{yeet}from"c";` +
`import baz3,{cats}from"d";` +
`import{c,d}from"e";` +
`import{e as ee,f as ff}from"f";` +
`` +
`export function getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }}` +
`export default function () { return bar(); }`,
{ resourcePath: pageFile, isServer: true }
)
expect(code).toMatchInlineSnapshot(
`"import "core-js";import { foo, bar } from "a";import baz from "b";import ooo from "ooo";import * as React from "react";import baz2, { yeet } from "c";import baz3, { cats } from "d";import { c, d } from "e";import { e as ee, f as ff } from "f";export function getStaticProps() { foo(); baz2(); ff(); ooo(); return { props: {} };}export default function () { return bar();}"`
)
})
it('should keep used exports in a modern-apis page (client)', async () => {
const code = await babel(
// effectful
`import"core-js";` +
// basic
`import{foo,bar}from"a";import baz from"b";import ooo from"ooo";` +
// complex
`import*as React from"react";` +
`import baz2,{yeet}from"c";` +
`import baz3,{cats}from"d";` +
`import{c,d}from"e";` +
`import{e as ee,f as ff}from"f";` +
`` +
`export function getStaticProps() {foo();baz2();ff();ooo();cats; return { props: {} }}` +
`export default function () { return cats + bar(); }`,
{ resourcePath: pageFile, isServer: false }
)
expect(code).toMatchInlineSnapshot(
`"import "core-js";import { bar } from "a";import baz from "b";import * as React from "react";import { yeet } from "c";import baz3, { cats } from "d";import { c, d } from "e";import { e as ee } from "f";export var __N_SSG = true;export default function () { return cats + bar();}"`
)
})
it('should keep used exports and react in a modern-apis page with JSX (client)', async () => {
const code = await babel(
// effectful
`import"core-js";` +
// basic
`import{foo,bar}from"a";import baz from"b";import ooo from"ooo";` +
// complex
`import*as React from"react";` +
`import baz2,{yeet}from"c";` +
`import baz3,{cats}from"d";` +
`import{c,d}from"e";` +
`import{e as ee,f as ff}from"f";` +
`` +
`export function getStaticProps() {foo();baz2();ff();ooo(); return { props: {} }}` +
`export default function () { return <div>{cats + bar()}</div> }`,
{ resourcePath: pageFile, isServer: false }
)
expect(code).toContain(
`var __jsx = React.createElement;import "core-js";import { bar } from "a";import baz from "b";import * as React from "react";import { yeet } from "c";import baz3, { cats } from "d";import { c, d } from "e";import { e as ee } from "f";export var __N_SSG = true;export default function () { return __jsx("div", { __self: this, __source: { fileName: _jsxFileName, lineNumber: 1, columnNumber: 326 } }, cats + bar());}`
)
})
})
})