From 7a78889994c104f410e796c0c533a02b40465c6f Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Tue, 25 Sep 2018 15:35:03 -0700 Subject: [PATCH] test(ivy): refactor ngtsc tests to use an NgtscTestEnvironment helper (#26203) This commit gets ready for the introduction of ngtsc template type-checking tests by refactoring test environment setup into a custom helper. This helper will simplify the authoring of future ngtsc tests. Ngtsc tests previously returned a numeric error code (a la ngtsc's CLI interface) if any TypeScript errors occurred. The helper has the ability to run ngtsc and return the actual array of ts.Diagnostics, which greatly increases the ability to write clean tests. PR Close #26203 --- packages/compiler-cli/src/main.ts | 11 + packages/compiler-cli/test/ngtsc/BUILD.bazel | 4 +- packages/compiler-cli/test/ngtsc/env.ts | 125 +++++++ .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 351 ++++++------------ 4 files changed, 249 insertions(+), 242 deletions(-) create mode 100644 packages/compiler-cli/test/ngtsc/env.ts diff --git a/packages/compiler-cli/src/main.ts b/packages/compiler-cli/src/main.ts index f7c29cfa4f..0801be6106 100644 --- a/packages/compiler-cli/src/main.ts +++ b/packages/compiler-cli/src/main.ts @@ -37,6 +37,17 @@ export function main( return reportErrorsAndExit(compileDiags, options, consoleError); } +export function mainDiagnosticsForTest( + args: string[], config?: NgcParsedConfiguration): ReadonlyArray { + let {project, rootNames, options, errors: configErrors, watch, emitFlags} = + config || readNgcCommandLineAndConfiguration(args); + if (configErrors.length) { + return configErrors; + } + const {diagnostics: compileDiags} = performCompilation( + {rootNames, options, emitFlags, emitCallback: createEmitCallback(options)}); + return compileDiags; +} function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined { const transformDecorators = options.enableIvy !== 'ngtsc' && options.enableIvy !== 'tsc' && diff --git a/packages/compiler-cli/test/ngtsc/BUILD.bazel b/packages/compiler-cli/test/ngtsc/BUILD.bazel index 96fa99c5ec..5040f9048a 100644 --- a/packages/compiler-cli/test/ngtsc/BUILD.bazel +++ b/packages/compiler-cli/test/ngtsc/BUILD.bazel @@ -3,9 +3,7 @@ load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") ts_library( name = "ngtsc_lib", testonly = 1, - srcs = [ - "ngtsc_spec.ts", - ], + srcs = glob(["**/*.ts"]), deps = [ "//packages/compiler", "//packages/compiler-cli", diff --git a/packages/compiler-cli/test/ngtsc/env.ts b/packages/compiler-cli/test/ngtsc/env.ts new file mode 100644 index 0000000000..5106de7999 --- /dev/null +++ b/packages/compiler-cli/test/ngtsc/env.ts @@ -0,0 +1,125 @@ +/** + * @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 * as fs from 'fs'; +import * as path from 'path'; +import * as ts from 'typescript'; + +import {main, mainDiagnosticsForTest} from '../../src/main'; +import {TestSupport, isInBazel, setup} from '../test_support'; + +function setupFakeCore(support: TestSupport): void { + if (!process.env.TEST_SRCDIR) { + throw new Error('`setupFakeCore` must be run within a Bazel test'); + } + const fakeCore = path.join( + process.env.TEST_SRCDIR, 'angular/packages/compiler-cli/test/ngtsc/fake_core/npm_package'); + + const nodeModulesPath = path.join(support.basePath, 'node_modules'); + const angularCoreDirectory = path.join(nodeModulesPath, '@angular/core'); + + fs.symlinkSync(fakeCore, angularCoreDirectory); +} + +/** + * Manages a temporary testing directory structure and environment for testing ngtsc by feeding it + * TypeScript code. + */ +export class NgtscTestEnvironment { + private constructor(private support: TestSupport, readonly outDir: string) {} + + get basePath(): string { return this.support.basePath; } + + /** + * Set up a new testing environment. + */ + static setup(): NgtscTestEnvironment { + if (!NgtscTestEnvironment.supported) { + throw new Error(`Attempting to setup ngtsc tests in an unsupported environment`); + } + + const support = setup(); + const outDir = path.join(support.basePath, 'built'); + process.chdir(support.basePath); + + setupFakeCore(support); + + const env = new NgtscTestEnvironment(support, outDir); + + env.write('tsconfig-base.json', `{ + "compilerOptions": { + "experimentalDecorators": true, + "skipLibCheck": true, + "noImplicitAny": true, + "strictNullChecks": true, + "types": [], + "outDir": "built", + "rootDir": ".", + "baseUrl": ".", + "declaration": true, + "target": "es5", + "module": "es2015", + "moduleResolution": "node", + "lib": ["es6", "dom"], + "typeRoots": ["node_modules/@types"] + }, + "angularCompilerOptions": { + "enableIvy": "ngtsc" + } + }`); + + return env; + } + + assertExists(fileName: string) { + if (!fs.existsSync(path.resolve(this.outDir, fileName))) { + throw new Error(`Expected ${fileName} to be emitted (outDir: ${this.outDir})`); + } + } + + assertDoesNotExist(fileName: string) { + if (fs.existsSync(path.resolve(this.outDir, fileName))) { + throw new Error(`Did not expect ${fileName} to be emitted (outDir: ${this.outDir})`); + } + } + + getContents(fileName: string): string { + this.assertExists(fileName); + const modulePath = path.resolve(this.outDir, fileName); + return fs.readFileSync(modulePath, 'utf8'); + } + + write(fileName: string, content: string) { this.support.write(fileName, content); } + + tsconfig(extraOpts: {[key: string]: string | boolean} = {}): void { + const opts = JSON.stringify({...extraOpts, 'enableIvy': 'ngtsc'}); + const tsconfig: string = + `{"extends": "./tsconfig-base.json", "angularCompilerOptions": ${opts}}`; + this.write('tsconfig.json', tsconfig); + } + + /** + * Run the compiler to completion, and assert that no errors occurred. + */ + driveMain(): void { + const errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error); + const exitCode = main(['-p', this.basePath], errorSpy); + expect(errorSpy).not.toHaveBeenCalled(); + expect(exitCode).toBe(0); + } + + /** + * Run the compiler to completion, and return any `ts.Diagnostic` errors that may have occurred. + */ + driveDiagnostics(): ReadonlyArray { + // Cast is safe as ngtsc mode only produces ts.Diagnostics. + return mainDiagnosticsForTest(['-p', this.basePath]) as ReadonlyArray; + } + + static get supported(): boolean { return isInBazel(); } +} diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 3503d95c1e..a440d02ccd 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -6,102 +6,21 @@ * found in the LICENSE file at https://angular.io/license */ -import * as fs from 'fs'; -import * as path from 'path'; -import * as ts from 'typescript'; - -import {main, readCommandLineAndConfiguration, watchMode} from '../../src/main'; -import {TestSupport, isInBazel, makeTempDir, setup} from '../test_support'; - -function setupFakeCore(support: TestSupport): void { - if (!process.env.TEST_SRCDIR) { - throw new Error('`setupFakeCore` must be run within a Bazel test'); - } - const fakeCore = path.join( - process.env.TEST_SRCDIR, 'angular/packages/compiler-cli/test/ngtsc/fake_core/npm_package'); - - const nodeModulesPath = path.join(support.basePath, 'node_modules'); - const angularCoreDirectory = path.join(nodeModulesPath, '@angular/core'); - - fs.symlinkSync(fakeCore, angularCoreDirectory); -} - -function getNgRootDir() { - const moduleFilename = module.filename.replace(/\\/g, '/'); - const distIndex = moduleFilename.indexOf('/dist/all'); - return moduleFilename.substr(0, distIndex); -} +import {NgtscTestEnvironment} from './env'; describe('ngtsc behavioral tests', () => { - if (!isInBazel()) { + if (!NgtscTestEnvironment.supported) { // These tests should be excluded from the non-Bazel build. return; } - let basePath: string; - let outDir: string; - let write: (fileName: string, content: string) => void; - let errorSpy: jasmine.Spy&((s: string) => void); + let env !: NgtscTestEnvironment; - function shouldExist(fileName: string) { - if (!fs.existsSync(path.resolve(outDir, fileName))) { - throw new Error(`Expected ${fileName} to be emitted (outDir: ${outDir})`); - } - } - - function shouldNotExist(fileName: string) { - if (fs.existsSync(path.resolve(outDir, fileName))) { - throw new Error(`Did not expect ${fileName} to be emitted (outDir: ${outDir})`); - } - } - - function getContents(fileName: string): string { - shouldExist(fileName); - const modulePath = path.resolve(outDir, fileName); - return fs.readFileSync(modulePath, 'utf8'); - } - - function writeConfig(extraOpts: {[key: string]: string | boolean} = {}): void { - const opts = JSON.stringify({...extraOpts, 'enableIvy': 'ngtsc'}); - const tsconfig: string = - `{"extends": "./tsconfig-base.json", "angularCompilerOptions": ${opts}}`; - write('tsconfig.json', tsconfig); - } - - beforeEach(() => { - errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error); - const support = setup(); - basePath = support.basePath; - outDir = path.join(basePath, 'built'); - process.chdir(basePath); - write = (fileName: string, content: string) => { support.write(fileName, content); }; - - setupFakeCore(support); - write('tsconfig-base.json', `{ - "compilerOptions": { - "experimentalDecorators": true, - "skipLibCheck": true, - "noImplicitAny": true, - "types": [], - "outDir": "built", - "rootDir": ".", - "baseUrl": ".", - "declaration": true, - "target": "es5", - "module": "es2015", - "moduleResolution": "node", - "lib": ["es6", "dom"], - "typeRoots": ["node_modules/@types"] - }, - "angularCompilerOptions": { - "enableIvy": "ngtsc" - } - }`); - }); + beforeEach(() => { env = NgtscTestEnvironment.setup(); }); it('should compile Injectables without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Injectable} from '@angular/core'; @Injectable() @@ -113,23 +32,21 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('Dep.ngInjectableDef ='); expect(jsContents).toContain('Service.ngInjectableDef ='); expect(jsContents).not.toContain('__decorate'); - const dtsContents = getContents('test.d.ts'); + const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); }); it('should compile Components without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -139,23 +56,21 @@ describe('ngtsc behavioral tests', () => { export class TestCmp {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('TestCmp.ngComponentDef = i0.ɵdefineComponent'); expect(jsContents).not.toContain('__decorate'); - const dtsContents = getContents('test.d.ts'); + const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( 'static ngComponentDef: i0.ɵComponentDefWithMeta'); }); it('should compile Components without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -164,19 +79,17 @@ describe('ngtsc behavioral tests', () => { }) export class TestCmp {} `); - write('dir/test.html', '

Hello World

'); + env.write('dir/test.html', '

Hello World

'); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('Hello World'); }); it('should compile NgModules without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @Component({ @@ -192,17 +105,15 @@ describe('ngtsc behavioral tests', () => { export class TestModule {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents) .toContain( 'i0.ɵdefineNgModule({ type: TestModule, bootstrap: [TestCmp], ' + 'declarations: [TestCmp], imports: [], exports: [] })'); - const dtsContents = getContents('test.d.ts'); + const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( 'static ngComponentDef: i0.ɵComponentDefWithMeta'); @@ -213,8 +124,8 @@ describe('ngtsc behavioral tests', () => { }); it('should compile NgModules with services without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; export class Token {} @@ -236,11 +147,9 @@ describe('ngtsc behavioral tests', () => { export class TestModule {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('i0.ɵdefineNgModule({ type: TestModule,'); expect(jsContents) .toContain( @@ -248,7 +157,7 @@ describe('ngtsc behavioral tests', () => { `function TestModule_Factory(t) { return new (t || TestModule)(); }, providers: [{ provide: ` + `Token, useValue: 'test' }], imports: [[OtherModule]] });`); - const dtsContents = getContents('test.d.ts'); + const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( 'static ngModuleDef: i0.ɵNgModuleDefWithMeta'); @@ -256,8 +165,8 @@ describe('ngtsc behavioral tests', () => { }); it('should compile NgModules with references to local components', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {Foo} from './foo'; @@ -266,18 +175,16 @@ describe('ngtsc behavioral tests', () => { }) export class FooModule {} `); - write('foo.ts', ` + env.write('foo.ts', ` import {Component} from '@angular/core'; @Component({selector: 'foo', template: ''}) export class Foo {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); - const dtsContents = getContents('test.d.ts'); + const jsContents = env.getContents('test.js'); + const dtsContents = env.getContents('test.d.ts'); expect(jsContents).toContain('import { Foo } from \'./foo\';'); expect(jsContents).not.toMatch(/as i[0-9] from '.\/foo'/); @@ -285,8 +192,8 @@ describe('ngtsc behavioral tests', () => { }); it('should compile NgModules with references to absolute components', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {Foo} from 'foo'; @@ -295,19 +202,17 @@ describe('ngtsc behavioral tests', () => { }) export class FooModule {} `); - write('node_modules/foo/index.d.ts', ` + env.write('node_modules/foo/index.d.ts', ` import * as i0 from '@angular/core'; export class Foo { static ngComponentDef: i0.ɵComponentDef; } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); - const dtsContents = getContents('test.d.ts'); + const jsContents = env.getContents('test.js'); + const dtsContents = env.getContents('test.d.ts'); expect(jsContents).toContain('import { Foo } from \'foo\';'); expect(jsContents).not.toMatch(/as i[0-9] from 'foo'/); @@ -315,8 +220,8 @@ describe('ngtsc behavioral tests', () => { }); it('should compile Pipes without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Pipe} from '@angular/core'; @Pipe({ @@ -326,12 +231,10 @@ describe('ngtsc behavioral tests', () => { export class TestPipe {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); - const dtsContents = getContents('test.d.ts'); + const jsContents = env.getContents('test.js'); + const dtsContents = env.getContents('test.d.ts'); expect(jsContents) .toContain( @@ -342,8 +245,8 @@ describe('ngtsc behavioral tests', () => { }); it('should compile pure Pipes without errors', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Pipe} from '@angular/core'; @Pipe({ @@ -352,12 +255,10 @@ describe('ngtsc behavioral tests', () => { export class TestPipe {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); - const dtsContents = getContents('test.d.ts'); + const jsContents = env.getContents('test.js'); + const dtsContents = env.getContents('test.d.ts'); expect(jsContents) .toContain( @@ -368,8 +269,8 @@ describe('ngtsc behavioral tests', () => { }); it('should compile Pipes with dependencies', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Pipe} from '@angular/core'; export class Dep {} @@ -383,17 +284,15 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('return new (t || TestPipe)(i0.ɵdirectiveInject(Dep));'); }); it('should include @Pipes in @NgModule scopes', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component, NgModule, Pipe} from '@angular/core'; @Pipe({name: 'test'}) @@ -406,22 +305,20 @@ describe('ngtsc behavioral tests', () => { export class TestModule {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('pipes: [TestPipe]'); - const dtsContents = getContents('test.d.ts'); + const dtsContents = env.getContents('test.d.ts'); expect(dtsContents) .toContain( 'i0.ɵNgModuleDefWithMeta'); }); it('should unwrap a ModuleWithProviders function if a generic type is provided for it', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -429,7 +326,7 @@ describe('ngtsc behavioral tests', () => { export class TestModule {} `); - write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import {ModuleWithProviders} from '@angular/core'; declare class RouterModule { @@ -437,22 +334,20 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); - const dtsContents = getContents('test.d.ts'); + const dtsContents = env.getContents('test.d.ts'); expect(dtsContents).toContain(`import * as i1 from 'router';`); expect(dtsContents) .toContain('i0.ɵNgModuleDefWithMeta'); }); it('should inject special types according to the metadata', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import { Attribute, ChangeDetectorRef, @@ -481,18 +376,16 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents) .toContain( `factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵinjectRenderer2(), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`); }); it('should generate queries for components', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import {Component, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core'; @Component({ @@ -511,10 +404,8 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain(`i0.ɵquery(null, ["bar"], true, TemplateRef)`); expect(jsContents).toContain(`i0.ɵquery(null, TemplateRef, false)`); expect(jsContents).toContain(`i0.ɵquery(null, ["test2"], true)`); @@ -523,8 +414,8 @@ describe('ngtsc behavioral tests', () => { }); it('should handle queries that use forwardRef', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import {Component, ContentChild, TemplateRef, ViewContainerRef, forwardRef} from '@angular/core'; @Component({ @@ -538,17 +429,15 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain(`i0.ɵquery(null, TemplateRef, true)`); expect(jsContents).toContain(`i0.ɵquery(null, ViewContainerRef, true)`); }); it('should generate host bindings for directives', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import {Component, HostBinding, HostListener, TemplateRef} from '@angular/core'; @Component({ @@ -571,10 +460,8 @@ describe('ngtsc behavioral tests', () => { } `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents) .toContain( `i0.ɵelementProperty(elIndex, "attr.hello", i0.ɵbind(i0.ɵloadDirective(dirIndex).foo));`); @@ -590,8 +477,8 @@ describe('ngtsc behavioral tests', () => { }); it('should correctly recognize local symbols', () => { - writeConfig(); - write('module.ts', ` + env.tsconfig(); + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir, Comp} from './test'; @@ -601,7 +488,7 @@ describe('ngtsc behavioral tests', () => { }) class Module {} `); - write(`test.ts`, ` + env.write(`test.ts`, ` import {Component, Directive} from '@angular/core'; @Directive({ @@ -616,16 +503,14 @@ describe('ngtsc behavioral tests', () => { export class Comp {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents).not.toMatch(/import \* as i[0-9] from ['"].\/test['"]/); }); it('should generate exportAs declarations', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component, Directive} from '@angular/core'; @Directive({ @@ -635,18 +520,16 @@ describe('ngtsc behavioral tests', () => { class Dir {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain(`exportAs: "foo"`); }); it('should generate correct factory stubs for a test module', () => { - writeConfig({'allowEmptyCodegenFiles': true}); + env.tsconfig({'allowEmptyCodegenFiles': true}); - write('test.ts', ` + env.write('test.ts', ` import {Injectable, NgModule} from '@angular/core'; @Injectable() @@ -656,18 +539,16 @@ describe('ngtsc behavioral tests', () => { export class TestModule {} `); - write('empty.ts', ` + env.write('empty.ts', ` import {Injectable} from '@angular/core'; @Injectable() export class NotAModule {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const factoryContents = getContents('test.ngfactory.js'); + const factoryContents = env.getContents('test.ngfactory.js'); expect(factoryContents).toContain(`import * as i0 from '@angular/core';`); expect(factoryContents).toContain(`import { NotAModule, TestModule } from './test';`); expect(factoryContents) @@ -675,14 +556,14 @@ describe('ngtsc behavioral tests', () => { expect(factoryContents).not.toContain(`NotAModuleNgFactory`); expect(factoryContents).not.toContain('ɵNonEmptyModule'); - const emptyFactory = getContents('empty.ngfactory.js'); + const emptyFactory = env.getContents('empty.ngfactory.js'); expect(emptyFactory).toContain(`import * as i0 from '@angular/core';`); expect(emptyFactory).toContain(`export var ɵNonEmptyModule = true;`); }); it('should compile a banana-in-a-box inside of a template', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -692,14 +573,12 @@ describe('ngtsc behavioral tests', () => { class TestCmp {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); }); it('generates inherited factory definitions', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import {Injectable} from '@angular/core'; class Dep {} @@ -721,10 +600,8 @@ describe('ngtsc behavioral tests', () => { `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents) .toContain('function Base_Factory(t) { return new (t || Base)(i0.inject(Dep)); }'); @@ -736,8 +613,8 @@ describe('ngtsc behavioral tests', () => { }); it('generates base factories for directives', () => { - writeConfig(); - write(`test.ts`, ` + env.tsconfig(); + env.write(`test.ts`, ` import {Directive} from '@angular/core'; class Base {} @@ -750,18 +627,16 @@ describe('ngtsc behavioral tests', () => { `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); - const jsContents = getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('var ɵDir_BaseFactory = i0.ɵgetInheritedFactory(Dir)'); }); it('should wrap "directives" in component metadata in a closure when forward references are present', () => { - writeConfig(); - write('test.ts', ` + env.tsconfig(); + env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @Component({ @@ -782,11 +657,9 @@ describe('ngtsc behavioral tests', () => { class Module {} `); - const exitCode = main(['-p', basePath], errorSpy); - expect(errorSpy).not.toHaveBeenCalled(); - expect(exitCode).toBe(0); + env.driveMain(); - const jsContents = getContents('test.js'); + const jsContents = env.getContents('test.js'); expect(jsContents).toContain('directives: function () { return [CmpB]; }'); }); });