refactor(@schematics/angular): update application to use new workspace rules

This commit is contained in:
Charles Lyding 2019-04-15 23:22:44 -04:00 committed by Alex Eagle
parent 10be2672bb
commit 58f6282edf
2 changed files with 93 additions and 105 deletions

View File

@ -33,48 +33,15 @@ import {
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { Schema as ComponentOptions } from '../component/schema'; import { Schema as ComponentOptions } from '../component/schema';
import { Schema as E2eOptions } from '../e2e/schema'; import { Schema as E2eOptions } from '../e2e/schema';
import {
addProjectToWorkspace,
getWorkspace,
} from '../utility/config';
import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies'; import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies';
import { findPropertyInAstObject, insertPropertyInAstObjectInOrder } from '../utility/json-utils'; import { findPropertyInAstObject, insertPropertyInAstObjectInOrder } from '../utility/json-utils';
import { latestVersions } from '../utility/latest-versions'; import { latestVersions } from '../utility/latest-versions';
import { applyLintFix } from '../utility/lint-fix'; import { applyLintFix } from '../utility/lint-fix';
import { validateProjectName } from '../utility/validation'; import { validateProjectName } from '../utility/validation';
import { import { getWorkspace, updateWorkspace } from '../utility/workspace';
Builders, import { Builders, ProjectType } from '../utility/workspace-models';
ProjectType,
WorkspaceProject,
WorkspaceSchema,
} from '../utility/workspace-models';
import { Schema as ApplicationOptions, Style } from './schema'; import { Schema as ApplicationOptions, Style } from './schema';
// TODO: use JsonAST
// function appendPropertyInAstObject(
// recorder: UpdateRecorder,
// node: JsonAstObject,
// propertyName: string,
// value: JsonValue,
// indent = 4,
// ) {
// const indentStr = '\n' + new Array(indent + 1).join(' ');
// if (node.properties.length > 0) {
// // Insert comma.
// const last = node.properties[node.properties.length - 1];
// recorder.insertRight(last.start.offset + last.text.replace(/\s+$/, '').length, ',');
// }
// recorder.insertLeft(
// node.end.offset - 1,
// ' '
// + `"${propertyName}": ${JSON.stringify(value, null, 2).replace(/\n/g, indentStr)}`
// + indentStr.slice(0, -2),
// );
// }
function addDependenciesToPackageJson(options: ApplicationOptions) { function addDependenciesToPackageJson(options: ApplicationOptions) {
return (host: Tree, context: SchematicContext) => { return (host: Tree, context: SchematicContext) => {
[ [
@ -175,21 +142,10 @@ function mergeWithRootTsLint(parentHost: Tree) {
}; };
} }
function addAppToWorkspaceFile(options: ApplicationOptions, workspace: WorkspaceSchema): Rule { function addAppToWorkspaceFile(options: ApplicationOptions, newProjectRoot: string): Rule {
// TODO: use JsonAST
// const workspacePath = '/angular.json';
// const workspaceBuffer = host.read(workspacePath);
// if (workspaceBuffer === null) {
// throw new SchematicsException(`Configuration file (${workspacePath}) not found.`);
// }
// const workspaceJson = parseJson(workspaceBuffer.toString());
// if (workspaceJson.value === null) {
// throw new SchematicsException(`Unable to parse configuration file (${workspacePath}).`);
// }
let projectRoot = options.projectRoot !== undefined let projectRoot = options.projectRoot !== undefined
? options.projectRoot ? options.projectRoot
: `${workspace.newProjectRoot}/${options.name}`; : `${newProjectRoot}/${options.name}`;
if (projectRoot !== '' && !projectRoot.endsWith('/')) { if (projectRoot !== '' && !projectRoot.endsWith('/')) {
projectRoot += '/'; projectRoot += '/';
@ -224,13 +180,14 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace
} }
const sourceRoot = join(normalize(projectRoot), 'src'); const sourceRoot = join(normalize(projectRoot), 'src');
const project: WorkspaceProject = {
const project = {
root: projectRoot, root: projectRoot,
sourceRoot, sourceRoot,
projectType: ProjectType.Application, projectType: ProjectType.Application,
prefix: options.prefix || 'app', prefix: options.prefix || 'app',
schematics, schematics,
architect: { targets: {
build: { build: {
builder: Builders.Browser, builder: Builders.Browser,
options: { options: {
@ -321,7 +278,16 @@ function addAppToWorkspaceFile(options: ApplicationOptions, workspace: Workspace
}, },
}; };
return addProjectToWorkspace(workspace, options.name, project); return updateWorkspace(workspace => {
if (workspace.projects.size === 0) {
workspace.extensions.defaultProject = options.name;
}
workspace.projects.add({
name: options.name,
...project,
});
});
} }
function minimalPathFilter(path: string): boolean { function minimalPathFilter(path: string): boolean {
@ -331,7 +297,7 @@ function minimalPathFilter(path: string): boolean {
} }
export default function (options: ApplicationOptions): Rule { export default function (options: ApplicationOptions): Rule {
return (host: Tree, context: SchematicContext) => { return async (host: Tree, context: SchematicContext) => {
if (!options.name) { if (!options.name) {
throw new SchematicsException(`Invalid options, "name" is required.`); throw new SchematicsException(`Invalid options, "name" is required.`);
} }
@ -353,8 +319,8 @@ export default function (options: ApplicationOptions): Rule {
style: options.style, style: options.style,
}; };
const workspace = getWorkspace(host); const workspace = await getWorkspace(host);
const newProjectRoot = workspace.newProjectRoot || ''; const newProjectRoot = workspace.extensions.newProjectRoot as string || '';
const isRootApp = options.projectRoot !== undefined; const isRootApp = options.projectRoot !== undefined;
const appDir = isRootApp const appDir = isRootApp
? options.projectRoot as string ? options.projectRoot as string
@ -370,7 +336,7 @@ export default function (options: ApplicationOptions): Rule {
}; };
return chain([ return chain([
addAppToWorkspaceFile(options, workspace), addAppToWorkspaceFile(options, newProjectRoot),
mergeWith( mergeWith(
apply(url('./files'), [ apply(url('./files'), [
options.minimal ? filter(minimalPathFilter) : noop(), options.minimal ? filter(minimalPathFilter) : noop(),

View File

@ -38,10 +38,11 @@ describe('Application Schematic', () => {
workspaceTree = schematicRunner.runSchematic('workspace', workspaceOptions); workspaceTree = schematicRunner.runSchematic('workspace', workspaceOptions);
}); });
it('should create all files of an application', () => { it('should create all files of an application', async () => {
const options = { ...defaultOptions }; const options = { ...defaultOptions };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const files = tree.files; const files = tree.files;
expect(files).toEqual(jasmine.arrayContaining([ expect(files).toEqual(jasmine.arrayContaining([
'/projects/foo/karma.conf.js', '/projects/foo/karma.conf.js',
@ -64,35 +65,39 @@ describe('Application Schematic', () => {
])); ]));
}); });
it('should add the application to the workspace', () => { it('should add the application to the workspace', async () => {
const options = { ...defaultOptions }; const options = { ...defaultOptions };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const workspace = JSON.parse(tree.readContent('/angular.json')); const workspace = JSON.parse(tree.readContent('/angular.json'));
expect(workspace.projects.foo).toBeDefined(); expect(workspace.projects.foo).toBeDefined();
expect(workspace.defaultProject).toBe('foo'); expect(workspace.defaultProject).toBe('foo');
}); });
it('should set the prefix to app if none is set', () => { it('should set the prefix to app if none is set', async () => {
const options = { ...defaultOptions }; const options = { ...defaultOptions };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const workspace = JSON.parse(tree.readContent('/angular.json')); const workspace = JSON.parse(tree.readContent('/angular.json'));
expect(workspace.projects.foo.prefix).toEqual('app'); expect(workspace.projects.foo.prefix).toEqual('app');
}); });
it('should set the prefix correctly', () => { it('should set the prefix correctly', async () => {
const options = { ...defaultOptions, prefix: 'pre' }; const options = { ...defaultOptions, prefix: 'pre' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const workspace = JSON.parse(tree.readContent('/angular.json')); const workspace = JSON.parse(tree.readContent('/angular.json'));
expect(workspace.projects.foo.prefix).toEqual('pre'); expect(workspace.projects.foo.prefix).toEqual('pre');
}); });
it('should handle the routing flag', () => { it('should handle the routing flag', async () => {
const options = { ...defaultOptions, routing: true }; const options = { ...defaultOptions, routing: true };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const files = tree.files; const files = tree.files;
expect(files).toContain('/projects/foo/src/app/app.module.ts'); expect(files).toContain('/projects/foo/src/app/app.module.ts');
expect(files).toContain('/projects/foo/src/app/app-routing.module.ts'); expect(files).toContain('/projects/foo/src/app/app-routing.module.ts');
@ -102,33 +107,36 @@ describe('Application Schematic', () => {
expect(routingModuleContent).toMatch(/RouterModule.forRoot\(routes\)/); expect(routingModuleContent).toMatch(/RouterModule.forRoot\(routes\)/);
}); });
it('should import BrowserModule in the app module', () => { it('should import BrowserModule in the app module', async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const path = '/projects/foo/src/app/app.module.ts'; const path = '/projects/foo/src/app/app.module.ts';
const content = tree.readContent(path); const content = tree.readContent(path);
expect(content).toMatch(/import { BrowserModule } from \'@angular\/platform-browser\';/); expect(content).toMatch(/import { BrowserModule } from \'@angular\/platform-browser\';/);
}); });
it('should declare app component in the app module', () => { it('should declare app component in the app module', async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const path = '/projects/foo/src/app/app.module.ts'; const path = '/projects/foo/src/app/app.module.ts';
const content = tree.readContent(path); const content = tree.readContent(path);
expect(content).toMatch(/import { AppComponent } from \'\.\/app\.component\';/); expect(content).toMatch(/import { AppComponent } from \'\.\/app\.component\';/);
}); });
it(`should set 'defaultEncapsulation' in main.ts when 'ViewEncapsulation' is provided`, () => { it(`should set 'defaultEncapsulation' in main.ts when 'ViewEncapsulation' is provided`, async () => {
const tree = schematicRunner.runSchematic('application', { const tree = await schematicRunner.runSchematicAsync('application', {
...defaultOptions, ...defaultOptions,
viewEncapsulation: ViewEncapsulation.ShadowDom, viewEncapsulation: ViewEncapsulation.ShadowDom,
}, workspaceTree); }, workspaceTree).toPromise();
const path = '/projects/foo/src/main.ts'; const path = '/projects/foo/src/main.ts';
const content = tree.readContent(path); const content = tree.readContent(path);
expect(content).toContain('defaultEncapsulation: ViewEncapsulation.ShadowDom'); expect(content).toContain('defaultEncapsulation: ViewEncapsulation.ShadowDom');
expect(content).toContain(`import { enableProdMode, ViewEncapsulation } from '@angular/core'`); expect(content).toContain(`import { enableProdMode, ViewEncapsulation } from '@angular/core'`);
}); });
it('should set the right paths in the tsconfig files', () => { it('should set the right paths in the tsconfig files', async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
let path = '/projects/foo/tsconfig.app.json'; let path = '/projects/foo/tsconfig.app.json';
let content = tree.readContent(path); let content = tree.readContent(path);
expect(content).toMatch('../../tsconfig.json'); expect(content).toMatch('../../tsconfig.json');
@ -139,8 +147,9 @@ describe('Application Schematic', () => {
expect(specTsConfig.files).toEqual(['src/test.ts', 'src/polyfills.ts']); expect(specTsConfig.files).toEqual(['src/test.ts', 'src/polyfills.ts']);
}); });
it('should set the right path and prefix in the tslint file', () => { it('should set the right path and prefix in the tslint file', async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const path = '/projects/foo/tslint.json'; const path = '/projects/foo/tslint.json';
const content = JSON.parse(tree.readContent(path)); const content = JSON.parse(tree.readContent(path));
expect(content.extends).toMatch('../../tslint.json'); expect(content.extends).toMatch('../../tslint.json');
@ -148,33 +157,37 @@ describe('Application Schematic', () => {
expect(content.rules['component-selector'][2]).toMatch('app'); expect(content.rules['component-selector'][2]).toMatch('app');
}); });
it('should set the right prefix in the tslint file when provided is kebabed', () => { it('should set the right prefix in the tslint file when provided is kebabed', async () => {
const options: ApplicationOptions = { ...defaultOptions, prefix: 'foo-bar' }; const options: ApplicationOptions = { ...defaultOptions, prefix: 'foo-bar' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const path = '/projects/foo/tslint.json'; const path = '/projects/foo/tslint.json';
const content = JSON.parse(tree.readContent(path)); const content = JSON.parse(tree.readContent(path));
expect(content.rules['directive-selector'][2]).toMatch('fooBar'); expect(content.rules['directive-selector'][2]).toMatch('fooBar');
expect(content.rules['component-selector'][2]).toMatch('foo-bar'); expect(content.rules['component-selector'][2]).toMatch('foo-bar');
}); });
it('should set the right coverage folder in the karma.json file', () => { it('should set the right coverage folder in the karma.json file', async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const karmaConf = getFileContent(tree, '/projects/foo/karma.conf.js'); const karmaConf = getFileContent(tree, '/projects/foo/karma.conf.js');
expect(karmaConf).toContain(`dir: require('path').join(__dirname, '../../coverage/foo')`); expect(karmaConf).toContain(`dir: require('path').join(__dirname, '../../coverage/foo')`);
}); });
it('minimal=true should not create e2e and test targets', () => { it('minimal=true should not create e2e and test targets', async () => {
const options = { ...defaultOptions, minimal: true }; const options = { ...defaultOptions, minimal: true };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const config = JSON.parse(tree.readContent('/angular.json')); const config = JSON.parse(tree.readContent('/angular.json'));
const architect = config.projects.foo.architect; const architect = config.projects.foo.architect;
expect(architect.test).not.toBeDefined(); expect(architect.test).not.toBeDefined();
expect(architect.e2e).not.toBeDefined(); expect(architect.e2e).not.toBeDefined();
}); });
it('should create correct files when using minimal', () => { it('should create correct files when using minimal', async () => {
const options = { ...defaultOptions, minimal: true }; const options = { ...defaultOptions, minimal: true };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const files = tree.files; const files = tree.files;
[ [
'/projects/foo/tsconfig.spec.json', '/projects/foo/tsconfig.spec.json',
@ -201,38 +214,41 @@ describe('Application Schematic', () => {
}); });
describe(`update package.json`, () => { describe(`update package.json`, () => {
it(`should add build-angular to devDependencies`, () => { it(`should add build-angular to devDependencies`, async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const packageJson = JSON.parse(tree.readContent('package.json')); const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson.devDependencies['@angular-devkit/build-angular']) expect(packageJson.devDependencies['@angular-devkit/build-angular'])
.toEqual(latestVersions.DevkitBuildAngular); .toEqual(latestVersions.DevkitBuildAngular);
}); });
it('should use the latest known versions in package.json', () => { it('should use the latest known versions in package.json', async () => {
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const pkg = JSON.parse(tree.readContent('/package.json')); const pkg = JSON.parse(tree.readContent('/package.json'));
expect(pkg.devDependencies['@angular/compiler-cli']).toEqual(latestVersions.Angular); expect(pkg.devDependencies['@angular/compiler-cli']).toEqual(latestVersions.Angular);
expect(pkg.devDependencies['typescript']).toEqual(latestVersions.TypeScript); expect(pkg.devDependencies['typescript']).toEqual(latestVersions.TypeScript);
}); });
it(`should not override existing users dependencies`, () => { it(`should not override existing users dependencies`, async () => {
const oldPackageJson = workspaceTree.readContent('package.json'); const oldPackageJson = workspaceTree.readContent('package.json');
workspaceTree.overwrite('package.json', oldPackageJson.replace( workspaceTree.overwrite('package.json', oldPackageJson.replace(
`"typescript": "${latestVersions.TypeScript}"`, `"typescript": "${latestVersions.TypeScript}"`,
`"typescript": "~2.5.2"`, `"typescript": "~2.5.2"`,
)); ));
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', defaultOptions, workspaceTree)
.toPromise();
const packageJson = JSON.parse(tree.readContent('package.json')); const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson.devDependencies.typescript).toEqual('~2.5.2'); expect(packageJson.devDependencies.typescript).toEqual('~2.5.2');
}); });
it(`should not modify the file when --skipPackageJson`, () => { it(`should not modify the file when --skipPackageJson`, async () => {
const tree = schematicRunner.runSchematic('application', { const tree = await schematicRunner.runSchematicAsync('application', {
name: 'foo', name: 'foo',
skipPackageJson: true, skipPackageJson: true,
}, workspaceTree); }, workspaceTree).toPromise();
const packageJson = JSON.parse(tree.readContent('package.json')); const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson.devDependencies['@angular-devkit/build-angular']).toBeUndefined(); expect(packageJson.devDependencies['@angular-devkit/build-angular']).toBeUndefined();
@ -240,10 +256,11 @@ describe('Application Schematic', () => {
}); });
describe('custom projectRoot', () => { describe('custom projectRoot', () => {
it('should put app files in the right spot', () => { it('should put app files in the right spot', async () => {
const options = { ...defaultOptions, projectRoot: '' }; const options = { ...defaultOptions, projectRoot: '' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const files = tree.files; const files = tree.files;
expect(files).toEqual(jasmine.arrayContaining([ expect(files).toEqual(jasmine.arrayContaining([
'/karma.conf.js', '/karma.conf.js',
@ -266,10 +283,11 @@ describe('Application Schematic', () => {
])); ]));
}); });
it('should set values in angular.json correctly', () => { it('should set values in angular.json correctly', async () => {
const options = { ...defaultOptions, projectRoot: '' }; const options = { ...defaultOptions, projectRoot: '' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const config = JSON.parse(tree.readContent('/angular.json')); const config = JSON.parse(tree.readContent('/angular.json'));
const prj = config.projects.foo; const prj = config.projects.foo;
expect(prj.root).toEqual(''); expect(prj.root).toEqual('');
@ -288,9 +306,10 @@ describe('Application Schematic', () => {
]); ]);
}); });
it('should set values in angular.json correctly when using a style preprocessor', () => { it('should set values in angular.json correctly when using a style preprocessor', async () => {
const options = { ...defaultOptions, projectRoot: '', style: Style.Sass }; const options = { ...defaultOptions, projectRoot: '', style: Style.Sass };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const config = JSON.parse(tree.readContent('/angular.json')); const config = JSON.parse(tree.readContent('/angular.json'));
const prj = config.projects.foo; const prj = config.projects.foo;
const buildOpt = prj.architect.build.options; const buildOpt = prj.architect.build.options;
@ -304,9 +323,10 @@ describe('Application Schematic', () => {
expect(tree.exists('src/styles.sass')).toBe(true); expect(tree.exists('src/styles.sass')).toBe(true);
}); });
it('should set the relative tsconfig paths', () => { it('should set the relative tsconfig paths', async () => {
const options = { ...defaultOptions, projectRoot: '' }; const options = { ...defaultOptions, projectRoot: '' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const appTsConfig = JSON.parse(tree.readContent('/tsconfig.app.json')); const appTsConfig = JSON.parse(tree.readContent('/tsconfig.app.json'));
expect(appTsConfig.extends).toEqual('./tsconfig.json'); expect(appTsConfig.extends).toEqual('./tsconfig.json');
const specTsConfig = JSON.parse(tree.readContent('/tsconfig.spec.json')); const specTsConfig = JSON.parse(tree.readContent('/tsconfig.spec.json'));
@ -314,20 +334,22 @@ describe('Application Schematic', () => {
expect(specTsConfig.files).toEqual(['src/test.ts', 'src/polyfills.ts']); expect(specTsConfig.files).toEqual(['src/test.ts', 'src/polyfills.ts']);
}); });
it('should set the relative path and prefix in the tslint file', () => { it('should set the relative path and prefix in the tslint file', async () => {
const options = { ...defaultOptions, projectRoot: '' }; const options = { ...defaultOptions, projectRoot: '' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const content = JSON.parse(tree.readContent('/tslint.json')); const content = JSON.parse(tree.readContent('/tslint.json'));
expect(content.extends).toMatch('tslint:recommended'); expect(content.extends).toMatch('tslint:recommended');
expect(content.rules['directive-selector'][2]).toMatch('app'); expect(content.rules['directive-selector'][2]).toMatch('app');
expect(content.rules['component-selector'][2]).toMatch('app'); expect(content.rules['component-selector'][2]).toMatch('app');
}); });
it('should merge tslint file', () => { it('should merge tslint file', async () => {
const options = { ...defaultOptions, projectRoot: '' }; const options = { ...defaultOptions, projectRoot: '' };
const tree = schematicRunner.runSchematic('application', options, workspaceTree); const tree = await schematicRunner.runSchematicAsync('application', options, workspaceTree)
.toPromise();
const content = JSON.parse(tree.readContent('/tslint.json')); const content = JSON.parse(tree.readContent('/tslint.json'));
expect(content.extends).toMatch('tslint:recommended'); expect(content.extends).toMatch('tslint:recommended');
expect(content.rules['component-selector'][2]).toMatch('app'); expect(content.rules['component-selector'][2]).toMatch('app');