From 81bfeeb0e9ba33216e66257c1b0e70302da1845f Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Thu, 6 Feb 2025 14:51:38 -0500 Subject: [PATCH] feat(testing): disable jest runtime by default when inferring targets --- .../plugins/__snapshots__/plugin.spec.ts.snap | 166 -------- packages/jest/src/plugins/plugin.spec.ts | 399 +++--------------- packages/jest/src/plugins/plugin.ts | 105 +++-- 3 files changed, 109 insertions(+), 561 deletions(-) delete mode 100644 packages/jest/src/plugins/__snapshots__/plugin.spec.ts.snap diff --git a/packages/jest/src/plugins/__snapshots__/plugin.spec.ts.snap b/packages/jest/src/plugins/__snapshots__/plugin.spec.ts.snap deleted file mode 100644 index 5c61a128b4c03..0000000000000 --- a/packages/jest/src/plugins/__snapshots__/plugin.spec.ts.snap +++ /dev/null @@ -1,166 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`@nx/jest/plugin should add package as externalDependencies to the inputs when specified as preset and containing a jest-preset.cjs file 1`] = ` -[ - [ - "proj/jest.config.js", - { - "projects": { - "proj": { - "metadata": undefined, - "root": "proj", - "targets": { - "test": { - "cache": true, - "command": "jest", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - "some-package", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - }, - }, - }, - }, - ], -] -`; - -exports[`@nx/jest/plugin should add package as externalDependencies to the inputs when specified as preset and containing a jest-preset.js file 1`] = ` -[ - [ - "proj/jest.config.js", - { - "projects": { - "proj": { - "metadata": undefined, - "root": "proj", - "targets": { - "test": { - "cache": true, - "command": "jest", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - "some-package", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - }, - }, - }, - }, - ], -] -`; - -exports[`@nx/jest/plugin should add package as externalDependencies to the inputs when specified as preset and containing a jest-preset.json file 1`] = ` -[ - [ - "proj/jest.config.js", - { - "projects": { - "proj": { - "metadata": undefined, - "root": "proj", - "targets": { - "test": { - "cache": true, - "command": "jest", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - "some-package", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - }, - }, - }, - }, - ], -] -`; diff --git a/packages/jest/src/plugins/plugin.spec.ts b/packages/jest/src/plugins/plugin.spec.ts index 58ab6319ef7b1..317c4637c467f 100644 --- a/packages/jest/src/plugins/plugin.spec.ts +++ b/packages/jest/src/plugins/plugin.spec.ts @@ -8,7 +8,7 @@ jest.mock('nx/src/utils/cache-directory', () => ({ workspaceDataDirectory: 'tmp/project-graph-cache', })); -describe('@nx/jest/plugin', () => { +describe.each([true, false])('@nx/jest/plugin', (disableJestRuntime) => { let createNodesFunction = createNodesV2[1]; let context: CreateNodesContext; let tempFs: TempFs; @@ -54,6 +54,7 @@ describe('@nx/jest/plugin', () => { ['proj/jest.config.js'], { targetName: 'test', + disableJestRuntime, }, context ); @@ -127,6 +128,7 @@ describe('@nx/jest/plugin', () => { { targetName: 'test', ciTargetName: 'test-ci', + disableJestRuntime, }, context ); @@ -272,7 +274,7 @@ describe('@nx/jest/plugin', () => { const results = await createNodesFunction( ['proj/jest.config.js'], - { targetName: 'test' }, + { targetName: 'test', disableJestRuntime }, context ); @@ -352,353 +354,67 @@ describe('@nx/jest/plugin', () => { const results = await createNodesFunction( ['proj/jest.config.js'], - { targetName: 'test' }, + { targetName: 'test', disableJestRuntime }, context ); - expect(results).toMatchSnapshot(); - } - ); - - describe('disableJestRuntime', () => { - it('should create test and test-ci targets based on jest.config.ts', async () => { - mockJestConfig( - { - coverageDirectory: '../coverage', - testMatch: ['**/*.spec.ts'], - testPathIgnorePatterns: ['ignore.spec.ts'], - }, - context - ); - const results = await createNodesFunction( - ['proj/jest.config.js'], - { - targetName: 'test', - ciTargetName: 'test-ci', - disableJestRuntime: true, - }, - context - ); - - expect(results).toMatchInlineSnapshot(` - [ - [ - "proj/jest.config.js", - { - "projects": { - "proj": { - "metadata": { - "targetGroups": { - "TEST (CI)": [ - "test-ci", - "test-ci--src/unit.spec.ts", - ], - }, - }, - "root": "proj", - "targets": { - "test": { - "cache": true, - "command": "jest", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - "test-ci": { - "cache": true, - "dependsOn": [ - "test-ci--src/unit.spec.ts", - ], - "executor": "nx:noop", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests in CI", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "nonAtomizedTarget": "test", - "technologies": [ - "jest", - ], - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - "test-ci--src/unit.spec.ts": { - "cache": true, - "command": "jest src/unit.spec.ts", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests in src/unit.spec.ts", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], + const snapshot = ` + [ + [ + "proj/jest.config.js", + { + "projects": { + "proj": { + "metadata": undefined, + "root": "proj", + "targets": { + "test": { + "cache": true, + "command": "jest", + "inputs": [ + "default", + "^production", + { + "externalDependencies": [ + "jest", + "some-package", + ], + }, + ], + "metadata": { + "description": "Run Jest Tests", + "help": { + "command": "npx jest --help", + "example": { + "options": { + "coverage": true, }, }, }, + "technologies": [ + "jest", + ], }, - }, - ], - ] - `); - }); - - it.each` - preset | expectedInput - ${'/jest.preset.js'} | ${'{projectRoot}/jest.preset.js'} - ${'../jest.preset.js'} | ${'{workspaceRoot}/jest.preset.js'} - `('should correct input from preset', async ({ preset, expectedInput }) => { - mockJestConfig( - { - preset, - coverageDirectory: '../coverage', - testMatch: ['**/*.spec.ts'], - testPathIgnorePatterns: ['ignore.spec.ts'], - }, - context - ); - const results = await createNodesFunction( - ['proj/jest.config.js'], - { - targetName: 'test', - ciTargetName: 'test-ci', - disableJestRuntime: true, - }, - context - ); - - expect(results[0][1].projects['proj'].targets['test'].inputs).toContain( - expectedInput - ); - }); - - it.each` - testRegex - ${'\\.*\\.spec\\.ts'} - ${['\\.*\\.spec\\.ts']} - `( - 'should create test-ci targets from testRegex config option', - async ({ testRegex }) => { - mockJestConfig( - { - coverageDirectory: '../coverage', - testRegex, - testPathIgnorePatterns: ['ignore.spec.ts'], - }, - context - ); - const results = await createNodesFunction( - ['proj/jest.config.js'], - { - targetName: 'test', - ciTargetName: 'test-ci', - disableJestRuntime: true, - }, - context - ); - - expect(results).toMatchInlineSnapshot(` - [ - [ - "proj/jest.config.js", - { - "projects": { - "proj": { - "metadata": { - "targetGroups": { - "TEST (CI)": [ - "test-ci", - "test-ci--src/unit.spec.ts", - ], - }, - }, - "root": "proj", - "targets": { - "test": { - "cache": true, - "command": "jest", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - "test-ci": { - "cache": true, - "dependsOn": [ - "test-ci--src/unit.spec.ts", - ], - "executor": "nx:noop", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests in CI", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "nonAtomizedTarget": "test", - "technologies": [ - "jest", - ], - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - "test-ci--src/unit.spec.ts": { - "cache": true, - "command": "jest src/unit.spec.ts", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - ], - }, - ], - "metadata": { - "description": "Run Jest Tests in src/unit.spec.ts", - "help": { - "command": "npx jest --help", - "example": { - "options": { - "coverage": true, - }, - }, - }, - "technologies": [ - "jest", - ], - }, - "options": { - "cwd": "proj", - "env": { - "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", - }, - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], - }, - }, - }, + "options": { + "cwd": "proj", + "env": { + "TS_NODE_COMPILER_OPTIONS": "{"moduleResolution":"node10"}", }, }, - ], - ] - `); - } - ); - }); + "outputs": [ + "{workspaceRoot}/coverage", + ], + }, + }, + }, + }, + }, + ], + ] + `; + expect(results).toMatchInlineSnapshot(snapshot); + } + ); describe('ciGroupName', () => { it('should name atomized tasks group using provided group name', async () => { @@ -715,6 +431,7 @@ describe('@nx/jest/plugin', () => { { ciTargetName: 'test-ci', ciGroupName: 'MY ATOMIZED TEST TASKS (CI)', + disableJestRuntime, }, context ); @@ -864,6 +581,7 @@ describe('@nx/jest/plugin', () => { ['proj/jest.config.js'], { ciTargetName: 'test-ci', + disableJestRuntime, }, context ); @@ -1013,6 +731,7 @@ describe('@nx/jest/plugin', () => { ['proj/jest.config.js'], { ciTargetName: 'testci', // missing "-ci" suffix or similar construct, so group name cannot reliably be deducted from ci target name + disableJestRuntime, }, context ); diff --git a/packages/jest/src/plugins/plugin.ts b/packages/jest/src/plugins/plugin.ts index e94cde41fba70..96027aa670994 100644 --- a/packages/jest/src/plugins/plugin.ts +++ b/packages/jest/src/plugins/plugin.ts @@ -27,11 +27,8 @@ import { workspaceDataDirectory } from 'nx/src/utils/cache-directory'; import { combineGlobPatterns } from 'nx/src/utils/globs'; import { dirname, isAbsolute, join, relative, resolve } from 'path'; import { getInstalledJestMajorVersion } from '../utils/version-utils'; -import { - getFilesInDirectoryUsingContext, - globWithWorkspaceContext, -} from 'nx/src/utils/workspace-context'; -import { normalize } from 'node:path'; +import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context'; +import { normalize, sep } from 'node:path'; const pmc = getPackageManagerCommand(); @@ -44,7 +41,7 @@ export interface JestPluginOptions { */ ciGroupName?: string; /** - * Whether to use jest-config and jest-runtime to load Jest configuration and context. + * Whether to use jest-config and jest-runtime are used to load Jest configuration and context. * Disabling this is much faster but could be less correct since we are using our own config loader * and test matcher instead of Jest's. */ @@ -215,13 +212,16 @@ async function buildJestTargets( }, }); + // Not normalizing it here since also affects options for convert-to-inferred. + const disableJestRuntime = options.disableJestRuntime !== false; + const cache = (target.cache = true); const inputs = (target.inputs = getInputs( namedInputs, rawConfig.preset, projectRoot, context.workspaceRoot, - options.disableJestRuntime + disableJestRuntime )); let metadata: ProjectConfiguration['metadata']; @@ -229,7 +229,7 @@ async function buildJestTargets( const groupName = options?.ciGroupName ?? deductGroupNameFromTarget(options?.ciTargetName); - if (options.disableJestRuntime) { + if (disableJestRuntime) { const outputs = (target.outputs = getOutputs( projectRoot, rawConfig.coverageDirectory @@ -326,15 +326,21 @@ async function buildJestTargets( projectRoot, context.workspaceRoot ); - const config = await readConfig( - { - _: [], - $0: undefined, - }, - rawConfig, - undefined, - dirname(absConfigFilePath) - ); + let config; + try { + config = await readConfig( + { + _: [], + $0: undefined, + }, + rawConfig, + undefined, + dirname(absConfigFilePath) + ); + } catch (e) { + console.error(e); + throw e; + } const outputs = (target.outputs = getOutputs( projectRoot, @@ -363,8 +369,7 @@ async function buildJestTargets( const jestVersion = getInstalledJestMajorVersion()!; const specs = jestVersion >= 30 - ? // @ts-expect-error Jest 30+ expects the project config as the second argument - await source.getTestPaths(config.globalConfig, config.projectConfig) + ? await source.getTestPaths(config.globalConfig, config.projectConfig) : await source.getTestPaths(config.globalConfig); const testPaths = new Set(specs.tests.map(({ path }) => path)); @@ -627,45 +632,35 @@ async function getTestPaths( 'testMatch', presetCache ); - if (testMatch) { - return await globWithWorkspaceContext( - context.workspaceRoot, - testMatch.map((pattern) => join(projectRoot, pattern)), - [] - ); - } else { - const testRegex = await getJestOption( - rawConfig, - absConfigFilePath, - 'testRegex', - presetCache - ); - if (testRegex) { - const files: string[] = []; - const testRegexes = Array.isArray(rawConfig.testRegex) - ? rawConfig.testRegex.map((r: string) => new RegExp(r)) - : [new RegExp(rawConfig.testRegex)]; - const projectFiles = await getFilesInDirectoryUsingContext( - context.workspaceRoot, - projectRoot - ); - for (const file of projectFiles) { - if (testRegexes.some((r: RegExp) => r.test(file))) files.push(file); - } - return files; - } else { - // Default copied from https://github.com/jestjs/jest/blob/d1a2ed7/packages/jest-config/src/Defaults.ts#L84 - const defaultTestMatch = [ + + let paths = await globWithWorkspaceContext( + context.workspaceRoot, + ( + testMatch || [ + // Default copied from https://github.com/jestjs/jest/blob/d1a2ed7/packages/jest-config/src/Defaults.ts#L84 '**/__tests__/**/*.?([mc])[jt]s?(x)', '**/?(*.)+(spec|test).?([mc])[jt]s?(x)', - ]; - return await globWithWorkspaceContext( - context.workspaceRoot, - defaultTestMatch.map((pattern) => join(projectRoot, pattern)), - [] - ); - } + ] + ).map((pattern) => join(projectRoot, pattern)), + [] + ); + + const testRegex = await getJestOption( + rawConfig, + absConfigFilePath, + 'testRegex', + presetCache + ); + if (testRegex) { + const testRegexes = Array.isArray(rawConfig.testRegex) + ? rawConfig.testRegex.map((r: string) => new RegExp(r)) + : [new RegExp(rawConfig.testRegex)]; + paths = paths.filter((path: string) => + testRegexes.some((r: RegExp) => r.test(path)) + ); } + + return paths; } async function getJestOption(