Skip to content

Commit 5a547cc

Browse files
FrozenPandazvsavkin
authored andcommittedAug 29, 2018
feat(schematics): add schematics for jest
1 parent e608211 commit 5a547cc

22 files changed

+608
-50
lines changed
 

‎.prettierignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
tmp
22
build
3-
node_modules
3+
node_modules
4+
packages/schematics/src/collection/**/files/*.json

‎e2e/schematics/jest.test.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import {
2+
newProject,
3+
runCLI,
4+
newLib,
5+
copyMissingPackages,
6+
updateFile,
7+
readJson,
8+
runCommand,
9+
runCLIAsync
10+
} from '../utils';
11+
12+
describe('Jest', () => {
13+
beforeAll(() => {
14+
newProject();
15+
runCLI('generate jest', {
16+
silenceError: true
17+
});
18+
// TODO: remove this hack after there's a version of @nrwl/builders published
19+
const packageJson = readJson('package.json');
20+
packageJson.devDependencies['@nrwl/builders'] =
21+
'../../build/packages/builders';
22+
updateFile('package.json', JSON.stringify(packageJson));
23+
runCommand('npm install');
24+
copyMissingPackages();
25+
});
26+
27+
it(
28+
'should be able to generate a testable library using jest',
29+
async done => {
30+
newLib('jestlib --unit-test-runner jest');
31+
await Promise.all([
32+
runCLIAsync('generate service test --project jestlib'),
33+
runCLIAsync('generate component test --project jestlib')
34+
]);
35+
const jestResult = await runCLIAsync('test jestlib');
36+
expect(jestResult.stderr).toContain('Test Suites: 3 passed, 3 total');
37+
done();
38+
},
39+
10000
40+
);
41+
});

‎e2e/utils.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { execSync } from 'child_process';
1+
import { execSync, exec } from 'child_process';
22
import { readFileSync, statSync, writeFileSync } from 'fs';
33
import * as path from 'path';
44

@@ -84,6 +84,37 @@ function copyNodeModule(path: string, name: string) {
8484
execSync(`cp -a node_modules/${name} tmp/${path}/node_modules/${name}`);
8585
}
8686

87+
export function runCommandAsync(
88+
command: string,
89+
opts = {
90+
silenceError: false
91+
}
92+
): Promise<{ stdout: string; stderr: string }> {
93+
return new Promise((resolve, reject) => {
94+
exec(
95+
command,
96+
{
97+
cwd: `./tmp/proj`
98+
},
99+
(err, stdout, stderr) => {
100+
if (!opts.silenceError && err) {
101+
reject(err);
102+
}
103+
resolve({ stdout, stderr });
104+
}
105+
);
106+
});
107+
}
108+
109+
export function runCLIAsync(
110+
command: string,
111+
opts = {
112+
silenceError: false
113+
}
114+
): Promise<{ stdout: string; stderr: string }> {
115+
return runCommandAsync(`./node_modules/.bin/ng ${command}`, opts);
116+
}
117+
87118
export function runCLI(
88119
command?: string,
89120
opts = {

‎packages/schematics/src/collection.json

+13
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@
3636
"description": "Add NgRx support to a module"
3737
},
3838

39+
"jest": {
40+
"factory": "./collection/jest",
41+
"schema": "./collection/jest/schema.json",
42+
"description": "Add Jest configuration to the workspace"
43+
},
44+
45+
"jest-project": {
46+
"factory": "./collection/jest-project",
47+
"schema": "./collection/jest-project/schema.json",
48+
"description": "Add Jest configuration to a project",
49+
"hidden": true
50+
},
51+
3952
"upgrade-module": {
4053
"factory": "./collection/upgrade-module",
4154
"schema": "./collection/upgrade-module/schema.json",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
name: '<%= project %>',
3+
preset: '<%= offsetFromRoot %>jest.config.js',
4+
coverageDirectory: '<%= offsetFromRoot %>coverage/<%= projectRoot %>'
5+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import 'jest-preset-angular';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "<%= offsetFromRoot %>tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "<%= offsetFromRoot %>dist/out-tsc/<%= projectRoot %>",
5+
"module": "commonjs",
6+
"types": ["jest", "node"]
7+
},
8+
"files": [<% if(!skipSetupFile) { %>"src/test-setup.ts", <% } %>"src/polyfills.ts"],
9+
"include": ["**/*.spec.ts", "**/*.d.ts"]
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
Rule,
3+
Tree,
4+
mergeWith,
5+
chain,
6+
url,
7+
apply,
8+
SchematicContext,
9+
move,
10+
template,
11+
noop,
12+
filter
13+
} from '@angular-devkit/schematics';
14+
import {
15+
getProjectConfig,
16+
readJsonInTree,
17+
updateJsonInTree
18+
} from '../../utils/ast-utils';
19+
import { offsetFromRoot } from '../../utils/common';
20+
import { join, normalize } from '@angular-devkit/core';
21+
22+
export interface JestProjectSchema {
23+
project: string;
24+
skipSetupFile: boolean;
25+
}
26+
27+
function generateFiles(options: JestProjectSchema): Rule {
28+
return (host, context) => {
29+
const projectConfig = getProjectConfig(host, options.project);
30+
return mergeWith(
31+
apply(url('./files'), [
32+
template({
33+
tmpl: '',
34+
...options,
35+
projectRoot: projectConfig.root,
36+
offsetFromRoot: offsetFromRoot(projectConfig.root)
37+
}),
38+
options.skipSetupFile
39+
? filter(file => file !== '/src/test-setup.ts')
40+
: noop(),
41+
move(projectConfig.root)
42+
])
43+
)(host, context);
44+
};
45+
}
46+
47+
function updateAngularJson(options: JestProjectSchema): Rule {
48+
return updateJsonInTree('angular.json', json => {
49+
const projectConfig = json.projects[options.project];
50+
projectConfig.architect.test = {
51+
builder: '@nrwl/builders:jest',
52+
options: {
53+
jestConfig: join(normalize(projectConfig.root), 'jest.config.js'),
54+
tsConfig: join(normalize(projectConfig.root), 'tsconfig.spec.json')
55+
}
56+
};
57+
if (!options.skipSetupFile) {
58+
projectConfig.architect.test.options.setupFile = join(
59+
normalize(projectConfig.root),
60+
'src/test-setup.ts'
61+
);
62+
}
63+
if (projectConfig.architect.lint) {
64+
projectConfig.architect.lint.options.tsConfig = [
65+
...projectConfig.architect.lint.options.tsConfig,
66+
join(normalize(projectConfig.root), 'tsconfig.spec.json')
67+
];
68+
}
69+
return json;
70+
});
71+
}
72+
73+
function check(options: JestProjectSchema) {
74+
return (host: Tree, context: SchematicContext) => {
75+
const projectConfig = getProjectConfig(host, options.project);
76+
if (projectConfig.architect.test) {
77+
throw new Error(
78+
`${options.project} already has a test architect option.`
79+
);
80+
}
81+
const packageJson = readJsonInTree(host, 'package.json');
82+
if (!packageJson.devDependencies.jest) {
83+
throw new Error(
84+
`Your workspace does not have jest installed. Please run "ng generate jest" to setup your workspace to run tests with jest.`
85+
);
86+
}
87+
};
88+
}
89+
90+
export default function(options: JestProjectSchema): Rule {
91+
return chain([
92+
check(options),
93+
generateFiles(options),
94+
updateAngularJson(options)
95+
]);
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
2+
import * as path from 'path';
3+
import { Tree, VirtualTree } from '@angular-devkit/schematics';
4+
import { createEmptyWorkspace } from '../../utils/testing-utils';
5+
import { readJsonInTree } from '@nrwl/schematics/src/utils/ast-utils';
6+
7+
describe('lib', () => {
8+
const schematicRunner = new SchematicTestRunner(
9+
'@nrwl/schematics',
10+
path.join(__dirname, '../../collection.json')
11+
);
12+
13+
let appTree: Tree;
14+
15+
beforeEach(() => {
16+
appTree = new VirtualTree();
17+
appTree = createEmptyWorkspace(appTree);
18+
appTree = schematicRunner.runSchematic('jest', {}, appTree);
19+
});
20+
21+
it('should generate files', () => {
22+
appTree = schematicRunner.runSchematic(
23+
'lib',
24+
{
25+
name: 'lib1',
26+
unitTestRunner: 'none'
27+
},
28+
appTree
29+
);
30+
const resultTree = schematicRunner.runSchematic(
31+
'jest-project',
32+
{
33+
project: 'lib1'
34+
},
35+
appTree
36+
);
37+
expect(resultTree.exists('/libs/lib1/src/test-setup.ts')).toBeTruthy();
38+
expect(resultTree.exists('/libs/lib1/jest.config.js')).toBeTruthy();
39+
expect(resultTree.exists('/libs/lib1/tsconfig.spec.json')).toBeTruthy();
40+
});
41+
42+
it('should alter angular.json', () => {
43+
appTree = schematicRunner.runSchematic(
44+
'lib',
45+
{
46+
name: 'lib1',
47+
unitTestRunner: 'none'
48+
},
49+
appTree
50+
);
51+
const resultTree = schematicRunner.runSchematic(
52+
'jest-project',
53+
{
54+
project: 'lib1'
55+
},
56+
appTree
57+
);
58+
const angularJson = readJsonInTree(resultTree, 'angular.json');
59+
expect(angularJson.projects.lib1.architect.test).toEqual({
60+
builder: '@nrwl/builders:jest',
61+
options: {
62+
jestConfig: 'libs/lib1/jest.config.js',
63+
setupFile: 'libs/lib1/src/test-setup.ts',
64+
tsConfig: 'libs/lib1/tsconfig.spec.json'
65+
}
66+
});
67+
expect(angularJson.projects.lib1.architect.lint.options.tsConfig).toContain(
68+
'libs/lib1/tsconfig.spec.json'
69+
);
70+
});
71+
72+
it('should create a tsconfig.spec.json', () => {
73+
appTree = schematicRunner.runSchematic(
74+
'lib',
75+
{
76+
name: 'lib1',
77+
unitTestRunner: 'none'
78+
},
79+
appTree
80+
);
81+
const resultTree = schematicRunner.runSchematic(
82+
'jest-project',
83+
{
84+
project: 'lib1'
85+
},
86+
appTree
87+
);
88+
const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.spec.json');
89+
expect(tsConfig).toEqual({
90+
extends: '../../tsconfig.json',
91+
compilerOptions: {
92+
module: 'commonjs',
93+
outDir: '../../dist/out-tsc/libs/lib1',
94+
types: ['jest', 'node']
95+
},
96+
files: ['src/test-setup.ts', 'src/polyfills.ts'],
97+
include: ['**/*.spec.ts', '**/*.d.ts']
98+
});
99+
});
100+
101+
describe('--skip-setup-file', () => {
102+
it('should generate src/test-setup.ts', () => {
103+
appTree = schematicRunner.runSchematic(
104+
'lib',
105+
{
106+
name: 'lib1',
107+
unitTestRunner: 'none'
108+
},
109+
appTree
110+
);
111+
const resultTree = schematicRunner.runSchematic(
112+
'jest-project',
113+
{
114+
project: 'lib1',
115+
skipSetupFile: true
116+
},
117+
appTree
118+
);
119+
expect(resultTree.exists('src/test-setup.ts')).toBeFalsy();
120+
});
121+
122+
it('should not list the setup file in angular.json', () => {
123+
appTree = schematicRunner.runSchematic(
124+
'lib',
125+
{
126+
name: 'lib1',
127+
unitTestRunner: 'none'
128+
},
129+
appTree
130+
);
131+
const resultTree = schematicRunner.runSchematic(
132+
'jest-project',
133+
{
134+
project: 'lib1',
135+
skipSetupFile: true
136+
},
137+
appTree
138+
);
139+
const angularJson = readJsonInTree(resultTree, 'angular.json');
140+
expect(
141+
angularJson.projects.lib1.architect.test.options.setupFile
142+
).toBeUndefined();
143+
});
144+
145+
it('should not list the setup file in tsconfig.spec.json', () => {
146+
appTree = schematicRunner.runSchematic(
147+
'lib',
148+
{
149+
name: 'lib1',
150+
unitTestRunner: 'none'
151+
},
152+
appTree
153+
);
154+
const resultTree = schematicRunner.runSchematic(
155+
'jest-project',
156+
{
157+
project: 'lib1',
158+
skipSetupFile: true
159+
},
160+
appTree
161+
);
162+
const tsConfig = readJsonInTree(
163+
resultTree,
164+
'libs/lib1/tsconfig.spec.json'
165+
);
166+
expect(tsConfig.files).not.toContain('src/test-setup.ts');
167+
});
168+
});
169+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"id": "Nx Jest Project Schematics Schema",
4+
"title": "Create Jest Configuration for the workspace",
5+
"type": "object",
6+
"properties": {
7+
"project": {
8+
"type": "string",
9+
"description": "The name of the project.",
10+
"$default": {
11+
"$source": "projectName"
12+
}
13+
},
14+
"skipSetupFile": {
15+
"type": "boolean",
16+
"description": "Skips the setup file required for angular",
17+
"default": false
18+
}
19+
},
20+
"required": []
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = {
2+
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
3+
transform: {
4+
'^.+\\.(ts|js|html)$': 'jest-preset-angular/preprocessor.js'
5+
},
6+
resolver: '@nrwl/builders/plugins/jest/resolver',
7+
moduleFileExtensions: ['ts', 'js', 'html'],
8+
collectCoverage: true,
9+
coverageReporters: ['html']
10+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
mergeWith,
3+
SchematicContext,
4+
chain,
5+
url
6+
} from '@angular-devkit/schematics';
7+
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
8+
import { updateJsonInTree } from '../../utils/ast-utils';
9+
import { jestVersion, nxVersion } from '../../lib-versions';
10+
import { Rule } from '@angular-devkit/schematics';
11+
12+
const updatePackageJson = updateJsonInTree('package.json', json => {
13+
json.devDependencies = {
14+
...json.devDependencies,
15+
'@nrwl/builders': nxVersion,
16+
jest: jestVersion,
17+
'@types/jest': jestVersion,
18+
'jest-preset-angular': '6.0.0'
19+
};
20+
return json;
21+
});
22+
23+
function addInstall(_, context: SchematicContext) {
24+
context.addTask(new NodePackageInstallTask());
25+
}
26+
27+
export default function(): Rule {
28+
return chain([mergeWith(url('./files')), updatePackageJson, addInstall]);
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
2+
import * as path from 'path';
3+
import { Tree, VirtualTree } from '@angular-devkit/schematics';
4+
import { createEmptyWorkspace, createLib } from '../../utils/testing-utils';
5+
import { readJsonInTree } from '@nrwl/schematics/src/utils/ast-utils';
6+
7+
describe('lib', () => {
8+
const schematicRunner = new SchematicTestRunner(
9+
'@nrwl/schematics',
10+
path.join(__dirname, '../../collection.json')
11+
);
12+
13+
let appTree: Tree;
14+
15+
beforeEach(() => {
16+
appTree = new VirtualTree();
17+
appTree = createEmptyWorkspace(appTree);
18+
});
19+
20+
it('should generate files', () => {
21+
const resultTree = schematicRunner.runSchematic('jest', {}, appTree);
22+
expect(resultTree.exists('jest.config.js')).toBeTruthy();
23+
});
24+
25+
it('should add dependencies', () => {
26+
const resultTree = schematicRunner.runSchematic('jest', {}, appTree);
27+
const packageJson = readJsonInTree(resultTree, 'package.json');
28+
expect(packageJson.devDependencies.jest).toBeDefined();
29+
expect(packageJson.devDependencies['@nrwl/builders']).toBeDefined();
30+
expect(packageJson.devDependencies['@types/jest']).toBeDefined();
31+
expect(packageJson.devDependencies['jest-preset-angular']).toBeDefined();
32+
});
33+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"id": "Nx Jest Schematics Schema",
4+
"title": "Create Jest Configuration for the workspace",
5+
"type": "object",
6+
"properties": {},
7+
"required": []
8+
}

‎packages/schematics/src/collection/library/index.ts

+78-46
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
noop,
66
Rule,
77
Tree,
8-
SchematicContext
8+
SchematicContext,
9+
schematic
910
} from '@angular-devkit/schematics';
1011
import { Schema } from './schema';
1112
import * as path from 'path';
@@ -33,6 +34,7 @@ import {
3334
import { formatFiles } from '../../utils/rules/format-files';
3435
import { updateKarmaConf } from '../../utils/rules/update-karma-conf';
3536
import { excludeUnnecessaryFiles } from '@nrwl/schematics/src/utils/rules/filter-tree';
37+
import { join, normalize } from '@angular-devkit/core';
3638

3739
interface NormalizedSchema extends Schema {
3840
name: string;
@@ -240,6 +242,12 @@ function updateProject(options: NormalizedSchema): Rule {
240242
host.delete(path.join(options.projectRoot, 'package.json'));
241243
}
242244

245+
if (options.unitTestRunner !== 'karma') {
246+
host.delete(path.join(options.projectRoot, 'karma.conf.js'));
247+
host.delete(path.join(options.projectRoot, 'src/test.ts'));
248+
host.delete(path.join(options.projectRoot, 'tsconfig.spec.json'));
249+
}
250+
243251
host.overwrite(
244252
path.join(libRoot, `${options.name}.module.ts`),
245253
`
@@ -253,26 +261,29 @@ function updateProject(options: NormalizedSchema): Rule {
253261
export class ${options.moduleName} { }
254262
`
255263
);
256-
host.create(
257-
path.join(libRoot, `${options.name}.module.spec.ts`),
258-
`
259-
import { async, TestBed } from '@angular/core/testing';
260-
import { ${options.moduleName} } from './${options.name}.module';
261264

262-
describe('${options.moduleName}', () => {
263-
beforeEach(async(() => {
264-
TestBed.configureTestingModule({
265-
imports: [ ${options.moduleName} ]
266-
})
267-
.compileComponents();
268-
}));
269-
270-
it('should create', () => {
271-
expect(${options.moduleName}).toBeDefined();
265+
if (options.unitTestRunner !== 'none') {
266+
host.create(
267+
path.join(libRoot, `${options.name}.module.spec.ts`),
268+
`
269+
import { async, TestBed } from '@angular/core/testing';
270+
import { ${options.moduleName} } from './${options.name}.module';
271+
272+
describe('${options.moduleName}', () => {
273+
beforeEach(async(() => {
274+
TestBed.configureTestingModule({
275+
imports: [ ${options.moduleName} ]
276+
})
277+
.compileComponents();
278+
}));
279+
280+
it('should create', () => {
281+
expect(${options.moduleName}).toBeDefined();
282+
});
272283
});
273-
});
274-
`
275-
);
284+
`
285+
);
286+
}
276287
host.overwrite(
277288
`${options.projectRoot}/src/index.ts`,
278289
`
@@ -293,6 +304,16 @@ describe('${options.moduleName}', () => {
293304
delete fixedProject.architect.build;
294305
}
295306

307+
if (options.unitTestRunner !== 'karma') {
308+
delete fixedProject.architect.test;
309+
310+
fixedProject.architect.lint.options.tsConfig = fixedProject.architect.lint.options.tsConfig.filter(
311+
path =>
312+
path !==
313+
join(normalize(options.projectRoot), 'tsconfig.spec.json')
314+
);
315+
}
316+
296317
json.projects[options.name] = fixedProject;
297318
return json;
298319
}),
@@ -309,18 +330,6 @@ describe('${options.moduleName}', () => {
309330
}
310331
};
311332
}),
312-
updateJsonInTree(`${options.projectRoot}/tsconfig.spec.json`, json => {
313-
return {
314-
...json,
315-
extends: `${offsetFromRoot(options.projectRoot)}tsconfig.json`,
316-
compilerOptions: {
317-
...json.compilerOptions,
318-
outDir: `${offsetFromRoot(options.projectRoot)}dist/out-tsc/${
319-
options.projectRoot
320-
}`
321-
}
322-
};
323-
}),
324333
updateJsonInTree(`${options.projectRoot}/tslint.json`, json => {
325334
return {
326335
...json,
@@ -337,22 +346,43 @@ describe('${options.moduleName}', () => {
337346
};
338347
}),
339348
updateNgPackage(options),
340-
host => {
341-
const karma = host
342-
.read(`${options.projectRoot}/karma.conf.js`)
343-
.toString();
344-
host.overwrite(
345-
`${options.projectRoot}/karma.conf.js`,
346-
karma.replace(
347-
`'../../coverage${options.projectRoot}'`,
348-
`'${offsetFromRoot(options.projectRoot)}coverage'`
349-
)
350-
);
351-
}
349+
options.unitTestRunner === 'karma' ? updateKarmaConfig(options) : noop()
352350
])(host, null);
353351
};
354352
}
355353

354+
function updateKarmaConfig(options: NormalizedSchema) {
355+
return chain([
356+
host => {
357+
const karma = host
358+
.read(`${options.projectRoot}/karma.conf.js`)
359+
.toString();
360+
host.overwrite(
361+
`${options.projectRoot}/karma.conf.js`,
362+
karma.replace(
363+
`'../../coverage${options.projectRoot}'`,
364+
`'${offsetFromRoot(options.projectRoot)}coverage'`
365+
)
366+
);
367+
},
368+
updateJsonInTree(`${options.projectRoot}/tsconfig.spec.json`, json => {
369+
return {
370+
...json,
371+
extends: `${offsetFromRoot(options.projectRoot)}tsconfig.json`,
372+
compilerOptions: {
373+
...json.compilerOptions,
374+
outDir: `${offsetFromRoot(options.projectRoot)}dist/out-tsc/${
375+
options.projectRoot
376+
}`
377+
}
378+
};
379+
}),
380+
updateKarmaConf({
381+
projectName: options.name
382+
})
383+
]);
384+
}
385+
356386
function updateTsConfig(options: NormalizedSchema): Rule {
357387
return chain([
358388
(host: Tree, context: SchematicContext) => {
@@ -396,10 +426,12 @@ export default function(schema: Schema): Rule {
396426

397427
move(options.name, options.projectRoot),
398428
updateProject(options),
399-
updateKarmaConf({
400-
projectName: options.name
401-
}),
402429
updateTsConfig(options),
430+
options.unitTestRunner === 'jest'
431+
? schematic('jest-project', {
432+
project: options.name
433+
})
434+
: noop(),
403435

404436
options.publishable ? updateLibPackageNpmScope(options) : noop(),
405437
options.routing && options.lazy

‎packages/schematics/src/collection/library/library.spec.ts

+39-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('lib', () => {
4747
appTree
4848
);
4949
const packageJson = readJsonInTree(tree, '/package.json');
50-
expect(packageJson.devDependencies).toBeUndefined();
50+
expect(packageJson.devDependencies['ng-packagr']).toBeUndefined();
5151
});
5252

5353
it('should update package.json when publishable', () => {
@@ -392,4 +392,42 @@ describe('lib', () => {
392392
});
393393
});
394394
});
395+
396+
describe('--unit-test-runner jest', () => {
397+
beforeEach(() => {
398+
appTree = schematicRunner.runSchematic('jest', {}, appTree);
399+
});
400+
401+
it('should generate jest configuration', () => {
402+
const resultTree = schematicRunner.runSchematic(
403+
'lib',
404+
{ name: 'myLib', unitTestRunner: 'jest' },
405+
appTree
406+
);
407+
expect(resultTree.exists('libs/my-lib/src/test-setup.ts')).toBeTruthy();
408+
expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeTruthy();
409+
const angularJson = readJsonInTree(resultTree, 'angular.json');
410+
expect(angularJson.projects['my-lib'].architect.test.builder).toEqual(
411+
'@nrwl/builders:jest'
412+
);
413+
});
414+
});
415+
416+
describe('--unit-test-runner none', () => {
417+
it('should not generate test configuration', () => {
418+
const resultTree = schematicRunner.runSchematic(
419+
'lib',
420+
{ name: 'myLib', unitTestRunner: 'none' },
421+
appTree
422+
);
423+
expect(
424+
resultTree.exists('libs/my-lib/src/lib/my-lib.module.spec.ts')
425+
).toBeFalsy();
426+
expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy();
427+
expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeFalsy();
428+
expect(resultTree.exists('libs/my-lib/karma.config.js')).toBeFalsy();
429+
const angularJson = readJsonInTree(resultTree, 'angular.json');
430+
expect(angularJson.projects['my-lib'].architect.test).toBeUndefined();
431+
});
432+
});
395433
});

‎packages/schematics/src/collection/library/schema.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { UnitTestRunner } from '../../utils/test-runners';
2+
13
export interface Schema {
24
name: string;
35
skipFormat: boolean;
@@ -14,4 +16,6 @@ export interface Schema {
1416
lazy?: boolean;
1517
parentModule?: string;
1618
tags?: string;
19+
20+
unitTestRunner: UnitTestRunner;
1721
}

‎packages/schematics/src/collection/library/schema.json

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
"tags": {
6262
"type": "string",
6363
"description": "Add tags to the library (used for linting)"
64+
},
65+
"unitTestRunner": {
66+
"type": "string",
67+
"enum": ["karma", "jest", "none"],
68+
"description": "Test runner to use for unit tests",
69+
"default": "karma"
6470
}
6571
},
6672
"required": []

‎packages/schematics/src/collection/ng-new/schema.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { UnitTestRunner } from '../../utils/test-runners';
2+
13
export interface Schema {
24
directory: string;
35
name: string;

‎packages/schematics/src/lib-versions.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const latestMigration = '20180507-create-nx-json';
1212
export const prettierVersion = '1.13.7';
1313
export const typescriptVersion = '~2.7.2';
1414
export const rxjsVersion = '^6.0.0';
15+
export const jestVersion = '^23.0.0';
1516
export const jasmineMarblesVersion = '0.3.1';
1617

1718
export const libVersions = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type UnitTestRunner = 'karma' | 'jest' | 'none';

‎packages/schematics/src/utils/testing-utils.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ export function createEmptyWorkspace(tree: Tree): Tree {
2626
'/angular.json',
2727
JSON.stringify({ projects: {}, newProjectRoot: '' })
2828
);
29-
tree.create('/package.json', JSON.stringify({}));
29+
tree.create(
30+
'/package.json',
31+
JSON.stringify({
32+
dependencies: {},
33+
devDependencies: {}
34+
})
35+
);
3036
tree.create('/nx.json', JSON.stringify({ npmScope: 'proj', projects: {} }));
3137
tree.create(
3238
'/tsconfig.json',

0 commit comments

Comments
 (0)
Please sign in to comment.