From 3bac02e032792b07726ba795e5b07b502185667a Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 31 Jan 2022 17:15:15 +0100 Subject: [PATCH] Enable jest hoist transform when using next/jest (#33731) Fixes #32539 Implements what was shared at https://github.com/vercel/next.js/issues/32539#issuecomment-1023139083. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- packages/next/build/swc/options.js | 8 +++ test/lib/create-next-install.js | 7 ++- test/lib/e2e-utils.ts | 3 +- test/lib/next-modes/base.ts | 11 +++- test/production/next/jest/index.test.ts | 74 +++++++++++++++++++++++++ yarn.lock | 3 +- 6 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 test/production/next/jest/index.test.ts diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index c6d3e560e491c46..707c6098c3a680b 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -38,6 +38,14 @@ export function getBaseSWCOptions({ }, transform: { + // Enables https://github.com/swc-project/swc/blob/0359deb4841be743d73db4536d4a22ac797d7f65/crates/swc_ecma_ext_transforms/src/jest.rs + ...(jest + ? { + hidden: { + jest: true, + }, + } + : {}), legacyDecorator: enableDecorators, react: { importSource: jsConfig?.compilerOptions?.jsxImportSource || 'react', diff --git a/test/lib/create-next-install.js b/test/lib/create-next-install.js index 824bf1b29cbf5bd..466ff311da212eb 100644 --- a/test/lib/create-next-install.js +++ b/test/lib/create-next-install.js @@ -6,7 +6,11 @@ const childProcess = require('child_process') const { linkPackages } = require('../../.github/actions/next-stats-action/src/prepare/repo-setup')() -async function createNextInstall(dependencies, installCommand) { +async function createNextInstall( + dependencies, + installCommand, + packageJson = {} +) { const tmpDir = await fs.realpath(process.env.NEXT_TEST_DIR || os.tmpdir()) const origRepoDir = path.join(__dirname, '../../') const installDir = path.join(tmpDir, `next-install-${Date.now()}`) @@ -56,6 +60,7 @@ async function createNextInstall(dependencies, installCommand) { path.join(installDir, 'package.json'), JSON.stringify( { + ...packageJson, dependencies: combinedDependencies, private: true, }, diff --git a/test/lib/e2e-utils.ts b/test/lib/e2e-utils.ts index dcfc054dac4bf2b..fa9474f5ca35807 100644 --- a/test/lib/e2e-utils.ts +++ b/test/lib/e2e-utils.ts @@ -1,7 +1,7 @@ import path from 'path' import assert from 'assert' import { NextConfig } from 'next' -import { InstallCommand, NextInstance } from './next-modes/base' +import { InstallCommand, NextInstance, PackageJson } from './next-modes/base' import { NextDevInstance } from './next-modes/next-dev' import { NextStartInstance } from './next-modes/next-start' @@ -113,6 +113,7 @@ export async function createNext(opts: { skipStart?: boolean installCommand?: InstallCommand buildCommand?: string + packageJson?: PackageJson startCommand?: string }): Promise { try { diff --git a/test/lib/next-modes/base.ts b/test/lib/next-modes/base.ts index 5f9ad31cf4d7104..7c26af2cbac8541 100644 --- a/test/lib/next-modes/base.ts +++ b/test/lib/next-modes/base.ts @@ -12,6 +12,9 @@ export type InstallCommand = | string | ((ctx: { dependencies: { [key: string]: string } }) => string) +export type PackageJson = { + [key: string]: unknown +} export class NextInstance { protected files: { [filename: string]: string | FileRef @@ -28,6 +31,7 @@ export class NextInstance { protected childProcess: ChildProcess protected _url: string protected _parsedUrl: URL + protected packageJson: PackageJson constructor({ files, @@ -36,6 +40,7 @@ export class NextInstance { installCommand, buildCommand, startCommand, + packageJson = {}, }: { files: { [filename: string]: string | FileRef @@ -43,6 +48,7 @@ export class NextInstance { dependencies?: { [name: string]: string } + packageJson?: PackageJson nextConfig?: NextConfig installCommand?: InstallCommand buildCommand?: string @@ -54,6 +60,7 @@ export class NextInstance { this.installCommand = installCommand this.buildCommand = buildCommand this.startCommand = startCommand + this.packageJson = packageJson this.events = {} this.isDestroyed = false this.isStopping = false @@ -86,8 +93,10 @@ export class NextInstance { react: 'latest', 'react-dom': 'latest', ...this.dependencies, + ...((this.packageJson.dependencies as object | undefined) || {}), }, - this.installCommand + this.installCommand, + this.packageJson ) } console.log('created next.js install, writing test files') diff --git a/test/production/next/jest/index.test.ts b/test/production/next/jest/index.test.ts new file mode 100644 index 000000000000000..dd1ae29da53815f --- /dev/null +++ b/test/production/next/jest/index.test.ts @@ -0,0 +1,74 @@ +import { createNext } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP } from 'next-test-utils' + +describe('next/jest', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + 'jest.config.js': ` + // jest.config.js + const nextJest = require('next/jest') + + const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', + }) + + // Add any custom config to be passed to Jest + const customJestConfig = { + // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work + moduleDirectories: ['node_modules', '/'], + testEnvironment: 'jest-environment-jsdom', + } + + // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async + module.exports = createJestConfig(customJestConfig) + `, + 'test/mock.test.js': ` + import router from 'next/router' + + jest.mock('next/router', () => ({ + push: jest.fn(), + back: jest.fn(), + events: { + on: jest.fn(), + off: jest.fn(), + }, + asPath: jest.fn().mockReturnThis(), + beforePopState: jest.fn(() => null), + useRouter: () => ({ + push: jest.fn(), + }), + })) + + it('call mocked', async () => { + expect(router.push._isMockFunction).toBeTruthy() + }) + `, + }, + dependencies: { + jest: '27.4.7', + }, + packageJson: { + scripts: { + build: 'next build && yarn jest test/mock.test.js', + }, + }, + buildCommand: `yarn build`, + }) + }) + afterAll(() => next.destroy()) + + it('should work', async () => { + const html = await renderViaHTTP(next.url, '/') + expect(html).toContain('hello world') + }) +}) diff --git a/yarn.lock b/yarn.lock index 8ee3d99053edc44..6b8527a1f1e5375 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20572,8 +20572,7 @@ webpack-bundle-analyzer@4.3.0: source-list-map "^2.0.0" source-map "~0.6.1" -"webpack-sources3@npm:webpack-sources@3.2.3", webpack-sources@^3.2.3: - name webpack-sources3 +"webpack-sources3@npm:webpack-sources@3.2.3", webpack-sources@^3.2.2, webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==