refactor(compiler): don't print stack trace on template parse errors (#13390)

This commit is contained in:
Bowen Ni 2016-12-15 13:07:12 -08:00 committed by Chuck Jazdzewski
parent 14e785f5b7
commit f0e092515c
13 changed files with 102 additions and 71 deletions

View File

@ -14,6 +14,7 @@ import 'reflect-metadata';
import * as ts from 'typescript'; import * as ts from 'typescript';
import * as tsc from '@angular/tsc-wrapped'; import * as tsc from '@angular/tsc-wrapped';
import {SyntaxError} from '@angular/compiler';
import {CodeGenerator} from './codegen'; import {CodeGenerator} from './codegen';
function codegen( function codegen(
@ -28,7 +29,7 @@ export function main(
const cliOptions = new tsc.NgcCliOptions(args); const cliOptions = new tsc.NgcCliOptions(args);
return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => { return tsc.main(project, cliOptions, codegen).then(() => 0).catch(e => {
if (e instanceof tsc.UserError) { if (e instanceof tsc.UserError || e instanceof SyntaxError) {
consoleError(e.message); consoleError(e.message);
return Promise.resolve(1); return Promise.resolve(1);
} else { } else {

View File

@ -62,4 +62,5 @@ export * from './src/style_compiler';
export * from './src/template_parser/template_parser'; export * from './src/template_parser/template_parser';
export {ViewCompiler} from './src/view_compiler/view_compiler'; export {ViewCompiler} from './src/view_compiler/view_compiler';
export {AnimationParser} from './src/animation/animation_parser'; export {AnimationParser} from './src/animation/animation_parser';
export {SyntaxError} from './src/util';
// This file only reexports content of the `src` folder. Keep it that way. // This file only reexports content of the `src` folder. Keep it that way.

View File

@ -88,7 +88,7 @@ export class StaticSymbolResolver {
} }
} else { } else {
let value = baseMetadata; let value = baseMetadata;
for (var i = 0; i < members.length && value; i++) { for (let i = 0; i < members.length && value; i++) {
value = value[members[i]]; value = value[members[i]];
} }
return new ResolvedStaticSymbol(staticSymbol, value); return new ResolvedStaticSymbol(staticSymbol, value);

View File

@ -127,7 +127,7 @@ export enum CompileSummaryKind {
* the directive / module itself. * the directive / module itself.
*/ */
export interface CompileTypeSummary { export interface CompileTypeSummary {
summaryKind: CompileSummaryKind summaryKind: CompileSummaryKind;
type: CompileTypeMetadata; type: CompileTypeMetadata;
} }

View File

@ -19,7 +19,7 @@ import {ResourceLoader} from './resource_loader';
import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver'; import {extractStyleUrls, isStyleUrlResolvable} from './style_url_resolver';
import {PreparsedElementType, preparseElement} from './template_parser/template_preparser'; import {PreparsedElementType, preparseElement} from './template_parser/template_preparser';
import {UrlResolver} from './url_resolver'; import {UrlResolver} from './url_resolver';
import {SyncAsyncResult} from './util'; import {SyncAsyncResult, SyntaxError} from './util';
export interface PrenormalizedTemplateMetadata { export interface PrenormalizedTemplateMetadata {
componentType: any; componentType: any;
@ -71,7 +71,7 @@ export class DirectiveNormalizer {
} else if (prenormData.templateUrl) { } else if (prenormData.templateUrl) {
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData); normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
} else { } else {
throw new Error( throw new SyntaxError(
`No template specified for component ${stringify(prenormData.componentType)}`); `No template specified for component ${stringify(prenormData.componentType)}`);
} }
@ -105,7 +105,7 @@ export class DirectiveNormalizer {
template, stringify(prenomData.componentType), false, interpolationConfig); template, stringify(prenomData.componentType), false, interpolationConfig);
if (rootNodesAndErrors.errors.length > 0) { if (rootNodesAndErrors.errors.length > 0) {
const errorString = rootNodesAndErrors.errors.join('\n'); const errorString = rootNodesAndErrors.errors.join('\n');
throw new Error(`Template parse errors:\n${errorString}`); throw new SyntaxError(`Template parse errors:\n${errorString}`);
} }
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({ const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
styles: prenomData.styles, styles: prenomData.styles,

View File

@ -24,7 +24,7 @@ import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, ref
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver'; import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyncAsyncResult, ValueTransformer, visitValue} from './util'; import {MODULE_SUFFIX, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void; export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector'); export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
@ -253,7 +253,7 @@ export class CompileMetadataResolver {
// Directive // Directive
if (!selector) { if (!selector) {
this._reportError( this._reportError(
new Error(`Directive ${stringifyType(directiveType)} has no selector, please add it!`), new SyntaxError(`Directive ${stringifyType(directiveType)} has no selector, please add it!`),
directiveType); directiveType);
selector = 'error'; selector = 'error';
} }
@ -299,7 +299,7 @@ export class CompileMetadataResolver {
const dirMeta = this._directiveCache.get(directiveType); const dirMeta = this._directiveCache.get(directiveType);
if (!dirMeta) { if (!dirMeta) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`), `Illegal state: getDirectiveMetadata can only be called after loadNgModuleMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`),
directiveType); directiveType);
} }
@ -311,7 +311,7 @@ export class CompileMetadataResolver {
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (!dirSummary) { if (!dirSummary) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`), `Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`),
dirType); dirType);
} }
@ -394,7 +394,7 @@ export class CompileMetadataResolver {
const importedModuleSummary = this.getNgModuleSummary(importedModuleType); const importedModuleSummary = this.getNgModuleSummary(importedModuleType);
if (!importedModuleSummary) { if (!importedModuleSummary) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), `Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -402,7 +402,7 @@ export class CompileMetadataResolver {
importedModules.push(importedModuleSummary); importedModules.push(importedModuleSummary);
} else { } else {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -414,7 +414,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.exports).forEach((exportedType) => { flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
if (!isValidType(exportedType)) { if (!isValidType(exportedType)) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -435,7 +435,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.declarations).forEach((declaredType) => { flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
if (!isValidType(declaredType)) { if (!isValidType(declaredType)) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -452,7 +452,7 @@ export class CompileMetadataResolver {
this._addTypeToModule(declaredType, moduleType); this._addTypeToModule(declaredType, moduleType);
} else { } else {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), `Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -471,7 +471,7 @@ export class CompileMetadataResolver {
transitiveModule.addExportedPipe(exportedId); transitiveModule.addExportedPipe(exportedId);
} else { } else {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`), `Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
moduleType); moduleType);
} }
@ -494,7 +494,7 @@ export class CompileMetadataResolver {
flattenAndDedupeArray(meta.bootstrap).forEach(type => { flattenAndDedupeArray(meta.bootstrap).forEach(type => {
if (!isValidType(type)) { if (!isValidType(type)) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`), `Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`),
moduleType); moduleType);
return; return;
@ -557,7 +557,7 @@ export class CompileMetadataResolver {
const oldModule = this._ngModuleOfTypes.get(type); const oldModule = this._ngModuleOfTypes.get(type);
if (oldModule && oldModule !== moduleType) { if (oldModule && oldModule !== moduleType) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` + `Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` +
`Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` + `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
@ -653,7 +653,7 @@ export class CompileMetadataResolver {
const pipeMeta = this._pipeCache.get(pipeType); const pipeMeta = this._pipeCache.get(pipeType);
if (!pipeMeta) { if (!pipeMeta) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`), `Illegal state: getPipeMetadata can only be called after loadNgModuleMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`),
pipeType); pipeType);
} }
@ -665,7 +665,7 @@ export class CompileMetadataResolver {
<cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe); <cpl.CompilePipeSummary>this._loadSummary(pipeType, cpl.CompileSummaryKind.Pipe);
if (!pipeSummary) { if (!pipeSummary) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`), `Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`),
pipeType); pipeType);
} }
@ -748,7 +748,7 @@ export class CompileMetadataResolver {
const depsTokens = const depsTokens =
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', '); dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
this._reportError( this._reportError(
new Error( new SyntaxError(
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`), `Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`),
typeOrFunc); typeOrFunc);
} }
@ -797,7 +797,7 @@ export class CompileMetadataResolver {
[])) []))
.join(', '); .join(', ');
this._reportError( this._reportError(
new Error( new SyntaxError(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`), `Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
type); type);
} }
@ -818,13 +818,14 @@ export class CompileMetadataResolver {
if (provider.useFactory || provider.useExisting || provider.useClass) { if (provider.useFactory || provider.useExisting || provider.useClass) {
this._reportError( this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type); new SyntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
return []; return [];
} }
if (!provider.multi) { if (!provider.multi) {
this._reportError( this._reportError(
new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type); new SyntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`),
type);
return []; return [];
} }
@ -893,7 +894,7 @@ export class CompileMetadataResolver {
} else { } else {
if (!q.selector) { if (!q.selector) {
this._reportError( this._reportError(
new Error( new SyntaxError(
`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), `Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc); typeOrFunc);
} }
@ -961,7 +962,7 @@ export function componentModuleUrl(
const scheme = getUrlScheme(moduleId); const scheme = getUrlScheme(moduleId);
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`; return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
} else if (moduleId !== null && moduleId !== void 0) { } else if (moduleId !== null && moduleId !== void 0) {
throw new Error( throw new SyntaxError(
`moduleId should be a string in "${stringifyType(type)}". See https://goo.gl/wIDDiL for more information.\n` + `moduleId should be a string in "${stringifyType(type)}". 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.`); `If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`);
} }

View File

@ -24,7 +24,7 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer'
import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector'; import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver'; import {isStyleUrlResolvable} from '../style_url_resolver';
import {SyntaxError} from '../util';
import {BindingParser, BoundProperty} from './binding_parser'; import {BindingParser, BoundProperty} from './binding_parser';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
import {PreparsedElementType, preparseElement} from './template_preparser'; import {PreparsedElementType, preparseElement} from './template_preparser';
@ -99,7 +99,7 @@ export class TemplateParser {
if (errors.length > 0) { if (errors.length > 0) {
const errorString = errors.join('\n'); const errorString = errors.join('\n');
throw new Error(`Template parse errors:\n${errorString}`); throw new SyntaxError(`Template parse errors:\n${errorString}`);
} }
return result.templateAst; return result.templateAst;

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseError} from './facade/errors';
import {isPrimitive, isStrictStringMap} from './facade/lang'; import {isPrimitive, isStrictStringMap} from './facade/lang';
export const MODULE_SUFFIX = ''; export const MODULE_SUFFIX = '';
const CAMEL_CASE_REGEXP = /([A-Z])/g; const CAMEL_CASE_REGEXP = /([A-Z])/g;
@ -78,3 +78,5 @@ export class SyncAsyncResult<T> {
} }
} }
} }
export class SyntaxError extends BaseError {}

