feat(testing): add getJestProjectsAsync to support inferred targets (#21897)
This commit is contained in:
parent
dd2c7d2601
commit
77a01ca94c
@ -13,5 +13,6 @@ export { jestConfigObjectAst } from './src/utils/config/functions';
|
|||||||
export { jestInitGenerator } from './src/generators/init/init';
|
export { jestInitGenerator } from './src/generators/init/init';
|
||||||
export {
|
export {
|
||||||
getJestProjects,
|
getJestProjects,
|
||||||
|
getJestProjectsAsync,
|
||||||
getNestedJestProjects,
|
getNestedJestProjects,
|
||||||
} from './src/utils/config/get-jest-projects';
|
} from './src/utils/config/get-jest-projects';
|
||||||
|
|||||||
@ -36,6 +36,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/reporters": "^29.4.1",
|
"@jest/reporters": "^29.4.1",
|
||||||
"@jest/test-result": "^29.4.1",
|
"@jest/test-result": "^29.4.1",
|
||||||
|
"@nx/devkit": "file:../devkit",
|
||||||
|
"@nx/js": "file:../js",
|
||||||
"@phenomnomnominal/tsquery": "~5.0.1",
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"identity-obj-proxy": "3.0.0",
|
"identity-obj-proxy": "3.0.0",
|
||||||
@ -45,8 +47,7 @@
|
|||||||
"minimatch": "9.0.3",
|
"minimatch": "9.0.3",
|
||||||
"resolve.exports": "1.1.0",
|
"resolve.exports": "1.1.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"@nx/devkit": "file:../devkit",
|
"yargs-parser": "21.1.1"
|
||||||
"@nx/js": "file:../js"
|
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`createJestConfig should generate files 1`] = `
|
exports[`createJestConfig should generate files 1`] = `
|
||||||
"import { getJestProjects } from '@nx/jest';
|
"import { getJestProjectsAsync } from '@nx/jest';
|
||||||
|
|
||||||
export default {
|
export default async () => ({
|
||||||
projects: getJestProjects()
|
projects: await getJestProjectsAsync()
|
||||||
};"
|
});"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`createJestConfig should generate files 2`] = `
|
exports[`createJestConfig should generate files 2`] = `
|
||||||
@ -15,11 +15,11 @@ module.exports = { ...nxPreset }"
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`createJestConfig should generate files with --js flag 1`] = `
|
exports[`createJestConfig should generate files with --js flag 1`] = `
|
||||||
"const { getJestProjects } = require('@nx/jest');
|
"const { getJestProjectsAsync } = require('@nx/jest');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = async () => ({
|
||||||
projects: getJestProjects()
|
projects: await getJestProjectsAsync()
|
||||||
};"
|
});"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`createJestConfig should generate files with --js flag 2`] = `
|
exports[`createJestConfig should generate files with --js flag 2`] = `
|
||||||
|
|||||||
@ -162,11 +162,11 @@ export default {
|
|||||||
"
|
"
|
||||||
`);
|
`);
|
||||||
expect(tree.read('jest.config.ts', 'utf-8'))
|
expect(tree.read('jest.config.ts', 'utf-8'))
|
||||||
.toEqual(`import { getJestProjects } from '@nx/jest';
|
.toEqual(`import { getJestProjectsAsync } from '@nx/jest';
|
||||||
|
|
||||||
export default {
|
export default async () => ({
|
||||||
projects: getJestProjects()
|
projects: await getJestProjectsAsync()
|
||||||
};`);
|
});`);
|
||||||
expect(readProjectConfiguration(tree, 'my-project').targets.test)
|
expect(readProjectConfiguration(tree, 'my-project').targets.test)
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
@ -214,11 +214,11 @@ module.exports = {
|
|||||||
|
|
||||||
expect(tree.exists('jest.config.app.js')).toBeTruthy();
|
expect(tree.exists('jest.config.app.js')).toBeTruthy();
|
||||||
expect(tree.read('jest.config.js', 'utf-8'))
|
expect(tree.read('jest.config.js', 'utf-8'))
|
||||||
.toEqual(`const { getJestProjects } = require('@nx/jest');
|
.toEqual(`const { getJestProjectsAsync } = require('@nx/jest');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = async () => ({
|
||||||
projects: getJestProjects()
|
projects: await getJestProjectsAsync()
|
||||||
};`);
|
});`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -129,16 +129,16 @@ export async function createJestConfig(
|
|||||||
function generateGlobalConfig(tree: Tree, isJS: boolean) {
|
function generateGlobalConfig(tree: Tree, isJS: boolean) {
|
||||||
const contents = isJS
|
const contents = isJS
|
||||||
? stripIndents`
|
? stripIndents`
|
||||||
const { getJestProjects } = require('@nx/jest');
|
const { getJestProjectsAsync } = require('@nx/jest');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = async () => ({
|
||||||
projects: getJestProjects()
|
projects: await getJestProjectsAsync()
|
||||||
};`
|
});`
|
||||||
: stripIndents`
|
: stripIndents`
|
||||||
import { getJestProjects } from '@nx/jest';
|
import { getJestProjectsAsync } from '@nx/jest';
|
||||||
|
|
||||||
export default {
|
export default async () => ({
|
||||||
projects: getJestProjects()
|
projects: await getJestProjectsAsync()
|
||||||
};`;
|
});`;
|
||||||
tree.write(`jest.config.${isJS ? 'js' : 'ts'}`, contents);
|
tree.write(`jest.config.${isJS ? 'js' : 'ts'}`, contents);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,15 @@ import { readProjectConfiguration, Tree } from '@nx/devkit';
|
|||||||
|
|
||||||
function isUsingUtilityFunction(host: Tree) {
|
function isUsingUtilityFunction(host: Tree) {
|
||||||
const rootConfig = findRootJestConfig(host);
|
const rootConfig = findRootJestConfig(host);
|
||||||
|
if (!rootConfig) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootConfigContent = host.read(rootConfig, 'utf-8');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
rootConfig && host.read(rootConfig).toString().includes('getJestProjects()')
|
rootConfigContent.includes('getJestProjects()') ||
|
||||||
|
rootConfigContent.includes('getJestProjectsAsync()')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ describe('@nx/jest/plugin', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
tempFs = new TempFs('test');
|
tempFs = new TempFs('test');
|
||||||
|
process.chdir(tempFs.tempDir);
|
||||||
context = {
|
context = {
|
||||||
nxJsonConfiguration: {
|
nxJsonConfiguration: {
|
||||||
namedInputs: {
|
namedInputs: {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { dirname, join, relative, resolve } from 'path';
|
|||||||
|
|
||||||
import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils';
|
import { readTargetDefaultsForTarget } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||||
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
||||||
import { existsSync, readdirSync } from 'fs';
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
||||||
import { readConfig } from 'jest-config';
|
import { readConfig } from 'jest-config';
|
||||||
import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory';
|
import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory';
|
||||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||||
@ -82,6 +82,17 @@ export const createNodes: CreateNodes<JestPluginOptions> = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const jestConfigContent = readFileSync(
|
||||||
|
resolve(context.workspaceRoot, configFilePath),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
if (jestConfigContent.includes('getJestProjectsAsync()')) {
|
||||||
|
// The `getJestProjectsAsync` function uses the project graph, which leads to a
|
||||||
|
// circular dependency. We can skip this since it's no intended to be used for
|
||||||
|
// an Nx project.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
options = normalizeOptions(options);
|
options = normalizeOptions(options);
|
||||||
|
|
||||||
const hash = calculateHashForCreateNodes(projectRoot, options, context);
|
const hash = calculateHashForCreateNodes(projectRoot, options, context);
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import { getJestProjects } from './get-jest-projects';
|
import type {
|
||||||
|
ProjectConfiguration,
|
||||||
|
ProjectGraph,
|
||||||
|
WorkspaceJsonConfiguration,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import * as devkit from '@nx/devkit';
|
||||||
import * as Workspace from 'nx/src/project-graph/file-utils';
|
import * as Workspace from 'nx/src/project-graph/file-utils';
|
||||||
import type { WorkspaceJsonConfiguration } from '@nx/devkit';
|
import { getJestProjects, getJestProjectsAsync } from './get-jest-projects';
|
||||||
|
|
||||||
describe('getJestProjects', () => {
|
describe('getJestProjects', () => {
|
||||||
test('single project', () => {
|
test('single project', () => {
|
||||||
@ -142,7 +147,7 @@ describe('getJestProjects', () => {
|
|||||||
expect(getJestProjects()).toEqual(expectedResults);
|
expect(getJestProjects()).toEqual(expectedResults);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('other projects and targets that do not use the nrwl jest test runner', () => {
|
test('other projects and targets that do not use the nx jest test runner', () => {
|
||||||
const mockedWorkspaceConfig: WorkspaceJsonConfiguration = {
|
const mockedWorkspaceConfig: WorkspaceJsonConfiguration = {
|
||||||
projects: {
|
projects: {
|
||||||
otherTarget: {
|
otherTarget: {
|
||||||
@ -180,3 +185,315 @@ describe('getJestProjects', () => {
|
|||||||
expect(getJestProjects()).toEqual(expectedResults);
|
expect(getJestProjects()).toEqual(expectedResults);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getJestProjectsAsync', () => {
|
||||||
|
let projectGraph: ProjectGraph;
|
||||||
|
|
||||||
|
function addProject(name: string, config: ProjectConfiguration): void {
|
||||||
|
projectGraph.nodes[name] = { name, type: 'app', data: config };
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
projectGraph = { nodes: {}, dependencies: {} };
|
||||||
|
jest
|
||||||
|
.spyOn(devkit, 'createProjectGraphAsync')
|
||||||
|
.mockReturnValue(Promise.resolve(projectGraph));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('single project', async () => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'blah',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'test/jest/config/location/jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [
|
||||||
|
'<rootDir>/test/jest/config/location/jest.config.js',
|
||||||
|
];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('custom target name', async () => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'blah',
|
||||||
|
targets: {
|
||||||
|
'test-with-jest': {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'test/jest/config/location/jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [
|
||||||
|
'<rootDir>/test/jest/config/location/jest.config.js',
|
||||||
|
];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('root project', async () => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: '.',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'jest.config.app.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = ['<rootDir>/jest.config.app.js'];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('configuration set with unique jestConfig', async () => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'blah',
|
||||||
|
targets: {
|
||||||
|
'test-with-jest': {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'test/jest/config/location/jest.config.js',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
prod: {
|
||||||
|
jestConfig: 'configuration-specific/jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [
|
||||||
|
'<rootDir>/test/jest/config/location/jest.config.js',
|
||||||
|
'<rootDir>/configuration-specific/jest.config.js',
|
||||||
|
];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('configuration, set with same jestConfig on configuration', async () => {
|
||||||
|
addProject('test', {
|
||||||
|
root: 'blah',
|
||||||
|
targets: {
|
||||||
|
'test-with-jest': {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'test/jest/config/location/jest.config.js',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
prod: {
|
||||||
|
jestConfig: 'test/jest/config/location/jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [
|
||||||
|
'<rootDir>/test/jest/config/location/jest.config.js',
|
||||||
|
];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('other projects and targets that do not use the nx jest test runner', async () => {
|
||||||
|
addProject('otherTarget', {
|
||||||
|
root: 'test',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'something else',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
addProject('test', {
|
||||||
|
root: 'blah',
|
||||||
|
targets: {
|
||||||
|
'test-with-jest': {
|
||||||
|
executor: 'something else',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'something random',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
prod: {
|
||||||
|
jestConfig: 'configuration-specific/jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
command
|
||||||
|
${'jest'}
|
||||||
|
${'npx jest'}
|
||||||
|
${'yarn jest'}
|
||||||
|
${'pnpm jest'}
|
||||||
|
${'pnpm dlx jest'}
|
||||||
|
${'echo "foo" && jest'}
|
||||||
|
${'echo "foo" && npx jest'}
|
||||||
|
${'jest && echo "foo"'}
|
||||||
|
${'npx jest && echo "foo"'}
|
||||||
|
${'echo "foo" && jest && echo "bar"'}
|
||||||
|
${'echo "foo" && npx jest && echo "bar"'}
|
||||||
|
`(
|
||||||
|
'targets with nx:run-commands executor running "$command"',
|
||||||
|
async ({ command }) => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'projects/test-1',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
options: { command },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = ['<rootDir>/projects/test-1'];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
command
|
||||||
|
${'jest'}
|
||||||
|
${'npx jest'}
|
||||||
|
${'yarn jest'}
|
||||||
|
${'pnpm jest'}
|
||||||
|
${'pnpm dlx jest'}
|
||||||
|
${'echo "foo" && jest'}
|
||||||
|
${'echo "foo" && npx jest'}
|
||||||
|
${'jest && echo "foo"'}
|
||||||
|
${'npx jest && echo "foo"'}
|
||||||
|
${'echo "foo" && jest && echo "bar"'}
|
||||||
|
${'echo "foo" && npx jest && echo "bar"'}
|
||||||
|
`(
|
||||||
|
'targets with nx:run-commands executor using "commands" option and running "$command"',
|
||||||
|
async ({ command }) => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'projects/test-1',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
options: { commands: [command] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = ['<rootDir>/projects/test-1'];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
command
|
||||||
|
${'jest'}
|
||||||
|
${'npx jest'}
|
||||||
|
${'yarn jest'}
|
||||||
|
${'pnpm jest'}
|
||||||
|
${'pnpm dlx jest'}
|
||||||
|
${'echo "foo" && jest'}
|
||||||
|
${'echo "foo" && npx jest'}
|
||||||
|
${'jest && echo "foo"'}
|
||||||
|
${'npx jest && echo "foo"'}
|
||||||
|
${'echo "foo" && jest && echo "bar"'}
|
||||||
|
${'echo "foo" && npx jest && echo "bar"'}
|
||||||
|
`(
|
||||||
|
'targets with nx:run-commands executor using "commands" option using the object notation and running "$command"',
|
||||||
|
async ({ command }) => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'projects/test-1',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
options: { commands: [{ command }] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = ['<rootDir>/projects/test-1'];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each`
|
||||||
|
command | cwd
|
||||||
|
${'jest --config projects/test-1/jest.config.ts'} | ${'.'}
|
||||||
|
${'npx jest --config projects/test-1/jest.config.ts'} | ${'.'}
|
||||||
|
${'jest --config jest.config.ts'} | ${undefined}
|
||||||
|
${'npx jest --config jest.config.ts'} | ${undefined}
|
||||||
|
${'jest --config jest.config.ts'} | ${'projects/test-1'}
|
||||||
|
${'npx jest --config jest.config.ts'} | ${'projects/test-1'}
|
||||||
|
${'echo "foo" && jest --config jest.config.ts'} | ${undefined}
|
||||||
|
${'echo "foo" && npx jest --config jest.config.ts'} | ${undefined}
|
||||||
|
${'jest --config jest.config.ts && echo "foo"'} | ${undefined}
|
||||||
|
${'npx jest --config jest.config.ts && echo "foo"'} | ${undefined}
|
||||||
|
${'echo "foo" && jest --config jest.config.ts && echo "bar"'} | ${undefined}
|
||||||
|
${'echo "foo" && npx jest --config jest.config.ts && echo "bar"'} | ${undefined}
|
||||||
|
`(
|
||||||
|
'targets with nx:run-commands executor running "$command" at "$cwd"',
|
||||||
|
async ({ command, cwd }) => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'projects/test-1',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
options: { command, cwd },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = ['<rootDir>/projects/test-1/jest.config.ts'];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
test('targets with nx:run-commands executor with a command with multiple "jest" runs', async () => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'projects/test-1',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
options: {
|
||||||
|
command: 'jest && jest --config jest1.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [
|
||||||
|
'<rootDir>/projects/test-1',
|
||||||
|
'<rootDir>/projects/test-1/jest1.config.ts',
|
||||||
|
];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('projects with targets using both executors', async () => {
|
||||||
|
addProject('test-1', {
|
||||||
|
root: 'projects/test-1',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: '@nx/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'projects/test-1/jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
addProject('test-2', {
|
||||||
|
root: 'projects/test-2',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: 'nx:run-commands',
|
||||||
|
options: { command: 'jest' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expectedResults = [
|
||||||
|
'<rootDir>/projects/test-1/jest.config.js',
|
||||||
|
'<rootDir>/projects/test-2',
|
||||||
|
];
|
||||||
|
expect(await getJestProjectsAsync()).toEqual(expectedResults);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import { join } from 'path';
|
import {
|
||||||
import type { ProjectsConfigurations } from '@nx/devkit';
|
createProjectGraphAsync,
|
||||||
|
type ProjectsConfigurations,
|
||||||
|
type TargetConfiguration,
|
||||||
|
} from '@nx/devkit';
|
||||||
import { readWorkspaceConfig } from 'nx/src/project-graph/file-utils';
|
import { readWorkspaceConfig } from 'nx/src/project-graph/file-utils';
|
||||||
|
import { join } from 'path';
|
||||||
|
import * as yargs from 'yargs-parser';
|
||||||
|
|
||||||
function getJestConfigProjectPath(projectJestConfigPath: string): string {
|
function getJestConfigProjectPath(projectJestConfigPath: string): string {
|
||||||
return join('<rootDir>', projectJestConfigPath);
|
return join('<rootDir>', projectJestConfigPath);
|
||||||
@ -10,7 +15,8 @@ function getJestConfigProjectPath(projectJestConfigPath: string): string {
|
|||||||
* Get a list of paths to all the jest config files
|
* Get a list of paths to all the jest config files
|
||||||
* using the Nx Jest executor.
|
* using the Nx Jest executor.
|
||||||
*
|
*
|
||||||
* This is used to configure Jest multi-project support.
|
* This is used to configure Jest multi-project support. To support projects
|
||||||
|
* using inferred targets @see getJestProjectsAsync
|
||||||
*
|
*
|
||||||
* To add a project not using the Nx Jest executor:
|
* To add a project not using the Nx Jest executor:
|
||||||
* export default {
|
* export default {
|
||||||
@ -68,3 +74,146 @@ export function getNestedJestProjects() {
|
|||||||
const allProjects = getJestProjects();
|
const allProjects = getJestProjects();
|
||||||
return ['/node_modules/'];
|
return ['/node_modules/'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of paths to all the jest config files
|
||||||
|
* using the Nx Jest executor and `@nx/run:commands`
|
||||||
|
* running `jest`.
|
||||||
|
*
|
||||||
|
* This is used to configure Jest multi-project support.
|
||||||
|
*
|
||||||
|
* To add a project not using the Nx Jest executor:
|
||||||
|
* export default async () => ({
|
||||||
|
* projects: [...(await getJestProjectsAsync()), '<rootDir>/path/to/jest.config.ts'];
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
export async function getJestProjectsAsync() {
|
||||||
|
const graph = await createProjectGraphAsync({
|
||||||
|
exitOnError: false,
|
||||||
|
resetDaemonClient: true,
|
||||||
|
});
|
||||||
|
const jestConfigurations = new Set<string>();
|
||||||
|
for (const node of Object.values(graph.nodes)) {
|
||||||
|
const projectConfig = node.data;
|
||||||
|
if (!projectConfig.targets) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const targetConfiguration of Object.values(projectConfig.targets)) {
|
||||||
|
if (
|
||||||
|
targetConfiguration.executor === '@nx/jest:jest' ||
|
||||||
|
targetConfiguration.executor === '@nrwl/jest:jest'
|
||||||
|
) {
|
||||||
|
collectJestConfigFromJestExecutor(
|
||||||
|
targetConfiguration,
|
||||||
|
jestConfigurations
|
||||||
|
);
|
||||||
|
} else if (targetConfiguration.executor === 'nx:run-commands') {
|
||||||
|
collectJestConfigFromRunCommandsExecutor(
|
||||||
|
targetConfiguration,
|
||||||
|
projectConfig.root,
|
||||||
|
jestConfigurations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(jestConfigurations);
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectJestConfigFromJestExecutor(
|
||||||
|
targetConfiguration: TargetConfiguration,
|
||||||
|
jestConfigurations: Set<string>
|
||||||
|
): void {
|
||||||
|
if (targetConfiguration.options?.jestConfig) {
|
||||||
|
jestConfigurations.add(
|
||||||
|
getJestConfigProjectPath(targetConfiguration.options.jestConfig)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (targetConfiguration.configurations) {
|
||||||
|
for (const configurationObject of Object.values(
|
||||||
|
targetConfiguration.configurations
|
||||||
|
)) {
|
||||||
|
if (configurationObject.jestConfig) {
|
||||||
|
jestConfigurations.add(
|
||||||
|
getJestConfigProjectPath(configurationObject.jestConfig)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectJestConfigFromRunCommandsExecutor(
|
||||||
|
targetConfiguration: TargetConfiguration,
|
||||||
|
projectRoot: string,
|
||||||
|
jestConfigurations: Set<string>
|
||||||
|
): void {
|
||||||
|
if (targetConfiguration.options?.command) {
|
||||||
|
collectJestConfigFromCommand(
|
||||||
|
targetConfiguration.options.command,
|
||||||
|
targetConfiguration.options.cwd ?? projectRoot,
|
||||||
|
jestConfigurations
|
||||||
|
);
|
||||||
|
} else if (targetConfiguration.options?.commands) {
|
||||||
|
for (const command of targetConfiguration.options.commands) {
|
||||||
|
const commandScript =
|
||||||
|
typeof command === 'string' ? command : command.command;
|
||||||
|
collectJestConfigFromCommand(
|
||||||
|
commandScript,
|
||||||
|
targetConfiguration.options.cwd ?? projectRoot,
|
||||||
|
jestConfigurations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetConfiguration.configurations) {
|
||||||
|
for (const configurationObject of Object.values(
|
||||||
|
targetConfiguration.configurations
|
||||||
|
)) {
|
||||||
|
if (configurationObject.command) {
|
||||||
|
collectJestConfigFromCommand(
|
||||||
|
configurationObject.command,
|
||||||
|
configurationObject.cwd ?? projectRoot,
|
||||||
|
jestConfigurations
|
||||||
|
);
|
||||||
|
} else if (configurationObject.commands) {
|
||||||
|
for (const command of configurationObject.commands) {
|
||||||
|
const commandScript =
|
||||||
|
typeof command === 'string' ? command : command.command;
|
||||||
|
collectJestConfigFromCommand(
|
||||||
|
commandScript,
|
||||||
|
configurationObject.cwd ?? projectRoot,
|
||||||
|
jestConfigurations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectJestConfigFromCommand(
|
||||||
|
command: string,
|
||||||
|
cwd: string,
|
||||||
|
jestConfigurations: Set<string>
|
||||||
|
) {
|
||||||
|
const jestCommandRegex =
|
||||||
|
/(?<=^|&)(?:[^&\r\n\s]* )*jest(?: [^&\r\n\s]*)*(?=$|&)/g;
|
||||||
|
const matches = command.match(jestCommandRegex);
|
||||||
|
if (!matches) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const match of matches) {
|
||||||
|
const parsed = yargs(match, {
|
||||||
|
configuration: { 'strip-dashed': true },
|
||||||
|
string: ['config'],
|
||||||
|
});
|
||||||
|
if (parsed.config) {
|
||||||
|
jestConfigurations.add(
|
||||||
|
getJestConfigProjectPath(join(cwd, parsed.config))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
jestConfigurations.add(getJestConfigProjectPath(cwd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -66,7 +66,7 @@ describe('updateJestConfig', () => {
|
|||||||
expect(jestConfigAfter).toContain(
|
expect(jestConfigAfter).toContain(
|
||||||
`coverageDirectory: '../coverage/my-destination'`
|
`coverageDirectory: '../coverage/my-destination'`
|
||||||
);
|
);
|
||||||
expect(rootJestConfigAfter).toContain('getJestProjects()');
|
expect(rootJestConfigAfter).toContain('getJestProjectsAsync()');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the name and dir correctly when moving to a nested dir', async () => {
|
it('should update the name and dir correctly when moving to a nested dir', async () => {
|
||||||
@ -142,7 +142,7 @@ describe('updateJestConfig', () => {
|
|||||||
expect(jestConfigAfter).toContain(
|
expect(jestConfigAfter).toContain(
|
||||||
`coverageDirectory: '../coverage/other/test/dir/my-destination'`
|
`coverageDirectory: '../coverage/other/test/dir/my-destination'`
|
||||||
);
|
);
|
||||||
expect(rootJestConfigAfter).toContain('getJestProjects()');
|
expect(rootJestConfigAfter).toContain('getJestProjectsAsync()');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates the root config if not using `getJestProjects()`', async () => {
|
it('updates the root config if not using `getJestProjects()`', async () => {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ProjectConfiguration, Tree } from '@nx/devkit';
|
import { ProjectConfiguration, Tree } from '@nx/devkit';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { NormalizedSchema } from '../schema';
|
import { NormalizedSchema } from '../schema';
|
||||||
|
import { findRootJestConfig } from '../../utils/jest-config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the project name and coverage folder in the jest.config.js if it exists
|
* Updates the project name and coverage folder in the jest.config.js if it exists
|
||||||
@ -58,9 +59,9 @@ export function updateJestConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update root jest.config.ts
|
// update root jest.config.ts
|
||||||
const rootJestConfigPath = '/jest.config.ts';
|
const rootJestConfigPath = findRootJestConfig(tree);
|
||||||
|
|
||||||
if (!tree.exists(rootJestConfigPath)) {
|
if (!rootJestConfigPath || !tree.exists(rootJestConfigPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +69,8 @@ export function updateJestConfig(
|
|||||||
|
|
||||||
const oldRootJestConfigContent = tree.read(rootJestConfigPath, 'utf-8');
|
const oldRootJestConfigContent = tree.read(rootJestConfigPath, 'utf-8');
|
||||||
const usingJestProjects =
|
const usingJestProjects =
|
||||||
oldRootJestConfigContent.includes('getJestProjects()');
|
oldRootJestConfigContent.includes('getJestProjects()') ||
|
||||||
|
oldRootJestConfigContent.includes('getJestProjectsAsync()');
|
||||||
|
|
||||||
const newRootJestConfigContent = oldRootJestConfigContent.replace(
|
const newRootJestConfigContent = oldRootJestConfigContent.replace(
|
||||||
findProject,
|
findProject,
|
||||||
|
|||||||
@ -14,11 +14,22 @@ import type {
|
|||||||
} from 'typescript';
|
} from 'typescript';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { ensureTypescript } from '../../../utilities/typescript';
|
import { ensureTypescript } from '../../../utilities/typescript';
|
||||||
|
import { findRootJestConfig } from '../../utils/jest-config';
|
||||||
|
|
||||||
let tsModule: typeof import('typescript');
|
let tsModule: typeof import('typescript');
|
||||||
|
|
||||||
function isUsingUtilityFunction(host: Tree) {
|
function isUsingUtilityFunction(host: Tree) {
|
||||||
return host.read('jest.config.ts').toString().includes('getJestProjects()');
|
const rootConfigPath = findRootJestConfig(host);
|
||||||
|
if (!rootConfigPath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootConfig = host.read(rootConfigPath, 'utf-8');
|
||||||
|
|
||||||
|
return (
|
||||||
|
rootConfig.includes('getJestProjects()') ||
|
||||||
|
rootConfig.includes('getJestProjectsAsync()')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +38,12 @@ function isUsingUtilityFunction(host: Tree) {
|
|||||||
* in that case we do not need to edit it to remove it
|
* in that case we do not need to edit it to remove it
|
||||||
**/
|
**/
|
||||||
function isMonorepoConfig(tree: Tree) {
|
function isMonorepoConfig(tree: Tree) {
|
||||||
return tree.read('jest.config.ts', 'utf-8').includes('projects:');
|
const rootConfigPath = findRootJestConfig(tree);
|
||||||
|
if (!rootConfigPath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree.read(rootConfigPath, 'utf-8').includes('projects:');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,8 +66,10 @@ export function updateJestConfig(
|
|||||||
} = tsModule;
|
} = tsModule;
|
||||||
const projectToRemove = schema.projectName;
|
const projectToRemove = schema.projectName;
|
||||||
|
|
||||||
|
const rootConfigPath = findRootJestConfig(tree);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!tree.exists('jest.config.ts') ||
|
!tree.exists(rootConfigPath) ||
|
||||||
!tree.exists(join(projectConfig.root, 'jest.config.ts')) ||
|
!tree.exists(join(projectConfig.root, 'jest.config.ts')) ||
|
||||||
isUsingUtilityFunction(tree) ||
|
isUsingUtilityFunction(tree) ||
|
||||||
!isMonorepoConfig(tree)
|
!isMonorepoConfig(tree)
|
||||||
@ -59,9 +77,9 @@ export function updateJestConfig(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contents = tree.read('jest.config.ts', 'utf-8');
|
const contents = tree.read(rootConfigPath, 'utf-8');
|
||||||
const sourceFile = createSourceFile(
|
const sourceFile = createSourceFile(
|
||||||
'jest.config.ts',
|
rootConfigPath,
|
||||||
contents,
|
contents,
|
||||||
ScriptTarget.Latest
|
ScriptTarget.Latest
|
||||||
);
|
);
|
||||||
@ -104,7 +122,7 @@ export function updateJestConfig(
|
|||||||
: project.getStart(sourceFile);
|
: project.getStart(sourceFile);
|
||||||
|
|
||||||
tree.write(
|
tree.write(
|
||||||
'jest.config.ts',
|
rootConfigPath,
|
||||||
applyChangesToString(contents, [
|
applyChangesToString(contents, [
|
||||||
{
|
{
|
||||||
type: ChangeType.Delete,
|
type: ChangeType.Delete,
|
||||||
|
|||||||
13
packages/workspace/src/generators/utils/jest-config.ts
Normal file
13
packages/workspace/src/generators/utils/jest-config.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { 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;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user