From 4d36b46e6c67d0097aeca9d20f1ab10761df35bb Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Thu, 27 Jan 2022 13:43:50 +0100 Subject: [PATCH 1/3] Enable jest hoist transform when using next/jest Fixes #32539 Implements what was shared at https://github.com/vercel/next.js/issues/32539#issuecomment-1023139083. --- packages/next/build/swc/options.js | 8 ++++++++ test/mock.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 test/mock.test.js diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index 103119885e47b0a..59f8553a9e7dabd 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -38,6 +38,14 @@ 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/mock.test.js b/test/mock.test.js new file mode 100644 index 000000000000000..b04a0107e9e7cd2 --- /dev/null +++ b/test/mock.test.js @@ -0,0 +1,24 @@ +// home.test.js +import router from 'next/router' +// const router = require('next/router') +// jest.setup.js +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(), + }), +})) + +describe(`Page / Home`, () => { + it(`call mocked`, async () => { + console.log(router) + expect(typeof router.useRouter).toBe('function') // mocked + }) +}) From ac23010e199be6ea51cede8280c40f36bb6fa040 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 31 Jan 2022 12:07:09 +0100 Subject: [PATCH 2/3] Add test for mocking --- test/lib/create-next-install.js | 7 ++- test/lib/e2e-utils.ts | 3 +- test/lib/next-modes/base.ts | 11 +++- test/mock.test.js | 24 -------- test/production/next/jest/index.test.ts | 74 +++++++++++++++++++++++++ yarn.lock | 3 +- 6 files changed, 93 insertions(+), 29 deletions(-) delete mode 100644 test/mock.test.js create mode 100644 test/production/next/jest/index.test.ts 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..118474f461628d0 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/mock.test.js b/test/mock.test.js deleted file mode 100644 index b04a0107e9e7cd2..000000000000000 --- a/test/mock.test.js +++ /dev/null @@ -1,24 +0,0 @@ -// home.test.js -import router from 'next/router' -// const router = require('next/router') -// jest.setup.js -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(), - }), -})) - -describe(`Page / Home`, () => { - it(`call mocked`, async () => { - console.log(router) - expect(typeof router.useRouter).toBe('function') // mocked - }) -}) 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 b376efc5417dbc5..91a9860ccfea023 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20530,8 +20530,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== From 5e661e453a44026154d4154f28d4d1ec83abfaa3 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 31 Jan 2022 16:05:27 +0100 Subject: [PATCH 3/3] Add default for packageJson --- test/lib/next-modes/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/next-modes/base.ts b/test/lib/next-modes/base.ts index 118474f461628d0..7c26af2cbac8541 100644 --- a/test/lib/next-modes/base.ts +++ b/test/lib/next-modes/base.ts @@ -40,7 +40,7 @@ export class NextInstance { installCommand, buildCommand, startCommand, - packageJson, + packageJson = {}, }: { files: { [filename: string]: string | FileRef