Greg Magolan 5f0d5e9ccf build: update to nodejs rules 0.34.0 and bazel 0.28.1 (#31824)
nodejs rules 0.34.0 now includes protractor_web_test_suite rule (via new @bazel/protractor rule) so we switch to that location for that rule in this PR so that /packages/bazel/src/protractor can be removed in a future PR

this PR also brings in node toolchain support which was released in nodejs rules 0.33.0. this is a prerequisite for RBE for mac & windows users

bazel schematics also updated with the same. @bazel/bazel 0.28.1 npm package includes transitive dep on hide-bazel-files so we're able to remove an explicit dep on that as well.

PR Close #31824
2019-07-26 15:01:25 -07:00

308 lines
11 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {HostTree} from '@angular-devkit/schematics';
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
describe('ng-add schematic', () => {
const defaultOptions = {name: 'demo'};
let host: UnitTestTree;
let schematicRunner: SchematicTestRunner;
beforeEach(() => {
host = new UnitTestTree(new HostTree());
host.create('package.json', JSON.stringify({
name: 'demo',
dependencies: {
'@angular/core': '1.2.3',
'rxjs': '~6.3.3',
},
devDependencies: {
'typescript': '3.2.2',
},
}));
host.create('tsconfig.json', JSON.stringify({
compileOnSave: false,
compilerOptions: {
baseUrl: './',
outDir: './dist/out-tsc',
}
}));
host.create('angular.json', JSON.stringify({
projects: {
'demo': {
architect: {
build: {},
serve: {},
test: {},
'extract-i18n': {
builder: '@angular-devkit/build-angular:extract-i18n',
},
},
},
'demo-e2e': {
architect: {
e2e: {},
lint: {
builder: '@angular-devkit/build-angular:tslint',
},
},
},
},
defaultProject: 'demo',
}));
schematicRunner =
new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'));
});
it('throws if package.json is not found', async() => {
expect(host.files).toContain('/package.json');
host.delete('/package.json');
let message = 'No error';
try {
await schematicRunner.runSchematicAsync('ng-add', defaultOptions).toPromise();
} catch (e) {
message = e.message;
}
expect(message).toBe('Could not find package.json');
});
it('throws if angular.json is not found', async() => {
expect(host.files).toContain('/angular.json');
host.delete('/angular.json');
let message = 'No error';
try {
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
} catch (e) {
message = e.message;
}
expect(message).toBe('Could not find angular.json');
});
it('should add @angular/bazel to package.json dependencies', async() => {
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
expect(files).toContain('/package.json');
const content = host.readContent('/package.json');
expect(() => JSON.parse(content)).not.toThrow();
const json = JSON.parse(content);
const core = '@angular/core';
const bazel = '@angular/bazel';
expect(Object.keys(json)).toContain('dependencies');
expect(Object.keys(json)).toContain('devDependencies');
expect(Object.keys(json.dependencies)).toContain(core);
expect(Object.keys(json.devDependencies)).toContain(bazel);
expect(json.dependencies[core]).toBe(json.devDependencies[bazel]);
});
it('should add @bazel/* dev dependencies', async() => {
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const content = host.readContent('/package.json');
const json = JSON.parse(content);
const devDeps = Object.keys(json.devDependencies);
expect(devDeps).toContain('@bazel/bazel');
expect(devDeps).toContain('@bazel/ibazel');
expect(devDeps).toContain('@bazel/karma');
expect(devDeps).toContain('@bazel/protractor');
expect(devDeps).toContain('@bazel/typescript');
});
it('should replace an existing dev dependency', async() => {
expect(host.files).toContain('/package.json');
const packageJson = JSON.parse(host.readContent('/package.json'));
packageJson.devDependencies['@angular/bazel'] = '4.2.42';
host.overwrite('/package.json', JSON.stringify(packageJson));
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const content = host.readContent('/package.json');
// It is possible that a dep gets added twice if the package already exists.
expect(content.match(/@angular\/bazel/g) !.length).toEqual(1);
const json = JSON.parse(content);
expect(json.devDependencies['@angular/bazel']).toBe('1.2.3');
});
it('should remove an existing dependency', async() => {
expect(host.files).toContain('/package.json');
const packageJson = JSON.parse(host.readContent('/package.json'));
packageJson.dependencies['@angular/bazel'] = '4.2.42';
expect(Object.keys(packageJson.dependencies)).toContain('@angular/bazel');
host.overwrite('/package.json', JSON.stringify(packageJson));
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const content = host.readContent('/package.json');
const json = JSON.parse(content);
expect(Object.keys(json.dependencies)).not.toContain('@angular/bazel');
expect(json.devDependencies['@angular/bazel']).toBe('1.2.3');
});
it('should not create Bazel workspace file', async() => {
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
expect(files).not.toContain('/WORKSPACE');
expect(files).not.toContain('/BUILD.bazel');
});
it('should produce main.dev.ts and main.prod.ts for AOT', async() => {
host.create('/src/main.ts', 'generated by CLI');
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
// main.dev.ts and main.prod.ts are used by Bazel for AOT
expect(files).toContain('/src/main.dev.ts');
expect(files).toContain('/src/main.prod.ts');
// main.ts is produced by original ng-add schematics
// This file should be present for backwards compatibility.
expect(files).toContain('/src/main.ts');
});
it('should not overwrite index.html with script tags', async() => {
host.create('/src/index.html', '<html>Hello World</html>');
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
expect(files).toContain('/src/index.html');
const content = host.readContent('/src/index.html');
expect(content).not.toMatch('<script src="/zone.min.js"></script>');
expect(content).not.toMatch('<script src="/bundle.min.js"></script>');
});
it('should generate main.dev.ts and main.prod.ts', async() => {
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
expect(files).toContain('/src/main.dev.ts');
expect(files).toContain('/src/main.prod.ts');
});
it('should overwrite .gitignore for bazel-out directory', async() => {
host.create('.gitignore', '\n# compiled output\n');
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
expect(files).toContain('/.gitignore');
const content = host.readContent('/.gitignore');
expect(content).toMatch('\n# compiled output\n/bazel-out\n');
});
it('should create a backup for original angular.json', async() => {
expect(host.files).toContain('/angular.json');
const original = host.readContent('/angular.json');
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
expect(host.files).toContain('/angular.json.bak');
const content = host.readContent('/angular.json.bak');
expect(content.startsWith('// This is a backup file')).toBe(true);
expect(content).toMatch(original);
});
it('should update angular.json to use Bazel builder', async() => {
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
const {files} = host;
expect(files).toContain('/angular.json');
const content = host.readContent('/angular.json');
expect(() => JSON.parse(content)).not.toThrow();
const json = JSON.parse(content);
const demo = json.projects.demo;
const demo_e2e = json.projects['demo-e2e'];
const {build, serve, test} = demo.architect;
expect(build.builder).toBe('@angular/bazel:build');
expect(serve.builder).toBe('@angular/bazel:build');
expect(test.builder).toBe('@angular/bazel:build');
const {e2e, lint} = demo_e2e.architect;
expect(e2e.builder).toBe('@angular/bazel:build');
// it should leave non-Bazel commands unmodified
expect(demo.architect['extract-i18n'].builder)
.toBe('@angular-devkit/build-angular:extract-i18n');
expect(lint.builder).toBe('@angular-devkit/build-angular:tslint');
});
it('should get defaultProject if name is not provided', async() => {
const options = {};
host = await schematicRunner.runSchematicAsync('ng-add', options, host).toPromise();
const content = host.readContent('/angular.json');
const json = JSON.parse(content);
const builder = json.projects.demo.architect.build.builder;
expect(builder).toBe('@angular/bazel:build');
});
describe('rxjs', () => {
const cases = [
// version|upgrade
['6.3.3', true],
['~6.3.3', true],
['^6.3.3', true],
['~6.3.11', true],
['6.4.0', false],
['~6.4.0', false],
['~6.4.1', false],
['6.5.0', false],
['~6.5.0', false],
['^6.5.0', false],
['~7.0.1', false],
];
for (const [version, upgrade] of cases) {
it(`should ${upgrade ? '' : 'not '}upgrade v${version}')`, async() => {
host.overwrite('package.json', JSON.stringify({
name: 'demo',
dependencies: {
'@angular/core': '1.2.3',
'rxjs': version,
},
devDependencies: {
'typescript': '3.2.2',
},
}));
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
expect(host.files).toContain('/package.json');
const content = host.readContent('/package.json');
const json = JSON.parse(content);
if (upgrade) {
expect(json.dependencies.rxjs).toBe('~6.4.0');
} else {
expect(json.dependencies.rxjs).toBe(version);
}
});
}
});
it('should add a postinstall step to package.json', async() => {
host = await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
expect(host.files).toContain('/package.json');
const content = host.readContent('/package.json');
const json = JSON.parse(content);
expect(json.scripts.postinstall).toBe('ngc -p ./angular-metadata.tsconfig.json');
});
it('should work when run on a minimal project (without test and e2e targets)', async() => {
host.overwrite('angular.json', JSON.stringify({
projects: {
'demo': {
architect: {
build: {},
serve: {},
'extract-i18n': {
builder: '@angular-devkit/build-angular:extract-i18n',
},
},
},
},
}));
let error: Error|null = null;
try {
await schematicRunner.runSchematicAsync('ng-add', defaultOptions, host).toPromise();
} catch (e) {
error = e;
}
expect(error).toBeNull();
});
});