feat(testing): Using getJestProjects() out of the box (#5900)

* feat(testing): updating nx to use getJestProjects() itself

* feat(testing): using getJestProjects() for new workspaces

* feat(testing): accomodating for getJestConfig() when adding new project

* feat(testing): migration for updating the base jest.config.js

* testing...

* fix(testing): fixing formatting in tests and bumping to next version

* fix(testing): fixing broken tests

* fix(testing): fixing test for jest init

* fix(testing): removing unnecessary test in jest project

* fix(testing): updating remove generator to work with jest utility fn

* fix(testing): fixing line break on package.json

* fix(testing): fixing import statement

* fix(testing): using AST to update the jest config contents

* fix(testing): fixing snapshot tests

* fix(testing): fixing describe to 12.6

* fix(testing): adding back in import statement to jest.config.js

* fix(testing): updating generated docs
This commit is contained in:
Zachary DeRose 2021-07-09 13:32:01 -07:00 committed by GitHub
parent 033579712f
commit 2524fdbc3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 168 additions and 57 deletions

View File

@ -1,32 +1,5 @@
const { getJestProjects } = require('@nrwl/jest');
module.exports = { module.exports = {
projects: [ projects: getJestProjects(),
'<rootDir>/packages/tao',
'<rootDir>/packages/workspace',
'<rootDir>/packages/web',
'<rootDir>/packages/cypress',
'<rootDir>/packages/jest',
'<rootDir>/packages/storybook',
'<rootDir>/packages/react',
'<rootDir>/packages/nx-plugin',
'<rootDir>/packages/node',
'<rootDir>/packages/next',
'<rootDir>/packages/nest',
'<rootDir>/packages/linter',
'<rootDir>/packages/express',
'<rootDir>/packages/eslint-plugin-nx',
'<rootDir>/packages/create-nx-workspace',
'<rootDir>/packages/create-nx-plugin',
'<rootDir>/packages/cli',
'<rootDir>/packages/angular',
'<rootDir>/packages/gatsby',
'<rootDir>/dep-graph/dep-graph',
'<rootDir>/nx-dev/nx-dev',
'<rootDir>/nx-dev/ui/common',
'<rootDir>/nx-dev/feature-doc-viewer',
'<rootDir>/nx-dev/data-access-documents',
'<rootDir>/nx-dev/data-access-menu',
'<rootDir>/nx-dev/feature-search',
'<rootDir>/nx-dev/feature-analytics',
'<rootDir>/typedoc-theme',
],
}; };

View File

@ -83,6 +83,12 @@
"cli": "nx", "cli": "nx",
"description": "Support for Jest 27 via updating ts-jest + jest-preset-angular", "description": "Support for Jest 27 via updating ts-jest + jest-preset-angular",
"factory": "./src/migrations/update-12-4-0/update-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": { "packageJsonUpdates": {

View File

@ -14,8 +14,10 @@ describe('jest', () => {
expect(tree.exists('jest.config.js')).toBeTruthy(); expect(tree.exists('jest.config.js')).toBeTruthy();
expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(`
"module.exports = { "const { getJestProjects } = require('@nrwl/jest');
projects: []
module.exports = {
projects: getJestProjects()
};" };"
`); `);
}); });

View File

@ -39,8 +39,10 @@ function createJestConfig(host: Tree) {
host.write( host.write(
'jest.config.js', 'jest.config.js',
stripIndents` stripIndents`
const { getJestProjects } = require('@nrwl/jest');
module.exports = { module.exports = {
projects: [] projects: getJestProjects()
};` };`
); );
} }

View File

@ -83,16 +83,6 @@ describe('jestProject', () => {
expect(tree.read('libs/lib1/jest.config.js', 'utf-8')).toMatchSnapshot(); 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(['<rootDir>/libs/lib1']);
});
it('should add a reference to solution tsconfig.json', async () => { it('should add a reference to solution tsconfig.json', async () => {
await jestProjectGenerator(tree, { await jestProjectGenerator(tree, {
...defaultOptions, ...defaultOptions,

View File

@ -2,7 +2,14 @@ import { JestProjectSchema } from '../schema';
import { addPropertyToJestConfig } from '../../../utils/config/update-config'; import { addPropertyToJestConfig } from '../../../utils/config/update-config';
import { readProjectConfiguration, Tree } from '@nrwl/devkit'; 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) { export function updateJestConfig(host: Tree, options: JestProjectSchema) {
if (isUsingUtilityFunction(host)) {
return;
}
const project = readProjectConfiguration(host, options.project); const project = readProjectConfiguration(host, options.project);
addPropertyToJestConfig( addPropertyToJestConfig(
host, host,

View File

@ -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: ['<rootDir>/test-1']
}`
);
});
beforeEach(async () => {
const prettier = await import('prettier');
prettier.resolveConfig = mockResolveConfig as any;
});
test('all jest projects covered', async () => {
mockGetJestProjects.mockImplementation(() => ['<rootDir>/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(() => ['<rootDir>/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(), '<rootDir>/test-1'] };
"
`);
});
});

View File

@ -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);
}

View File

@ -11,12 +11,14 @@ import {
* @param path - path to the jest config file * @param path - path to the jest config file
* @param propertyName - Property to update. Can be dot delimited to access deeply nested properties * @param propertyName - Property to update. Can be dot delimited to access deeply nested properties
* @param value * @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( export function addPropertyToJestConfig(
host: Tree, host: Tree,
path: string, path: string,
propertyName: string, propertyName: string,
value: unknown value: unknown,
options: { valueAsString: boolean } = { valueAsString: false }
) { ) {
if (!host.exists(path)) { if (!host.exists(path)) {
throw new Error(`Cannot find '${path}' in your workspace.`); throw new Error(`Cannot find '${path}' in your workspace.`);
@ -28,7 +30,7 @@ export function addPropertyToJestConfig(
host, host,
configObject, configObject,
properties, properties,
JSON.stringify(value), options.valueAsString ? value : JSON.stringify(value),
path path
); );
} catch (e) { } catch (e) {
@ -83,3 +85,15 @@ export function removePropertyFromJestConfig(
console.log(`Please manually update ${path}`); 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);
}

View File

@ -143,22 +143,25 @@ describe('lib', () => {
...defaultOptions, ...defaultOptions,
name: 'myLib', name: 'myLib',
}); });
const expectedRootJestConfig = `
"const { getJestProjects } = require('@nrwl/jest');
expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` module.exports = {
"module.exports = { projects: getJestProjects()
projects: [\\"<rootDir>/libs/my-lib\\"]
};" };"
`); `;
expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(
expectedRootJestConfig
);
await libraryGenerator(tree, { await libraryGenerator(tree, {
...defaultOptions, ...defaultOptions,
name: 'myLib2', name: 'myLib2',
}); });
expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(` expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(
"module.exports = { expectedRootJestConfig
projects: [\\"<rootDir>/libs/my-lib\\",\\"<rootDir>/libs/my-lib2\\"] );
};"
`);
}); });
}); });

View File

@ -66,8 +66,7 @@ describe('updateJestConfig', () => {
`coverageDirectory: '../../coverage/libs/my-destination'` `coverageDirectory: '../../coverage/libs/my-destination'`
); );
expect(rootJestConfigAfter).not.toContain('<rootDir>/libs/my-source'); expect(rootJestConfigAfter).toContain('getJestProjects()');
expect(rootJestConfigAfter).toContain('<rootDir>/libs/my-destination');
}); });
it('should update jest configs properly even if project is in many layers of subfolders', async () => { it('should update jest configs properly even if project is in many layers of subfolders', async () => {

View File

@ -19,6 +19,10 @@ import {
} from 'typescript'; } from 'typescript';
import { join } from 'path'; 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. * Updates the root jest config projects array and removes the project.
*/ */
@ -31,7 +35,8 @@ export function updateJestConfig(
if ( if (
!tree.exists('jest.config.js') || !tree.exists('jest.config.js') ||
!tree.exists(join(projectConfig.root, 'jest.config.js')) !tree.exists(join(projectConfig.root, 'jest.config.js')) ||
isUsingUtilityFunction(tree)
) { ) {
return; return;
} }