2063ff3338
<!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change that you're making: --> In #42987 we added support for `transpilePackages` in `next/jest`, but the pattern is not functioning how we would expect here, and as a result all modules from `node_modules` are getting transformed. File patterns that should not be transformed should match, but due to the trailing `/`, no packages are matching. Currently (no match, incorrectly gets transformed): https://regexr.com/73fvo With this fix (matches, correctly does not get transformed): https://regexr.com/73fvr As far as I can tell, the `pnpm` pattern is being generated correctly. @balazsorban44 I wasn't sure the best way to test this one, let me know if you've got an idea. 🙏 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/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` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
182 lines
6.4 KiB
TypeScript
182 lines
6.4 KiB
TypeScript
import { loadEnvConfig } from '@next/env'
|
|
import { resolve, join } from 'path'
|
|
import loadConfig from '../../server/config'
|
|
import type { NextConfigComplete } from '../../server/config-shared'
|
|
import { PHASE_TEST } from '../../shared/lib/constants'
|
|
import loadJsConfig from '../load-jsconfig'
|
|
import * as Log from '../output/log'
|
|
import { findPagesDir } from '../../lib/find-pages-dir'
|
|
import { loadBindings, lockfilePatchPromise } from '../swc'
|
|
|
|
async function getConfig(dir: string) {
|
|
const conf = await loadConfig(PHASE_TEST, dir)
|
|
return conf
|
|
}
|
|
|
|
/**
|
|
* Loads closest package.json in the directory hierarchy
|
|
*/
|
|
function loadClosestPackageJson(dir: string, attempts = 1): any {
|
|
if (attempts > 5) {
|
|
throw new Error("Can't resolve main package.json file")
|
|
}
|
|
var mainPath = attempts === 1 ? './' : Array(attempts).join('../')
|
|
try {
|
|
return require(join(dir, mainPath + 'package.json'))
|
|
} catch (e) {
|
|
return loadClosestPackageJson(dir, attempts + 1)
|
|
}
|
|
}
|
|
|
|
/** Loads dotenv files and sets environment variables based on next config. */
|
|
function setUpEnv(dir: string, nextConfig: NextConfigComplete) {
|
|
const dev = false
|
|
loadEnvConfig(dir, dev, Log)
|
|
|
|
if (nextConfig.experimental.newNextLinkBehavior) {
|
|
process.env.__NEXT_NEW_LINK_BEHAVIOR = 'true'
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Usage in jest.config.js
|
|
const nextJest = require('next/jest');
|
|
|
|
// Optionally provide path to Next.js app which will enable loading next.config.js and .env files
|
|
const createJestConfig = nextJest({ dir })
|
|
|
|
// Any custom config you want to pass to Jest
|
|
const customJestConfig = {
|
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
|
}
|
|
|
|
// createJestConfig is exported in this way to ensure that next/jest can load the Next.js config which is async
|
|
module.exports = createJestConfig(customJestConfig)
|
|
*/
|
|
export default function nextJest(options: { dir?: string } = {}) {
|
|
// createJestConfig
|
|
return (customJestConfig?: any) => {
|
|
// Function that is provided as the module.exports of jest.config.js
|
|
// Will be called and awaited by Jest
|
|
return async () => {
|
|
let nextConfig
|
|
let jsConfig
|
|
let resolvedBaseUrl
|
|
let isEsmProject = false
|
|
let pagesDir: string | undefined
|
|
let hasServerComponents: boolean | undefined
|
|
|
|
if (options.dir) {
|
|
const resolvedDir = resolve(options.dir)
|
|
const packageConfig = loadClosestPackageJson(resolvedDir)
|
|
isEsmProject = packageConfig.type === 'module'
|
|
|
|
nextConfig = await getConfig(resolvedDir)
|
|
const isAppDirEnabled = !!nextConfig.experimental.appDir
|
|
const findPagesDirResult = findPagesDir(resolvedDir, isAppDirEnabled)
|
|
hasServerComponents = !!findPagesDirResult.appDir
|
|
pagesDir = findPagesDirResult.pagesDir
|
|
setUpEnv(resolvedDir, nextConfig)
|
|
// TODO: revisit when bug in SWC is fixed that strips `.css`
|
|
const result = await loadJsConfig(resolvedDir, nextConfig)
|
|
jsConfig = result.jsConfig
|
|
resolvedBaseUrl = result.resolvedBaseUrl
|
|
}
|
|
// Ensure provided async config is supported
|
|
const resolvedJestConfig =
|
|
(typeof customJestConfig === 'function'
|
|
? await customJestConfig()
|
|
: customJestConfig) ?? {}
|
|
|
|
// eagerly load swc bindings instead of waiting for transform calls
|
|
await loadBindings()
|
|
|
|
if (lockfilePatchPromise.cur) {
|
|
await lockfilePatchPromise.cur
|
|
}
|
|
|
|
const transpiled = (
|
|
nextConfig?.experimental?.transpilePackages ?? []
|
|
).join('|')
|
|
return {
|
|
...resolvedJestConfig,
|
|
|
|
moduleNameMapper: {
|
|
// Handle CSS imports (with CSS modules)
|
|
// https://jestjs.io/docs/webpack#mocking-css-modules
|
|
'^.+\\.module\\.(css|sass|scss)$':
|
|
require.resolve('./object-proxy.js'),
|
|
|
|
// Handle CSS imports (without CSS modules)
|
|
'^.+\\.(css|sass|scss)$': require.resolve('./__mocks__/styleMock.js'),
|
|
|
|
// Handle image imports
|
|
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp)$': require.resolve(
|
|
`./__mocks__/fileMock.js`
|
|
),
|
|
|
|
// Keep .svg to it's own rule to make overriding easy
|
|
'^.+\\.(svg)$': require.resolve(`./__mocks__/fileMock.js`),
|
|
|
|
// Handle @next/font
|
|
'@next/font/(.*)': require.resolve('./__mocks__/nextFontMock.js'),
|
|
|
|
// custom config comes last to ensure the above rules are matched,
|
|
// fixes the case where @pages/(.*) -> src/pages/$! doesn't break
|
|
// CSS/image mocks
|
|
...(resolvedJestConfig.moduleNameMapper || {}),
|
|
},
|
|
testPathIgnorePatterns: [
|
|
// Don't look for tests in node_modules
|
|
'/node_modules/',
|
|
// Don't look for tests in the Next.js build output
|
|
'/.next/',
|
|
// Custom config can append to testPathIgnorePatterns but not modify it
|
|
// This is to ensure `.next` and `node_modules` are always excluded
|
|
...(resolvedJestConfig.testPathIgnorePatterns || []),
|
|
],
|
|
|
|
transform: {
|
|
// Use SWC to compile tests
|
|
'^.+\\.(js|jsx|ts|tsx|mjs)$': [
|
|
require.resolve('../swc/jest-transformer'),
|
|
{
|
|
nextConfig,
|
|
jsConfig,
|
|
resolvedBaseUrl,
|
|
hasServerComponents,
|
|
isEsmProject,
|
|
pagesDir,
|
|
},
|
|
],
|
|
// Allow for appending/overriding the default transforms
|
|
...(resolvedJestConfig.transform || {}),
|
|
},
|
|
|
|
transformIgnorePatterns: [
|
|
// To match Next.js behavior node_modules is not transformed, only `transpiledPackages`
|
|
...(transpiled
|
|
? [
|
|
`/node_modules/(?!.pnpm)(?!(${transpiled})/)`,
|
|
`/node_modules/.pnpm/(?!(${transpiled.replace(
|
|
/\//g,
|
|
'\\+'
|
|
)})@)`,
|
|
]
|
|
: ['/node_modules/']),
|
|
// CSS modules are mocked so they don't need to be transformed
|
|
'^.+\\.module\\.(css|sass|scss)$',
|
|
|
|
// Custom config can append to transformIgnorePatterns but not modify it
|
|
// This is to ensure `node_modules` and .module.css/sass/scss are always excluded
|
|
...(resolvedJestConfig.transformIgnorePatterns || []),
|
|
],
|
|
watchPathIgnorePatterns: [
|
|
// Don't re-run tests when the Next.js build output changes
|
|
'/.next/',
|
|
...(resolvedJestConfig.watchPathIgnorePatterns || []),
|
|
],
|
|
}
|
|
}
|
|
}
|
|
}
|