diff --git a/jest.config.js b/jest.config.js index d75c071a15..42c86fd0ca 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,32 +1,5 @@ +const { getJestProjects } = require('@nrwl/jest'); + module.exports = { - projects: [ - '/packages/tao', - '/packages/workspace', - '/packages/web', - '/packages/cypress', - '/packages/jest', - '/packages/storybook', - '/packages/react', - '/packages/nx-plugin', - '/packages/node', - '/packages/next', - '/packages/nest', - '/packages/linter', - '/packages/express', - '/packages/eslint-plugin-nx', - '/packages/create-nx-workspace', - '/packages/create-nx-plugin', - '/packages/cli', - '/packages/angular', - '/packages/gatsby', - '/dep-graph/dep-graph', - '/nx-dev/nx-dev', - '/nx-dev/ui/common', - '/nx-dev/feature-doc-viewer', - '/nx-dev/data-access-documents', - '/nx-dev/data-access-menu', - '/nx-dev/feature-search', - '/nx-dev/feature-analytics', - '/typedoc-theme', - ], + projects: getJestProjects(), }; diff --git a/packages/jest/migrations.json b/packages/jest/migrations.json index d643a70c9a..2891d9925f 100644 --- a/packages/jest/migrations.json +++ b/packages/jest/migrations.json @@ -83,6 +83,12 @@ "cli": "nx", "description": "Support for Jest 27 via updating ts-jest + jest-preset-angular", "factory": "./src/migrations/update-12-4-0/update-jest-preset-angular" + }, + "update-jest-config-to-use-util": { + "version": "12.6.0-beta.0", + "cli": "nx", + "description": "Uses `getJestProjects()` to populate projects array in root level `jest.config.js` file.", + "factory": "./src/migrations/update-12-6-0/update-base-jest-config" } }, "packageJsonUpdates": { diff --git a/packages/jest/src/generators/init/init.spec.ts b/packages/jest/src/generators/init/init.spec.ts index 6774542a00..4d87ccef72 100644 --- a/packages/jest/src/generators/init/init.spec.ts +++ b/packages/jest/src/generators/init/init.spec.ts @@ -14,8 +14,10 @@ describe('jest', () => { expect(tree.exists('jest.config.js')).toBeTruthy(); expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` - "module.exports = { - projects: [] + "const { getJestProjects } = require('@nrwl/jest'); + + module.exports = { + projects: getJestProjects() };" `); }); diff --git a/packages/jest/src/generators/init/init.ts b/packages/jest/src/generators/init/init.ts index 6199701f7e..179f16a979 100644 --- a/packages/jest/src/generators/init/init.ts +++ b/packages/jest/src/generators/init/init.ts @@ -39,8 +39,10 @@ function createJestConfig(host: Tree) { host.write( 'jest.config.js', stripIndents` + const { getJestProjects } = require('@nrwl/jest'); + module.exports = { - projects: [] + projects: getJestProjects() };` ); } diff --git a/packages/jest/src/generators/jest-project/jest-project.spec.ts b/packages/jest/src/generators/jest-project/jest-project.spec.ts index 91afed0fb3..a1edf3f3fa 100644 --- a/packages/jest/src/generators/jest-project/jest-project.spec.ts +++ b/packages/jest/src/generators/jest-project/jest-project.spec.ts @@ -83,16 +83,6 @@ describe('jestProject', () => { expect(tree.read('libs/lib1/jest.config.js', 'utf-8')).toMatchSnapshot(); }); - it('should add a project reference in the root jest.config.js', async () => { - await jestProjectGenerator(tree, { - ...defaultOptions, - project: 'lib1', - } as JestProjectSchema); - const jestConfig = jestConfigObject(tree, 'jest.config.js'); - - expect(jestConfig.projects).toEqual(['/libs/lib1']); - }); - it('should add a reference to solution tsconfig.json', async () => { await jestProjectGenerator(tree, { ...defaultOptions, diff --git a/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts b/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts index bd0bf1c9ea..47b1dda5c9 100644 --- a/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts +++ b/packages/jest/src/generators/jest-project/lib/update-jestconfig.ts @@ -2,7 +2,14 @@ import { JestProjectSchema } from '../schema'; import { addPropertyToJestConfig } from '../../../utils/config/update-config'; import { readProjectConfiguration, Tree } from '@nrwl/devkit'; +function isUsingUtilityFunction(host: Tree) { + return host.read('jest.config.js').toString().includes('getJestProjects()'); +} + export function updateJestConfig(host: Tree, options: JestProjectSchema) { + if (isUsingUtilityFunction(host)) { + return; + } const project = readProjectConfiguration(host, options.project); addPropertyToJestConfig( host, diff --git a/packages/jest/src/migrations/update-12-6-0/update-base-jest-config.spec.ts b/packages/jest/src/migrations/update-12-6-0/update-base-jest-config.spec.ts new file mode 100644 index 0000000000..5e6885b2db --- /dev/null +++ b/packages/jest/src/migrations/update-12-6-0/update-base-jest-config.spec.ts @@ -0,0 +1,53 @@ +const mockGetJestProjects = jest.fn(() => []); +jest.mock('../../utils/config/get-jest-projects', () => ({ + getJestProjects: mockGetJestProjects, +})); +const mockResolveConfig = jest.fn(() => + Promise.resolve({ singleQuote: true, endOfLine: 'lf' }) +); + +import { Tree } from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from 'packages/devkit/src/tests/create-tree-with-empty-workspace'; +import update from './update-base-jest-config'; + +describe('update 12.6.0', () => { + let tree: Tree; + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + tree.write( + 'jest.config.js', + `module.exports = { + projects: ['/test-1'] + }` + ); + }); + + beforeEach(async () => { + const prettier = await import('prettier'); + prettier.resolveConfig = mockResolveConfig as any; + }); + + test('all jest projects covered', async () => { + mockGetJestProjects.mockImplementation(() => ['/test-1']); + await update(tree); + const result = tree.read('jest.config.js').toString(); + expect(result).toMatchInlineSnapshot(` + "const { getJestProjects } = require('@nrwl/jest'); + + module.exports = { projects: getJestProjects() }; + " + `); + }); + + test('some jest projects uncovered', async () => { + mockGetJestProjects.mockImplementation(() => ['/test-2']); + await update(tree); + const result = tree.read('jest.config.js').toString(); + expect(result).toMatchInlineSnapshot(` + "const { getJestProjects } = require('@nrwl/jest'); + + module.exports = { projects: [...getJestProjects(), '/test-1'] }; + " + `); + }); +}); diff --git a/packages/jest/src/migrations/update-12-6-0/update-base-jest-config.ts b/packages/jest/src/migrations/update-12-6-0/update-base-jest-config.ts new file mode 100644 index 0000000000..0732cf3182 --- /dev/null +++ b/packages/jest/src/migrations/update-12-6-0/update-base-jest-config.ts @@ -0,0 +1,57 @@ +import { formatFiles, Tree } from '@nrwl/devkit'; +import { jestConfigObject } from '../../utils/config/functions'; +import { getJestProjects } from '../../utils/config/get-jest-projects'; +import { + addImportStatementToJestConfig, + addPropertyToJestConfig, + removePropertyFromJestConfig, +} from '../../utils/config/update-config'; + +function determineUncoveredJestProjects(existingProjects: string[]) { + const coveredJestProjects = (getJestProjects() as string[]).reduce( + (acc, key) => { + acc[key] = true; + return acc; + }, + {} + ); + return existingProjects.filter((project) => !coveredJestProjects[project]); +} + +function determineProjectsValue(uncoveredJestProjects: string[]): string { + if (!uncoveredJestProjects.length) { + return `getJestProjects()`; + } + return `[...getJestProjects(), ${uncoveredJestProjects.map( + (projectName) => `'${projectName}', ` + )}]`; +} + +function updateBaseJestConfig( + tree: Tree, + baseJestConfigPath = 'jest.config.js' +) { + const currentConfig = jestConfigObject(tree, baseJestConfigPath); + const uncoveredJestProjects = determineUncoveredJestProjects( + currentConfig.projects as string[] + ); + removePropertyFromJestConfig(tree, baseJestConfigPath, 'projects'); + addPropertyToJestConfig( + tree, + baseJestConfigPath, + 'projects', + determineProjectsValue(uncoveredJestProjects), + { valueAsString: true } + ); + addImportStatementToJestConfig( + tree, + baseJestConfigPath, + `const { getJestProjects } = require('@nrwl/jest');` + ); + return; +} + +export default async function update(tree: Tree) { + updateBaseJestConfig(tree); + await formatFiles(tree); +} diff --git a/packages/jest/src/utils/config/update-config.ts b/packages/jest/src/utils/config/update-config.ts index 5525eb617f..a016d57066 100644 --- a/packages/jest/src/utils/config/update-config.ts +++ b/packages/jest/src/utils/config/update-config.ts @@ -11,12 +11,14 @@ import { * @param path - path to the jest config file * @param propertyName - Property to update. Can be dot delimited to access deeply nested properties * @param value + * @param options - set `valueAsString` option to true if the `value` being passed represents a string of the code that should be associated with the `propertyName` */ export function addPropertyToJestConfig( host: Tree, path: string, propertyName: string, - value: unknown + value: unknown, + options: { valueAsString: boolean } = { valueAsString: false } ) { if (!host.exists(path)) { throw new Error(`Cannot find '${path}' in your workspace.`); @@ -28,7 +30,7 @@ export function addPropertyToJestConfig( host, configObject, properties, - JSON.stringify(value), + options.valueAsString ? value : JSON.stringify(value), path ); } catch (e) { @@ -83,3 +85,15 @@ export function removePropertyFromJestConfig( console.log(`Please manually update ${path}`); } } + +export function addImportStatementToJestConfig( + host: Tree, + path: string, + importStatement: string +) { + const currentContents = host.read(path, 'utf-8'); + const newContents = `${importStatement} + +${currentContents}`; + host.write(path, newContents); +} diff --git a/packages/workspace/src/generators/library/library.spec.ts b/packages/workspace/src/generators/library/library.spec.ts index 683689e040..71b145e339 100644 --- a/packages/workspace/src/generators/library/library.spec.ts +++ b/packages/workspace/src/generators/library/library.spec.ts @@ -143,22 +143,25 @@ describe('lib', () => { ...defaultOptions, name: 'myLib', }); + const expectedRootJestConfig = ` + "const { getJestProjects } = require('@nrwl/jest'); - expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` - "module.exports = { - projects: [\\"/libs/my-lib\\"] + module.exports = { + projects: getJestProjects() };" - `); + `; + + expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot( + expectedRootJestConfig + ); await libraryGenerator(tree, { ...defaultOptions, name: 'myLib2', }); - expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` - "module.exports = { - projects: [\\"/libs/my-lib\\",\\"/libs/my-lib2\\"] - };" - `); + expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot( + expectedRootJestConfig + ); }); }); diff --git a/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts b/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts index 3f02701517..35e1933ef2 100644 --- a/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-jest-config.spec.ts @@ -66,8 +66,7 @@ describe('updateJestConfig', () => { `coverageDirectory: '../../coverage/libs/my-destination'` ); - expect(rootJestConfigAfter).not.toContain('/libs/my-source'); - expect(rootJestConfigAfter).toContain('/libs/my-destination'); + expect(rootJestConfigAfter).toContain('getJestProjects()'); }); it('should update jest configs properly even if project is in many layers of subfolders', async () => { diff --git a/packages/workspace/src/generators/remove/lib/update-jest-config.ts b/packages/workspace/src/generators/remove/lib/update-jest-config.ts index 3f56f1c97c..d8178652d2 100644 --- a/packages/workspace/src/generators/remove/lib/update-jest-config.ts +++ b/packages/workspace/src/generators/remove/lib/update-jest-config.ts @@ -19,6 +19,10 @@ import { } from 'typescript'; import { join } from 'path'; +function isUsingUtilityFunction(host: Tree) { + return host.read('jest.config.js').toString().includes('getJestProjects()'); +} + /** * Updates the root jest config projects array and removes the project. */ @@ -31,7 +35,8 @@ export function updateJestConfig( if ( !tree.exists('jest.config.js') || - !tree.exists(join(projectConfig.root, 'jest.config.js')) + !tree.exists(join(projectConfig.root, 'jest.config.js')) || + isUsingUtilityFunction(tree) ) { return; }