View File

@ -7,8 +7,7 @@
*/ */
import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler'; import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
import {AotSummarySerializerHost} from '@angular/compiler/src/aot/summary_serializer'; import {AotSummarySerializerHost, deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
import * as path from 'path'; import * as path from 'path';
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec'; import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {SyntaxError} from '@angular/compiler';
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompilerConfig} from '@angular/compiler/src/config'; import {CompilerConfig} from '@angular/compiler/src/config';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer'; import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
@ -31,7 +31,7 @@ export function main() {
expect(() => normalizer.normalizeTemplate({ expect(() => normalizer.normalizeTemplate({
componentType: SomeComp, componentType: SomeComp,
moduleUrl: SOME_MODULE_URL, moduleUrl: SOME_MODULE_URL,
})).toThrowError('No template specified for component SomeComp'); })).toThrowError(SyntaxError, 'No template specified for component SomeComp');
})); }));
}); });

View File

@ -15,8 +15,8 @@ import {identifierName} from '../src/compile_metadata';
import {stringify} from '../src/facade/lang'; import {stringify} from '../src/facade/lang';
import {CompileMetadataResolver} from '../src/metadata_resolver'; import {CompileMetadataResolver} from '../src/metadata_resolver';
import {ResourceLoader} from '../src/resource_loader'; import {ResourceLoader} from '../src/resource_loader';
import {SyntaxError} from '../src/util';
import {MockResourceLoader} from '../testing/resource_loader_mock'; import {MockResourceLoader} from '../testing/resource_loader_mock';
import {MalformedStylesComponent} from './metadata_resolver_fixture'; import {MalformedStylesComponent} from './metadata_resolver_fixture';
export function main() { export function main() {
@ -34,8 +34,9 @@ export function main() {
} }
expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline)) expect(() => resolver.getDirectiveMetadata(ComponentWithEverythingInline))
.toThrowError(/Illegal state/); .toThrowError(SyntaxError, /Illegal state/);
expect(() => resolver.getPipeMetadata(SomePipe)).toThrowError(/Illegal state/); expect(() => resolver.getPipeMetadata(SomePipe))
.toThrowError(SyntaxError, /Illegal state/);
})); }));
it('should read metadata in sync for components with inline resources', it('should read metadata in sync for components with inline resources',
@ -121,7 +122,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(
`moduleId should be a string in "ComponentWithInvalidModuleId". See` + SyntaxError, `moduleId should be a string in "ComponentWithInvalidModuleId". See` +
` https://goo.gl/wIDDiL for more information.\n` + ` https://goo.gl/wIDDiL for more information.\n` +
`If you're using Webpack you should inline the template and the styles, see` + `If you're using Webpack you should inline the template and the styles, see` +
` https://goo.gl/X2J8zc.`); ` https://goo.gl/X2J8zc.`);
@ -145,7 +146,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`); .toThrowError(SyntaxError, `Can't resolve all parameters for MyBrokenComp1: (?).`);
})); }));
it('should throw with descriptive error message when a directive is passed to imports', it('should throw with descriptive error message when a directive is passed to imports',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
@ -155,6 +156,7 @@ export function main() {
expect( expect(
() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true)) () => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedComponent, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`); `Unexpected directive 'ComponentWithoutModuleId' imported by the module 'ModuleWithImportedComponent'`);
})); }));
@ -168,6 +170,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithImportedPipe, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`); `Unexpected pipe 'SomePipe' imported by the module 'ModuleWithImportedPipe'`);
})); }));
@ -181,6 +184,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithDeclaredModule, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`); `Unexpected module 'SomeModule' declared by the module 'ModuleWithDeclaredModule'`);
})); }));
@ -191,6 +195,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullDeclared, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`); `Unexpected value 'null' declared by the module 'ModuleWithNullDeclared'`);
})); }));
@ -201,6 +206,7 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullImported, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'null' imported by the module 'ModuleWithNullImported'`); `Unexpected value 'null' imported by the module 'ModuleWithNullImported'`);
})); }));
@ -212,7 +218,8 @@ export function main() {
} }
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`); .toThrowError(
SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`);
})); }));
it('should throw with descriptive error message when one of providers is not present', it('should throw with descriptive error message when one of providers is not present',
@ -223,6 +230,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(
SyntaxError,
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`); `Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
})); }));
@ -234,6 +242,7 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true))
.toThrowError( .toThrowError(
SyntaxError,
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`); `Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
})); }));
@ -248,11 +257,13 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true)) expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithNullBootstrap, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`); `Unexpected value 'null' used in the bootstrap property of module 'ModuleWithNullBootstrap'`);
expect( expect(
() => () =>
resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true)) resolver.loadNgModuleDirectiveAndPipeMetadata(ModuleWithUndefinedBootstrap, true))
.toThrowError( .toThrowError(
SyntaxError,
`Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`); `Unexpected value 'undefined' used in the bootstrap property of module 'ModuleWithUndefinedBootstrap'`);
})); }));

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {SyntaxError} from '@angular/compiler';
import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenReference} from '@angular/compiler/src/compile_metadata'; import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenReference} from '@angular/compiler/src/compile_metadata';
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
@ -374,7 +374,7 @@ export function main() {
describe('errors', () => { describe('errors', () => {
it('should throw error when binding to an unknown property', () => { it('should throw error when binding to an unknown property', () => {
expect(() => parse('<my-component [invalidProp]="bar"></my-component>', [])) expect(() => parse('<my-component [invalidProp]="bar"></my-component>', []))
.toThrowError(`Template parse errors: .toThrowError(SyntaxError, `Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'my-component'. Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module. 1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module.
2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. 2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.
@ -382,14 +382,16 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
}); });
it('should throw error when binding to an unknown element w/o bindings', () => { it('should throw error when binding to an unknown element w/o bindings', () => {
expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors: expect(() => parse('<unknown></unknown>', []))
.toThrowError(SyntaxError, `Template parse errors:
'unknown' is not a known element: 'unknown' is not a known element:
1. If 'unknown' is an Angular component, then verify that it is part of this module. 1. If 'unknown' is an Angular component, then verify that it is part of this module.
2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`); 2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`);
}); });
it('should throw error when binding to an unknown custom element w/o bindings', () => { it('should throw error when binding to an unknown custom element w/o bindings', () => {
expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors: expect(() => parse('<un-known></un-known>', []))
.toThrowError(SyntaxError, `Template parse errors:
'un-known' is not a known element: 'un-known' is not a known element:
1. If 'un-known' is an Angular component, then verify that it is part of this module. 1. If 'un-known' is an Angular component, then verify that it is part of this module.
2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`); 2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`);
@ -397,13 +399,13 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
it('should throw error when binding to an invalid property', () => { it('should throw error when binding to an invalid property', () => {
expect(() => parse('<my-component [onEvent]="bar"></my-component>', [])) expect(() => parse('<my-component [onEvent]="bar"></my-component>', []))
.toThrowError(`Template parse errors: .toThrowError(SyntaxError, `Template parse errors:
Binding to property 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][onEvent]="bar"></my-component>"): TestComp@0:14`); Binding to property 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][onEvent]="bar"></my-component>"): TestComp@0:14`);
}); });
it('should throw error when binding to an invalid attribute', () => { it('should throw error when binding to an invalid attribute', () => {
expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', [])) expect(() => parse('<my-component [attr.onEvent]="bar"></my-component>', []))
.toThrowError(`Template parse errors: .toThrowError(SyntaxError, `Template parse errors:
Binding to attribute 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][attr.onEvent]="bar"></my-component>"): TestComp@0:14`); Binding to attribute 'onEvent' is disallowed for security reasons ("<my-component [ERROR ->][attr.onEvent]="bar"></my-component>"): TestComp@0:14`);
}); });
}); });
@ -445,6 +447,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
() => { () => {
expect(() => { parse('<div @someAnimation="value2">', [], [], []); }) expect(() => { parse('<div @someAnimation="value2">', [], [], []); })
.toThrowError( .toThrowError(
SyntaxError,
/Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@someAnimation="value2">"\): TestComp@0:5/); /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@someAnimation="value2">"\): TestComp@0:5/);
}); });
@ -479,6 +482,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
expect(() => { parse('<broken></broken>', [dirA]); }) expect(() => { parse('<broken></broken>', [dirA]); })
.toThrowError( .toThrowError(
SyntaxError,
`Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); `Template parse errors:\nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`);
}); });
@ -494,6 +498,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
expect(() => { parse('<broken></broken>', [dirA]); }) expect(() => { parse('<broken></broken>', [dirA]); })
.toThrowError( .toThrowError(
SyntaxError,
`Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`); `Template parse errors:\nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA`);
}); });
@ -940,7 +945,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirB = createDir('[dirB]', {providers: [provider1]}); const dirB = createDir('[dirB]', {providers: [provider1]});
expect(() => parse('<div dirA dirB>', [dirA, dirB])) expect(() => parse('<div dirA dirB>', [dirA, dirB]))
.toThrowError( .toThrowError(
`Template parse errors:\n` + SyntaxError, `Template parse errors:\n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`); `Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`);
}); });
@ -1033,6 +1038,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = createDir('[dirA]', {deps: ['self:provider0']}); const dirA = createDir('[dirA]', {deps: ['self:provider0']});
expect(() => parse('<div dirA></div>', [dirA])) expect(() => parse('<div dirA></div>', [dirA]))
.toThrowError( .toThrowError(
SyntaxError,
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
}); });
@ -1047,6 +1053,7 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
const dirA = createDir('[dirA]', {deps: ['host:provider0']}); const dirA = createDir('[dirA]', {deps: ['host:provider0']});
expect(() => parse('<div dirA></div>', [dirA])) expect(() => parse('<div dirA></div>', [dirA]))
.toThrowError( .toThrowError(
SyntaxError,
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0'); 'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
}); });
@ -1098,23 +1105,26 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
}); });
it('should report references with values that dont match a directive as errors', () => { it('should report references with values that dont match a directive as errors', () => {
expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div #a="dirA"></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`); There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`);
}); });
it('should report invalid reference names', () => { it('should report invalid reference names', () => {
expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div #a-b></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
"-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`); "-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`);
}); });
it('should report variables as errors', () => { it('should report variables as errors', () => {
expect(() => parse('<div let-a></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div let-a></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
"let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`); "let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`);
}); });
it('should report duplicate reference names', () => { it('should report duplicate reference names', () => {
expect(() => parse('<div #a></div><div #a></div>', [])) expect(() => parse('<div #a></div><div #a></div>', []))
.toThrowError(`Template parse errors: .toThrowError(SyntaxError, `Template parse errors:
Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`); Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`);
}); });
@ -1466,7 +1476,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
it('should report when ng-content has non WS content', () => { it('should report when ng-content has non WS content', () => {
expect(() => parse('<ng-content>content</ng-content>', [])) expect(() => parse('<ng-content>content</ng-content>', []))
.toThrowError( .toThrowError(
`Template parse errors:\n` + SyntaxError, `Template parse errors:\n` +
`<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`); `<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`);
}); });
@ -1477,18 +1487,20 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
() => { expect(() => parse('<template template="ngIf">', [])).not.toThrowError(); }); () => { expect(() => parse('<template template="ngIf">', [])).not.toThrowError(); });
it('should report when mutliple *attrs are used on the same element', () => { it('should report when mutliple *attrs are used on the same element', () => {
expect(() => parse('<div *ngIf *ngFor>', [])).toThrowError(`Template parse errors: expect(() => parse('<div *ngIf *ngFor>', []))
.toThrowError(SyntaxError, `Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11`); Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11`);
}); });
it('should report when mix of template and *attrs are used on the same element', () => { it('should report when mix of template and *attrs are used on the same element', () => {
expect(() => parse('<span template="ngIf" *ngFor>', [])) expect(() => parse('<span template="ngIf" *ngFor>', []))
.toThrowError(`Template parse errors: .toThrowError(SyntaxError, `Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<span template="ngIf" [ERROR ->]*ngFor>"): TestComp@0:22`); Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<span template="ngIf" [ERROR ->]*ngFor>"): TestComp@0:22`);
}); });
it('should report invalid property names', () => { it('should report invalid property names', () => {
expect(() => parse('<div [invalidProp]></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div [invalidProp]></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5`); Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5`);
}); });
@ -1501,12 +1513,13 @@ Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ER
host: {'[invalidProp]': 'someProp'} host: {'[invalidProp]': 'someProp'}
}) })
.toSummary(); .toSummary();
expect(() => parse('<div></div>', [dirA])).toThrowError(`Template parse errors: expect(() => parse('<div></div>', [dirA])).toThrowError(SyntaxError, `Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA`); Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA`);
}); });
it('should report errors in expressions', () => { it('should report errors in expressions', () => {
expect(() => parse('<div [prop]="a b"></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div [prop]="a b"></div>', []))
.toThrowError(SyntaxError, `Template parse errors:
Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [ERROR ->][prop]="a b"></div>"): TestComp@0:5`); Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [ERROR ->][prop]="a b"></div>"): TestComp@0:5`);
}); });
@ -1544,7 +1557,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
.toSummary(); .toSummary();
expect(() => parse('<div>', [dirB, dirA])) expect(() => parse('<div>', [dirB, dirA]))
.toThrowError( .toThrowError(
`Template parse errors:\n` + SyntaxError, `Template parse errors:\n` +
`More than one component matched on this element.\n` + `More than one component matched on this element.\n` +
`Make sure that only one component's selector can match a given element.\n` + `Make sure that only one component's selector can match a given element.\n` +
`Conflicting components: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`); `Conflicting components: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`);
@ -1562,7 +1575,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
}) })
.toSummary(); .toSummary();
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA])) expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
.toThrowError(`Template parse errors: .toThrowError(SyntaxError, `Template parse errors:
Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("<template [a]="b" [ERROR ->](e)="f"></template>"): TestComp@0:18 Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("<template [a]="b" [ERROR ->](e)="f"></template>"): TestComp@0:18
Components on an embedded template: DirA ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0 Components on an embedded template: DirA ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0
Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0`); Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0`);
@ -1578,7 +1591,8 @@ Property binding a not used by any directive on an embedded template. Make sure
template: new CompileTemplateMetadata({ngContentSelectors: []}) template: new CompileTemplateMetadata({ngContentSelectors: []})
}) })
.toSummary(); .toSummary();
expect(() => parse('<div *a="b"></div>', [dirA])).toThrowError(`Template parse errors: expect(() => parse('<div *a="b"></div>', [dirA]))
.toThrowError(SyntaxError, `Template parse errors:
Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0 Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0
Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0`); Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations". ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0`);
}); });
@ -1838,7 +1852,7 @@ Property binding a not used by any directive on an embedded template. Make sure
}); });
it('should report pipes as error that have not been defined as dependencies', () => { it('should report pipes as error that have not been defined as dependencies', () => {
expect(() => parse('{{a | test}}', [])).toThrowError(`Template parse errors: expect(() => parse('{{a | test}}', [])).toThrowError(SyntaxError, `Template parse errors:
The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`); The pipe 'test' could not be found ("{{[ERROR ->]a | test}}"): TestComp@0:2`);
}); });

View File

@ -51,13 +51,15 @@ export function main() {
})); }));
it('should output an error message to the console and re-throw', fakeAsync(() => { it('should output an error message to the console and re-throw', fakeAsync(() => {
spyOn(console, 'error'); let consoleErrorSpy: jasmine.Spy = spyOn(console, 'error');
expect(() => { expect(() => {
adapter.bootstrap(html('<ng2></ng2>'), ['ng1']); adapter.bootstrap(html('<ng2></ng2>'), ['ng1']);
flushMicrotasks(); flushMicrotasks();
}).toThrowError(); }).toThrowError();
expect(console.error).toHaveBeenCalled(); let args: any[] = consoleErrorSpy.calls.mostRecent().args;
expect(console.error).toHaveBeenCalledWith(jasmine.any(Error), jasmine.any(String)); expect(consoleErrorSpy).toHaveBeenCalled();
expect(args.length).toBeGreaterThan(0);
expect(args[0]).toEqual(jasmine.any(Error));
})); }));
}); });