Colum Ferry f5587dc984
fix(angular): broken buildable libs importPath calculation (#6429)
Buildable libs with Angular Lib generator were broken due to additional `/` in the `importPath`.
2021-07-21 09:29:21 +01:00

1137 lines
34 KiB
TypeScript

import {
NxJsonConfiguration,
readJson,
Tree,
updateJson,
parseJson,
} from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { Linter } from '@nrwl/linter';
import { createApp } from '../../utils/nx-devkit/testing';
import libraryGenerator from './library';
import { Schema } from './schema';
import { UnitTestRunner } from '../../utils/test-runners';
describe('lib', () => {
let appTree: Tree;
async function runLibraryGeneratorWithOpts(opts: Partial<Schema> = {}) {
await libraryGenerator(appTree, {
name: 'myLib',
publishable: false,
buildable: false,
enableIvy: false,
linter: Linter.EsLint,
skipFormat: false,
unitTestRunner: UnitTestRunner.Jest,
simpleModuleName: false,
strict: true,
...opts,
});
}
beforeEach(() => {
appTree = createTreeWithEmptyWorkspace();
});
describe('not nested', () => {
it('should update ng-package.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
let ngPackage = readJson(appTree, 'libs/my-lib/ng-package.json');
expect(ngPackage.dest).toEqual('../../dist/libs/my-lib');
});
it('should update ng-package.json $schema to the correct folder', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
let ngPackage = readJson(appTree, 'libs/my-lib/ng-package.json');
expect(ngPackage.$schema).toEqual(
'../../node_modules/ng-packagr/ng-package.schema.json'
);
});
it('should not update package.json by default', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const packageJson = readJson(appTree, '/package.json');
expect(packageJson.devDependencies['ng-packagr']).toBeUndefined();
});
it('should update package.json when publishable', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
const packageJson = readJson(appTree, '/package.json');
expect(packageJson.devDependencies['ng-packagr']).toBeDefined();
});
it('should update tsconfig.lib.prod.json when enableIvy', async () => {
// ACT
await runLibraryGeneratorWithOpts({
buildable: true,
enableIvy: true,
});
// ASSERT
const tsConfig = readJson(appTree, '/libs/my-lib/tsconfig.lib.prod.json');
expect(tsConfig.angularCompilerOptions['enableIvy']).toBe(true);
});
it('should update workspace.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
const workspaceJson = readJson(appTree, '/workspace.json');
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(workspaceJson.projects['my-lib'].architect.build).toBeDefined();
});
it('should remove "build" target from workspace.json when a library is not publishable', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: false,
});
// ASSERT
const workspaceJson = readJson(appTree, '/workspace.json');
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(
workspaceJson.projects['my-lib'].architect.build
).not.toBeDefined();
});
it('should have a "build" target when a library is buildable', async () => {
// ACT
await runLibraryGeneratorWithOpts({
buildable: true,
publishable: false,
});
// ASSERT
const workspaceJson = readJson(appTree, '/workspace.json');
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(workspaceJson.projects['my-lib'].architect.build).toBeDefined();
});
it('should remove tsconfib.lib.prod.json when library is not publishable', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: false,
buildable: false,
});
// ASSERT
const libProdConfig = appTree.read('libs/my-lib/tsconfig.lib.prod.json');
expect(libProdConfig).toBeFalsy();
});
it('should update nx.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: false,
buildable: false,
tags: 'one,two',
});
// ASSERT
const nxJson = readJson<NxJsonConfiguration>(appTree, '/nx.json');
expect(nxJson.projects).toEqual({
'my-lib': {
tags: ['one', 'two'],
},
});
});
it('should update root tsconfig.json', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const tsconfigJson = readJson(appTree, '/tsconfig.base.json');
expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([
'libs/my-lib/src/index.ts',
]);
});
it('should create a local tsconfig.json', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.json');
expect(tsconfigJson).toEqual({
extends: '../../tsconfig.base.json',
angularCompilerOptions: {
strictInjectionParameters: true,
strictInputAccessModifiers: true,
strictTemplates: true,
},
compilerOptions: {
forceConsistentCasingInFileNames: true,
noFallthroughCasesInSwitch: true,
noImplicitReturns: true,
strict: true,
},
files: [],
include: [],
references: [
{
path: './tsconfig.lib.json',
},
{
path: './tsconfig.spec.json',
},
],
});
});
it('should check for existance of spec files before deleting them', async () => {
// ARRANGE
updateJson(appTree, '/workspace.json', (workspaceJSON) => {
workspaceJSON.schematics = {
'@schematics/angular:service': {
skipTests: true,
},
'@schematics/angular:component': {
skipTests: true,
},
};
return workspaceJSON;
});
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
expect(
appTree.read('libs/my-lib/src/lib/my-lib.component.spec.ts')
).toBeFalsy();
expect(
appTree.read('libs/my-lib/src/lib/my-lib.service.spec.ts')
).toBeFalsy();
});
it('should extend the local tsconfig.json with tsconfig.spec.json', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.spec.json');
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
});
describe('when creating the tsconfig.lib.json', () => {
it('should extend the local tsconfig.json', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.lib.json');
expect(tsconfigJson.extends).toEqual('./tsconfig.json');
});
it('should contain includes', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const tsConfigJson = readJson(appTree, 'libs/my-lib/tsconfig.lib.json');
expect(tsConfigJson.include).toEqual(['**/*.ts']);
});
it('should exclude the test setup file when unitTestRunner is jest', async () => {
// ACT
await runLibraryGeneratorWithOpts();
// ASSERT
const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.lib.json');
expect(tsconfigJson.exclude).toEqual([
'src/test-setup.ts',
'**/*.spec.ts',
]);
});
it('should leave the excludes alone when unitTestRunner is karma', async () => {
// ACT
await runLibraryGeneratorWithOpts({
unitTestRunner: UnitTestRunner.Karma,
});
// ASSERT
const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.lib.json');
expect(tsconfigJson.exclude).toEqual(['src/test.ts', '**/*.spec.ts']);
});
it('should remove the excludes when unitTestRunner is none', async () => {
// ACT
await runLibraryGeneratorWithOpts({
unitTestRunner: UnitTestRunner.None,
});
// ASSERT
const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.lib.json');
expect(tsconfigJson.exclude).toEqual([]);
});
});
it('should generate files', async () => {
// ACT
await runLibraryGeneratorWithOpts();
await runLibraryGeneratorWithOpts({ name: 'my-lib2' });
// ASSERT
expect(appTree.exists(`libs/my-lib/jest.config.js`)).toBeTruthy();
expect(appTree.exists('libs/my-lib/src/index.ts')).toBeTruthy();
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.module.ts')
).toBeTruthy();
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.component.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.component.spec.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.service.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.service.spec.ts')
).toBeFalsy();
expect(appTree.exists(`libs/my-lib2/jest.config.js`)).toBeTruthy();
expect(appTree.exists('libs/my-lib2/src/index.ts')).toBeTruthy();
expect(
appTree.exists('libs/my-lib2/src/lib/my-lib2.module.ts')
).toBeTruthy();
expect(
appTree.exists('libs/my-lib2/src/lib/my-lib2.component.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-lib2/src/lib/my-lib2.component.spec.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-lib2/src/lib/my-lib2.service.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-lib2/src/lib/my-lib2.service.spec.ts')
).toBeFalsy();
});
it('should default the prefix to npmScope', async () => {
// ACT
await runLibraryGeneratorWithOpts();
await runLibraryGeneratorWithOpts({
name: 'myLibWithPrefix',
prefix: 'custom',
});
// ASSERT
expect(
JSON.parse(appTree.read('workspace.json').toString()).projects['my-lib']
.prefix
).toEqual('proj');
expect(
JSON.parse(appTree.read('workspace.json').toString()).projects[
'my-lib-with-prefix'
].prefix
).toEqual('custom');
});
});
describe('nested', () => {
it('should update nx.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({ tags: 'one', directory: 'my-dir' });
await runLibraryGeneratorWithOpts({
name: 'myLib2',
directory: 'myDir',
tags: 'one,two',
});
// ASSERT
const nxJson = readJson<NxJsonConfiguration>(appTree, '/nx.json');
expect(nxJson.projects).toEqual({
'my-dir-my-lib': {
tags: ['one'],
},
'my-dir-my-lib2': {
tags: ['one', 'two'],
},
});
});
it('should generate files', async () => {
// ACT
await runLibraryGeneratorWithOpts({ tags: 'one', directory: 'my-dir' });
await runLibraryGeneratorWithOpts({
name: 'myLib2',
directory: 'myDir',
simpleModuleName: true,
});
// ASSERT
expect(appTree.exists(`libs/my-dir/my-lib/jest.config.js`)).toBeTruthy();
expect(appTree.exists('libs/my-dir/my-lib/src/index.ts')).toBeTruthy();
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
).toBeTruthy();
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-lib.component.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-lib.component.spec.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-lib.service.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-lib.service.spec.ts')
).toBeFalsy();
expect(appTree.exists(`libs/my-dir/my-lib2/jest.config.js`)).toBeTruthy();
expect(appTree.exists('libs/my-dir/my-lib2/src/index.ts')).toBeTruthy();
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
).toBeTruthy();
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.component.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.component.spec.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.service.ts')
).toBeFalsy();
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.service.spec.ts')
).toBeFalsy();
});
it('should update ng-package.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({
directory: 'myDir',
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
let ngPackage = readJson(appTree, 'libs/my-dir/my-lib/ng-package.json');
expect(ngPackage.dest).toEqual('../../../dist/libs/my-dir/my-lib');
});
it('should update workspace.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({ directory: 'myDir' });
// ASSERT
const workspaceJson = readJson(appTree, '/workspace.json');
expect(workspaceJson.projects['my-dir-my-lib'].root).toEqual(
'libs/my-dir/my-lib'
);
});
it('should update tsconfig.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({ directory: 'myDir' });
// ASSERT
const tsconfigJson = readJson(appTree, '/tsconfig.base.json');
expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual(
['libs/my-dir/my-lib/src/index.ts']
);
expect(
tsconfigJson.compilerOptions.paths['my-dir-my-lib/*']
).toBeUndefined();
});
it('should update tsconfig.json (no existing path mappings)', async () => {
// ARRANGE
updateJson(appTree, 'tsconfig.base.json', (json) => {
json.compilerOptions.paths = undefined;
return json;
});
// ACT
await runLibraryGeneratorWithOpts({ directory: 'myDir' });
// ASSERT
const tsconfigJson = readJson(appTree, '/tsconfig.base.json');
expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual(
['libs/my-dir/my-lib/src/index.ts']
);
expect(
tsconfigJson.compilerOptions.paths['my-dir-my-lib/*']
).toBeUndefined();
});
it('should create a local tsconfig.json', async () => {
// ACT
await runLibraryGeneratorWithOpts({ directory: 'myDir' });
// ASSERT
const tsconfigJson = readJson(
appTree,
'libs/my-dir/my-lib/tsconfig.json'
);
expect(tsconfigJson).toEqual({
extends: '../../../tsconfig.base.json',
angularCompilerOptions: {
strictInjectionParameters: true,
strictInputAccessModifiers: true,
strictTemplates: true,
},
compilerOptions: {
forceConsistentCasingInFileNames: true,
noFallthroughCasesInSwitch: true,
noImplicitReturns: true,
strict: true,
},
files: [],
include: [],
references: [
{
path: './tsconfig.lib.json',
},
{
path: './tsconfig.spec.json',
},
],
});
});
});
describe('router', () => {
it('should error when lazy is set without routing', async () => {
try {
// ACT
await runLibraryGeneratorWithOpts({ lazy: true });
fail();
} catch (e) {
// ASSERT
expect(e.message).toEqual(
'To use --lazy option, --routing must also be set.'
);
}
});
describe('lazy', () => {
it('should add RouterModule.forChild', async () => {
// ACT
await runLibraryGeneratorWithOpts({
directory: 'myDir',
routing: true,
lazy: true,
});
await runLibraryGeneratorWithOpts({
name: 'myLib2',
directory: 'myDir',
routing: true,
lazy: true,
simpleModuleName: true,
});
// ASSERT
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
).toBeTruthy();
expect(
appTree
.read('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
.toString()
).toContain('RouterModule.forChild');
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
).toBeTruthy();
expect(
appTree
.read('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
.toString()
).toContain('RouterModule.forChild');
});
it('should update the parent module', async () => {
// ARRANGE
createApp(appTree, 'myapp');
// ACT
await runLibraryGeneratorWithOpts({
directory: 'myDir',
routing: true,
lazy: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
const moduleContents = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
const tsConfigAppJson = readJson(
appTree,
'apps/myapp/tsconfig.app.json'
);
await runLibraryGeneratorWithOpts({
name: 'myLib2',
directory: 'myDir',
routing: true,
lazy: true,
simpleModuleName: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
const moduleContents2 = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
const tsConfigAppJson2 = parseJson(
appTree.read('apps/myapp/tsconfig.app.json').toString()
);
await runLibraryGeneratorWithOpts({
name: 'myLib3',
directory: 'myDir',
routing: true,
lazy: true,
simpleModuleName: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
const moduleContents3 = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
const tsConfigAppJson3 = parseJson(
appTree.read('apps/myapp/tsconfig.app.json').toString()
);
// ASSERT
expect(moduleContents).toContain('RouterModule.forRoot([');
expect(moduleContents).toContain(
`{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(module => module.MyDirMyLibModule)}`
);
expect(tsConfigAppJson.include).toEqual([
'**/*.ts',
'../../libs/my-dir/my-lib/src/index.ts',
]);
expect(moduleContents2).toContain('RouterModule.forRoot([');
expect(moduleContents2).toContain(
`{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(module => module.MyDirMyLibModule)}`
);
expect(moduleContents2).toContain(
`{path: 'my-lib2', loadChildren: () => import('@proj/my-dir/my-lib2').then(module => module.MyLib2Module)}`
);
expect(tsConfigAppJson2.include).toEqual([
'**/*.ts',
'../../libs/my-dir/my-lib/src/index.ts',
'../../libs/my-dir/my-lib2/src/index.ts',
]);
expect(moduleContents3).toContain('RouterModule.forRoot([');
expect(moduleContents3).toContain(
`{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(module => module.MyDirMyLibModule)}`
);
expect(moduleContents3).toContain(
`{path: 'my-lib2', loadChildren: () => import('@proj/my-dir/my-lib2').then(module => module.MyLib2Module)}`
);
expect(moduleContents3).toContain(
`{path: 'my-lib3', loadChildren: () => import('@proj/my-dir/my-lib3').then(module => module.MyLib3Module)}`
);
expect(tsConfigAppJson3.include).toEqual([
'**/*.ts',
'../../libs/my-dir/my-lib/src/index.ts',
'../../libs/my-dir/my-lib2/src/index.ts',
'../../libs/my-dir/my-lib3/src/index.ts',
]);
});
it('should update the parent module even if the route is declared outside the .forRoot(...)', async () => {
// ARRANGE
createApp(appTree, 'myapp');
appTree.write(
'apps/myapp/src/app/app.module.ts',
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
const routes = [];
@NgModule({
imports: [BrowserModule, RouterModule.forRoot(routes)],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
`
);
// ACT
await runLibraryGeneratorWithOpts({
directory: 'myDir',
routing: true,
lazy: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
// ASSERT
const moduleContents = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
expect(moduleContents).toContain('RouterModule.forRoot(routes)');
expect(moduleContents).toContain(
`const routes = [{path: 'my-dir-my-lib', loadChildren: () => import('@proj/my-dir/my-lib').then(module => module.MyDirMyLibModule)}];`
);
});
});
describe('eager', () => {
it('should add RouterModule and define an array of routes', async () => {
// ACT
await runLibraryGeneratorWithOpts({
directory: 'myDir',
routing: true,
});
await runLibraryGeneratorWithOpts({
name: 'myLib2',
directory: 'myDir',
simpleModuleName: true,
routing: true,
});
// ASSERT
expect(
appTree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
).toBeTruthy();
expect(
appTree
.read('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
.toString()
).toContain('RouterModule');
expect(
appTree
.read('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
.toString()
).toContain('const myDirMyLibRoutes: Route[] = ');
expect(
appTree.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
).toBeTruthy();
expect(
appTree
.read('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
.toString()
).toContain('RouterModule');
expect(
appTree
.read('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
.toString()
).toContain('const myLib2Routes: Route[] = ');
});
it('should update the parent module', async () => {
// ARRANGE
createApp(appTree, 'myapp');
// ACT
await runLibraryGeneratorWithOpts({
name: 'myLib',
directory: 'myDir',
routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
const moduleContents = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
await runLibraryGeneratorWithOpts({
name: 'myLib2',
directory: 'myDir',
simpleModuleName: true,
routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
const moduleContents2 = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
await runLibraryGeneratorWithOpts({
name: 'myLib3',
directory: 'myDir',
routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
simpleModuleName: true,
});
const moduleContents3 = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
// ASSERT
expect(moduleContents).toContain('MyDirMyLibModule');
expect(moduleContents).toContain('RouterModule.forRoot([');
expect(moduleContents).toContain(
"{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }"
);
expect(moduleContents2).toContain('MyLib2Module');
expect(moduleContents2).toContain('RouterModule.forRoot([');
expect(moduleContents2).toContain(
"{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }"
);
expect(moduleContents2).toContain(
"{ path: 'my-lib2', children: myLib2Routes }"
);
expect(moduleContents3).toContain('MyLib3Module');
expect(moduleContents3).toContain('RouterModule.forRoot([');
expect(moduleContents3).toContain(
"{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }"
);
expect(moduleContents3).toContain(
"{ path: 'my-lib2', children: myLib2Routes }"
);
expect(moduleContents3).toContain(
"{ path: 'my-lib3', children: myLib3Routes }"
);
});
it('should update the parent module even if the route is declared outside the .forRoot(...)', async () => {
// ARRANGE
createApp(appTree, 'myapp');
appTree.write(
'apps/myapp/src/app/app.module.ts',
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
const routes = [];
@NgModule({
imports: [BrowserModule, RouterModule.forRoot(routes)],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
`
);
// ACT
await runLibraryGeneratorWithOpts({
name: 'myLib',
directory: 'myDir',
routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts',
});
// ASSERT
const moduleContents = appTree
.read('apps/myapp/src/app/app.module.ts')
.toString();
expect(moduleContents).toContain('RouterModule.forRoot(routes)');
expect(moduleContents).toContain(
`const routes = [{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }];`
);
});
});
});
describe('--unit-test-runner karma', () => {
it('should generate karma configuration', async () => {
// ACT
await runLibraryGeneratorWithOpts({
unitTestRunner: UnitTestRunner.Karma,
});
// ASSERT
const workspaceJson = readJson(appTree, 'workspace.json');
expect(appTree.exists('libs/my-lib/src/test.ts')).toBeTruthy();
expect(appTree.exists('libs/my-lib/src/test-setup.ts')).toBeFalsy();
expect(appTree.exists('libs/my-lib/tsconfig.spec.json')).toBeTruthy();
expect(appTree.exists('libs/my-lib/karma.conf.js')).toBeTruthy();
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.module.spec.ts')
).toBeFalsy();
expect(appTree.exists('karma.conf.js')).toBeTruthy();
expect(workspaceJson.projects['my-lib'].architect.test.builder).toEqual(
'@angular-devkit/build-angular:karma'
);
});
it('should generate module spec when addModuleSpec is specified', async () => {
// ACT
await runLibraryGeneratorWithOpts({
unitTestRunner: UnitTestRunner.Karma,
addModuleSpec: true,
});
// ASSERT
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.module.spec.ts')
).toBeTruthy();
});
});
describe('--unit-test-runner none', () => {
it('should not generate test configuration', async () => {
// ACT
await runLibraryGeneratorWithOpts({
unitTestRunner: UnitTestRunner.None,
});
// ASSERT
const workspaceJson = readJson(appTree, 'workspace.json');
expect(
appTree.exists('libs/my-lib/src/lib/my-lib.module.spec.ts')
).toBeFalsy();
expect(appTree.exists('libs/my-lib/src/test.ts')).toBeFalsy();
expect(appTree.exists('libs/my-lib/src/test.ts')).toBeFalsy();
expect(appTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy();
expect(appTree.exists('libs/my-lib/jest.config.js')).toBeFalsy();
expect(appTree.exists('libs/my-lib/karma.conf.js')).toBeFalsy();
expect(workspaceJson.projects['my-lib'].architect.test).toBeUndefined();
});
});
describe('--importPath', () => {
it('should update the package.json & tsconfig with the given import path', async () => {
// ACT
await runLibraryGeneratorWithOpts({
directory: 'myDir',
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
const packageJson = readJson(appTree, 'libs/my-dir/my-lib/package.json');
const tsconfigJson = readJson(appTree, '/tsconfig.base.json');
expect(packageJson.name).toBe('@myorg/lib');
expect(
tsconfigJson.compilerOptions.paths[packageJson.name]
).toBeDefined();
});
it('should fail if the same importPath has already been used', async () => {
// ACT
await runLibraryGeneratorWithOpts({
publishable: true,
importPath: '@myorg/lib',
});
try {
// ACT
await runLibraryGeneratorWithOpts({
name: 'myLib2',
publishable: true,
importPath: '@myorg/lib',
});
} catch (e) {
expect(e.message).toContain(
'You already have a library using the import path'
);
}
expect.assertions(1);
});
});
describe('--strict', () => {
it('should enable strict type checking', async () => {
// ACT
await runLibraryGeneratorWithOpts({
strict: true,
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
const { compilerOptions, angularCompilerOptions } = readJson(
appTree,
'libs/my-lib/tsconfig.json'
);
const workspaceJson = readJson(appTree, 'workspace.json');
// check that the TypeScript compiler options have been updated
expect(compilerOptions.forceConsistentCasingInFileNames).toBe(true);
expect(compilerOptions.strict).toBe(true);
expect(compilerOptions.noImplicitReturns).toBe(true);
expect(compilerOptions.noFallthroughCasesInSwitch).toBe(true);
// check that the Angular Template options have been updated
expect(angularCompilerOptions.strictInjectionParameters).toBe(true);
expect(angularCompilerOptions.strictTemplates).toBe(true);
// check to see if the workspace configuration has been updated to use strict
// mode by default in future libraries
expect(
workspaceJson.schematics['@nrwl/angular:library'].strict
).not.toBeDefined();
});
it('should set defaults when --strict=false', async () => {
// ACT
await runLibraryGeneratorWithOpts({
strict: false,
publishable: true,
importPath: '@myorg/lib',
});
// ASSERT
// check to see if the workspace configuration has been updated to turn off
// strict mode by default in future libraries
const workspaceJson = readJson(appTree, 'workspace.json');
expect(workspaceJson.schematics['@nrwl/angular:library'].strict).toBe(
false
);
});
});
describe('--linter', () => {
describe('eslint', () => {
it('should add an architect target for lint', async () => {
// ACT
await runLibraryGeneratorWithOpts({ linter: Linter.EsLint });
// ASSERT
const workspaceJson = readJson(appTree, 'workspace.json');
expect(appTree.exists('libs/my-lib/tslint.json')).toBe(false);
expect(workspaceJson.projects['my-lib'].architect.lint)
.toMatchInlineSnapshot(`
Object {
"builder": "@nrwl/linter:eslint",
"options": Object {
"lintFilePatterns": Array [
"libs/my-lib/src/**/*.ts",
"libs/my-lib/src/**/*.html",
],
},
}
`);
});
it('should add valid eslint JSON configuration which extends from Nx presets', async () => {
// ACT
await runLibraryGeneratorWithOpts({ linter: Linter.EsLint });
// ASSERT
const eslintConfig = readJson(appTree, 'libs/my-lib/.eslintrc.json');
expect(eslintConfig).toMatchInlineSnapshot(`
Object {
"extends": Array [
"../../.eslintrc.json",
],
"ignorePatterns": Array [
"!**/*",
],
"overrides": Array [
Object {
"extends": Array [
"plugin:@nrwl/nx/angular",
"plugin:@angular-eslint/template/process-inline-templates",
],
"files": Array [
"*.ts",
],
"rules": Object {
"@angular-eslint/component-selector": Array [
"error",
Object {
"prefix": "proj",
"style": "kebab-case",
"type": "element",
},
],
"@angular-eslint/directive-selector": Array [
"error",
Object {
"prefix": "proj",
"style": "camelCase",
"type": "attribute",
},
],
},
},
Object {
"extends": Array [
"plugin:@nrwl/nx/angular-template",
],
"files": Array [
"*.html",
],
"rules": Object {},
},
],
}
`);
});
});
describe('none', () => {
it('should not add an architect target for lint', async () => {
// ACT
await runLibraryGeneratorWithOpts({ linter: Linter.None });
// ASSERT
const workspaceJson = readJson(appTree, 'workspace.json');
expect(workspaceJson.projects['my-lib'].architect.lint).toBeUndefined();
});
});
});
});