From 3c9f9963f5a694650a67245b3ed2e67ad7b588ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Thu, 16 May 2024 15:46:39 +0200 Subject: [PATCH] fix(testing): handle existing jest preset file correctly --- packages/jest/preset.ts | 5 ++ .../configuration/configuration.spec.ts | 18 ++++++- .../generators/configuration/configuration.ts | 4 +- .../create-jest-config.spec.ts.snap | 4 +- .../configuration/lib/create-files.ts | 3 +- .../configuration/lib/create-jest-config.ts | 29 +++++++---- .../configuration/lib/update-jestconfig.ts | 6 +-- packages/jest/src/generators/init/init.ts | 21 ++++---- packages/jest/src/utils/config/config-file.ts | 49 +++++++++++++++++++ .../src/utils/config/find-root-jest-files.ts | 25 ---------- .../jest/src/utils/config/is-preset-cjs.ts | 14 ------ .../files/jest-config/jest.config.__ext__ | 2 +- packages/js/src/generators/library/library.ts | 10 ++++ .../e2e-project/e2e-project.spec.ts | 2 + .../src/generators/e2e-project/e2e-project.ts | 4 ++ .../files/cli/jest.config.ts__tmpl__ | 4 +- .../server/common/jest.config.ts__tmpl__ | 2 +- .../application/application.impl.ts | 2 +- 18 files changed, 132 insertions(+), 72 deletions(-) create mode 100644 packages/jest/preset.ts create mode 100644 packages/jest/src/utils/config/config-file.ts delete mode 100644 packages/jest/src/utils/config/find-root-jest-files.ts delete mode 100644 packages/jest/src/utils/config/is-preset-cjs.ts diff --git a/packages/jest/preset.ts b/packages/jest/preset.ts new file mode 100644 index 0000000000000..0fc1472fabfbd --- /dev/null +++ b/packages/jest/preset.ts @@ -0,0 +1,5 @@ +import { nxPreset } from './preset/jest-preset'; + +export { nxPreset }; + +export default nxPreset; diff --git a/packages/jest/src/generators/configuration/configuration.spec.ts b/packages/jest/src/generators/configuration/configuration.spec.ts index 68691b9079705..36da18e1ae821 100644 --- a/packages/jest/src/generators/configuration/configuration.spec.ts +++ b/packages/jest/src/generators/configuration/configuration.spec.ts @@ -262,19 +262,33 @@ describe('jestProject', () => { expect(tree.exists('libs/lib1/jest.config.js')).toBeTruthy(); }); - it('should always use jest.preset.js with --js', async () => { - tree.write('jest.preset.ts', ''); + it('should generate a jest.preset.js when it does not exist', async () => { await configurationGenerator(tree, { ...defaultOptions, project: 'lib1', js: true, } as JestProjectSchema); expect(tree.exists('libs/lib1/jest.config.js')).toBeTruthy(); + expect(tree.exists('jest.preset.js')).toBeTruthy(); expect(tree.read('libs/lib1/jest.config.js', 'utf-8')).toContain( "preset: '../../jest.preset.js'," ); }); + it('should not override existing jest preset file and should point to it in jest.config files', async () => { + tree.write('jest.preset.mjs', 'export default {}'); + await configurationGenerator(tree, { + ...defaultOptions, + project: 'lib1', + js: true, + } as JestProjectSchema); + expect(tree.exists('libs/lib1/jest.config.js')).toBeTruthy(); + expect(tree.exists('jest.preset.mjs')).toBeTruthy(); + expect(tree.read('libs/lib1/jest.config.js', 'utf-8')).toContain( + "preset: '../../jest.preset.mjs'," + ); + }); + it('should use module.exports with --js flag', async () => { await configurationGenerator(tree, { ...defaultOptions, diff --git a/packages/jest/src/generators/configuration/configuration.ts b/packages/jest/src/generators/configuration/configuration.ts index 0595c01e97d41..6bf6640b540f0 100644 --- a/packages/jest/src/generators/configuration/configuration.ts +++ b/packages/jest/src/generators/configuration/configuration.ts @@ -17,7 +17,7 @@ import { } from '@nx/devkit'; import { initGenerator as jsInitGenerator } from '@nx/js'; import { JestPluginOptions } from '../../plugins/plugin'; -import { isPresetCjs } from '../../utils/config/is-preset-cjs'; +import { getPresetExt } from '../../utils/config/config-file'; const schemaDefaults = { setupFile: 'none', @@ -90,7 +90,7 @@ export async function configurationGeneratorInternal( tasks.push(ensureDependencies(tree, options)); } - const presetExt = isPresetCjs(tree) ? 'cjs' : 'js'; + const presetExt = getPresetExt(tree); await createJestConfig(tree, options, presetExt); checkForTestTarget(tree, options); diff --git a/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap b/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap index 45bec790ca796..88cd8457a2c2a 100644 --- a/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap +++ b/packages/jest/src/generators/configuration/lib/__snapshots__/create-jest-config.spec.ts.snap @@ -11,7 +11,7 @@ projects: await getJestProjectsAsync() exports[`createJestConfig should generate files 2`] = ` "const nxPreset = require('@nx/jest/preset').default; -module.exports = { ...nxPreset }" +module.exports = { ...nxPreset };" `; exports[`createJestConfig should generate files with --js flag 1`] = ` @@ -25,5 +25,5 @@ projects: await getJestProjectsAsync() exports[`createJestConfig should generate files with --js flag 2`] = ` "const nxPreset = require('@nx/jest/preset').default; -module.exports = { ...nxPreset }" +module.exports = { ...nxPreset };" `; diff --git a/packages/jest/src/generators/configuration/lib/create-files.ts b/packages/jest/src/generators/configuration/lib/create-files.ts index 1a9a0b07aa1ab..da718b7d89904 100644 --- a/packages/jest/src/generators/configuration/lib/create-files.ts +++ b/packages/jest/src/generators/configuration/lib/create-files.ts @@ -5,12 +5,13 @@ import { Tree, } from '@nx/devkit'; import { join } from 'path'; +import type { JestPresetExtension } from '../../../utils/config/config-file'; import { NormalizedJestProjectSchema } from '../schema'; export function createFiles( tree: Tree, options: NormalizedJestProjectSchema, - presetExt: 'cjs' | 'js' + presetExt: JestPresetExtension ) { const projectConfig = readProjectConfiguration(tree, options.project); diff --git a/packages/jest/src/generators/configuration/lib/create-jest-config.ts b/packages/jest/src/generators/configuration/lib/create-jest-config.ts index 07f106f263deb..e64c62d88dd62 100644 --- a/packages/jest/src/generators/configuration/lib/create-jest-config.ts +++ b/packages/jest/src/generators/configuration/lib/create-jest-config.ts @@ -8,23 +8,34 @@ import { type Tree, } from '@nx/devkit'; import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils'; -import { findRootJestConfig } from '../../../utils/config/find-root-jest-files'; +import { + findRootJestConfig, + type JestPresetExtension, +} from '../../../utils/config/config-file'; import type { NormalizedJestProjectSchema } from '../schema'; export async function createJestConfig( tree: Tree, options: Partial, - presetExt: 'cjs' | 'js' + presetExt: JestPresetExtension ) { if (!tree.exists(`jest.preset.${presetExt}`)) { - // preset is always js file. - tree.write( - `jest.preset.${presetExt}`, - ` - const nxPreset = require('@nx/jest/preset').default; + if (presetExt === 'mjs') { + tree.write( + `jest.preset.${presetExt}`, + `import { nxPreset } from '@nx/jest/preset.js'; - module.exports = { ...nxPreset }` - ); +export default { ...nxPreset };` + ); + } else { + // js or cjs + tree.write( + `jest.preset.${presetExt}`, + `const nxPreset = require('@nx/jest/preset').default; + +module.exports = { ...nxPreset };` + ); + } } if (options.rootProject) { // we don't want any config to be made because the `configurationGenerator` will do it. diff --git a/packages/jest/src/generators/configuration/lib/update-jestconfig.ts b/packages/jest/src/generators/configuration/lib/update-jestconfig.ts index 0fae25ceb72ad..d33a9a98f80df 100644 --- a/packages/jest/src/generators/configuration/lib/update-jestconfig.ts +++ b/packages/jest/src/generators/configuration/lib/update-jestconfig.ts @@ -1,7 +1,7 @@ -import { findRootJestConfig } from '../../../utils/config/find-root-jest-files'; -import { NormalizedJestProjectSchema } from '../schema'; +import { readProjectConfiguration, type Tree } from '@nx/devkit'; +import { findRootJestConfig } from '../../../utils/config/config-file'; import { addPropertyToJestConfig } from '../../../utils/config/update-config'; -import { readProjectConfiguration, Tree } from '@nx/devkit'; +import type { NormalizedJestProjectSchema } from '../schema'; function isUsingUtilityFunction(host: Tree) { const rootConfig = findRootJestConfig(host); diff --git a/packages/jest/src/generators/init/init.ts b/packages/jest/src/generators/init/init.ts index 78c6fed32061d..72e3173ff1717 100644 --- a/packages/jest/src/generators/init/init.ts +++ b/packages/jest/src/generators/init/init.ts @@ -1,5 +1,6 @@ import { addDependenciesToPackageJson, + createProjectGraphAsync, formatFiles, readNxJson, removeDependenciesFromPackageJson, @@ -7,15 +8,17 @@ import { updateNxJson, type GeneratorCallback, type Tree, - createProjectGraphAsync, } from '@nx/devkit'; +import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; import { createNodes } from '../../plugins/plugin'; +import { + getPresetExt, + type JestPresetExtension, +} from '../../utils/config/config-file'; import { jestVersion, nxVersion } from '../../utils/versions'; -import { isPresetCjs } from '../../utils/config/is-preset-cjs'; import type { JestInitSchema } from './schema'; -import { addPlugin } from '@nx/devkit/src/utils/add-plugin'; -function updateProductionFileSet(tree: Tree, presetExt: 'cjs' | 'js') { +function updateProductionFileSet(tree: Tree) { const nxJson = readNxJson(tree); const productionFileSet = nxJson.namedInputs?.production; @@ -40,7 +43,7 @@ function updateProductionFileSet(tree: Tree, presetExt: 'cjs' | 'js') { updateNxJson(tree, nxJson); } -function addJestTargetDefaults(tree: Tree, presetEnv: 'cjs' | 'js') { +function addJestTargetDefaults(tree: Tree, presetExt: JestPresetExtension) { const nxJson = readNxJson(tree); nxJson.targetDefaults ??= {}; @@ -53,7 +56,7 @@ function addJestTargetDefaults(tree: Tree, presetEnv: 'cjs' | 'js') { nxJson.targetDefaults['@nx/jest:jest'].inputs ??= [ 'default', productionFileSet ? '^production' : '^default', - `{workspaceRoot}/jest.preset.${presetEnv}`, + `{workspaceRoot}/jest.preset.${presetExt}`, ]; nxJson.targetDefaults['@nx/jest:jest'].options ??= { @@ -96,10 +99,10 @@ export async function jestInitGeneratorInternal( nxJson.useInferencePlugins !== false; options.addPlugin ??= addPluginDefault; - const presetExt = isPresetCjs(tree) ? 'cjs' : 'js'; + const presetExt = getPresetExt(tree); - if (!tree.exists('jest.preset.js') && !tree.exists('jest.preset.cjs')) { - updateProductionFileSet(tree, presetExt); + if (!tree.exists(`jest.preset.${presetExt}`)) { + updateProductionFileSet(tree); if (options.addPlugin) { await addPlugin( tree, diff --git a/packages/jest/src/utils/config/config-file.ts b/packages/jest/src/utils/config/config-file.ts new file mode 100644 index 0000000000000..76d1e7d664f00 --- /dev/null +++ b/packages/jest/src/utils/config/config-file.ts @@ -0,0 +1,49 @@ +import { readJson, type Tree } from '@nx/devkit'; + +export const jestConfigExtensions = [ + 'js', + 'ts', + 'mjs', + 'cjs', + 'mts', + 'cts', +] as const; +export type JestConfigExtension = typeof jestConfigExtensions[number]; + +export const jestPresetExtensions = ['js', 'cjs', 'mjs'] as const; +export type JestPresetExtension = typeof jestPresetExtensions[number]; + +export function getPresetExt(tree: Tree): JestPresetExtension { + const ext = jestPresetExtensions.find((ext) => + tree.exists(`jest.preset.${ext}`) + ); + + if (ext) { + return ext; + } + + const rootPkgJson = readJson(tree, 'package.json'); + if (rootPkgJson.type && rootPkgJson.type === 'module') { + // use cjs if package.json type is module + return 'cjs'; + } + + // default to js + return 'js'; +} + +export function findRootJestConfig(tree: Tree): string | null { + const ext = jestConfigExtensions.find((ext) => + tree.exists(`jest.config.${ext}`) + ); + + return ext ? `jest.config.${ext}` : null; +} + +export function findRootJestPreset(tree: Tree): string | null { + const ext = jestPresetExtensions.find((ext) => + tree.exists(`jest.preset.${ext}`) + ); + + return ext ? `jest.preset.${ext}` : null; +} diff --git a/packages/jest/src/utils/config/find-root-jest-files.ts b/packages/jest/src/utils/config/find-root-jest-files.ts deleted file mode 100644 index 5b88608451982..0000000000000 --- a/packages/jest/src/utils/config/find-root-jest-files.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Tree } from '@nx/devkit'; - -export function findRootJestConfig(tree: Tree): string | null { - if (tree.exists('jest.config.js')) { - return 'jest.config.js'; - } - - if (tree.exists('jest.config.ts')) { - return 'jest.config.ts'; - } - - return null; -} - -export function findRootJestPreset(tree: Tree): string | null { - if (tree.exists('jest.preset.js')) { - return 'jest.preset.js'; - } - - if (tree.exists('jest.preset.cjs')) { - return 'jest.preset.cjs'; - } - - return null; -} diff --git a/packages/jest/src/utils/config/is-preset-cjs.ts b/packages/jest/src/utils/config/is-preset-cjs.ts deleted file mode 100644 index c1ba4752bf1e2..0000000000000 --- a/packages/jest/src/utils/config/is-preset-cjs.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { type Tree, readJson } from '@nx/devkit'; - -export function isPresetCjs(tree: Tree) { - if (tree.exists('jest.preset.cjs')) { - return true; - } - - const rootPkgJson = readJson(tree, 'package.json'); - if (rootPkgJson.type && rootPkgJson.type === 'module') { - return true; - } - - return false; -} diff --git a/packages/js/src/generators/library/files/jest-config/jest.config.__ext__ b/packages/js/src/generators/library/files/jest-config/jest.config.__ext__ index aa6ab15eca99c..7bf4aa8b87ded 100644 --- a/packages/js/src/generators/library/files/jest-config/jest.config.__ext__ +++ b/packages/js/src/generators/library/files/jest-config/jest.config.__ext__ @@ -20,7 +20,7 @@ if (swcJestConfig.swcrc === undefined) { <% if(js) {%>module.exports =<% } else { %>export default<% } %> { displayName: '<%= project %>', - preset: '<%= offsetFromRoot %>jest.preset.js', + preset: '<%= offsetFromRoot %><%= jestPreset %>', transform: { '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig], }, diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 5bf5838b03ce7..e491cd8b800ff 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -603,10 +603,12 @@ function replaceJestConfig(tree: Tree, options: NormalizedSchema) { if (tree.exists(existingJestConfig)) { tree.delete(existingJestConfig); } + const jestPreset = findRootJestPreset(tree) ?? 'jest.presets.js'; // replace with JS:SWC specific jest config generateFiles(tree, filesDir, options.projectRoot, { ext: options.js ? 'js' : 'ts', + jestPreset, js: !!options.js, project: options.name, offsetFromRoot: offsetFromRoot(options.projectRoot), @@ -1013,4 +1015,12 @@ function logNxReleaseDocsInfo() { }); } +function findRootJestPreset(tree: Tree): string | null { + const ext = ['js', 'cjs', 'mjs'].find((ext) => + tree.exists(`jest.preset.${ext}`) + ); + + return ext ? `jest.preset.${ext}` : null; +} + export default libraryGenerator; diff --git a/packages/node/src/generators/e2e-project/e2e-project.spec.ts b/packages/node/src/generators/e2e-project/e2e-project.spec.ts index 8b993a68b03b9..b2b142c03a6a5 100644 --- a/packages/node/src/generators/e2e-project/e2e-project.spec.ts +++ b/packages/node/src/generators/e2e-project/e2e-project.spec.ts @@ -1,3 +1,5 @@ +import 'nx/src/internal-testing-utils/mock-project-graph'; + import { Tree } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { applicationGenerator } from '../application/application'; diff --git a/packages/node/src/generators/e2e-project/e2e-project.ts b/packages/node/src/generators/e2e-project/e2e-project.ts index 29896ac177864..6a9729e9b8710 100644 --- a/packages/node/src/generators/e2e-project/e2e-project.ts +++ b/packages/node/src/generators/e2e-project/e2e-project.ts @@ -28,6 +28,7 @@ import { replaceOverridesInLintConfig, } from '@nx/eslint/src/generators/utils/eslint-file'; import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command'; +import { findRootJestPreset } from '@nx/jest/src/utils/config/config-file'; export async function e2eProjectGenerator(host: Tree, options: Schema) { return await e2eProjectGeneratorInternal(host, { @@ -90,6 +91,7 @@ export async function e2eProjectGeneratorInternal( }); } + const jestPreset = findRootJestPreset(host) ?? 'jest.preset.js'; if (options.projectType === 'server') { generateFiles( host, @@ -99,6 +101,7 @@ export async function e2eProjectGeneratorInternal( ...options, ...names(options.rootProject ? 'server' : options.project), offsetFromRoot: offsetFromRoot(options.e2eProjectRoot), + jestPreset, tmpl: '', } ); @@ -127,6 +130,7 @@ export async function e2eProjectGeneratorInternal( ...names(options.rootProject ? 'cli' : options.project), mainFile, offsetFromRoot: offsetFromRoot(options.e2eProjectRoot), + jestPreset, tmpl: '', } ); diff --git a/packages/node/src/generators/e2e-project/files/cli/jest.config.ts__tmpl__ b/packages/node/src/generators/e2e-project/files/cli/jest.config.ts__tmpl__ index 73f75c65c1163..d14e4d6f9eb52 100644 --- a/packages/node/src/generators/e2e-project/files/cli/jest.config.ts__tmpl__ +++ b/packages/node/src/generators/e2e-project/files/cli/jest.config.ts__tmpl__ @@ -1,7 +1,7 @@ /* eslint-disable */ export default { displayName: '<%= e2eProjectName %>', - preset: '<%= offsetFromRoot %>/jest.preset.js', + preset: '<%= offsetFromRoot %><%= jestPreset %>', setupFiles: ['/src/test-setup.ts'], testEnvironment: 'node', transform: { @@ -10,5 +10,5 @@ export default { }], }, moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '<%= offsetFromRoot %>/coverage/<%= e2eProjectName %>', + coverageDirectory: '<%= offsetFromRoot %>coverage/<%= e2eProjectName %>', }; diff --git a/packages/node/src/generators/e2e-project/files/server/common/jest.config.ts__tmpl__ b/packages/node/src/generators/e2e-project/files/server/common/jest.config.ts__tmpl__ index 88b69282083af..c2e3fc1d6a249 100644 --- a/packages/node/src/generators/e2e-project/files/server/common/jest.config.ts__tmpl__ +++ b/packages/node/src/generators/e2e-project/files/server/common/jest.config.ts__tmpl__ @@ -1,7 +1,7 @@ /* eslint-disable */ export default { displayName: '<%= e2eProjectName %>', - preset: '<%= offsetFromRoot %>jest.preset.js', + preset: '<%= offsetFromRoot %><%= jestPreset %>', globalSetup: '/src/support/global-setup.ts', globalTeardown: '/src/support/global-teardown.ts', setupFiles: ['/src/support/test-setup.ts'], diff --git a/packages/remix/src/generators/application/application.impl.ts b/packages/remix/src/generators/application/application.impl.ts index a3377ce50698b..fc99f0f8653b8 100644 --- a/packages/remix/src/generators/application/application.impl.ts +++ b/packages/remix/src/generators/application/application.impl.ts @@ -284,7 +284,7 @@ export async function remixApplicationGeneratorInternal( if (options.unitTestRunner === 'jest') { tree.write( 'jest.preset.js', - `import { nxPreset } from '@nx/jest/preset/jest-preset.js'; + `import { nxPreset } from '@nx/jest/preset.js'; export default {...nxPreset}; ` );