feat(testing): support root project generation for jest (#13353)
Co-authored-by: Miroslav Jonas <missing.manual@gmail.com>
This commit is contained in:
parent
8f2fb24605
commit
74bd0bb00c
@ -41,6 +41,12 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Use JavaScript instead of TypeScript for config files"
|
"description": "Use JavaScript instead of TypeScript for config files"
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"description": "initialize Jest for an application at the root of the workspace",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [],
|
"required": [],
|
||||||
@ -123,6 +129,12 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Use JavaScript instead of TypeScript for config files"
|
"description": "Use JavaScript instead of TypeScript for config files"
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"description": "Add Jest to an application at the root of the workspace",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [],
|
"required": [],
|
||||||
|
|||||||
73
e2e/jest/src/jest-root.test.ts
Normal file
73
e2e/jest/src/jest-root.test.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { newProject, runCLI, uniq, runCLIAsync } from '@nrwl/e2e/utils';
|
||||||
|
|
||||||
|
describe('Jest root projects', () => {
|
||||||
|
const myapp = uniq('myapp');
|
||||||
|
const mylib = uniq('mylib');
|
||||||
|
|
||||||
|
describe('angular', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
newProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test root level app projects', async () => {
|
||||||
|
runCLI(`generate @nrwl/angular:app ${myapp} --rootProject=true`);
|
||||||
|
|
||||||
|
const rootProjectTestResults = await runCLIAsync(`test ${myapp}`);
|
||||||
|
|
||||||
|
expect(rootProjectTestResults.combinedOutput).toContain(
|
||||||
|
'Test Suites: 1 passed, 1 total'
|
||||||
|
);
|
||||||
|
}, 300_000);
|
||||||
|
|
||||||
|
it('should add lib project and tests should still work', async () => {
|
||||||
|
runCLI(`generate @nrwl/angular:lib ${mylib}`);
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:component ${mylib} --export --standalone --project=${mylib} --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
const libProjectTestResults = await runCLIAsync(`test ${mylib}`);
|
||||||
|
|
||||||
|
expect(libProjectTestResults.combinedOutput).toContain(
|
||||||
|
'Test Suites: 1 passed, 1 total'
|
||||||
|
);
|
||||||
|
|
||||||
|
const rootProjectTestResults = await runCLIAsync(`test ${myapp}`);
|
||||||
|
|
||||||
|
expect(rootProjectTestResults.combinedOutput).toContain(
|
||||||
|
'Test Suites: 1 passed, 1 total'
|
||||||
|
);
|
||||||
|
}, 300_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('react', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
newProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test root level app projects', async () => {
|
||||||
|
runCLI(`generate @nrwl/react:app ${myapp} --rootProject=true`);
|
||||||
|
|
||||||
|
const rootProjectTestResults = await runCLIAsync(`test ${myapp}`);
|
||||||
|
|
||||||
|
expect(rootProjectTestResults.combinedOutput).toContain(
|
||||||
|
'Test Suites: 1 passed, 1 total'
|
||||||
|
);
|
||||||
|
}, 300_000);
|
||||||
|
|
||||||
|
it('should add lib project and tests should still work', async () => {
|
||||||
|
runCLI(`generate @nrwl/react:lib ${mylib}`);
|
||||||
|
|
||||||
|
const libProjectTestResults = await runCLIAsync(`test ${mylib}`);
|
||||||
|
|
||||||
|
expect(libProjectTestResults.combinedOutput).toContain(
|
||||||
|
'Test Suites: 1 passed, 1 total'
|
||||||
|
);
|
||||||
|
|
||||||
|
const rootProjectTestResults = await runCLIAsync(`test ${myapp}`);
|
||||||
|
|
||||||
|
expect(rootProjectTestResults.combinedOutput).toContain(
|
||||||
|
'Test Suites: 1 passed, 1 total'
|
||||||
|
);
|
||||||
|
}, 300_000);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -4,9 +4,12 @@ import {
|
|||||||
runCLI,
|
runCLI,
|
||||||
runCLIAsync,
|
runCLIAsync,
|
||||||
uniq,
|
uniq,
|
||||||
|
readJson,
|
||||||
updateFile,
|
updateFile,
|
||||||
expectJestTestsToPass,
|
expectJestTestsToPass,
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
|
readFile,
|
||||||
|
checkFilesExist,
|
||||||
} from '@nrwl/e2e/utils';
|
} from '@nrwl/e2e/utils';
|
||||||
|
|
||||||
describe('Jest', () => {
|
describe('Jest', () => {
|
||||||
|
|||||||
@ -473,9 +473,6 @@ export function tslibC(): string {
|
|||||||
'plugin:@nrwl/nx/javascript',
|
'plugin:@nrwl/nx/javascript',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log(JSON.stringify(rootEslint, null, 2));
|
|
||||||
console.log(JSON.stringify(e2eEslint, null, 2));
|
|
||||||
|
|
||||||
runCLI(`generate @nrwl/react:lib ${mylib}`);
|
runCLI(`generate @nrwl/react:lib ${mylib}`);
|
||||||
// should add new tslint
|
// should add new tslint
|
||||||
expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow();
|
expect(() => checkFilesExist(`.eslintrc.base.json`)).not.toThrow();
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export async function addUnitTestRunner(host: Tree, options: NormalizedSchema) {
|
|||||||
supportTsx: false,
|
supportTsx: false,
|
||||||
skipSerializers: false,
|
skipSerializers: false,
|
||||||
skipPackageJson: options.skipPackageJson,
|
skipPackageJson: options.skipPackageJson,
|
||||||
|
rootProject: options.rootProject,
|
||||||
});
|
});
|
||||||
} else if (options.unitTestRunner === UnitTestRunner.Karma) {
|
} else if (options.unitTestRunner === UnitTestRunner.Karma) {
|
||||||
await karmaProjectGenerator(host, {
|
await karmaProjectGenerator(host, {
|
||||||
|
|||||||
@ -5,4 +5,7 @@ export {
|
|||||||
export { jestConfigObjectAst } from './src/utils/config/functions';
|
export { jestConfigObjectAst } from './src/utils/config/functions';
|
||||||
export { jestProjectGenerator } from './src/generators/jest-project/jest-project';
|
export { jestProjectGenerator } from './src/generators/jest-project/jest-project';
|
||||||
export { jestInitGenerator } from './src/generators/init/init';
|
export { jestInitGenerator } from './src/generators/init/init';
|
||||||
export { getJestProjects } from './src/utils/config/get-jest-projects';
|
export {
|
||||||
|
getJestProjects,
|
||||||
|
getNestedJestProjects,
|
||||||
|
} from './src/utils/config/get-jest-projects';
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
NxJsonConfiguration,
|
NxJsonConfiguration,
|
||||||
readJson,
|
readJson,
|
||||||
|
readProjectConfiguration,
|
||||||
stripIndents,
|
stripIndents,
|
||||||
Tree,
|
Tree,
|
||||||
updateJson,
|
updateJson,
|
||||||
@ -41,9 +43,29 @@ describe('jest', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not override existing files', async () => {
|
it('should not override existing files', async () => {
|
||||||
tree.write('jest.config.ts', `test`);
|
addProjectConfiguration(tree, 'my-project', {
|
||||||
|
root: 'apps/my-app',
|
||||||
|
name: 'my-app',
|
||||||
|
sourceRoot: 'apps/my-app/src',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: '@nrwl/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'apps/my-app/jest.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expected = stripIndents`
|
||||||
|
import { getJestProjects } from '@nrwl/jest';
|
||||||
|
export default {
|
||||||
|
projects: getJestProjects(),
|
||||||
|
extraThing: "Goes Here"
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
tree.write('jest.config.ts', expected);
|
||||||
jestInitGenerator(tree, {});
|
jestInitGenerator(tree, {});
|
||||||
expect(tree.read('jest.config.ts', 'utf-8')).toEqual('test');
|
expect(tree.read('jest.config.ts', 'utf-8')).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add target defaults for test', async () => {
|
it('should add target defaults for test', async () => {
|
||||||
@ -144,6 +166,102 @@ describe('jest', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('root project', () => {
|
||||||
|
it('should not add a monorepo jest.config.ts to the project', () => {
|
||||||
|
jestInitGenerator(tree, { rootProject: true });
|
||||||
|
expect(tree.exists('jest.config.ts')).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should rename the project jest.config.ts to project jest config', () => {
|
||||||
|
addProjectConfiguration(tree, 'my-project', {
|
||||||
|
root: '.',
|
||||||
|
name: 'my-project',
|
||||||
|
sourceRoot: 'src',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: '@nrwl/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'jest.config.ts',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
tree.write(
|
||||||
|
'jest.config.ts',
|
||||||
|
`
|
||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||||
|
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||||
|
displayName: 'my-project',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
preset: './jest.preset.js',
|
||||||
|
};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
jestInitGenerator(tree, { rootProject: false });
|
||||||
|
expect(tree.exists('jest.config.app.ts')).toBeTruthy();
|
||||||
|
expect(tree.read('jest.config.ts', 'utf-8'))
|
||||||
|
.toEqual(`import { getJestProjects } from '@nrwl/jest';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
projects: getJestProjects()
|
||||||
|
};`);
|
||||||
|
expect(readProjectConfiguration(tree, 'my-project').targets.test)
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"executor": "@nrwl/jest:jest",
|
||||||
|
"options": Object {
|
||||||
|
"jestConfig": "jest.config.app.ts",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with --js', () => {
|
||||||
|
addProjectConfiguration(tree, 'my-project', {
|
||||||
|
root: '.',
|
||||||
|
name: 'my-project',
|
||||||
|
sourceRoot: 'src',
|
||||||
|
targets: {
|
||||||
|
test: {
|
||||||
|
executor: '@nrwl/jest:jest',
|
||||||
|
options: {
|
||||||
|
jestConfig: 'jest.config.js',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
tree.write(
|
||||||
|
'jest.config.js',
|
||||||
|
`
|
||||||
|
/* eslint-disable */
|
||||||
|
module.exports = {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||||
|
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
|
||||||
|
displayName: 'my-project',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
preset: './jest.preset.js',
|
||||||
|
};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
jestInitGenerator(tree, { js: true, rootProject: false });
|
||||||
|
expect(tree.exists('jest.config.app.js')).toBeTruthy();
|
||||||
|
expect(tree.read('jest.config.js', 'utf-8'))
|
||||||
|
.toEqual(`const { getJestProjects } = require('@nrwl/jest');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
projects: getJestProjects()
|
||||||
|
};`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('adds jest extension', () => {
|
describe('adds jest extension', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
writeJson(tree, '.vscode/extensions.json', {
|
writeJson(tree, '.vscode/extensions.json', {
|
||||||
|
|||||||
@ -8,7 +8,10 @@ import {
|
|||||||
Tree,
|
Tree,
|
||||||
updateJson,
|
updateJson,
|
||||||
updateWorkspaceConfiguration,
|
updateWorkspaceConfiguration,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
getProjects,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
|
import { findRootJestConfig } from '../../utils/config/find-root-jest-files';
|
||||||
import {
|
import {
|
||||||
babelJestVersion,
|
babelJestVersion,
|
||||||
jestTypesVersion,
|
jestTypesVersion,
|
||||||
@ -18,6 +21,7 @@ import {
|
|||||||
tsJestVersion,
|
tsJestVersion,
|
||||||
tslibVersion,
|
tslibVersion,
|
||||||
tsNodeVersion,
|
tsNodeVersion,
|
||||||
|
typesNodeVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { JestInitSchema } from './schema';
|
import { JestInitSchema } from './schema';
|
||||||
|
|
||||||
@ -26,12 +30,11 @@ interface NormalizedSchema extends ReturnType<typeof normalizeOptions> {}
|
|||||||
const schemaDefaults = {
|
const schemaDefaults = {
|
||||||
compiler: 'tsc',
|
compiler: 'tsc',
|
||||||
js: false,
|
js: false,
|
||||||
|
rootProject: false,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
function createJestConfig(tree: Tree, js: boolean = false) {
|
function generateGlobalConfig(tree: Tree, isJS: boolean) {
|
||||||
// if the root ts config already exists then don't make a js one or vice versa
|
const contents = isJS
|
||||||
if (!tree.exists('jest.config.ts') && !tree.exists('jest.config.js')) {
|
|
||||||
const contents = js
|
|
||||||
? stripIndents`
|
? stripIndents`
|
||||||
const { getJestProjects } = require('@nrwl/jest');
|
const { getJestProjects } = require('@nrwl/jest');
|
||||||
|
|
||||||
@ -44,9 +47,10 @@ function createJestConfig(tree: Tree, js: boolean = false) {
|
|||||||
export default {
|
export default {
|
||||||
projects: getJestProjects()
|
projects: getJestProjects()
|
||||||
};`;
|
};`;
|
||||||
tree.write(`jest.config.${js ? 'js' : 'ts'}`, contents);
|
tree.write(`jest.config.${isJS ? 'js' : 'ts'}`, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createJestConfig(tree: Tree, options: NormalizedSchema) {
|
||||||
if (!tree.exists('jest.preset.js')) {
|
if (!tree.exists('jest.preset.js')) {
|
||||||
// preset is always js file.
|
// preset is always js file.
|
||||||
tree.write(
|
tree.write(
|
||||||
@ -59,6 +63,48 @@ function createJestConfig(tree: Tree, js: boolean = false) {
|
|||||||
|
|
||||||
addTestInputs(tree);
|
addTestInputs(tree);
|
||||||
}
|
}
|
||||||
|
if (options.rootProject) {
|
||||||
|
// we don't want any config to be made because the `jestProjectGenerator`
|
||||||
|
// will copy the template config file
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rootJestPath = findRootJestConfig(tree);
|
||||||
|
if (!rootJestPath) {
|
||||||
|
// if there's not root jest config, we will create one and return
|
||||||
|
// this can happen when:
|
||||||
|
// - root jest config was renamed => in which case there is migration needed
|
||||||
|
// - root project didn't have jest setup => again, no migration is needed
|
||||||
|
generateGlobalConfig(tree, options.js);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.exists(rootJestPath)) {
|
||||||
|
// moving from root project config to monorepo-style config
|
||||||
|
const projects = getProjects(tree);
|
||||||
|
const projectNames = Array.from(projects.keys());
|
||||||
|
const rootProject = projectNames.find(
|
||||||
|
(projectName) => projects.get(projectName)?.root === '.'
|
||||||
|
);
|
||||||
|
// root project might have been removed,
|
||||||
|
// if it's missing there's nothing to migrate
|
||||||
|
if (rootProject) {
|
||||||
|
const rootProjectConfig = projects.get(rootProject);
|
||||||
|
const jestTarget = Object.values(rootProjectConfig.targets || {}).find(
|
||||||
|
(t) => t?.executor === '@nrwl/jest:jest'
|
||||||
|
);
|
||||||
|
const isProjectConfig = jestTarget?.options?.jestConfig === rootJestPath;
|
||||||
|
// if root project doesn't have jest target, there's nothing to migrate
|
||||||
|
if (isProjectConfig) {
|
||||||
|
const jestAppConfig = `jest.config.app.${options.js ? 'js' : 'ts'}`;
|
||||||
|
|
||||||
|
tree.rename(rootJestPath, jestAppConfig);
|
||||||
|
jestTarget.options.jestConfig = jestAppConfig;
|
||||||
|
updateProjectConfiguration(tree, rootProject, rootProjectConfig);
|
||||||
|
}
|
||||||
|
// generate new global config as it was move to project config or is missing
|
||||||
|
generateGlobalConfig(tree, options.js);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTestInputs(tree: Tree) {
|
function addTestInputs(tree: Tree) {
|
||||||
@ -113,7 +159,7 @@ function updateDependencies(tree: Tree, options: NormalizedSchema) {
|
|||||||
if (!options.js) {
|
if (!options.js) {
|
||||||
devDeps['ts-node'] = tsNodeVersion;
|
devDeps['ts-node'] = tsNodeVersion;
|
||||||
devDeps['@types/jest'] = jestTypesVersion;
|
devDeps['@types/jest'] = jestTypesVersion;
|
||||||
devDeps['@types/node'] = '16.11.7';
|
devDeps['@types/node'] = typesNodeVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.compiler === 'babel' || options.babelJest) {
|
if (options.compiler === 'babel' || options.babelJest) {
|
||||||
@ -144,7 +190,7 @@ function updateExtensions(host: Tree) {
|
|||||||
|
|
||||||
export function jestInitGenerator(tree: Tree, schema: JestInitSchema) {
|
export function jestInitGenerator(tree: Tree, schema: JestInitSchema) {
|
||||||
const options = normalizeOptions(schema);
|
const options = normalizeOptions(schema);
|
||||||
createJestConfig(tree, options.js);
|
createJestConfig(tree, options);
|
||||||
|
|
||||||
let installTask: GeneratorCallback = () => {};
|
let installTask: GeneratorCallback = () => {};
|
||||||
if (!options.skipPackageJson) {
|
if (!options.skipPackageJson) {
|
||||||
|
|||||||
@ -6,4 +6,5 @@ export interface JestInitSchema {
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
babelJest?: boolean;
|
babelJest?: boolean;
|
||||||
|
rootProject?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,12 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Use JavaScript instead of TypeScript for config files"
|
"description": "Use JavaScript instead of TypeScript for config files"
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"description": "initialize Jest for an application at the root of the workspace",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|||||||
@ -19,5 +19,9 @@
|
|||||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||||
'jest-preset-angular/build/serializers/html-comment',
|
'jest-preset-angular/build/serializers/html-comment',
|
||||||
]<% } %>
|
]<% } %><% if(rootProject){ %>,
|
||||||
|
testMatch: [
|
||||||
|
'<rootDir>/src/**/__tests__/**/*.[jt]s?(x)',
|
||||||
|
'<rootDir>/src/**/?(*.)+(spec|test).[jt]s?(x)',
|
||||||
|
],<% } %>
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,5 +13,9 @@
|
|||||||
<% if (supportTsx){ %>'^.+\\.[tj]sx?$'<% } else { %>'^.+\\.[tj]s$'<% } %>: <% if (supportTsx && transformer === '@swc/jest') { %>['<%= transformer %>', { jsc: { transform: { react: { runtime: 'automatic' } } } }]<% } else { %>'<%= transformer %>'<% } %>
|
<% if (supportTsx){ %>'^.+\\.[tj]sx?$'<% } else { %>'^.+\\.[tj]s$'<% } %>: <% if (supportTsx && transformer === '@swc/jest') { %>['<%= transformer %>', { jsc: { transform: { react: { runtime: 'automatic' } } } }]<% } else { %>'<%= transformer %>'<% } %>
|
||||||
},
|
},
|
||||||
<% if (supportTsx) { %>moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],<% } else { %>moduleFileExtensions: ['ts', 'js', 'html'],<% } %><% } %>
|
<% if (supportTsx) { %>moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],<% } else { %>moduleFileExtensions: ['ts', 'js', 'html'],<% } %><% } %>
|
||||||
coverageDirectory: '<%= offsetFromRoot %>coverage/<%= projectRoot %>'
|
coverageDirectory: '<%= offsetFromRoot %>coverage/<%= projectRoot %>'<% if(rootProject){ %>,
|
||||||
|
testMatch: [
|
||||||
|
'<rootDir>/src/**/__tests__/**/*.[jt]s?(x)',
|
||||||
|
'<rootDir>/src/**/?(*.)+(spec|test).[jt]s?(x)',
|
||||||
|
],<% } %>
|
||||||
};
|
};
|
||||||
|
|||||||
@ -362,4 +362,81 @@ describe('jestProject', () => {
|
|||||||
expect(tree.read('libs/lib1/jest.config.ts', 'utf-8')).toMatchSnapshot();
|
expect(tree.read('libs/lib1/jest.config.ts', 'utf-8')).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('root project', () => {
|
||||||
|
it('root jest.config.ts should be project config', async () => {
|
||||||
|
writeJson(tree, 'tsconfig.json', {
|
||||||
|
files: [],
|
||||||
|
include: [],
|
||||||
|
references: [],
|
||||||
|
});
|
||||||
|
addProjectConfiguration(tree, 'my-project', {
|
||||||
|
root: '',
|
||||||
|
sourceRoot: 'src',
|
||||||
|
name: 'my-project',
|
||||||
|
targets: {},
|
||||||
|
});
|
||||||
|
await jestProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
project: 'my-project',
|
||||||
|
rootProject: true,
|
||||||
|
});
|
||||||
|
expect(tree.read('jest.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'my-project',
|
||||||
|
preset: '../jest.preset.js',
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
coverageDirectory: '../coverage/my-project',
|
||||||
|
testMatch: [
|
||||||
|
'<rootDir>/src/**/__tests__/**/*.[jt]s?(x)',
|
||||||
|
'<rootDir>/src/**/?(*.)+(spec|test).[jt]s?(x)',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('root jest.config.js should be project config', async () => {
|
||||||
|
writeJson(tree, 'tsconfig.json', {
|
||||||
|
files: [],
|
||||||
|
include: [],
|
||||||
|
references: [],
|
||||||
|
});
|
||||||
|
addProjectConfiguration(tree, 'my-project', {
|
||||||
|
root: '',
|
||||||
|
sourceRoot: 'src',
|
||||||
|
name: 'my-project',
|
||||||
|
targets: {},
|
||||||
|
});
|
||||||
|
await jestProjectGenerator(tree, {
|
||||||
|
...defaultOptions,
|
||||||
|
project: 'my-project',
|
||||||
|
rootProject: true,
|
||||||
|
js: true,
|
||||||
|
});
|
||||||
|
expect(tree.read('jest.config.js', 'utf-8')).toMatchInlineSnapshot(`
|
||||||
|
"/* eslint-disable */
|
||||||
|
module.exports = {
|
||||||
|
displayName: 'my-project',
|
||||||
|
preset: '../jest.preset.js',
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
coverageDirectory: '../coverage/my-project',
|
||||||
|
testMatch: [
|
||||||
|
'<rootDir>/src/**/__tests__/**/*.[jt]s?(x)',
|
||||||
|
'<rootDir>/src/**/?(*.)+(spec|test).[jt]s?(x)',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const schemaDefaults = {
|
|||||||
supportTsx: false,
|
supportTsx: false,
|
||||||
skipSetupFile: false,
|
skipSetupFile: false,
|
||||||
skipSerializers: false,
|
skipSerializers: false,
|
||||||
|
rootProject: false,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
function normalizeOptions(options: JestProjectSchema) {
|
function normalizeOptions(options: JestProjectSchema) {
|
||||||
@ -42,7 +43,6 @@ function normalizeOptions(options: JestProjectSchema) {
|
|||||||
|
|
||||||
// setupFile is always 'none'
|
// setupFile is always 'none'
|
||||||
options.setupFile = schemaDefaults.setupFile;
|
options.setupFile = schemaDefaults.setupFile;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...schemaDefaults,
|
...schemaDefaults,
|
||||||
...options,
|
...options,
|
||||||
@ -55,11 +55,12 @@ export async function jestProjectGenerator(
|
|||||||
) {
|
) {
|
||||||
const options = normalizeOptions(schema);
|
const options = normalizeOptions(schema);
|
||||||
const installTask = init(tree, options);
|
const installTask = init(tree, options);
|
||||||
|
|
||||||
checkForTestTarget(tree, options);
|
checkForTestTarget(tree, options);
|
||||||
createFiles(tree, options);
|
createFiles(tree, options);
|
||||||
updateTsConfig(tree, options);
|
updateTsConfig(tree, options);
|
||||||
updateWorkspace(tree, options);
|
updateWorkspace(tree, options);
|
||||||
updateJestConfig(tree, options);
|
|
||||||
if (!schema.skipFormat) {
|
if (!schema.skipFormat) {
|
||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { JestProjectSchema } from '../schema';
|
|||||||
|
|
||||||
export function checkForTestTarget(tree: Tree, options: JestProjectSchema) {
|
export function checkForTestTarget(tree: Tree, options: JestProjectSchema) {
|
||||||
const projectConfig = readProjectConfiguration(tree, options.project);
|
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||||
if (projectConfig.targets.test) {
|
if (projectConfig?.targets?.test) {
|
||||||
throw new Error(`${options.project}: already has a test architect option.`);
|
throw new Error(`${options.project}: already has a test target set.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,8 @@ export function createFiles(tree: Tree, options: JestProjectSchema) {
|
|||||||
...options,
|
...options,
|
||||||
transformer,
|
transformer,
|
||||||
js: !!options.js,
|
js: !!options.js,
|
||||||
projectRoot: projectConfig.root,
|
rootProject: options.rootProject,
|
||||||
|
projectRoot: options.rootProject ? options.project : projectConfig.root,
|
||||||
offsetFromRoot: offsetFromRoot(projectConfig.root),
|
offsetFromRoot: offsetFromRoot(projectConfig.root),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,10 +4,10 @@ import { addPropertyToJestConfig } from '../../../utils/config/update-config';
|
|||||||
import { readProjectConfiguration, Tree } from '@nrwl/devkit';
|
import { readProjectConfiguration, Tree } from '@nrwl/devkit';
|
||||||
|
|
||||||
function isUsingUtilityFunction(host: Tree) {
|
function isUsingUtilityFunction(host: Tree) {
|
||||||
return host
|
const rootConfig = findRootJestConfig(host);
|
||||||
.read(findRootJestConfig(host))
|
return (
|
||||||
.toString()
|
rootConfig && host.read(rootConfig).toString().includes('getJestProjects()')
|
||||||
.includes('getJestProjects()');
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateJestConfig(host: Tree, options: JestProjectSchema) {
|
export function updateJestConfig(host: Tree, options: JestProjectSchema) {
|
||||||
@ -15,10 +15,13 @@ export function updateJestConfig(host: Tree, options: JestProjectSchema) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const project = readProjectConfiguration(host, options.project);
|
const project = readProjectConfiguration(host, options.project);
|
||||||
|
const rootConfig = findRootJestConfig(host);
|
||||||
|
if (rootConfig) {
|
||||||
addPropertyToJestConfig(
|
addPropertyToJestConfig(
|
||||||
host,
|
host,
|
||||||
findRootJestConfig(host),
|
findRootJestConfig(host),
|
||||||
'projects',
|
'projects',
|
||||||
`<rootDir>/${project.root}`
|
`<rootDir>/${project.root}`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,4 +16,5 @@ export interface JestProjectSchema {
|
|||||||
compiler?: 'tsc' | 'babel' | 'swc';
|
compiler?: 'tsc' | 'babel' | 'swc';
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
|
rootProject?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,6 +68,12 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Use JavaScript instead of TypeScript for config files"
|
"description": "Use JavaScript instead of TypeScript for config files"
|
||||||
|
},
|
||||||
|
"rootProject": {
|
||||||
|
"description": "Add Jest to an application at the root of the workspace",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|||||||
@ -41,3 +41,17 @@ export function getJestProjects() {
|
|||||||
}
|
}
|
||||||
return Array.from(jestConfigurationSet);
|
return Array.from(jestConfigurationSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of nested projects that have jest configured
|
||||||
|
* to be used in the testPathIgnorePatterns property of a given jest config
|
||||||
|
* https://jestjs.io/docs/configuration#testpathignorepatterns-arraystring
|
||||||
|
* */
|
||||||
|
export function getNestedJestProjects() {
|
||||||
|
// TODO(caleb): get current project path and list of all projects and their rootDir
|
||||||
|
// return a list of all projects that are nested in the current projects path
|
||||||
|
// always include node_modules as that's the default
|
||||||
|
|
||||||
|
const allProjects = getJestProjects();
|
||||||
|
return ['/node_modules/'];
|
||||||
|
}
|
||||||
|
|||||||
@ -14,5 +14,6 @@ export async function addJest(host: Tree, options: NormalizedSchema) {
|
|||||||
skipSerializers: true,
|
skipSerializers: true,
|
||||||
setupFile: 'none',
|
setupFile: 'none',
|
||||||
compiler: options.compiler,
|
compiler: options.compiler,
|
||||||
|
rootProject: options.rootProject,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import {
|
|||||||
Tree,
|
Tree,
|
||||||
updateWorkspaceConfiguration,
|
updateWorkspaceConfiguration,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { jestInitGenerator } from '@nrwl/jest';
|
|
||||||
import { webInitGenerator } from '@nrwl/web';
|
import { webInitGenerator } from '@nrwl/web';
|
||||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||||
import {
|
import {
|
||||||
@ -71,10 +70,6 @@ export async function reactInitGenerator(host: Tree, schema: InitSchema) {
|
|||||||
|
|
||||||
setDefault(host);
|
setDefault(host);
|
||||||
|
|
||||||
if (!schema.unitTestRunner || schema.unitTestRunner === 'jest') {
|
|
||||||
const jestTask = jestInitGenerator(host, schema);
|
|
||||||
tasks.push(jestTask);
|
|
||||||
}
|
|
||||||
if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') {
|
if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') {
|
||||||
const cypressTask = cypressInitGenerator(host, {});
|
const cypressTask = cypressInitGenerator(host, {});
|
||||||
tasks.push(cypressTask);
|
tasks.push(cypressTask);
|
||||||
|
|||||||
@ -44,7 +44,6 @@ async function createPreset(tree: Tree, options: Schema) {
|
|||||||
name: options.name,
|
name: options.name,
|
||||||
style: options.style,
|
style: options.style,
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
unitTestRunner: 'none',
|
|
||||||
standaloneConfig: options.standaloneConfig,
|
standaloneConfig: options.standaloneConfig,
|
||||||
rootProject: true,
|
rootProject: true,
|
||||||
});
|
});
|
||||||
@ -68,7 +67,6 @@ async function createPreset(tree: Tree, options: Schema) {
|
|||||||
name: options.name,
|
name: options.name,
|
||||||
style: options.style,
|
style: options.style,
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
unitTestRunner: 'none',
|
|
||||||
standaloneConfig: options.standaloneConfig,
|
standaloneConfig: options.standaloneConfig,
|
||||||
rootProject: true,
|
rootProject: true,
|
||||||
bundler: 'vite',
|
bundler: 'vite',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user