2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
2020-05-19 15:08:49 -04:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2016-06-23 12:47:54 -04:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2017-05-18 16:46:51 -04:00
|
|
|
import {LIFECYCLE_HOOKS_VALUES, LifecycleHooks} from '@angular/compiler/src/lifecycle_reflector';
|
2017-03-02 12:37:01 -05:00
|
|
|
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, Directive, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation, ɵstringify as stringify} from '@angular/core';
|
2020-04-08 13:14:18 -04:00
|
|
|
import {async, inject, TestBed} from '@angular/core/testing';
|
2017-05-18 16:46:51 -04:00
|
|
|
|
2019-04-23 20:02:31 -04:00
|
|
|
import {CompileDiDependencyMetadata, identifierName} from '../src/compile_metadata';
|
2016-04-28 20:50:03 -04:00
|
|
|
import {CompileMetadataResolver} from '../src/metadata_resolver';
|
2016-11-10 17:07:30 -05:00
|
|
|
import {ResourceLoader} from '../src/resource_loader';
|
2017-03-02 15:12:46 -05:00
|
|
|
import {MockResourceLoader} from '../testing/src/resource_loader_mock';
|
2017-05-18 16:46:51 -04:00
|
|
|
|
2016-02-18 13:53:21 -05:00
|
|
|
import {MalformedStylesComponent} from './metadata_resolver_fixture';
|
2017-08-16 12:00:03 -04:00
|
|
|
import {TEST_COMPILER_PROVIDERS} from './test_bindings';
|
2015-09-14 18:59:09 -04:00
|
|
|
|
2017-12-16 17:42:55 -05:00
|
|
|
{
|
2016-02-18 13:53:21 -05:00
|
|
|
describe('CompileMetadataResolver', () => {
|
2020-04-08 13:14:18 -04:00
|
|
|
beforeEach(() => {
|
|
|
|
TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS});
|
|
|
|
});
|
2015-09-14 18:59:09 -04:00
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
it('should throw on the getDirectiveMetadata/getPipeMetadata methods if the module has not been loaded yet',
|
2016-11-10 17:07:30 -05:00
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Pipe({name: 'pipe'})
|
|
|
|
class SomePipe {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline))
|
2017-01-27 16:19:00 -05:00
|
|
|
.toThrowError(/Illegal state/);
|
|
|
|
expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
it('should read metadata in sync for components with inline resources',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [ComponentWithEverythingInline]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
2016-11-29 11:08:22 -05:00
|
|
|
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true);
|
2016-11-10 17:07:30 -05:00
|
|
|
|
|
|
|
const meta = resolver.getDirectiveMetadata(ComponentWithEverythingInline);
|
|
|
|
expect(meta.selector).toEqual('someSelector');
|
|
|
|
expect(meta.exportAs).toEqual('someExportAs');
|
|
|
|
expect(meta.isComponent).toBe(true);
|
|
|
|
expect(meta.type.reference).toBe(ComponentWithEverythingInline);
|
2016-11-23 12:42:19 -05:00
|
|
|
expect(identifierName(meta.type)).toEqual(stringify(ComponentWithEverythingInline));
|
2016-11-10 17:07:30 -05:00
|
|
|
expect(meta.type.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES);
|
|
|
|
expect(meta.changeDetection).toBe(ChangeDetectionStrategy.Default);
|
|
|
|
expect(meta.inputs).toEqual({'someProp': 'someProp'});
|
|
|
|
expect(meta.outputs).toEqual({'someEvent': 'someEvent'});
|
|
|
|
expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'});
|
|
|
|
expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'});
|
|
|
|
expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
|
2017-04-14 17:40:56 -04:00
|
|
|
expect(meta.template !.encapsulation).toBe(ViewEncapsulation.Emulated);
|
|
|
|
expect(meta.template !.styles).toEqual(['someStyle']);
|
|
|
|
expect(meta.template !.template).toEqual('someTemplate');
|
|
|
|
expect(meta.template !.interpolation).toEqual(['{{', '}}']);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw when reading metadata for component with external resources when sync=true is passed',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [ComponentWithExternalResources]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2020-04-08 13:14:18 -04:00
|
|
|
.toThrowError(`Can't compile synchronously as ${
|
|
|
|
stringify(ComponentWithExternalResources)} is still being loaded!`);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
it('should read external metadata when sync=false',
|
|
|
|
async(inject(
|
|
|
|
[CompileMetadataResolver, ResourceLoader],
|
|
|
|
(resolver: CompileMetadataResolver, resourceLoader: MockResourceLoader) => {
|
|
|
|
@NgModule({declarations: [ComponentWithExternalResources]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
|
|
|
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
2016-11-29 18:36:33 -05:00
|
|
|
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
|
2016-11-10 17:07:30 -05:00
|
|
|
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
|
|
|
expect(meta.selector).toEqual('someSelector');
|
2017-04-14 17:40:56 -04:00
|
|
|
expect(meta.template !.styleUrls).toEqual(['someStyleUrl']);
|
|
|
|
expect(meta.template !.templateUrl).toEqual('someTemplateUrl');
|
|
|
|
expect(meta.template !.template).toEqual('someTemplate');
|
2016-11-10 17:07:30 -05:00
|
|
|
});
|
|
|
|
resourceLoader.flush();
|
|
|
|
})));
|
|
|
|
|
2016-11-23 12:42:19 -05:00
|
|
|
it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
|
|
|
|
async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
|
|
|
|
class ComponentWithoutModuleId {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-10 17:07:30 -05:00
|
|
|
@NgModule({declarations: [ComponentWithoutModuleId]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 18:36:33 -05:00
|
|
|
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
|
2016-11-23 12:42:19 -05:00
|
|
|
const value: string =
|
2020-04-08 13:14:18 -04:00
|
|
|
resolver.getDirectiveMetadata(ComponentWithoutModuleId).template !.templateUrl!;
|
2016-11-23 12:42:19 -05:00
|
|
|
const expectedEndValue = './someUrl';
|
|
|
|
expect(value.endsWith(expectedEndValue)).toBe(true);
|
|
|
|
});
|
|
|
|
})));
|
2016-11-10 17:07:30 -05:00
|
|
|
|
|
|
|
it('should throw when the moduleId is not a string',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [ComponentWithInvalidModuleId]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
2017-01-27 16:19:00 -05:00
|
|
|
`moduleId should be a string in "ComponentWithInvalidModuleId". See` +
|
|
|
|
` https://goo.gl/wIDDiL for more information.\n` +
|
|
|
|
`If you're using Webpack you should inline the template and the styles, see` +
|
|
|
|
` https://goo.gl/X2J8zc.`);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
it('should throw when metadata is incorrectly typed',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [MalformedStylesComponent]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(`Expected 'styles' to be an array of strings.`);
|
|
|
|
}));
|
|
|
|
|
2017-03-06 20:00:25 -05:00
|
|
|
it('should throw with descriptive error message when a module imports itself',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({imports: [SomeModule]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
|
|
|
.toThrowError(`'SomeModule' module can't import itself`);
|
|
|
|
}));
|
|
|
|
|
2016-11-10 17:07:30 -05:00
|
|
|
it('should throw with descriptive error message when provider token can not be resolved',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [MyBrokenComp1]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2017-01-27 16:19:00 -05:00
|
|
|
.toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
2017-03-06 20:00:25 -05:00
|
|
|
|
2016-11-10 17:07:30 -05:00
|
|
|
it('should throw with descriptive error message when a directive is passed to imports',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({imports: [ComponentWithoutModuleId]})
|
|
|
|
class ModuleWithImportedComponent {
|
|
|
|
}
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(
|
|
|
|
() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
2017-03-14 20:12:18 -04:00
|
|
|
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'. Please add a @NgModule annotation.`);
|
2016-10-28 17:08:54 -04:00
|
|
|
}));
|
|
|
|
|
2016-11-10 17:07:30 -05:00
|
|
|
it('should throw with descriptive error message when a pipe is passed to imports',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@Pipe({name: 'somePipe'})
|
|
|
|
class SomePipe {
|
|
|
|
}
|
|
|
|
@NgModule({imports: [SomePipe]})
|
|
|
|
class ModuleWithImportedPipe {
|
|
|
|
}
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
2017-03-14 20:12:18 -04:00
|
|
|
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'. Please add a @NgModule annotation.`);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when a module is passed to declarations',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
@NgModule({declarations: [SomeModule]})
|
|
|
|
class ModuleWithDeclaredModule {
|
|
|
|
}
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
2017-03-14 20:12:18 -04:00
|
|
|
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'. Please add a @Pipe/@Directive/@Component annotation.`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when a declared pipe is missing annotation',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
class SomePipe {}
|
|
|
|
@NgModule({declarations: [SomePipe]})
|
|
|
|
class ModuleWithDeclaredModule {
|
|
|
|
}
|
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true))
|
|
|
|
.toThrowError(
|
|
|
|
`Unexpected value 'SomePipe' declared by the module 'ModuleWithDeclaredModule'. Please add a @Pipe/@Directive/@Component annotation.`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when an imported module is missing annotation',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
class SomeModule {}
|
|
|
|
@NgModule({imports: [SomeModule]})
|
|
|
|
class ModuleWithImportedModule {
|
|
|
|
}
|
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedModule, true))
|
|
|
|
.toThrowError(
|
|
|
|
`Unexpected value 'SomeModule' imported by the module 'ModuleWithImportedModule'. Please add a @NgModule annotation.`);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when null is passed to declarations',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
2020-04-08 13:14:18 -04:00
|
|
|
@NgModule({declarations: [null!]})
|
2016-11-10 17:07:30 -05:00
|
|
|
class ModuleWithNullDeclared {
|
|
|
|
}
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
|
|
|
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when null is passed to imports',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
2020-04-08 13:14:18 -04:00
|
|
|
@NgModule({imports: [null!]})
|
2016-11-10 17:07:30 -05:00
|
|
|
class ModuleWithNullImported {
|
|
|
|
}
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
|
|
|
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when a param token of a dependency is undefined',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [MyBrokenComp2]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2017-01-27 16:19:00 -05:00
|
|
|
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`);
|
2016-11-10 17:07:30 -05:00
|
|
|
}));
|
|
|
|
|
2016-12-27 17:02:28 -05:00
|
|
|
it('should throw with descriptive error message when encounter invalid provider',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
2020-04-08 13:14:18 -04:00
|
|
|
@NgModule({providers: [{provide: SimpleService, useClass: undefined!}]})
|
2016-12-27 17:02:28 -05:00
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2017-01-27 16:19:00 -05:00
|
|
|
.toThrowError(/Invalid provider for SimpleService. useClass cannot be undefined./);
|
2016-12-27 17:02:28 -05:00
|
|
|
}));
|
|
|
|
|
2016-12-27 20:05:14 -05:00
|
|
|
it('should throw with descriptive error message when provider is undefined',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
2020-04-08 13:14:18 -04:00
|
|
|
@NgModule({providers: [undefined!]})
|
2016-12-27 20:05:14 -05:00
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2017-01-27 16:19:00 -05:00
|
|
|
.toThrowError(/Encountered undefined provider!/);
|
2016-12-27 20:05:14 -05:00
|
|
|
}));
|
|
|
|
|
2016-11-10 17:07:30 -05:00
|
|
|
it('should throw with descriptive error message when one of providers is not present',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [MyBrokenComp3]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
|
|
|
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when one of viewProviders is not present',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [MyBrokenComp4]})
|
|
|
|
class SomeModule {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
|
|
|
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw with descriptive error message when null or undefined is passed to module bootstrap',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
2020-04-08 13:14:18 -04:00
|
|
|
@NgModule({bootstrap: [null!]})
|
2016-11-10 17:07:30 -05:00
|
|
|
class ModuleWithNullBootstrap {
|
|
|
|
}
|
2020-04-08 13:14:18 -04:00
|
|
|
@NgModule({bootstrap: [undefined!]})
|
2016-11-10 17:07:30 -05:00
|
|
|
class ModuleWithUndefinedBootstrap {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
|
|
|
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(
|
|
|
|
() =>
|
|
|
|
resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(
|
|
|
|
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should throw an error when the interpolation config has invalid symbols',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [ComponentWithInvalidInterpolation1]})
|
|
|
|
class Module1 {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module1, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(`[' ', ' '] contains unusable interpolation symbol.`);
|
|
|
|
|
|
|
|
@NgModule({declarations: [ComponentWithInvalidInterpolation2]})
|
|
|
|
class Module2 {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module2, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(`['{', '}'] contains unusable interpolation symbol.`);
|
|
|
|
|
|
|
|
@NgModule({declarations: [ComponentWithInvalidInterpolation3]})
|
|
|
|
class Module3 {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module3, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`);
|
|
|
|
|
|
|
|
@NgModule({declarations: [ComponentWithInvalidInterpolation4]})
|
|
|
|
class Module4 {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module4, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`);
|
|
|
|
|
|
|
|
@NgModule({declarations: [ComponentWithInvalidInterpolation5]})
|
|
|
|
class Module5 {
|
|
|
|
}
|
|
|
|
|
2016-11-29 11:08:22 -05:00
|
|
|
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module5, true))
|
2016-11-10 17:07:30 -05:00
|
|
|
.toThrowError(`['{', '}}'] contains unusable interpolation symbol.`);
|
|
|
|
}));
|
2017-02-06 20:55:58 -05:00
|
|
|
|
|
|
|
it(`should throw an error when a Pipe is added to module's bootstrap list`,
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@Pipe({name: 'pipe'})
|
|
|
|
class MyPipe {
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [MyPipe], bootstrap: [MyPipe]})
|
|
|
|
class ModuleWithPipeInBootstrap {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.getNgModuleMetadata(ModuleWithPipeInBootstrap))
|
|
|
|
.toThrowError(`MyPipe cannot be used as an entry component.`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it(`should throw an error when a Service is added to module's bootstrap list`,
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({declarations: [], bootstrap: [SimpleService]})
|
|
|
|
class ModuleWithServiceInBootstrap {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.getNgModuleMetadata(ModuleWithServiceInBootstrap))
|
|
|
|
.toThrowError(`SimpleService cannot be used as an entry component.`);
|
|
|
|
}));
|
|
|
|
|
2019-04-23 20:02:31 -04:00
|
|
|
it('should generate an error when a dependency could not be resolved',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
// Override the errorCollector so that error gets collected instead of
|
|
|
|
// being thrown.
|
|
|
|
(resolver as any)._errorCollector = (error: Error, type?: any) => {
|
|
|
|
expect(error.message).toBe(`Can't resolve all parameters for MyComponent: (?).`);
|
|
|
|
};
|
|
|
|
|
|
|
|
@Component({template: ''})
|
|
|
|
class MyComponent {
|
|
|
|
// @ts-ignore UserService is a non-existent class.
|
|
|
|
constructor(service: UserService) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [MyComponent]})
|
|
|
|
class AppModule {
|
|
|
|
}
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
const moduleMetadata = resolver.getNgModuleMetadata(AppModule)!;
|
2019-04-23 20:02:31 -04:00
|
|
|
expect(moduleMetadata).toBeTruthy();
|
|
|
|
expect(moduleMetadata.declaredDirectives.length).toBe(1);
|
|
|
|
const directive = moduleMetadata.declaredDirectives[0];
|
2020-04-08 13:14:18 -04:00
|
|
|
const directiveMetadata = resolver.getNonNormalizedDirectiveMetadata(directive.reference)!;
|
2019-04-23 20:02:31 -04:00
|
|
|
expect(directiveMetadata).toBeTruthy();
|
|
|
|
const {metadata} = directiveMetadata;
|
|
|
|
const diDeps: CompileDiDependencyMetadata[] = metadata.type.diDeps;
|
|
|
|
// 'null' does not conform to the shape of `CompileDiDependencyMetadata`
|
|
|
|
expect(diDeps.every(d => d !== null)).toBe(true);
|
|
|
|
}));
|
|
|
|
|
2017-02-06 20:55:58 -05:00
|
|
|
it(`should throw an error when a Directive is added to module's bootstrap list`,
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@Directive({selector: 'directive'})
|
|
|
|
class MyDirective {
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [], bootstrap: [MyDirective]})
|
|
|
|
class ModuleWithDirectiveInBootstrap {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.getNgModuleMetadata(ModuleWithDirectiveInBootstrap))
|
|
|
|
.toThrowError(`MyDirective cannot be used as an entry component.`);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it(`should not throw an error when a Component is added to module's bootstrap list`,
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@Component({template: ''})
|
|
|
|
class MyComp {
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [MyComp], bootstrap: [MyComp]})
|
|
|
|
class ModuleWithComponentInBootstrap {
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(() => resolver.getNgModuleMetadata(ModuleWithComponentInBootstrap)).not.toThrow();
|
|
|
|
}));
|
2017-11-06 13:01:27 -05:00
|
|
|
|
|
|
|
// #20049
|
|
|
|
it('should throw a reasonable message when an invalid import is given',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@NgModule({imports: [{ngModule: true as any}]})
|
|
|
|
class InvalidModule {
|
|
|
|
}
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
expect(() => {
|
|
|
|
resolver.getNgModuleMetadata(InvalidModule);
|
|
|
|
})
|
2017-11-06 13:01:27 -05:00
|
|
|
.toThrowError(
|
2018-04-18 20:09:41 -04:00
|
|
|
`Unexpected value '[object Object]' imported by the module 'InvalidModule'. Please add a @NgModule annotation.`);
|
2017-11-06 13:01:27 -05:00
|
|
|
}));
|
2015-09-14 18:59:09 -04:00
|
|
|
});
|
2016-11-10 17:07:30 -05:00
|
|
|
|
|
|
|
it('should dedupe declarations in NgModule',
|
|
|
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
|
|
|
@Component({template: ''})
|
|
|
|
class MyComp {
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [MyComp, MyComp]})
|
|
|
|
class MyModule {
|
|
|
|
}
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
const modMeta = resolver.getNgModuleMetadata(MyModule)!;
|
2016-11-10 17:07:30 -05:00
|
|
|
expect(modMeta.declaredDirectives.length).toBe(1);
|
|
|
|
expect(modMeta.declaredDirectives[0].reference).toBe(MyComp);
|
|
|
|
}));
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
|
|
|
|
2015-12-13 20:35:33 -05:00
|
|
|
@Component({selector: 'someComponent', template: ''})
|
|
|
|
class ComponentWithoutModuleId {
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
|
|
|
|
2016-09-14 19:49:13 -04:00
|
|
|
@Component({selector: 'someComponent', template: '', moduleId: <any>0})
|
|
|
|
class ComponentWithInvalidModuleId {
|
|
|
|
}
|
|
|
|
|
2016-11-10 17:07:30 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'someSelector',
|
|
|
|
templateUrl: 'someTemplateUrl',
|
|
|
|
styleUrls: ['someStyleUrl'],
|
|
|
|
})
|
|
|
|
class ComponentWithExternalResources {
|
|
|
|
}
|
|
|
|
|
2015-09-14 18:59:09 -04:00
|
|
|
@Component({
|
|
|
|
selector: 'someSelector',
|
2015-09-30 23:59:23 -04:00
|
|
|
inputs: ['someProp'],
|
|
|
|
outputs: ['someEvent'],
|
2015-09-14 18:59:09 -04:00
|
|
|
host: {
|
|
|
|
'[someHostProp]': 'someHostPropExpr',
|
|
|
|
'(someHostListener)': 'someHostListenerExpr',
|
|
|
|
'someHostAttr': 'someHostAttrValue'
|
|
|
|
},
|
2015-09-18 13:33:23 -04:00
|
|
|
exportAs: 'someExportAs',
|
2015-09-14 18:59:09 -04:00
|
|
|
moduleId: 'someModuleId',
|
2016-06-27 23:00:30 -04:00
|
|
|
changeDetection: ChangeDetectionStrategy.Default,
|
2015-09-14 18:59:09 -04:00
|
|
|
template: 'someTemplate',
|
|
|
|
encapsulation: ViewEncapsulation.Emulated,
|
|
|
|
styles: ['someStyle'],
|
2016-06-20 12:52:41 -04:00
|
|
|
interpolation: ['{{', '}}']
|
2015-09-14 18:59:09 -04:00
|
|
|
})
|
2020-04-08 13:14:18 -04:00
|
|
|
class ComponentWithEverythingInline implements OnChanges, OnInit, DoCheck, OnDestroy,
|
|
|
|
AfterContentInit, AfterContentChecked, AfterViewInit,
|
|
|
|
AfterViewChecked {
|
2016-05-09 18:45:04 -04:00
|
|
|
ngOnChanges(changes: SimpleChanges): void {}
|
refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
* onInit
* onDestroy
* doCheck
* onChanges
* afterContentInit
* afterContentChecked
* afterViewInit
* afterViewChecked
* _Router Hooks_
* onActivate
* onReuse
* onDeactivate
* canReuse
* canDeactivate
To:
* ngOnInit,
* ngOnDestroy,
* ngDoCheck,
* ngOnChanges,
* ngAfterContentInit,
* ngAfterContentChecked,
* ngAfterViewInit,
* ngAfterViewChecked
* _Router Hooks_
* routerOnActivate
* routerOnReuse
* routerOnDeactivate
* routerCanReuse
* routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
2015-11-16 20:04:36 -05:00
|
|
|
ngOnInit(): void {}
|
|
|
|
ngDoCheck(): void {}
|
|
|
|
ngOnDestroy(): void {}
|
|
|
|
ngAfterContentInit(): void {}
|
|
|
|
ngAfterContentChecked(): void {}
|
|
|
|
ngAfterViewInit(): void {}
|
|
|
|
ngAfterViewChecked(): void {}
|
2015-09-14 18:59:09 -04:00
|
|
|
}
|
2016-06-09 19:07:06 -04:00
|
|
|
|
|
|
|
@Component({selector: 'my-broken-comp', template: ''})
|
|
|
|
class MyBrokenComp1 {
|
|
|
|
constructor(public dependency: any) {}
|
|
|
|
}
|
2016-06-20 12:52:41 -04:00
|
|
|
|
2016-06-18 12:42:34 -04:00
|
|
|
class NonAnnotatedService {
|
|
|
|
constructor(dep: any) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({selector: 'my-broken-comp', template: '', providers: [NonAnnotatedService]})
|
|
|
|
class MyBrokenComp2 {
|
|
|
|
constructor(dependency: NonAnnotatedService) {}
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:27:27 -04:00
|
|
|
@Injectable()
|
|
|
|
class SimpleService {
|
|
|
|
}
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
@Component({selector: 'my-broken-comp', template: '', providers: [SimpleService, null!, [null]]})
|
2016-06-21 20:27:27 -04:00
|
|
|
class MyBrokenComp3 {
|
|
|
|
}
|
|
|
|
|
2017-03-24 12:59:58 -04:00
|
|
|
@Component(
|
2020-04-08 13:14:18 -04:00
|
|
|
{selector: 'my-broken-comp', template: '', viewProviders: [null!, SimpleService, [null]]})
|
2016-06-21 20:27:27 -04:00
|
|
|
class MyBrokenComp4 {
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:52:41 -04:00
|
|
|
@Component({selector: 'someSelector', template: '', interpolation: [' ', ' ']})
|
|
|
|
class ComponentWithInvalidInterpolation1 {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({selector: 'someSelector', template: '', interpolation: ['{', '}']})
|
|
|
|
class ComponentWithInvalidInterpolation2 {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({selector: 'someSelector', template: '', interpolation: ['<%', '%>']})
|
|
|
|
class ComponentWithInvalidInterpolation3 {
|
|
|
|
}
|
2016-06-21 19:55:17 -04:00
|
|
|
|
|
|
|
@Component({selector: 'someSelector', template: '', interpolation: ['&#', '}}']})
|
|
|
|
class ComponentWithInvalidInterpolation4 {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({selector: 'someSelector', template: '', interpolation: ['{', '}}']})
|
|
|
|
class ComponentWithInvalidInterpolation5 {
|
|
|
|
}
|