refactor(view_compiler): codegen DI and Queries

BREAKING CHANGE:
- Renderer:
  * renderComponent method is removed form `Renderer`, only present on `RootRenderer`
  * Renderer.setDebugInfo is removed. Renderer.createElement / createText / createTemplateAnchor
    now take the DebugInfo directly.
- Query semantics:
  * Queries don't work with dynamically loaded components.
  * e.g. for router-outlet: loaded components can't be queries via @ViewQuery,
    but router-outlet emits an event `activate` now that emits the activated component
- Exception classes and the context inside changed (renamed fields)
- DebugElement.attributes is an Object and not a Map in JS any more
- ChangeDetectorGenConfig was renamed into CompilerConfig
- AppViewManager.createEmbeddedViewInContainer / AppViewManager.createHostViewInContainer
  are removed, use the methods in ViewContainerRef instead
- Change detection order changed:
  * 1. dirty check component inputs
  * 2. dirty check content children
  * 3. update render nodes

Closes #6301
Closes #6567
This commit is contained in:
Tobias Bosch
2016-01-06 14:13:44 -08:00
parent 45f09ba686
commit 2b34c88b69
312 changed files with 14271 additions and 16566 deletions

View File

@ -1,194 +0,0 @@
import {
beforeEach,
describe,
expect,
inject,
it,
beforeEachProviders
} from 'angular2/testing_internal';
import {MapWrapper} from 'angular2/src/facade/collection';
import {
CompileDirectiveMetadata,
CompileTypeMetadata
} from 'angular2/src/compiler/directive_metadata';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
Parser,
Lexer,
ChangeDetectorGenConfig,
DynamicProtoChangeDetector,
ChangeDetectionStrategy,
Locals,
ChangeDetector
} from 'angular2/src/core/change_detection/change_detection';
import {Pipes} from 'angular2/src/core/change_detection/pipes';
import {createChangeDetectorDefinitions} from 'angular2/src/compiler/change_definition_factory';
import {TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
import {TEST_PROVIDERS} from './test_bindings';
export function main() {
describe('ChangeDefinitionFactory', () => {
beforeEachProviders(() => TEST_PROVIDERS);
var parser: TemplateParser;
var dispatcher: TestDispatcher;
var context: TestContext;
var directive: TestDirective;
var locals: Locals;
var pipes: Pipes;
beforeEach(inject([TemplateParser], (_templateParser) => {
parser = _templateParser;
context = new TestContext();
directive = new TestDirective();
dispatcher = new TestDispatcher([directive], []);
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
pipes = new TestPipes();
}));
function createChangeDetector(template: string, directives: CompileDirectiveMetadata[],
protoViewIndex: number = 0): ChangeDetector {
var protoChangeDetectors =
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
ChangeDetectionStrategy.Default,
new ChangeDetectorGenConfig(true, false, false),
parser.parse(template, directives, [], 'TestComp'))
.map(definition => new DynamicProtoChangeDetector(definition));
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate();
changeDetector.hydrate(context, locals, dispatcher, pipes);
return changeDetector;
}
it('should watch element properties', () => {
var changeDetector = createChangeDetector('<div [elProp]="someProp">', [], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['elementProperty(elProp)=someValue']);
});
it('should watch text nodes', () => {
var changeDetector = createChangeDetector('{{someProp}}', [], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['textNode(null)=someValue']);
});
it('should handle events on regular elements', () => {
var changeDetector = createChangeDetector('<div on-click="onEvent($event)">', [], 0);
changeDetector.handleEvent('click', 0, 'click');
expect(context.eventLog).toEqual(['click']);
});
it('should handle events on template elements', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: 'template',
outputs: ['click']
});
var changeDetector =
createChangeDetector('<template on-click="onEvent($event)">', [dirMeta], 0);
changeDetector.handleEvent('click', 0, 'click');
expect(context.eventLog).toEqual(['click']);
});
it('should handle events with targets', () => {
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
changeDetector.handleEvent('window:click', 0, 'click');
expect(context.eventLog).toEqual(['click']);
});
it('should watch variables', () => {
var changeDetector = createChangeDetector('<div #someVar [elProp]="someVar">', [], 0);
locals.set('someVar', 'someValue');
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['elementProperty(elProp)=someValue']);
});
it('should write directive properties', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: '[dirProp]',
inputs: ['dirProp']
});
var changeDetector = createChangeDetector('<div [dirProp]="someProp">', [dirMeta], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(directive.dirProp).toEqual('someValue');
});
it('should write template directive properties', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: '[dirProp]',
inputs: ['dirProp']
});
var changeDetector = createChangeDetector('<template [dirProp]="someProp">', [dirMeta], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(directive.dirProp).toEqual('someValue');
});
it('should watch directive host properties', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: 'div',
host: {'[elProp]': 'dirProp'}
});
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
directive.dirProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['elementProperty(elProp)=someValue']);
});
it('should handle directive events', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: 'div',
host: {'(click)': 'onEvent($event)'}
});
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
changeDetector.handleEvent('click', 0, 'click');
expect(directive.eventLog).toEqual(['click']);
});
it('should create change detectors for embedded templates', () => {
var changeDetector = createChangeDetector('<template>{{someProp}}<template>', [], 1);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['textNode(null)=someValue']);
});
it('should watch expressions after embedded templates', () => {
var changeDetector =
createChangeDetector('<template>{{someProp2}}</template>{{someProp}}', [], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['textNode(null)=someValue']);
});
});
}
class TestContext {
eventLog: string[] = [];
someProp: string;
someProp2: string;
onEvent(value: string) { this.eventLog.push(value); }
}

View File

@ -1,143 +0,0 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {provide} from 'angular2/src/core/di';
import {CONST_EXPR, stringify, IS_DART} from 'angular2/src/facade/lang';
import {MapWrapper} from 'angular2/src/facade/collection';
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
import {
CompileDirectiveMetadata,
CompileTypeMetadata
} from 'angular2/src/compiler/directive_metadata';
import {
SourceModule,
SourceExpression,
SourceExpressions,
moduleRef
} from 'angular2/src/compiler/source_module';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
ChangeDetectorGenConfig,
ChangeDetectionStrategy,
Locals,
} from 'angular2/src/core/change_detection/change_detection';
import {evalModule} from './eval_module';
import {TEST_PROVIDERS} from './test_bindings';
import {TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
// Attention: These module names have to correspond to real modules!
var THIS_MODULE_ID = 'angular2/test/compiler/change_detector_compiler_spec';
var THIS_MODULE_URL = `package:${THIS_MODULE_ID}${MODULE_SUFFIX}`;
var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
export function main() {
// Dart's isolate support is broken, and these tests will be obsolote soon with
// https://github.com/angular/angular/issues/6270
if (IS_DART) {
return;
}
describe('ChangeDetectorCompiler', () => {
beforeEachProviders(() => [
TEST_PROVIDERS,
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, false)})
]);
var parser: TemplateParser;
var compiler: ChangeDetectionCompiler;
beforeEachProviders(() => [
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, false)})
]);
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
parser = _parser;
compiler = _compiler;
}));
describe('compileComponentRuntime', () => {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
var type =
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
var parsedTemplate = parser.parse(template, directives, [], 'TestComp');
var factories =
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
return testChangeDetector(factories[0]);
}
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
});
describe('compileComponentCodeGen', () => {
function detectChanges(
compiler: ChangeDetectionCompiler, template: string,
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
var type =
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
var parsedTemplate = parser.parse(template, directives, [], 'TestComp');
var sourceExpressions =
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null);
}
it('should watch element properties', inject([AsyncTestCompleter], (async) => {
detectChanges(compiler, '<div [elProp]="someProp">')
.then((value) => {
expect(value).toEqual(['elementProperty(elProp)=someValue']);
async.done();
});
}));
});
});
}
function createTestableModule(source: SourceExpressions,
changeDetectorIndex: number): SourceModule {
var resultExpression =
`${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`;
var testableSource = `${source.declarations.join('\n')}
${codeGenValueFn(['_'], resultExpression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(null, testableSource);
}
export function testChangeDetector(changeDetectorFactory: Function): string[] {
var dispatcher = new TestDispatcher([], []);
var cd = changeDetectorFactory();
var ctx = new SomeComponent();
ctx.someProp = 'someValue';
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
cd.detectChanges();
return dispatcher.log;
}
class SomeComponent {
someProp: string;
}

View File

@ -1,48 +0,0 @@
import {isBlank} from 'angular2/src/facade/lang';
import {Pipes} from 'angular2/src/core/change_detection/pipes';
import {EventEmitter} from 'angular2/src/facade/async';
import {
ChangeDetector,
ChangeDispatcher,
DirectiveIndex,
BindingTarget
} from 'angular2/src/core/change_detection/change_detection';
export class TestDirective {
eventLog: string[] = [];
dirProp: string;
click: EventEmitter<any> = new EventEmitter<any>();
onEvent(value: string) { this.eventLog.push(value); }
}
export class TestDispatcher implements ChangeDispatcher {
log: string[];
constructor(public directives: any[], public detectors: ChangeDetector[]) { this.clear(); }
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
getDetectorFor(di: DirectiveIndex) { return this.detectors[di.directiveIndex]; }
clear() { this.log = []; }
notifyOnBinding(target: BindingTarget, value) {
this.log.push(`${target.mode}(${target.name})=${this._asString(value)}`);
}
logBindingUpdate(target, value) {}
notifyAfterContentChecked() {}
notifyAfterViewChecked() {}
notifyOnDestroy() {}
getDebugContext(a, b, c) { return null; }
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
}
export class TestPipes implements Pipes {
get(type: string) { return null; }
}

View File

@ -20,14 +20,15 @@ import {
CompileDiDependencyMetadata,
CompileQueryMetadata,
CompileIdentifierMetadata,
CompileFactoryMetadata
} from 'angular2/src/compiler/directive_metadata';
CompileFactoryMetadata,
CompileTokenMetadata
} from 'angular2/src/compiler/compile_metadata';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
import {LifecycleHooks} from 'angular2/src/core/linker/interfaces';
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
export function main() {
describe('DirectiveMetadata', () => {
describe('CompileMetadata', () => {
var fullTypeMeta: CompileTypeMetadata;
var fullTemplateMeta: CompileTemplateMetadata;
var fullDirectiveMeta: CompileDirectiveMetadata;
@ -39,11 +40,19 @@ export function main() {
isHost: true,
isSkipSelf: true,
isOptional: true,
token: 'someToken',
query: new CompileQueryMetadata(
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'}),
viewQuery: new CompileQueryMetadata(
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'})
token: new CompileTokenMetadata({value: 'someToken'}),
query: new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'one'})],
descendants: true,
first: true,
propertyName: 'one'
}),
viewQuery: new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'one'})],
descendants: true,
first: true,
propertyName: 'one'
})
});
fullTypeMeta = new CompileTypeMetadata(
@ -59,7 +68,6 @@ export function main() {
fullDirectiveMeta = CompileDirectiveMetadata.create({
selector: 'someSelector',
isComponent: true,
dynamicLoadable: true,
type: fullTypeMeta,
template: fullTemplateMeta,
changeDetection: ChangeDetectionStrategy.Default,
@ -69,29 +77,42 @@ export function main() {
lifecycleHooks: [LifecycleHooks.OnChanges],
providers: [
new CompileProviderMetadata({
token: 'token',
token: new CompileTokenMetadata({value: 'token'}),
multi: true,
useClass: fullTypeMeta,
useExisting: new CompileIdentifierMetadata({name: 'someName'}),
useExisting: new CompileTokenMetadata({
identifier: new CompileIdentifierMetadata({name: 'someName'}),
identifierIsInstance: true
}),
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
useValue: 'someValue',
})
],
viewProviders: [
new CompileProviderMetadata({
token: 'token',
token: new CompileTokenMetadata({value: 'token'}),
useClass: fullTypeMeta,
useExisting: new CompileIdentifierMetadata({name: 'someName'}),
useExisting: new CompileTokenMetadata(
{identifier: new CompileIdentifierMetadata({name: 'someName'})}),
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
useValue: 'someValue',
useValue: 'someValue'
})
],
queries: [
new CompileQueryMetadata(
{selectors: ['selector'], descendants: true, first: false, propertyName: 'prop'})
new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'selector'})],
descendants: true,
first: false,
propertyName: 'prop'
})
],
viewQueries: [
new CompileQueryMetadata(
{selectors: ['selector'], descendants: true, first: false, propertyName: 'prop'})
new CompileQueryMetadata({
selectors: [new CompileTokenMetadata({value: 'selector'})],
descendants: true,
first: false,
propertyName: 'prop'
})
]
});
@ -100,7 +121,7 @@ export function main() {
describe('CompileIdentifierMetadata', () => {
it('should serialize with full data', () => {
let full = new CompileIdentifierMetadata(
{name: 'name', moduleUrl: 'module', constConstructor: true, value: ['one', ['two']]});
{name: 'name', moduleUrl: 'module', value: ['one', ['two']]});
expect(CompileIdentifierMetadata.fromJson(full.toJson())).toEqual(full);
});

View File

@ -0,0 +1,148 @@
library angular2.test.core.compiler.directive_lifecycle_spec;
import 'package:angular2/testing_internal.dart';
import 'package:angular2/src/compiler/directive_lifecycle_reflector.dart';
import 'package:angular2/src/core/metadata/lifecycle_hooks.dart';
main() {
describe('Create DirectiveMetadata', () {
describe('lifecycle', () {
describe("ngOnChanges", () {
it("should be true when the directive has the ngOnChanges method", () {
expect(hasLifecycleHook(
LifecycleHooks.OnChanges, DirectiveImplementingOnChanges))
.toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks))
.toBe(false);
});
});
describe("ngOnDestroy", () {
it("should be true when the directive has the ngOnDestroy method", () {
expect(hasLifecycleHook(
LifecycleHooks.OnDestroy, DirectiveImplementingOnDestroy))
.toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks))
.toBe(false);
});
});
describe("ngOnInit", () {
it("should be true when the directive has the ngOnInit method", () {
expect(hasLifecycleHook(
LifecycleHooks.OnInit, DirectiveImplementingOnInit)).toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks))
.toBe(false);
});
});
describe("ngDoCheck", () {
it("should be true when the directive has the ngDoCheck method", () {
expect(hasLifecycleHook(
LifecycleHooks.DoCheck, DirectiveImplementingOnCheck)).toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks))
.toBe(false);
});
});
describe("ngAfterContentInit", () {
it("should be true when the directive has the ngAfterContentInit method",
() {
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit,
DirectiveImplementingAfterContentInit)).toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(
LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
});
});
describe("ngAfterContentChecked", () {
it("should be true when the directive has the ngAfterContentChecked method",
() {
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked,
DirectiveImplementingAfterContentChecked)).toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(
LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
.toBe(false);
});
});
describe("ngAfterViewInit", () {
it("should be true when the directive has the ngAfterViewInit method",
() {
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit,
DirectiveImplementingAfterViewInit)).toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(
LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
});
});
describe("ngAfterViewChecked", () {
it("should be true when the directive has the ngAfterViewChecked method",
() {
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked,
DirectiveImplementingAfterViewChecked)).toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(
LifecycleHooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
});
});
});
});
}
class DirectiveNoHooks {}
class DirectiveImplementingOnChanges implements OnChanges {
ngOnChanges(_) {}
}
class DirectiveImplementingOnCheck implements DoCheck {
ngDoCheck() {}
}
class DirectiveImplementingOnInit implements OnInit {
ngOnInit() {}
}
class DirectiveImplementingOnDestroy implements OnDestroy {
ngOnDestroy() {}
}
class DirectiveImplementingAfterContentInit implements AfterContentInit {
ngAfterContentInit() {}
}
class DirectiveImplementingAfterContentChecked implements AfterContentChecked {
ngAfterContentChecked() {}
}
class DirectiveImplementingAfterViewInit implements AfterViewInit {
ngAfterViewInit() {}
}
class DirectiveImplementingAfterViewChecked implements AfterViewChecked {
ngAfterViewChecked() {}
}

View File

@ -0,0 +1,149 @@
import {
AsyncTestCompleter,
beforeEach,
xdescribe,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
SpyObject,
proxy
} from 'angular2/testing_internal';
import {hasLifecycleHook} from 'angular2/src/compiler/directive_lifecycle_reflector';
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
export function main() {
describe('Create DirectiveMetadata', () => {
describe('lifecycle', () => {
describe("ngOnChanges", () => {
it("should be true when the directive has the ngOnChanges method", () => {
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveWithOnChangesMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
});
});
describe("ngOnDestroy", () => {
it("should be true when the directive has the ngOnDestroy method", () => {
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveWithOnDestroyMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
});
});
describe("ngOnInit", () => {
it("should be true when the directive has the ngOnInit method", () => {
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveWithOnInitMethod)).toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
});
});
describe("ngDoCheck", () => {
it("should be true when the directive has the ngDoCheck method", () => {
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
});
});
describe("ngAfterContentInit", () => {
it("should be true when the directive has the ngAfterContentInit method", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit,
DirectiveWithAfterContentInitMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
});
});
describe("ngAfterContentChecked", () => {
it("should be true when the directive has the ngAfterContentChecked method", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked,
DirectiveWithAfterContentCheckedMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
.toBe(false);
});
});
describe("ngAfterViewInit", () => {
it("should be true when the directive has the ngAfterViewInit method", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
});
});
describe("ngAfterViewChecked", () => {
it("should be true when the directive has the ngAfterViewChecked method", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked,
DirectiveWithAfterViewCheckedMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
});
});
});
});
}
class DirectiveNoHooks {}
class DirectiveWithOnChangesMethod {
ngOnChanges(_) {}
}
class DirectiveWithOnInitMethod {
ngOnInit() {}
}
class DirectiveWithOnCheckMethod {
ngDoCheck() {}
}
class DirectiveWithOnDestroyMethod {
ngOnDestroy() {}
}
class DirectiveWithAfterContentInitMethod {
ngAfterContentInit() {}
}
class DirectiveWithAfterContentCheckedMethod {
ngAfterContentChecked() {}
}
class DirectiveWithAfterViewInitMethod {
ngAfterViewInit() {}
}
class DirectiveWithAfterViewCheckedMethod {
ngAfterViewChecked() {}
}

View File

@ -13,19 +13,16 @@ import {
beforeEachProviders
} from 'angular2/testing_internal';
import {
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {CompileTypeMetadata, CompileTemplateMetadata} from 'angular2/src/compiler/compile_metadata';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
import {DirectiveNormalizer} from 'angular2/src/compiler/directive_normalizer';
import {XHR} from 'angular2/src/compiler/xhr';
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
import {TEST_PROVIDERS} from './test_bindings';
export function main() {
describe('TemplateNormalizer', () => {
describe('DirectiveNormalizer', () => {
var dirType: CompileTypeMetadata;
var dirTypeWithHttpUrl: CompileTypeMetadata;
@ -40,8 +37,8 @@ export function main() {
describe('loadTemplate', () => {
describe('inline template', () => {
it('should store the template',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
inject([AsyncTestCompleter, DirectiveNormalizer],
(async, normalizer: DirectiveNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: 'a',
@ -57,8 +54,8 @@ export function main() {
}));
it('should resolve styles on the annotation against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
inject([AsyncTestCompleter, DirectiveNormalizer],
(async, normalizer: DirectiveNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
@ -73,8 +70,8 @@ export function main() {
}));
it('should resolve styles in the template against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
inject([AsyncTestCompleter, DirectiveNormalizer],
(async, normalizer: DirectiveNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '<style>@import test.css</style>',
@ -92,8 +89,8 @@ export function main() {
describe('templateUrl', () => {
it('should load a template from a url that is resolved against moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
inject([AsyncTestCompleter, DirectiveNormalizer, XHR],
(async, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/sometplurl.html', 'a');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
@ -112,8 +109,8 @@ export function main() {
}));
it('should resolve styles on the annotation against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
inject([AsyncTestCompleter, DirectiveNormalizer, XHR],
(async, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/tpl/sometplurl.html', '');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
@ -130,8 +127,8 @@ export function main() {
}));
it('should resolve styles in the template against the templateUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
inject([AsyncTestCompleter, DirectiveNormalizer, XHR],
(async, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/tpl/sometplurl.html',
'<style>@import test.css</style>');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
@ -151,7 +148,7 @@ export function main() {
});
it('should throw if no template was specified',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect(() => normalizer.normalizeTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: [], styleUrls: []})))
@ -162,7 +159,7 @@ export function main() {
describe('normalizeLoadedTemplate', () => {
it('should store the viewEncapsulationin the result',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var viewEncapsulation = ViewEncapsulation.Native;
var template = normalizer.normalizeLoadedTemplate(
@ -173,7 +170,7 @@ export function main() {
}));
it('should keep the template as html',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
@ -182,7 +179,7 @@ export function main() {
}));
it('should collect ngContent',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -191,7 +188,7 @@ export function main() {
}));
it('should normalize ngContent wildcard selector',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -201,7 +198,7 @@ export function main() {
}));
it('should collect top level styles in the template',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -210,7 +207,7 @@ export function main() {
}));
it('should collect styles inside in elements',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -219,7 +216,7 @@ export function main() {
}));
it('should collect styleUrls in the template',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -228,7 +225,7 @@ export function main() {
}));
it('should collect styleUrls in elements',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -237,7 +234,7 @@ export function main() {
}));
it('should ignore link elements with non stylesheet rel attribute',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -246,7 +243,7 @@ export function main() {
}));
it('should ignore link elements with absolute urls but non package: scheme',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -255,7 +252,7 @@ export function main() {
}));
it('should extract @import style urls into styleAbsUrl',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
@ -265,7 +262,7 @@ export function main() {
}));
it('should not resolve relative urls in inline styles',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata({
encapsulation: null,
@ -277,7 +274,7 @@ export function main() {
}));
it('should resolve relative style urls in styleUrls',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
@ -287,7 +284,7 @@ export function main() {
}));
it('should resolve relative style urls in styleUrls with http directive url',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirTypeWithHttpUrl, new CompileTemplateMetadata(
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
@ -297,7 +294,7 @@ export function main() {
}));
it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no styles nor stylesheets',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: ViewEncapsulation.Emulated, styles: [], styleUrls: []}),
@ -306,7 +303,7 @@ export function main() {
}));
it('should ignore ng-content in elements with ngNonBindable',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
@ -316,7 +313,7 @@ export function main() {
}));
it('should still collect <style> in elements with ngNonBindable',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),

View File

@ -0,0 +1,209 @@
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/testing_internal';
import {DirectiveResolver} from 'angular2/src/compiler/directive_resolver';
import {
DirectiveMetadata,
Directive,
Input,
Output,
HostBinding,
HostListener,
ContentChildren,
ContentChildrenMetadata,
ViewChildren,
ViewChildrenMetadata,
ContentChild,
ContentChildMetadata,
ViewChild,
ViewChildMetadata
} from 'angular2/src/core/metadata';
@Directive({selector: 'someDirective'})
class SomeDirective {
}
@Directive({selector: 'someChildDirective'})
class SomeChildDirective extends SomeDirective {
}
@Directive({selector: 'someDirective', inputs: ['c']})
class SomeDirectiveWithInputs {
@Input() a;
@Input("renamed") b;
c;
}
@Directive({selector: 'someDirective', outputs: ['c']})
class SomeDirectiveWithOutputs {
@Output() a;
@Output("renamed") b;
c;
}
@Directive({selector: 'someDirective', outputs: ['a']})
class SomeDirectiveWithDuplicateOutputs {
@Output() a;
}
@Directive({selector: 'someDirective', properties: ['a']})
class SomeDirectiveWithProperties {
}
@Directive({selector: 'someDirective', events: ['a']})
class SomeDirectiveWithEvents {
}
@Directive({selector: 'someDirective'})
class SomeDirectiveWithSetterProps {
@Input("renamed")
set a(value) {
}
}
@Directive({selector: 'someDirective'})
class SomeDirectiveWithGetterOutputs {
@Output("renamed")
get a() {
return null;
}
}
@Directive({selector: 'someDirective', host: {'[c]': 'c'}})
class SomeDirectiveWithHostBindings {
@HostBinding() a;
@HostBinding("renamed") b;
c;
}
@Directive({selector: 'someDirective', host: {'(c)': 'onC()'}})
class SomeDirectiveWithHostListeners {
@HostListener('a')
onA() {
}
@HostListener('b', ['$event.value'])
onB(value) {
}
}
@Directive({selector: 'someDirective', queries: {"cs": new ContentChildren("c")}})
class SomeDirectiveWithContentChildren {
@ContentChildren("a") as: any;
c;
}
@Directive({selector: 'someDirective', queries: {"cs": new ViewChildren("c")}})
class SomeDirectiveWithViewChildren {
@ViewChildren("a") as: any;
c;
}
@Directive({selector: 'someDirective', queries: {"c": new ContentChild("c")}})
class SomeDirectiveWithContentChild {
@ContentChild("a") a: any;
c;
}
@Directive({selector: 'someDirective', queries: {"c": new ViewChild("c")}})
class SomeDirectiveWithViewChild {
@ViewChild("a") a: any;
c;
}
class SomeDirectiveWithoutMetadata {}
export function main() {
describe("DirectiveResolver", () => {
var resolver: DirectiveResolver;
beforeEach(() => { resolver = new DirectiveResolver(); });
it('should read out the Directive metadata', () => {
var directiveMetadata = resolver.resolve(SomeDirective);
expect(directiveMetadata)
.toEqual(new DirectiveMetadata(
{selector: 'someDirective', inputs: [], outputs: [], host: {}, queries: {}}));
});
it('should throw if not matching metadata is found', () => {
expect(() => { resolver.resolve(SomeDirectiveWithoutMetadata); })
.toThrowError('No Directive annotation found on SomeDirectiveWithoutMetadata');
});
it('should not read parent class Directive metadata', function() {
var directiveMetadata = resolver.resolve(SomeChildDirective);
expect(directiveMetadata)
.toEqual(new DirectiveMetadata(
{selector: 'someChildDirective', inputs: [], outputs: [], host: {}, queries: {}}));
});
describe('inputs', () => {
it('should append directive inputs', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithInputs);
expect(directiveMetadata.inputs).toEqual(['c', 'a', 'b: renamed']);
});
it('should work with getters and setters', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithSetterProps);
expect(directiveMetadata.inputs).toEqual(['a: renamed']);
});
});
describe('outputs', () => {
it('should append directive outputs', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithOutputs);
expect(directiveMetadata.outputs).toEqual(['c', 'a', 'b: renamed']);
});
it('should work with getters and setters', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithGetterOutputs);
expect(directiveMetadata.outputs).toEqual(['a: renamed']);
});
it('should throw if duplicate outputs', () => {
expect(() => { resolver.resolve(SomeDirectiveWithDuplicateOutputs); })
.toThrowError(
`Output event 'a' defined multiple times in 'SomeDirectiveWithDuplicateOutputs'`);
});
});
describe('host', () => {
it('should append host bindings', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithHostBindings);
expect(directiveMetadata.host).toEqual({'[c]': 'c', '[a]': 'a', '[renamed]': 'b'});
});
it('should append host listeners', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithHostListeners);
expect(directiveMetadata.host)
.toEqual({'(c)': 'onC()', '(a)': 'onA()', '(b)': 'onB($event.value)'});
});
});
describe('queries', () => {
it('should append ContentChildren', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChildren);
expect(directiveMetadata.queries)
.toEqual({"cs": new ContentChildren("c"), "as": new ContentChildren("a")});
});
it('should append ViewChildren', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChildren);
expect(directiveMetadata.queries)
.toEqual({"cs": new ViewChildren("c"), "as": new ViewChildren("a")});
});
it('should append ContentChild', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChild);
expect(directiveMetadata.queries)
.toEqual({"c": new ContentChild("c"), "a": new ContentChild("a")});
});
it('should append ViewChild', () => {
var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChild);
expect(directiveMetadata.queries)
.toEqual({"c": new ViewChild("c"), "a": new ViewChild("a")});
});
});
});
}

View File

@ -1,55 +0,0 @@
import "dart:isolate";
import "dart:async";
Uri toDartDataUri(String source) {
return Uri.parse("data:application/dart;charset=utf-8,"
"${Uri.encodeComponent(source)}");
}
createIsolateSource(String moduleSource, List<List<String>> moduleImports) {
var moduleSourceParts = ['import "dart:isolate";'];
moduleImports.forEach((sourceImport) {
String modName = sourceImport[0];
String modAlias = sourceImport[1];
moduleSourceParts.add("import '${modName}' as ${modAlias};");
});
moduleSourceParts.add(moduleSource);
moduleSourceParts.add("""
main(List args, SendPort replyPort) {
replyPort.send(run(args));
}
""");
return moduleSourceParts.join('\n');
}
var timeStamp = new DateTime.now().millisecondsSinceEpoch;
dynamic callModule(dynamic data) {
return data.map((a) => a + 1);
}
evalModule(String moduleSource, List<List<String>> imports, List args) {
String source = createIsolateSource(moduleSource, imports);
Completer completer = new Completer();
RawReceivePort receivePort;
receivePort = new RawReceivePort((message) {
receivePort.close();
completer.complete(message);
});
// Note: we have a special karma plugin that sends files under
// urls like /package_1234 as permanently cached.
// With this, spawning multiple isolates gets faster as Darts does not
// reload the files from the server.
var packageRoot = Uri.parse('/packages_${timeStamp}');
return Isolate
.spawnUri(toDartDataUri(source), args, receivePort.sendPort,
packageRoot: packageRoot)
.then((isolate) {
RawReceivePort errorPort;
errorPort = new RawReceivePort((message) {
completer.completeError(message);
});
isolate.addErrorListener(errorPort.sendPort);
return completer.future;
});
}

View File

@ -1,44 +0,0 @@
import {PromiseWrapper} from 'angular2/src/facade/async';
import {isPresent, global, StringWrapper} from 'angular2/src/facade/lang';
var evalCounter = 0;
var MODULE_URL_REGEX = /^package:(.*)\.js/;
function nextModuleId() {
return `evalScript${evalCounter++}`;
}
export function evalModule(moduleSource: string, imports: string[][], args: any[]): Promise<any> {
var moduleId = nextModuleId();
var moduleSourceWithImports = [];
var importModuleIds = [];
imports.forEach(sourceImport => {
var modUrl = sourceImport[0];
var modMatch = MODULE_URL_REGEX.exec(modUrl);
if (!modMatch) {
throw new Error(`Module url ${modUrl} does not match the pattern ${MODULE_URL_REGEX}`);
}
var modId = modMatch[1];
var modAlias = sourceImport[1];
importModuleIds.push(modId);
// Note: After transpilation to commonJS and loading this file in a browser
// using SystemJS, the loader might get confused by the presence of require,
// and attempt to load "+ modName +.js" !?!
// A simple string concat manages to prevent that, but that is one compiler
// optimization away from breaking again. Proceed with caution!
moduleSourceWithImports.push(`var ${modAlias} = require` + `('${modId}');`);
});
moduleSourceWithImports.push(moduleSource);
var moduleBody = new Function('require', 'exports', 'module', moduleSourceWithImports.join('\n'));
var System = global['System'];
if (isPresent(System) && isPresent(System.registerDynamic)) {
System.registerDynamic(moduleId, importModuleIds, false, moduleBody);
return <Promise<any>>System.import(moduleId).then((module) => module.run(args));
} else {
var exports = {};
moduleBody(require, exports, {});
return PromiseWrapper.resolve(exports['run'](args));
}
}

View File

@ -1,55 +0,0 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject
} from 'angular2/testing_internal';
import {IS_DART} from 'angular2/src/facade/lang';
import {evalModule} from './eval_module';
// This export is used by this test code
// when evaling the test module!
export var TEST_VALUE = 23;
const THIS_MODULE_URL = `package:angular2/test/compiler/eval_module_spec${IS_DART?'.dart':'.js'}`;
export function main() {
// Dart's isolate support is broken, and these tests will be obsolote soon with
// https://github.com/angular/angular/issues/6270
if (IS_DART) {
return;
}
describe('evalModule', () => {
it('should call the "run" function and allow to use imports',
inject([AsyncTestCompleter], (async) => {
var moduleSource = IS_DART ? testDartModule : testJsModule;
evalModule(moduleSource, [[THIS_MODULE_URL, 'tst']], [1])
.then((value) => {
expect(value).toEqual([1, 23]);
async.done();
});
}));
});
}
var testDartModule = `
run(data) {
data.add(tst.TEST_VALUE);
return data;
}
`;
var testJsModule = `
exports.run = function(data) {
data.push(tst.TEST_VALUE);
return data;
}
`;

View File

@ -0,0 +1,249 @@
import {ddescribe, describe, it, expect} from 'angular2/testing_internal';
import {Lexer, Token} from 'angular2/src/compiler/expression_parser/lexer';
import {StringWrapper} from "angular2/src/facade/lang";
function lex(text: string): any[] {
return new Lexer().tokenize(text);
}
function expectToken(token, index) {
expect(token instanceof Token).toBe(true);
expect(token.index).toEqual(index);
}
function expectCharacterToken(token, index, character) {
expect(character.length).toBe(1);
expectToken(token, index);
expect(token.isCharacter(StringWrapper.charCodeAt(character, 0))).toBe(true);
}
function expectOperatorToken(token, index, operator) {
expectToken(token, index);
expect(token.isOperator(operator)).toBe(true);
}
function expectNumberToken(token, index, n) {
expectToken(token, index);
expect(token.isNumber()).toBe(true);
expect(token.toNumber()).toEqual(n);
}
function expectStringToken(token, index, str) {
expectToken(token, index);
expect(token.isString()).toBe(true);
expect(token.toString()).toEqual(str);
}
function expectIdentifierToken(token, index, identifier) {
expectToken(token, index);
expect(token.isIdentifier()).toBe(true);
expect(token.toString()).toEqual(identifier);
}
function expectKeywordToken(token, index, keyword) {
expectToken(token, index);
expect(token.isKeyword()).toBe(true);
expect(token.toString()).toEqual(keyword);
}
export function main() {
describe('lexer', function() {
describe('token', function() {
it('should tokenize a simple identifier', function() {
var tokens: number[] = lex("j");
expect(tokens.length).toEqual(1);
expectIdentifierToken(tokens[0], 0, 'j');
});
it('should tokenize a dotted identifier', function() {
var tokens: number[] = lex("j.k");
expect(tokens.length).toEqual(3);
expectIdentifierToken(tokens[0], 0, 'j');
expectCharacterToken(tokens[1], 1, '.');
expectIdentifierToken(tokens[2], 2, 'k');
});
it('should tokenize an operator', function() {
var tokens: number[] = lex("j-k");
expect(tokens.length).toEqual(3);
expectOperatorToken(tokens[1], 1, '-');
});
it('should tokenize an indexed operator', function() {
var tokens: number[] = lex("j[k]");
expect(tokens.length).toEqual(4);
expectCharacterToken(tokens[1], 1, "[");
expectCharacterToken(tokens[3], 3, "]");
});
it('should tokenize numbers', function() {
var tokens: number[] = lex("88");
expect(tokens.length).toEqual(1);
expectNumberToken(tokens[0], 0, 88);
});
it('should tokenize numbers within index ops',
function() { expectNumberToken(lex("a[22]")[2], 2, 22); });
it('should tokenize simple quoted strings',
function() { expectStringToken(lex('"a"')[0], 0, "a"); });
it('should tokenize quoted strings with escaped quotes',
function() { expectStringToken(lex('"a\\""')[0], 0, 'a"'); });
it('should tokenize a string', function() {
var tokens: Token[] = lex("j-a.bc[22]+1.3|f:'a\\\'c':\"d\\\"e\"");
expectIdentifierToken(tokens[0], 0, 'j');
expectOperatorToken(tokens[1], 1, '-');
expectIdentifierToken(tokens[2], 2, 'a');
expectCharacterToken(tokens[3], 3, '.');
expectIdentifierToken(tokens[4], 4, 'bc');
expectCharacterToken(tokens[5], 6, '[');
expectNumberToken(tokens[6], 7, 22);
expectCharacterToken(tokens[7], 9, ']');
expectOperatorToken(tokens[8], 10, '+');
expectNumberToken(tokens[9], 11, 1.3);
expectOperatorToken(tokens[10], 14, '|');
expectIdentifierToken(tokens[11], 15, 'f');
expectCharacterToken(tokens[12], 16, ':');
expectStringToken(tokens[13], 17, "a'c");
expectCharacterToken(tokens[14], 23, ':');
expectStringToken(tokens[15], 24, 'd"e');
});
it('should tokenize undefined', function() {
var tokens: Token[] = lex("undefined");
expectKeywordToken(tokens[0], 0, "undefined");
expect(tokens[0].isKeywordUndefined()).toBe(true);
});
it('should ignore whitespace', function() {
var tokens: Token[] = lex("a \t \n \r b");
expectIdentifierToken(tokens[0], 0, 'a');
expectIdentifierToken(tokens[1], 8, 'b');
});
it('should tokenize quoted string', () => {
var str = "['\\'', \"\\\"\"]";
var tokens: Token[] = lex(str);
expectStringToken(tokens[1], 1, "'");
expectStringToken(tokens[3], 7, '"');
});
it('should tokenize escaped quoted string', () => {
var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"';
var tokens: Token[] = lex(str);
expect(tokens.length).toEqual(1);
expect(tokens[0].toString()).toEqual('"\n\f\r\t\v\u00A0');
});
it('should tokenize unicode', function() {
var tokens: Token[] = lex('"\\u00A0"');
expect(tokens.length).toEqual(1);
expect(tokens[0].toString()).toEqual('\u00a0');
});
it('should tokenize relation', function() {
var tokens: Token[] = lex("! == != < > <= >= === !==");
expectOperatorToken(tokens[0], 0, '!');
expectOperatorToken(tokens[1], 2, '==');
expectOperatorToken(tokens[2], 5, '!=');
expectOperatorToken(tokens[3], 8, '<');
expectOperatorToken(tokens[4], 10, '>');
expectOperatorToken(tokens[5], 12, '<=');
expectOperatorToken(tokens[6], 15, '>=');
expectOperatorToken(tokens[7], 18, '===');
expectOperatorToken(tokens[8], 22, '!==');
});
it('should tokenize statements', function() {
var tokens: Token[] = lex("a;b;");
expectIdentifierToken(tokens[0], 0, 'a');
expectCharacterToken(tokens[1], 1, ';');
expectIdentifierToken(tokens[2], 2, 'b');
expectCharacterToken(tokens[3], 3, ';');
});
it('should tokenize function invocation', function() {
var tokens: Token[] = lex("a()");
expectIdentifierToken(tokens[0], 0, 'a');
expectCharacterToken(tokens[1], 1, '(');
expectCharacterToken(tokens[2], 2, ')');
});
it('should tokenize simple method invocations', function() {
var tokens: Token[] = lex("a.method()");
expectIdentifierToken(tokens[2], 2, 'method');
});
it('should tokenize method invocation', function() {
var tokens: Token[] = lex("a.b.c (d) - e.f()");
expectIdentifierToken(tokens[0], 0, 'a');
expectCharacterToken(tokens[1], 1, '.');
expectIdentifierToken(tokens[2], 2, 'b');
expectCharacterToken(tokens[3], 3, '.');
expectIdentifierToken(tokens[4], 4, 'c');
expectCharacterToken(tokens[5], 6, '(');
expectIdentifierToken(tokens[6], 7, 'd');
expectCharacterToken(tokens[7], 8, ')');
expectOperatorToken(tokens[8], 10, '-');
expectIdentifierToken(tokens[9], 12, 'e');
expectCharacterToken(tokens[10], 13, '.');
expectIdentifierToken(tokens[11], 14, 'f');
expectCharacterToken(tokens[12], 15, '(');
expectCharacterToken(tokens[13], 16, ')');
});
it('should tokenize number', function() {
var tokens: Token[] = lex("0.5");
expectNumberToken(tokens[0], 0, 0.5);
});
// NOTE(deboer): NOT A LEXER TEST
// it('should tokenize negative number', () => {
// var tokens:Token[] = lex("-0.5");
// expectNumberToken(tokens[0], 0, -0.5);
// });
it('should tokenize number with exponent', function() {
var tokens: Token[] = lex("0.5E-10");
expect(tokens.length).toEqual(1);
expectNumberToken(tokens[0], 0, 0.5E-10);
tokens = lex("0.5E+10");
expectNumberToken(tokens[0], 0, 0.5E+10);
});
it('should throws exception for invalid exponent', function() {
expect(() => { lex("0.5E-"); })
.toThrowError('Lexer Error: Invalid exponent at column 4 in expression [0.5E-]');
expect(() => { lex("0.5E-A"); })
.toThrowError('Lexer Error: Invalid exponent at column 4 in expression [0.5E-A]');
});
it('should tokenize number starting with a dot', function() {
var tokens: Token[] = lex(".5");
expectNumberToken(tokens[0], 0, 0.5);
});
it('should throw error on invalid unicode', function() {
expect(() => { lex("'\\u1''bla'"); })
.toThrowError(
"Lexer Error: Invalid unicode escape [\\u1''b] at column 2 in expression ['\\u1''bla']");
});
it('should tokenize hash as operator', function() {
var tokens: Token[] = lex("#");
expectOperatorToken(tokens[0], 0, '#');
});
it('should tokenize ?. as operator', () => {
var tokens: Token[] = lex('?.');
expectOperatorToken(tokens[0], 0, '?.');
});
});
});
}

View File

@ -0,0 +1,465 @@
import {ddescribe, describe, it, xit, iit, expect, beforeEach} from 'angular2/testing_internal';
import {isBlank, isPresent} from 'angular2/src/facade/lang';
import {Parser} from 'angular2/src/compiler/expression_parser/parser';
import {Unparser} from './unparser';
import {Lexer} from 'angular2/src/compiler/expression_parser/lexer';
import {BindingPipe, LiteralPrimitive, AST} from 'angular2/src/compiler/expression_parser/ast';
export function main() {
function createParser() { return new Parser(new Lexer()); }
function parseAction(text, location = null): any {
return createParser().parseAction(text, location);
}
function parseBinding(text, location = null): any {
return createParser().parseBinding(text, location);
}
function parseTemplateBindings(text, location = null): any {
return createParser().parseTemplateBindings(text, location);
}
function parseInterpolation(text, location = null): any {
return createParser().parseInterpolation(text, location);
}
function parseSimpleBinding(text, location = null): any {
return createParser().parseSimpleBinding(text, location);
}
function unparse(ast: AST): string { return new Unparser().unparse(ast); }
function checkInterpolation(exp: string, expected?: string) {
var ast = parseInterpolation(exp);
if (isBlank(expected)) expected = exp;
expect(unparse(ast)).toEqual(expected);
}
function checkBinding(exp: string, expected?: string) {
var ast = parseBinding(exp);
if (isBlank(expected)) expected = exp;
expect(unparse(ast)).toEqual(expected);
}
function checkAction(exp: string, expected?: string) {
var ast = parseAction(exp);
if (isBlank(expected)) expected = exp;
expect(unparse(ast)).toEqual(expected);
}
function expectActionError(text) { return expect(() => parseAction(text)); }
function expectBindingError(text) { return expect(() => parseBinding(text)); }
describe("parser", () => {
describe("parseAction", () => {
it('should parse numbers', () => { checkAction("1"); });
it('should parse strings', () => {
checkAction("'1'", '"1"');
checkAction('"1"');
});
it('should parse null', () => { checkAction("null"); });
it('should parse unary - expressions', () => {
checkAction("-1", "0 - 1");
checkAction("+1", "1");
});
it('should parse unary ! expressions', () => {
checkAction("!true");
checkAction("!!true");
checkAction("!!!true");
});
it('should parse multiplicative expressions',
() => { checkAction("3*4/2%5", "3 * 4 / 2 % 5"); });
it('should parse additive expressions', () => { checkAction("3 + 6 - 2"); });
it('should parse relational expressions', () => {
checkAction("2 < 3");
checkAction("2 > 3");
checkAction("2 <= 2");
checkAction("2 >= 2");
});
it('should parse equality expressions', () => {
checkAction("2 == 3");
checkAction("2 != 3");
});
it('should parse strict equality expressions', () => {
checkAction("2 === 3");
checkAction("2 !== 3");
});
it('should parse expressions', () => {
checkAction("true && true");
checkAction("true || false");
});
it('should parse grouped expressions', () => { checkAction("(1 + 2) * 3", "1 + 2 * 3"); });
it('should ignore comments in expressions', () => { checkAction('a //comment', 'a'); });
it('should parse an empty string', () => { checkAction(''); });
describe("literals", () => {
it('should parse array', () => {
checkAction("[1][0]");
checkAction("[[1]][0][0]");
checkAction("[]");
checkAction("[].length");
checkAction("[1, 2].length");
});
it('should parse map', () => {
checkAction("{}");
checkAction("{a: 1}[2]");
checkAction("{}[\"a\"]");
});
it('should only allow identifier, string, or keyword as map key', () => {
expectActionError('{(:0}')
.toThrowError(new RegExp('expected identifier, keyword, or string'));
expectActionError('{1234:0}')
.toThrowError(new RegExp('expected identifier, keyword, or string'));
});
});
describe("member access", () => {
it("should parse field access", () => {
checkAction("a");
checkAction("a.a");
});
it('should only allow identifier or keyword as member names', () => {
expectActionError('x.(').toThrowError(new RegExp('identifier or keyword'));
expectActionError('x. 1234').toThrowError(new RegExp('identifier or keyword'));
expectActionError('x."foo"').toThrowError(new RegExp('identifier or keyword'));
});
it('should parse safe field access', () => {
checkAction('a?.a');
checkAction('a.a?.a');
});
});
describe("method calls", () => {
it("should parse method calls", () => {
checkAction("fn()");
checkAction("add(1, 2)");
checkAction("a.add(1, 2)");
checkAction("fn().add(1, 2)");
});
});
describe("functional calls",
() => { it("should parse function calls", () => { checkAction("fn()(1, 2)"); }); });
describe("conditional", () => {
it('should parse ternary/conditional expressions', () => {
checkAction("7 == 3 + 4 ? 10 : 20");
checkAction("false ? 10 : 20");
});
it('should throw on incorrect ternary operator syntax', () => {
expectActionError("true?1").toThrowError(new RegExp(
'Parser Error: Conditional expression true\\?1 requires all 3 expressions'));
});
});
describe("assignment", () => {
it("should support field assignments", () => {
checkAction("a = 12");
checkAction("a.a.a = 123");
checkAction("a = 123; b = 234;");
});
it("should throw on safe field assignments", () => {
expectActionError("a?.a = 123")
.toThrowError(new RegExp('cannot be used in the assignment'));
});
it("should support array updates", () => { checkAction("a[0] = 200"); });
});
it("should error when using pipes",
() => { expectActionError('x|blah').toThrowError(new RegExp('Cannot have a pipe')); });
it('should store the source in the result',
() => { expect(parseAction('someExpr').source).toBe('someExpr'); });
it('should store the passed-in location',
() => { expect(parseAction('someExpr', 'location').location).toBe('location'); });
it("should throw when encountering interpolation", () => {
expectActionError("{{a()}}")
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
});
});
describe("general error handling", () => {
it("should throw on an unexpected token", () => {
expectActionError("[1,2] trac").toThrowError(new RegExp('Unexpected token \'trac\''));
});
it('should throw a reasonable error for unconsumed tokens', () => {
expectActionError(")")
.toThrowError(new RegExp("Unexpected token \\) at column 1 in \\[\\)\\]"));
});
it('should throw on missing expected token', () => {
expectActionError("a(b").toThrowError(
new RegExp("Missing expected \\) at the end of the expression \\[a\\(b\\]"));
});
});
describe("parseBinding", () => {
describe("pipes", () => {
it("should parse pipes", () => {
checkBinding('a(b | c)', 'a((b | c))');
checkBinding('a.b(c.d(e) | f)', 'a.b((c.d(e) | f))');
checkBinding('[1, 2, 3] | a', '([1, 2, 3] | a)');
checkBinding('{a: 1} | b', '({a: 1} | b)');
checkBinding('a[b] | c', '(a[b] | c)');
checkBinding('a?.b | c', '(a?.b | c)');
checkBinding('true | a', '(true | a)');
checkBinding('a | b:c | d', '((a | b:c) | d)');
checkBinding('a | b:(c | d)', '(a | b:(c | d))');
});
it('should only allow identifier or keyword as formatter names', () => {
expectBindingError('"Foo"|(').toThrowError(new RegExp('identifier or keyword'));
expectBindingError('"Foo"|1234').toThrowError(new RegExp('identifier or keyword'));
expectBindingError('"Foo"|"uppercase"').toThrowError(new RegExp('identifier or keyword'));
});
it('should parse quoted expressions', () => { checkBinding('a:b', 'a:b'); });
it('should not crash when prefix part is not tokenizable',
() => { checkBinding('"a:b"', '"a:b"'); });
it('should ignore whitespace around quote prefix', () => { checkBinding(' a :b', 'a:b'); });
it('should refuse prefixes that are not single identifiers', () => {
expectBindingError('a + b:c').toThrowError();
expectBindingError('1:c').toThrowError();
});
});
it('should store the source in the result',
() => { expect(parseBinding('someExpr').source).toBe('someExpr'); });
it('should store the passed-in location',
() => { expect(parseBinding('someExpr', 'location').location).toBe('location'); });
it('should throw on chain expressions', () => {
expect(() => parseBinding("1;2")).toThrowError(new RegExp("contain chained expression"));
});
it('should throw on assignment', () => {
expect(() => parseBinding("a=2")).toThrowError(new RegExp("contain assignments"));
});
it('should throw when encountering interpolation', () => {
expectBindingError("{{a.b}}")
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
});
it('should parse conditional expression', () => { checkBinding('a < b ? a : b'); });
it('should ignore comments in bindings', () => { checkBinding('a //comment', 'a'); });
});
describe('parseTemplateBindings', () => {
function keys(templateBindings: any[]) {
return templateBindings.map(binding => binding.key);
}
function keyValues(templateBindings: any[]) {
return templateBindings.map(binding => {
if (binding.keyIsVar) {
return '#' + binding.key + (isBlank(binding.name) ? '=null' : '=' + binding.name);
} else {
return binding.key + (isBlank(binding.expression) ? '' : `=${binding.expression}`)
}
});
}
function exprSources(templateBindings: any[]) {
return templateBindings.map(
binding => isPresent(binding.expression) ? binding.expression.source : null);
}
it('should parse an empty string', () => { expect(parseTemplateBindings('')).toEqual([]); });
it('should parse a string without a value',
() => { expect(keys(parseTemplateBindings('a'))).toEqual(['a']); });
it('should only allow identifier, string, or keyword including dashes as keys', () => {
var bindings = parseTemplateBindings("a:'b'");
expect(keys(bindings)).toEqual(['a']);
bindings = parseTemplateBindings("'a':'b'");
expect(keys(bindings)).toEqual(['a']);
bindings = parseTemplateBindings("\"a\":'b'");
expect(keys(bindings)).toEqual(['a']);
bindings = parseTemplateBindings("a-b:'c'");
expect(keys(bindings)).toEqual(['a-b']);
expect(() => { parseTemplateBindings('(:0'); })
.toThrowError(new RegExp('expected identifier, keyword, or string'));
expect(() => { parseTemplateBindings('1234:0'); })
.toThrowError(new RegExp('expected identifier, keyword, or string'));
});
it('should detect expressions as value', () => {
var bindings = parseTemplateBindings("a:b");
expect(exprSources(bindings)).toEqual(['b']);
bindings = parseTemplateBindings("a:1+1");
expect(exprSources(bindings)).toEqual(['1+1']);
});
it('should detect names as value', () => {
var bindings = parseTemplateBindings("a:#b");
expect(keyValues(bindings)).toEqual(['a', '#b=\$implicit']);
});
it('should allow space and colon as separators', () => {
var bindings = parseTemplateBindings("a:b");
expect(keys(bindings)).toEqual(['a']);
expect(exprSources(bindings)).toEqual(['b']);
bindings = parseTemplateBindings("a b");
expect(keys(bindings)).toEqual(['a']);
expect(exprSources(bindings)).toEqual(['b']);
});
it('should allow multiple pairs', () => {
var bindings = parseTemplateBindings("a 1 b 2");
expect(keys(bindings)).toEqual(['a', 'aB']);
expect(exprSources(bindings)).toEqual(['1 ', '2']);
});
it('should store the sources in the result', () => {
var bindings = parseTemplateBindings("a 1,b 2");
expect(bindings[0].expression.source).toEqual('1');
expect(bindings[1].expression.source).toEqual('2');
});
it('should store the passed-in location', () => {
var bindings = parseTemplateBindings("a 1,b 2", 'location');
expect(bindings[0].expression.location).toEqual('location');
});
it('should support var/# notation', () => {
var bindings = parseTemplateBindings("var i");
expect(keyValues(bindings)).toEqual(['#i=\$implicit']);
bindings = parseTemplateBindings("#i");
expect(keyValues(bindings)).toEqual(['#i=\$implicit']);
bindings = parseTemplateBindings("var a; var b");
expect(keyValues(bindings)).toEqual(['#a=\$implicit', '#b=\$implicit']);
bindings = parseTemplateBindings("#a; #b;");
expect(keyValues(bindings)).toEqual(['#a=\$implicit', '#b=\$implicit']);
bindings = parseTemplateBindings("var i-a = k-a");
expect(keyValues(bindings)).toEqual(['#i-a=k-a']);
bindings = parseTemplateBindings("keyword var item; var i = k");
expect(keyValues(bindings)).toEqual(['keyword', '#item=\$implicit', '#i=k']);
bindings = parseTemplateBindings("keyword: #item; #i = k");
expect(keyValues(bindings)).toEqual(['keyword', '#item=\$implicit', '#i=k']);
bindings = parseTemplateBindings("directive: var item in expr; var a = b", 'location');
expect(keyValues(bindings))
.toEqual(['directive', '#item=\$implicit', 'directiveIn=expr in location', '#a=b']);
});
it('should parse pipes', () => {
var bindings = parseTemplateBindings('key value|pipe');
var ast = bindings[0].expression.ast;
expect(ast).toBeAnInstanceOf(BindingPipe);
});
});
describe('parseInterpolation', () => {
it('should return null if no interpolation',
() => { expect(parseInterpolation('nothing')).toBe(null); });
it('should parse no prefix/suffix interpolation', () => {
var ast = parseInterpolation('{{a}}').ast;
expect(ast.strings).toEqual(['', '']);
expect(ast.expressions.length).toEqual(1);
expect(ast.expressions[0].name).toEqual('a');
});
it('should parse prefix/suffix with multiple interpolation', () => {
var originalExp = 'before {{ a }} middle {{ b }} after';
var ast = parseInterpolation(originalExp).ast;
expect(new Unparser().unparse(ast)).toEqual(originalExp);
});
it("should throw on empty interpolation expressions", () => {
expect(() => parseInterpolation("{{}}"))
.toThrowErrorWith(
"Parser Error: Blank expressions are not allowed in interpolated strings");
expect(() => parseInterpolation("foo {{ }}"))
.toThrowErrorWith(
"Parser Error: Blank expressions are not allowed in interpolated strings");
});
it('should parse conditional expression',
() => { checkInterpolation('{{ a < b ? a : b }}'); });
it('should parse expression with newline characters', () => {
checkInterpolation(`{{ 'foo' +\n 'bar' +\r 'baz' }}`, `{{ "foo" + "bar" + "baz" }}`);
});
it('should ignore comments in interpolation expressions',
() => { checkInterpolation('{{a //comment}}', '{{ a }}'); });
});
describe("parseSimpleBinding", () => {
it("should parse a field access", () => {
var p = parseSimpleBinding("name");
expect(unparse(p)).toEqual("name");
});
it("should parse a constant", () => {
var p = parseSimpleBinding("[1, 2]");
expect(unparse(p)).toEqual("[1, 2]");
});
it("should throw when the given expression is not just a field name", () => {
expect(() => parseSimpleBinding("name + 1"))
.toThrowErrorWith(
'Host binding expression can only contain field access and constants');
});
it('should throw when encountering interpolation', () => {
expect(() => parseSimpleBinding('{{exp}}'))
.toThrowErrorWith('Got interpolation ({{}}) where expression was expected');
});
});
describe('wrapLiteralPrimitive', () => {
it('should wrap a literal primitive', () => {
expect(unparse(createParser().wrapLiteralPrimitive("foo", null))).toEqual('"foo"');
});
});
});
}

View File

@ -0,0 +1,196 @@
import {
AST,
AstVisitor,
PropertyRead,
PropertyWrite,
Binary,
Chain,
Conditional,
EmptyExpr,
BindingPipe,
FunctionCall,
ImplicitReceiver,
Interpolation,
KeyedRead,
KeyedWrite,
LiteralArray,
LiteralMap,
LiteralPrimitive,
MethodCall,
PrefixNot,
Quote,
SafePropertyRead,
SafeMethodCall
} from 'angular2/src/compiler/expression_parser/ast';
import {StringWrapper, isPresent, isString} from 'angular2/src/facade/lang';
export class Unparser implements AstVisitor {
private static _quoteRegExp = /"/g;
private _expression: string;
unparse(ast: AST) {
this._expression = '';
this._visit(ast);
return this._expression;
}
visitPropertyRead(ast: PropertyRead, context: any) {
this._visit(ast.receiver);
this._expression += ast.receiver instanceof ImplicitReceiver ? `${ast.name}` : `.${ast.name}`;
}
visitPropertyWrite(ast: PropertyWrite, context: any) {
this._visit(ast.receiver);
this._expression +=
ast.receiver instanceof ImplicitReceiver ? `${ast.name} = ` : `.${ast.name} = `;
this._visit(ast.value);
}
visitBinary(ast: Binary, context: any) {
this._visit(ast.left);
this._expression += ` ${ast.operation} `;
this._visit(ast.right);
}
visitChain(ast: Chain, context: any) {
var len = ast.expressions.length;
for (let i = 0; i < len; i++) {
this._visit(ast.expressions[i]);
this._expression += i == len - 1 ? ';' : '; ';
}
}
visitConditional(ast: Conditional, context: any) {
this._visit(ast.condition);
this._expression += ' ? ';
this._visit(ast.trueExp);
this._expression += ' : ';
this._visit(ast.falseExp);
}
visitPipe(ast: BindingPipe, context: any) {
this._expression += '(';
this._visit(ast.exp);
this._expression += ` | ${ast.name}`;
ast.args.forEach(arg => {
this._expression += ':';
this._visit(arg);
});
this._expression += ')';
}
visitFunctionCall(ast: FunctionCall, context: any) {
this._visit(ast.target);
this._expression += '(';
var isFirst = true;
ast.args.forEach(arg => {
if (!isFirst) this._expression += ', ';
isFirst = false;
this._visit(arg);
});
this._expression += ')';
}
visitImplicitReceiver(ast: ImplicitReceiver, context: any) {}
visitInterpolation(ast: Interpolation, context: any) {
for (let i = 0; i < ast.strings.length; i++) {
this._expression += ast.strings[i];
if (i < ast.expressions.length) {
this._expression += '{{ ';
this._visit(ast.expressions[i]);
this._expression += ' }}';
}
}
}
visitKeyedRead(ast: KeyedRead, context: any) {
this._visit(ast.obj);
this._expression += '[';
this._visit(ast.key);
this._expression += ']';
}
visitKeyedWrite(ast: KeyedWrite, context: any) {
this._visit(ast.obj);
this._expression += '[';
this._visit(ast.key);
this._expression += '] = ';
this._visit(ast.value);
}
visitLiteralArray(ast: LiteralArray, context: any) {
this._expression += '[';
var isFirst = true;
ast.expressions.forEach(expression => {
if (!isFirst) this._expression += ', ';
isFirst = false;
this._visit(expression);
});
this._expression += ']';
}
visitLiteralMap(ast: LiteralMap, context: any) {
this._expression += '{';
var isFirst = true;
for (let i = 0; i < ast.keys.length; i++) {
if (!isFirst) this._expression += ', ';
isFirst = false;
this._expression += `${ast.keys[i]}: `;
this._visit(ast.values[i]);
}
this._expression += '}';
}
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {
if (isString(ast.value)) {
this._expression += `"${StringWrapper.replaceAll(ast.value, Unparser._quoteRegExp, '\"')}"`;
} else {
this._expression += `${ast.value}`;
}
}
visitMethodCall(ast: MethodCall, context: any) {
this._visit(ast.receiver);
this._expression += ast.receiver instanceof ImplicitReceiver ? `${ast.name}(` : `.${ast.name}(`;
var isFirst = true;
ast.args.forEach(arg => {
if (!isFirst) this._expression += ', ';
isFirst = false;
this._visit(arg);
});
this._expression += ')';
}
visitPrefixNot(ast: PrefixNot, context: any) {
this._expression += '!';
this._visit(ast.expression);
}
visitSafePropertyRead(ast: SafePropertyRead, context: any) {
this._visit(ast.receiver);
this._expression += `?.${ast.name}`;
}
visitSafeMethodCall(ast: SafeMethodCall, context: any) {
this._visit(ast.receiver);
this._expression += `?.${ast.name}(`;
var isFirst = true;
ast.args.forEach(arg => {
if (!isFirst) this._expression += ', ';
isFirst = false;
this._visit(arg);
});
this._expression += ')';
}
visitQuote(ast: Quote, context: any) {
this._expression += `${ast.prefix}:${ast.uninterpretedExpression}`;
}
private _visit(ast: AST) { ast.visit(this); }
}

View File

@ -0,0 +1,19 @@
// ATTENTION: This file will be overwritten with generated code by main()
import {print, IS_DART} from 'angular2/src/facade/lang';
import {TypeScriptEmitter} from 'angular2/src/compiler/output/ts_emitter';
import {DartEmitter} from 'angular2/src/compiler/output/dart_emitter';
import * as o from 'angular2/src/compiler/output/output_ast';
import {compileComp, compAMetadata} from './offline_compiler_util';
import {HostViewFactory} from 'angular2/src/core/linker/view';
export const hostViewFactory_CompA: HostViewFactory = null;
// Generator
export function main(args: string[]) {
var emitter = IS_DART ? new DartEmitter() : new TypeScriptEmitter();
compileComp(emitter, compAMetadata)
.then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -0,0 +1,17 @@
// ATTENTION: This file will be overwritten with generated code by main()
import {print} from 'angular2/src/facade/lang';
import {JavaScriptEmitter} from 'angular2/src/compiler/output/js_emitter';
import {compileComp, compAMetadata} from './offline_compiler_util';
import {HostViewFactory} from 'angular2/src/core/linker/view';
export const hostViewFactory_CompA: HostViewFactory = null;
// Generator
export function main(args: string[]) {
var emitter = new JavaScriptEmitter();
compileComp(emitter, compAMetadata)
.then((source) => {
// debug: console.error(source);
print(source);
});
}

View File

@ -0,0 +1,3 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
export const styles = CONST_EXPR([`.greenStyle[_ngcontent-a-1] { color: green; }`]);

View File

@ -0,0 +1,88 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders,
el
} from 'angular2/testing_internal';
import {IS_DART} from 'angular2/src/facade/lang';
import {Injector} from 'angular2/core';
import {DebugNode, DebugElement, getDebugNode} from 'angular2/src/core/debug/debug_node';
import {HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
import {HostViewFactory} from 'angular2/src/core/linker/view';
import * as typed from './offline_compiler_codegen_typed';
import * as untyped from './offline_compiler_codegen_untyped';
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {SharedStylesHost} from "angular2/src/platform/dom/shared_styles_host";
import {CompA} from './offline_compiler_util';
var _nextRootElementId = 0;
export function main() {
var outputDefs = [];
var typedHostViewFactory = typed.hostViewFactory_CompA;
var untypedHostViewFactory = untyped.hostViewFactory_CompA;
if (IS_DART || !DOM.supportsDOMEvents()) {
// Our generator only works on node.js and Dart...
outputDefs.push({'compAHostViewFactory': typedHostViewFactory, 'name': 'typed'});
}
if (!IS_DART) {
// Our generator only works on node.js and Dart...
if (!DOM.supportsDOMEvents()) {
outputDefs.push({'compAHostViewFactory': untypedHostViewFactory, 'name': 'untyped'});
}
}
describe('OfflineCompiler', () => {
var viewManager: AppViewManager;
var injector: Injector;
var sharedStylesHost: SharedStylesHost;
var rootEl;
beforeEach(inject([AppViewManager, Injector, SharedStylesHost],
(_viewManager, _injector, _sharedStylesHost) => {
viewManager = _viewManager;
injector = _injector;
sharedStylesHost = _sharedStylesHost;
}));
function createHostComp(hvf: HostViewFactory): DebugElement {
var doc = injector.get(DOCUMENT);
var oldRoots = DOM.querySelectorAll(doc, hvf.selector);
for (var i = 0; i < oldRoots.length; i++) {
DOM.remove(oldRoots[i]);
}
rootEl = el(`<${hvf.selector}></${hvf.selector}>`);
DOM.appendChild(doc.body, rootEl);
viewManager.createRootHostView(new HostViewFactoryRef_(hvf), hvf.selector, injector, []);
return <DebugElement>getDebugNode(rootEl);
}
outputDefs.forEach((outputDef) => {
describe(`${outputDef['name']}`, () => {
it('should compile components', () => {
var hostEl = createHostComp(outputDef['compAHostViewFactory']);
expect(hostEl.componentInstance).toBeAnInstanceOf(CompA);
var styles = sharedStylesHost.getAllStyles();
expect(styles[0]).toContain('.redStyle[_ngcontent');
expect(styles[1]).toContain('.greenStyle[_ngcontent');
});
});
});
});
}

View File

@ -0,0 +1,67 @@
import {print, IS_DART} from 'angular2/src/facade/lang';
import {OutputEmitter} from 'angular2/src/compiler/output/abstract_emitter';
import {
OfflineCompiler,
NormalizedComponentWithViewDirectives,
SourceModule
} from 'angular2/src/compiler/offline_compiler';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {Parser} from 'angular2/src/compiler/expression_parser/parser';
import {Lexer} from 'angular2/src/compiler/expression_parser/lexer';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
import {ViewCompiler} from 'angular2/src/compiler/view_compiler/view_compiler';
import {DirectiveNormalizer} from 'angular2/src/compiler/directive_normalizer';
import {CompilerConfig} from 'angular2/src/compiler/config';
import {createOfflineCompileUrlResolver} from 'angular2/src/compiler/url_resolver';
import {MockSchemaRegistry} from './schema_registry_mock';
import {MODULE_SUFFIX} from 'angular2/src/compiler/util';
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/compile_metadata';
export class CompA { user: string; }
var THIS_MODULE_PATH = `asset:angular2/test/compiler`;
var THIS_MODULE_URL = `${THIS_MODULE_PATH}/offline_compiler_util${MODULE_SUFFIX}`;
export var compAMetadata = CompileDirectiveMetadata.create({
isComponent: true,
selector: 'comp-a',
type: new CompileTypeMetadata(
{name: 'CompA', moduleUrl: THIS_MODULE_URL, runtime: CompA, diDeps: []}),
template: new CompileTemplateMetadata({
templateUrl: './offline_compiler_compa.html',
styles: ['.redStyle { color: red; }'],
styleUrls: ['./offline_compiler_compa.css']
})
});
function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCompiler {
var urlResolver = createOfflineCompileUrlResolver();
xhr.when(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, 'Hello World {{user}}!');
var htmlParser = new HtmlParser();
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
return new OfflineCompiler(
normalizer,
new TemplateParser(new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, []),
new StyleCompiler(urlResolver), new ViewCompiler(new CompilerConfig(true, true, true)),
emitter);
}
export function compileComp(emitter: OutputEmitter,
comp: CompileDirectiveMetadata): Promise<string> {
var xhr = new MockXHR();
var compiler = _createOfflineCompiler(xhr, emitter);
var result = compiler.normalizeDirectiveMetadata(comp).then((normComp) => {
return compiler.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])
.source;
});
xhr.flush();
return result;
}

View File

@ -0,0 +1,38 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {escapeSingleQuoteString} from 'angular2/src/compiler/output/abstract_emitter';
export function main() {
describe('AbstractEmitter', () => {
describe('escapeSingleQuoteString', () => {
it('should escape single quotes',
() => { expect(escapeSingleQuoteString(`'`, false)).toEqual(`'\\''`); });
it('should escape backslash',
() => { expect(escapeSingleQuoteString('\\', false)).toEqual(`'\\\\'`); });
it('should escape newlines',
() => { expect(escapeSingleQuoteString('\n', false)).toEqual(`'\\n'`); });
it('should escape carriage returns',
() => { expect(escapeSingleQuoteString('\r', false)).toEqual(`'\\r'`); });
it('should escape $', () => { expect(escapeSingleQuoteString('$', true)).toEqual(`'\\$'`); });
it('should not escape $',
() => { expect(escapeSingleQuoteString('$', false)).toEqual(`'$'`); });
});
});
}

View File

@ -0,0 +1,310 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {isBlank} from 'angular2/src/facade/lang';
import {DartEmitter} from 'angular2/src/compiler/output/dart_emitter';
import {CompileIdentifierMetadata} from 'angular2/src/compiler/compile_metadata';
import * as o from 'angular2/src/compiler/output/output_ast';
var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';
var sameModuleIdentifier =
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl});
var externalModuleIdentifier =
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl});
export function main() {
// Not supported features of our OutputAst in Dart:
// - declaring what should be exported via a special statement like `export`.
// Dart exports everything that has no `_` in its name.
// - declaring private fields via a statement like `private`.
// Dart exports everything that has no `_` in its name.
// - return types for function expressions
describe('DartEmitter', () => {
var emitter: DartEmitter;
var someVar: o.ReadVarExpr;
beforeEach(() => {
emitter = new DartEmitter();
someVar = o.variable('someVar');
});
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string {
if (isBlank(exportedVars)) {
exportedVars = [];
}
return emitter.emitStatements(someModuleUrl, [stmt], exportedVars);
}
it('should declare variables', () => {
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt())).toEqual(`var someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(null, [o.StmtModifier.Final])))
.toEqual(`final someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1, new o.BuiltinType(o.BuiltinTypeName.Int,
[o.TypeModifier.Const])))
.toDeclStmt(null, [o.StmtModifier.Final])))
.toEqual(`const int someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(), ['someVar']))
.toEqual(`var someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(o.INT_TYPE)))
.toEqual(`int someVar = 1;`);
});
it('should read and write variables', () => {
expect(emitStmt(someVar.toStmt())).toEqual(`someVar;`);
expect(emitStmt(someVar.set(o.literal(1)).toStmt())).toEqual(`someVar = 1;`);
expect(emitStmt(someVar.set(o.variable('someOtherVar').set(o.literal(1))).toStmt()))
.toEqual(`someVar = (someOtherVar = 1);`);
});
it('should read and write keys', () => {
expect(emitStmt(o.variable('someMap').key(o.variable('someKey')).toStmt()))
.toEqual(`someMap[someKey];`);
expect(emitStmt(o.variable('someMap').key(o.variable('someKey')).set(o.literal(1)).toStmt()))
.toEqual(`someMap[someKey] = 1;`);
});
it('should read and write properties', () => {
expect(emitStmt(o.variable('someObj').prop('someProp').toStmt()))
.toEqual(`someObj.someProp;`);
expect(emitStmt(o.variable('someObj').prop('someProp').set(o.literal(1)).toStmt()))
.toEqual(`someObj.someProp = 1;`);
});
it('should invoke functions and methods and constructors', () => {
expect(emitStmt(o.variable('someFn').callFn([o.literal(1)]).toStmt())).toEqual('someFn(1);');
expect(emitStmt(o.variable('someObj').callMethod('someMethod', [o.literal(1)]).toStmt()))
.toEqual('someObj.someMethod(1);');
expect(emitStmt(o.variable('SomeClass').instantiate([o.literal(1)]).toStmt()))
.toEqual('new SomeClass(1);');
});
it('should support builtin methods', () => {
expect(emitStmt(o.variable('arr1')
.callMethod(o.BuiltinMethod.ConcatArray, [o.variable('arr2')])
.toStmt()))
.toEqual('arr1..addAll(arr2);');
expect(emitStmt(o.variable('observable')
.callMethod(o.BuiltinMethod.SubscribeObservable, [o.variable('listener')])
.toStmt()))
.toEqual('observable.listen(listener);');
});
it('should support literals', () => {
expect(emitStmt(o.literal(0).toStmt())).toEqual('0;');
expect(emitStmt(o.literal(true).toStmt())).toEqual('true;');
expect(emitStmt(o.literal('someStr').toStmt())).toEqual(`'someStr';`);
expect(emitStmt(o.literal('$a').toStmt())).toEqual(`'\\$a';`);
expect(emitStmt(o.literalArr([o.literal(1)]).toStmt())).toEqual(`[1];`);
expect(emitStmt(o.literalMap([['someKey', o.literal(1)]]).toStmt()))
.toEqual(`{'someKey': 1};`);
expect(emitStmt(
o.literalMap([['someKey', o.literal(1)]], new o.MapType(o.NUMBER_TYPE)).toStmt()))
.toEqual(`<String, num>{'someKey': 1};`);
});
it('should support external identifiers', () => {
expect(emitStmt(o.importExpr(sameModuleIdentifier).toStmt())).toEqual('someLocalId;');
expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt()))
.toEqual([`import 'someOtherPath' as import0;`, `import0.someExternalId;`].join('\n'));
});
it('should support operators', () => {
var lhs = o.variable('lhs');
var rhs = o.variable('rhs');
expect(emitStmt(someVar.cast(o.INT_TYPE).toStmt())).toEqual('(someVar as int);');
expect(emitStmt(o.not(someVar).toStmt())).toEqual('!someVar;');
expect(
emitStmt(someVar.conditional(o.variable('trueCase'), o.variable('falseCase')).toStmt()))
.toEqual('someVar? trueCase: falseCase;');
expect(emitStmt(lhs.equals(rhs).toStmt())).toEqual('(lhs == rhs);');
expect(emitStmt(lhs.notEquals(rhs).toStmt())).toEqual('(lhs != rhs);');
expect(emitStmt(lhs.identical(rhs).toStmt())).toEqual('identical(lhs, rhs);');
expect(emitStmt(lhs.notIdentical(rhs).toStmt())).toEqual('!identical(lhs, rhs);');
expect(emitStmt(lhs.minus(rhs).toStmt())).toEqual('(lhs - rhs);');
expect(emitStmt(lhs.plus(rhs).toStmt())).toEqual('(lhs + rhs);');
expect(emitStmt(lhs.divide(rhs).toStmt())).toEqual('(lhs / rhs);');
expect(emitStmt(lhs.multiply(rhs).toStmt())).toEqual('(lhs * rhs);');
expect(emitStmt(lhs.modulo(rhs).toStmt())).toEqual('(lhs % rhs);');
expect(emitStmt(lhs.and(rhs).toStmt())).toEqual('(lhs && rhs);');
expect(emitStmt(lhs.or(rhs).toStmt())).toEqual('(lhs || rhs);');
expect(emitStmt(lhs.lower(rhs).toStmt())).toEqual('(lhs < rhs);');
expect(emitStmt(lhs.lowerEquals(rhs).toStmt())).toEqual('(lhs <= rhs);');
expect(emitStmt(lhs.bigger(rhs).toStmt())).toEqual('(lhs > rhs);');
expect(emitStmt(lhs.biggerEquals(rhs).toStmt())).toEqual('(lhs >= rhs);');
});
it('should support function expressions', () => {
expect(emitStmt(o.fn([], []).toStmt())).toEqual(['() {', '};'].join('\n'));
expect(emitStmt(o.fn([new o.FnParam('param1', o.INT_TYPE)], []).toStmt()))
.toEqual(['(int param1) {', '};'].join('\n'));
});
it('should support function statements', () => {
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [])))
.toEqual(['void someFn() {', '}'].join('\n'));
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [new o.ReturnStatement(o.literal(1))],
o.INT_TYPE)))
.toEqual(['int someFn() {', ' return 1;', '}'].join('\n'));
expect(
emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1', o.INT_TYPE)], [])))
.toEqual(['void someFn(int param1) {', '}'].join('\n'));
});
it('should support comments',
() => { expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n')); });
it('should support if stmt', () => {
var trueCase = o.variable('trueCase').callFn([]).toStmt();
var falseCase = o.variable('falseCase').callFn([]).toStmt();
expect(emitStmt(new o.IfStmt(o.variable('cond'), [trueCase])))
.toEqual(['if (cond) { trueCase(); }'].join('\n'));
expect(emitStmt(new o.IfStmt(o.variable('cond'), [trueCase], [falseCase])))
.toEqual(['if (cond) {', ' trueCase();', '} else {', ' falseCase();', '}'].join('\n'));
});
it('should support try/catch', () => {
var bodyStmt = o.variable('body').callFn([]).toStmt();
var catchStmt = o.variable('catchFn').callFn([o.CATCH_ERROR_VAR, o.CATCH_STACK_VAR]).toStmt();
expect(emitStmt(new o.TryCatchStmt([bodyStmt], [catchStmt])))
.toEqual(
['try {', ' body();', '} catch (error, stack) {', ' catchFn(error,stack);', '}']
.join('\n'));
});
it('should support support throwing',
() => { expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); });
describe('classes', () => {
var callSomeMethod: o.Statement;
beforeEach(() => { callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); });
it('should support declaring classes', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null, [])))
.toEqual(['class SomeClass {', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null, [])))
.toEqual(['class SomeClass extends SomeSuperClass {', '}'].join('\n'));
});
it('should support declaring constructors', () => {
var superCall = o.SUPER_EXPR.callFn([o.variable('someParam')]).toStmt();
expect(emitStmt(
new o.ClassStmt('SomeClass', null, [], [], new o.ClassMethod(null, [], []), [])))
.toEqual(['class SomeClass {', ' SomeClass() {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt(
'SomeClass', null, [], [],
new o.ClassMethod(null, [new o.FnParam('someParam', o.INT_TYPE)], []), [])))
.toEqual(['class SomeClass {', ' SomeClass(int someParam) {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [],
new o.ClassMethod(null, [], [superCall]), [])))
.toEqual(
['class SomeClass {', ' SomeClass(): super(someParam) {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [],
new o.ClassMethod(null, [], [callSomeMethod]), [])))
.toEqual(
['class SomeClass {', ' SomeClass() {', ' this.someMethod();', ' }', '}'].join(
'\n'));
});
it('should support declaring fields', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [new o.ClassField('someField')], [],
null, [])))
.toEqual(['class SomeClass {', ' var someField;', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null,
[new o.ClassField('someField', o.INT_TYPE)], [], null, [])))
.toEqual(['class SomeClass {', ' int someField;', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt(
'SomeClass', null,
[new o.ClassField('someField', o.INT_TYPE, [o.StmtModifier.Final])], [], null, [])))
.toEqual(['class SomeClass {', ' final int someField;', '}'].join('\n'));
});
it('should support declaring getters', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [])], null, [])))
.toEqual(['class SomeClass {', ' get someGetter {', ' }', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [], o.INT_TYPE)], null, [])))
.toEqual(['class SomeClass {', ' int get someGetter {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [callSomeMethod])], null,
[])))
.toEqual(
['class SomeClass {', ' get someGetter {', ' this.someMethod();', ' }', '}']
.join('\n'));
});
it('should support methods', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [])])))
.toEqual(['class SomeClass {', ' void someMethod() {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [], o.INT_TYPE)])))
.toEqual(['class SomeClass {', ' int someMethod() {', ' }', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt(
'SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [new o.FnParam('someParam', o.INT_TYPE)], [])])))
.toEqual(
['class SomeClass {', ' void someMethod(int someParam) {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [callSomeMethod])])))
.toEqual(
['class SomeClass {', ' void someMethod() {', ' this.someMethod();', ' }', '}']
.join('\n'));
});
});
it('should support builtin types', () => {
var writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(o.DYNAMIC_TYPE))).toEqual('dynamic a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.BOOL_TYPE))).toEqual('bool a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.INT_TYPE))).toEqual('int a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.NUMBER_TYPE))).toEqual('num a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.STRING_TYPE))).toEqual('String a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.FUNCTION_TYPE))).toEqual('Function a = null;');
});
it('should support external types', () => {
var writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(o.importType(sameModuleIdentifier))))
.toEqual('someLocalId a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.importType(externalModuleIdentifier))))
.toEqual([`import 'someOtherPath' as import0;`, `import0.someExternalId a = null;`].join(
'\n'));
});
it('should support combined types', () => {
var writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null))))
.toEqual('List<dynamic> a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(o.INT_TYPE))))
.toEqual('List<int> a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(null))))
.toEqual('Map<String, dynamic> a = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(o.INT_TYPE))))
.toEqual('Map<String, int> a = null;');
});
});
}

View File

@ -0,0 +1,296 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {isBlank} from 'angular2/src/facade/lang';
import {JavaScriptEmitter} from 'angular2/src/compiler/output/js_emitter';
import {CompileIdentifierMetadata} from 'angular2/src/compiler/compile_metadata';
import * as o from 'angular2/src/compiler/output/output_ast';
var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';
var sameModuleIdentifier =
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl});
var externalModuleIdentifier =
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl});
export function main() {
// Note supported features of our OutputAstin JavaScript / ES5:
// - types
// - declaring fields
describe('JavaScriptEmitter', () => {
var emitter: JavaScriptEmitter;
var someVar: o.ReadVarExpr;
beforeEach(() => {
emitter = new JavaScriptEmitter();
someVar = o.variable('someVar');
});
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string {
if (isBlank(exportedVars)) {
exportedVars = [];
}
return emitter.emitStatements(someModuleUrl, [stmt], exportedVars);
}
it('should declare variables', () => {
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt())).toEqual(`var someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(), ['someVar']))
.toEqual([
'var someVar = 1;',
`Object.defineProperty(exports, 'someVar', { get: function() { return someVar; }});`
].join('\n'));
});
it('should read and write variables', () => {
expect(emitStmt(someVar.toStmt())).toEqual(`someVar;`);
expect(emitStmt(someVar.set(o.literal(1)).toStmt())).toEqual(`someVar = 1;`);
expect(emitStmt(someVar.set(o.variable('someOtherVar').set(o.literal(1))).toStmt()))
.toEqual(`someVar = (someOtherVar = 1);`);
});
it('should read and write keys', () => {
expect(emitStmt(o.variable('someMap').key(o.variable('someKey')).toStmt()))
.toEqual(`someMap[someKey];`);
expect(emitStmt(o.variable('someMap').key(o.variable('someKey')).set(o.literal(1)).toStmt()))
.toEqual(`someMap[someKey] = 1;`);
});
it('should read and write properties', () => {
expect(emitStmt(o.variable('someObj').prop('someProp').toStmt()))
.toEqual(`someObj.someProp;`);
expect(emitStmt(o.variable('someObj').prop('someProp').set(o.literal(1)).toStmt()))
.toEqual(`someObj.someProp = 1;`);
});
it('should invoke functions and methods and constructors', () => {
expect(emitStmt(o.variable('someFn').callFn([o.literal(1)]).toStmt())).toEqual('someFn(1);');
expect(emitStmt(o.variable('someObj').callMethod('someMethod', [o.literal(1)]).toStmt()))
.toEqual('someObj.someMethod(1);');
expect(emitStmt(o.variable('SomeClass').instantiate([o.literal(1)]).toStmt()))
.toEqual('new SomeClass(1);');
});
it('should support builtin methods', () => {
expect(emitStmt(o.variable('arr1')
.callMethod(o.BuiltinMethod.ConcatArray, [o.variable('arr2')])
.toStmt()))
.toEqual('arr1.concat(arr2);');
expect(emitStmt(o.variable('observable')
.callMethod(o.BuiltinMethod.SubscribeObservable, [o.variable('listener')])
.toStmt()))
.toEqual('observable.subscribe(listener);');
});
it('should support literals', () => {
expect(emitStmt(o.literal(0).toStmt())).toEqual('0;');
expect(emitStmt(o.literal(true).toStmt())).toEqual('true;');
expect(emitStmt(o.literal('someStr').toStmt())).toEqual(`'someStr';`);
expect(emitStmt(o.literalArr([o.literal(1)]).toStmt())).toEqual(`[1];`);
expect(emitStmt(o.literalMap([['someKey', o.literal(1)]]).toStmt()))
.toEqual(`{'someKey': 1};`);
});
it('should support external identifiers', () => {
expect(emitStmt(o.importExpr(sameModuleIdentifier).toStmt())).toEqual('someLocalId;');
expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt()))
.toEqual(
[`var import0 = re` + `quire('./someOtherPath');`, `import0.someExternalId;`].join(
'\n'));
});
it('should support operators', () => {
var lhs = o.variable('lhs');
var rhs = o.variable('rhs');
expect(emitStmt(o.not(someVar).toStmt())).toEqual('!someVar;');
expect(
emitStmt(someVar.conditional(o.variable('trueCase'), o.variable('falseCase')).toStmt()))
.toEqual('someVar? trueCase: falseCase;');
expect(emitStmt(lhs.equals(rhs).toStmt())).toEqual('(lhs == rhs);');
expect(emitStmt(lhs.notEquals(rhs).toStmt())).toEqual('(lhs != rhs);');
expect(emitStmt(lhs.identical(rhs).toStmt())).toEqual('(lhs === rhs);');
expect(emitStmt(lhs.notIdentical(rhs).toStmt())).toEqual('(lhs !== rhs);');
expect(emitStmt(lhs.minus(rhs).toStmt())).toEqual('(lhs - rhs);');
expect(emitStmt(lhs.plus(rhs).toStmt())).toEqual('(lhs + rhs);');
expect(emitStmt(lhs.divide(rhs).toStmt())).toEqual('(lhs / rhs);');
expect(emitStmt(lhs.multiply(rhs).toStmt())).toEqual('(lhs * rhs);');
expect(emitStmt(lhs.modulo(rhs).toStmt())).toEqual('(lhs % rhs);');
expect(emitStmt(lhs.and(rhs).toStmt())).toEqual('(lhs && rhs);');
expect(emitStmt(lhs.or(rhs).toStmt())).toEqual('(lhs || rhs);');
expect(emitStmt(lhs.lower(rhs).toStmt())).toEqual('(lhs < rhs);');
expect(emitStmt(lhs.lowerEquals(rhs).toStmt())).toEqual('(lhs <= rhs);');
expect(emitStmt(lhs.bigger(rhs).toStmt())).toEqual('(lhs > rhs);');
expect(emitStmt(lhs.biggerEquals(rhs).toStmt())).toEqual('(lhs >= rhs);');
});
it('should support function expressions', () => {
expect(emitStmt(o.fn([], []).toStmt())).toEqual(['function() {', '};'].join('\n'));
expect(emitStmt(o.fn([], [new o.ReturnStatement(o.literal(1))]).toStmt()))
.toEqual(['function() {', ' return 1;\n};'].join('\n'));
expect(emitStmt(o.fn([new o.FnParam('param1')], []).toStmt()))
.toEqual(['function(param1) {', '};'].join('\n'));
});
it('should support function statements', () => {
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [])))
.toEqual(['function someFn() {', '}'].join('\n'));
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], []), ['someFn']))
.toEqual([
'function someFn() {',
'}',
`Object.defineProperty(exports, 'someFn', { get: function() { return someFn; }});`
].join('\n'));
expect(
emitStmt(new o.DeclareFunctionStmt('someFn', [], [new o.ReturnStatement(o.literal(1))])))
.toEqual(['function someFn() {', ' return 1;', '}'].join('\n'));
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1')], [])))
.toEqual(['function someFn(param1) {', '}'].join('\n'));
});
it('should support comments',
() => { expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n')); });
it('should support if stmt', () => {
var trueCase = o.variable('trueCase').callFn([]).toStmt();
var falseCase = o.variable('falseCase').callFn([]).toStmt();
expect(emitStmt(new o.IfStmt(o.variable('cond'), [trueCase])))
.toEqual(['if (cond) { trueCase(); }'].join('\n'));
expect(emitStmt(new o.IfStmt(o.variable('cond'), [trueCase], [falseCase])))
.toEqual(['if (cond) {', ' trueCase();', '} else {', ' falseCase();', '}'].join('\n'));
});
it('should support try/catch', () => {
var bodyStmt = o.variable('body').callFn([]).toStmt();
var catchStmt = o.variable('catchFn').callFn([o.CATCH_ERROR_VAR, o.CATCH_STACK_VAR]).toStmt();
expect(emitStmt(new o.TryCatchStmt([bodyStmt], [catchStmt])))
.toEqual([
'try {',
' body();',
'} catch (error) {',
' var stack = error.stack;',
' catchFn(error,stack);',
'}'
].join('\n'));
});
it('should support support throwing',
() => { expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); });
describe('classes', () => {
var callSomeMethod: o.Statement;
beforeEach(() => { callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); });
it('should support declaring classes', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null, [])))
.toEqual(['function SomeClass() {', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null, []), ['SomeClass']))
.toEqual([
'function SomeClass() {',
'}',
`Object.defineProperty(exports, 'SomeClass', { get: function() { return SomeClass; }});`
].join('\n'));
expect(
emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null, [])))
.toEqual([
'function SomeClass() {',
'}',
'SomeClass.prototype = Object.create(SomeSuperClass.prototype);'
].join('\n'));
});
it('should support declaring constructors', () => {
var superCall = o.SUPER_EXPR.callFn([o.variable('someParam')]).toStmt();
expect(emitStmt(
new o.ClassStmt('SomeClass', null, [], [], new o.ClassMethod(null, [], []), [])))
.toEqual(['function SomeClass() {', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [],
new o.ClassMethod(null, [new o.FnParam('someParam')], []),
[])))
.toEqual(['function SomeClass(someParam) {', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [],
new o.ClassMethod(null, [], [superCall]), [])))
.toEqual([
'function SomeClass() {',
' var self = this;',
' SomeSuperClass.call(this, someParam);',
'}',
'SomeClass.prototype = Object.create(SomeSuperClass.prototype);'
].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [],
new o.ClassMethod(null, [], [callSomeMethod]), [])))
.toEqual(
['function SomeClass() {', ' var self = this;', ' self.someMethod();', '}'].join(
'\n'));
});
it('should support declaring getters', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [])], null, [])))
.toEqual([
'function SomeClass() {',
'}',
`Object.defineProperty(SomeClass.prototype, 'someGetter', { get: function() {`,
`}});`
].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [callSomeMethod])], null,
[])))
.toEqual([
'function SomeClass() {',
'}',
`Object.defineProperty(SomeClass.prototype, 'someGetter', { get: function() {`,
` var self = this;`,
` self.someMethod();`,
`}});`
].join('\n'));
});
it('should support methods', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [])])))
.toEqual([
'function SomeClass() {',
'}',
'SomeClass.prototype.someMethod = function() {',
'};'
].join('\n'));
expect(emitStmt(new o.ClassStmt(
'SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [new o.FnParam('someParam')], [])])))
.toEqual([
'function SomeClass() {',
'}',
'SomeClass.prototype.someMethod = function(someParam) {',
'};'
].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [callSomeMethod])])))
.toEqual([
'function SomeClass() {',
'}',
'SomeClass.prototype.someMethod = function() {',
' var self = this;',
' self.someMethod();',
'};'
].join('\n'));
});
});
});
}

View File

@ -0,0 +1,21 @@
// ATTENTION: This file will be overwritten with generated code by main()
import {print, IS_DART} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {codegenExportsVars, codegenStmts} from './output_emitter_util';
import {TypeScriptEmitter} from 'angular2/src/compiler/output/ts_emitter';
import {DartEmitter} from 'angular2/src/compiler/output/dart_emitter';
import * as o from 'angular2/src/compiler/output/output_ast';
export function getExpressions(): any {
return unimplemented();
}
// Generator
export function main(args: string[]) {
var emitter = IS_DART ? new DartEmitter() : new TypeScriptEmitter();
var emittedCode =
emitter.emitStatements('asset:angular2/test/compiler/output/output_emitter_codegen_typed',
codegenStmts, codegenExportsVars);
// debug: console.error(emittedCode);
print(emittedCode);
}

View File

@ -0,0 +1,19 @@
// ATTENTION: This file will be overwritten with generated code by main()
import {print} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {codegenExportsVars, codegenStmts} from './output_emitter_util';
import {JavaScriptEmitter} from 'angular2/src/compiler/output/js_emitter';
export function getExpressions(): any {
return unimplemented();
}
// Generator
export function main(args: string[]) {
var emitter = new JavaScriptEmitter();
var emittedCode =
emitter.emitStatements('asset:angular2/test/compiler/output/output_emitter_codegen_untyped',
codegenStmts, codegenExportsVars);
// debug: console.error(emittedCode);
print(emittedCode);
}

View File

@ -0,0 +1,197 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder,
browserDetection
} from 'angular2/testing_internal';
import {IS_DART} from 'angular2/src/facade/lang';
import * as typed from './output_emitter_codegen_typed';
import * as untyped from './output_emitter_codegen_untyped';
import {jitStatements} from 'angular2/src/compiler/output/output_jit';
import {interpretStatements} from 'angular2/src/compiler/output/output_interpreter';
import {codegenStmts, ExternalClass, DynamicClassInstanceFactory} from './output_emitter_util';
import {EventEmitter} from 'angular2/src/facade/async';
import {ViewType} from 'angular2/src/core/linker/view_type';
import {BaseException} from 'angular2/src/facade/exceptions';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
export function main() {
var outputDefs = [];
outputDefs.push({
'getExpressions': () => interpretStatements(codegenStmts, 'getExpressions',
new DynamicClassInstanceFactory()),
'name': 'interpreted'
});
if (IS_DART || !DOM.supportsDOMEvents()) {
// Our generator only works on node.js and Dart...
outputDefs.push({'getExpressions': () => typed.getExpressions, 'name': 'typed'});
}
if (!IS_DART) {
// Our generator only works on node.js and Dart...
if (!DOM.supportsDOMEvents()) {
outputDefs.push({'getExpressions': () => untyped.getExpressions, 'name': 'untyped'});
}
outputDefs.push({
'getExpressions': () => jitStatements('output_emitter_spec', codegenStmts, 'getExpressions'),
'name': 'jit'
});
}
describe('output emitter', () => {
outputDefs.forEach((outputDef) => {
describe(`${outputDef['name']}`, () => {
var expressions;
beforeEach(() => { expressions = outputDef['getExpressions']()(); });
it('should support literals', () => {
expect(expressions['stringLiteral']).toEqual('Hello World!');
expect(expressions['intLiteral']).toEqual(42);
expect(expressions['boolLiteral']).toEqual(true);
expect(expressions['arrayLiteral']).toEqual([0]);
expect(expressions['mapLiteral']).toEqual({'key0': 0});
});
it('should support reading vars/keys/props', () => {
expect(expressions['readVar']).toEqual('someValue');
expect(expressions['readKey']).toEqual('someValue');
expect(expressions['readPropExternalInstance']).toEqual('someValue');
expect(expressions['readPropDynamicInstance']).toEqual('dynamicValue');
expect(expressions['readGetterDynamicInstance'])
.toEqual({'data': 'someValue', 'dynamicProp': 'dynamicValue'});
});
it('should support writing to vars / keys / props', () => {
expect(expressions['changedVar']).toEqual('changedValue');
expect(expressions['changedKey']).toEqual('changedValue');
expect(expressions['changedPropExternalInstance']).toEqual('changedValue');
expect(expressions['changedPropDynamicInstance']).toEqual('changedValue');
});
it('should support declaring functions with parameters and return', () => {
expect(expressions['fn']('someParam')).toEqual({'param': 'someParam'});
expect(expressions['closureInDynamicInstance']('someParam'))
.toEqual({'param': 'someParam', 'data': 'someValue', 'dynamicProp': 'dynamicValue'});
});
it('should support invoking functions and methods', () => {
expect(expressions['invokeFn']).toEqual({'param': 'someParam'});
expect(expressions['concatedArray']).toEqual([0, 1]);
expect(expressions['invokeMethodExternalInstance'])
.toEqual({'data': 'someValue', 'param': 'someParam'});
expect(expressions['invokeMethodDynamicInstance'])
.toEqual({'data': 'someValue', 'dynamicProp': 'dynamicValue', 'param': 'someParam'});
});
it('should support conditionals', () => {
expect(expressions['conditionalTrue']).toEqual('true');
expect(expressions['conditionalFalse']).toEqual('false');
});
it('should support not', () => { expect(expressions['not']).toEqual(true); });
it('should support reading external identifiers', () => {
expect(expressions['externalTestIdentifier']).toBe(ExternalClass);
expect(expressions['externalSrcIdentifier']).toBe(EventEmitter);
expect(expressions['externalEnumIdentifier']).toBe(ViewType.HOST);
});
it('should support instantiating classes', () => {
expect(expressions['externalInstance']).toBeAnInstanceOf(ExternalClass);
// Note: toBeAnInstanceOf does not check super classes in Dart...
expect(expressions['dynamicInstance'] instanceof ExternalClass).toBe(true);
});
describe('operators', () => {
var ops;
var aObj, bObj;
beforeEach(() => {
ops = expressions['operators'];
aObj = new Object();
bObj = new Object();
});
it('should support ==', () => {
expect(ops['=='](aObj, aObj)).toBe(true);
expect(ops['=='](aObj, bObj)).toBe(false);
expect(ops['=='](1, 1)).toBe(true);
expect(ops['=='](0, 1)).toBe(false);
expect(ops['==']('a', 'a')).toBe(true);
expect(ops['==']('a', 'b')).toBe(false);
});
it('should support !=', () => {
expect(ops['!='](aObj, aObj)).toBe(false);
expect(ops['!='](aObj, bObj)).toBe(true);
expect(ops['!='](1, 1)).toBe(false);
expect(ops['!='](0, 1)).toBe(true);
expect(ops['!=']('a', 'a')).toBe(false);
expect(ops['!=']('a', 'b')).toBe(true);
});
it('should support ===', () => {
expect(ops['==='](aObj, aObj)).toBe(true);
expect(ops['==='](aObj, bObj)).toBe(false);
expect(ops['==='](1, 1)).toBe(true);
expect(ops['==='](0, 1)).toBe(false);
});
it('should support !==', () => {
expect(ops['!=='](aObj, aObj)).toBe(false);
expect(ops['!=='](aObj, bObj)).toBe(true);
expect(ops['!=='](1, 1)).toBe(false);
expect(ops['!=='](0, 1)).toBe(true);
});
it('should support -', () => { expect(ops['-'](3, 2)).toEqual(1); });
it('should support +', () => { expect(ops['+'](1, 2)).toEqual(3); });
it('should support /', () => { expect(ops['/'](6, 2)).toEqual(3); });
it('should support *', () => { expect(ops['*'](2, 3)).toEqual(6); });
it('should support %', () => { expect(ops['%'](3, 2)).toEqual(1); });
it('should support &&', () => {
expect(ops['&&'](true, true)).toBe(true);
expect(ops['&&'](true, false)).toBe(false);
});
it('should support ||', () => {
expect(ops['||'](true, false)).toBe(true);
expect(ops['||'](false, false)).toBe(false);
});
it('should support <', () => {
expect(ops['<'](1, 2)).toBe(true);
expect(ops['<'](1, 1)).toBe(false);
});
it('should support <=', () => {
expect(ops['<='](1, 2)).toBe(true);
expect(ops['<='](1, 1)).toBe(true);
});
it('should support >', () => {
expect(ops['>'](2, 1)).toBe(true);
expect(ops['>'](1, 1)).toBe(false);
});
it('should support >=', () => {
expect(ops['>='](2, 1)).toBe(true);
expect(ops['>='](1, 1)).toBe(true);
});
});
it('should support throwing errors',
() => { expect(expressions['throwError']).toThrowError('someError'); });
it('should support catching errors', () => {
function someOperation() { throw new BaseException('Boom!'); }
var errorAndStack = expressions['catchError'](someOperation);
expect(errorAndStack[0].message).toEqual('Boom!');
// Somehow we don't get stacktraces on ios7...
if (!browserDetection.isIOS7 && !browserDetection.isIE) {
expect(errorAndStack[1].toString()).toContain('someOperation');
}
});
});
});
});
}

View File

@ -0,0 +1,267 @@
import {CompileIdentifierMetadata} from 'angular2/src/compiler/compile_metadata';
import {EventEmitter} from 'angular2/src/facade/async';
import {ViewType} from 'angular2/src/core/linker/view_type';
import {BaseException} from 'angular2/src/facade/exceptions';
import {InstanceFactory, DynamicInstance} from 'angular2/src/compiler/output/output_interpreter';
import {MODULE_SUFFIX} from 'angular2/src/compiler/util';
import * as o from 'angular2/src/compiler/output/output_ast';
export class ExternalClass {
changeable: any;
constructor(public data: any) { this.changeable = data; }
someMethod(a) { return {'param': a, 'data': this.data}; }
}
var testDataIdentifier = new CompileIdentifierMetadata({
name: 'ExternalClass',
moduleUrl: `asset:angular2/test/compiler/output/output_emitter_util${MODULE_SUFFIX}`,
runtime: ExternalClass
});
var eventEmitterIdentifier = new CompileIdentifierMetadata({
name: 'EventEmitter',
moduleUrl: `asset:angular2/lib/src/facade/async${MODULE_SUFFIX}`,
runtime: EventEmitter
});
var enumIdentifier = new CompileIdentifierMetadata({
name: 'ViewType.HOST',
moduleUrl: `asset:angular2/lib/src/core/linker/view_type${MODULE_SUFFIX}`,
runtime: ViewType.HOST
});
var baseExceptionIdentifier = new CompileIdentifierMetadata({
name: 'BaseException',
moduleUrl: `asset:angular2/lib/src/facade/exceptions${MODULE_SUFFIX}`,
runtime: BaseException
});
export var codegenExportsVars = [
'getExpressions',
];
var _getExpressionsStmts: o.Statement[] = [
o.variable('readVar')
.set(o.literal('someValue'))
.toDeclStmt(),
o.variable('changedVar').set(o.literal('initialValue')).toDeclStmt(),
o.variable('changedVar').set(o.literal('changedValue')).toStmt(),
o.variable('map')
.set(o.literalMap([
['someKey', o.literal('someValue')],
['changeable', o.literal('initialValue')],
]))
.toDeclStmt(),
o.variable('map').key(o.literal('changeable')).set(o.literal('changedValue')).toStmt(),
o.variable('externalInstance')
.set(o.importExpr(testDataIdentifier).instantiate([o.literal('someValue')]))
.toDeclStmt(),
o.variable('externalInstance').prop('changeable').set(o.literal('changedValue')).toStmt(),
o.variable('fn')
.set(o.fn([new o.FnParam('param')],
[new o.ReturnStatement(o.literalMap([['param', o.variable('param')]]))],
o.DYNAMIC_TYPE))
.toDeclStmt(),
o.variable('throwError')
.set(o.fn([],
[
new o.ThrowStmt(o.importExpr(baseExceptionIdentifier)
.instantiate([o.literal('someError')]))
]))
.toDeclStmt(),
o.variable('catchError')
.set(o.fn([new o.FnParam('runCb')],
[
new o.TryCatchStmt([o.variable('runCb').callFn([]).toStmt()],
[
new o.ReturnStatement(
o.literalArr([o.CATCH_ERROR_VAR, o.CATCH_STACK_VAR]))
])
],
o.DYNAMIC_TYPE))
.toDeclStmt(),
o.variable('dynamicInstance')
.set(o.variable('DynamicClass')
.instantiate([o.literal('someValue'), o.literal('dynamicValue')]))
.toDeclStmt(),
o.variable('dynamicInstance').prop('dynamicChangeable').set(o.literal('changedValue')).toStmt(),
new o.ReturnStatement(o.literalMap([
['stringLiteral', o.literal('Hello World!')],
['intLiteral', o.literal(42)],
['boolLiteral', o.literal(true)],
['arrayLiteral', o.literalArr([o.literal(0)])],
['mapLiteral', o.literalMap([['key0', o.literal(0)]])],
['readVar', o.variable('readVar')],
['changedVar', o.variable('changedVar')],
['readKey', o.variable('map').key(o.literal('someKey'))],
['changedKey', o.variable('map').key(o.literal('changeable'))],
['readPropExternalInstance', o.variable('externalInstance').prop('data')],
['readPropDynamicInstance', o.variable('dynamicInstance').prop('dynamicProp')],
['readGetterDynamicInstance', o.variable('dynamicInstance').prop('dynamicGetter')],
['changedPropExternalInstance', o.variable('externalInstance').prop('changeable')],
['changedPropDynamicInstance', o.variable('dynamicInstance').prop('dynamicChangeable')],
[
'invokeMethodExternalInstance',
o.variable('externalInstance').callMethod('someMethod', [o.literal('someParam')])
],
[
'invokeMethodDynamicInstance',
o.variable('dynamicInstance').callMethod('dynamicMethod', [o.literal('someParam')])
],
[
'concatedArray',
o.literalArr([o.literal(0)])
.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr([o.literal(1)])])
],
['fn', o.variable('fn')],
['closureInDynamicInstance', o.variable('dynamicInstance').prop('closure')],
['invokeFn', o.variable('fn').callFn([o.literal('someParam')])],
[
'conditionalTrue',
o.literal('')
.prop('length')
.equals(o.literal(0))
.conditional(o.literal('true'), o.literal('false'))
],
[
'conditionalFalse',
o.literal('')
.prop('length')
.notEquals(o.literal(0))
.conditional(o.literal('true'), o.literal('false'))
],
['not', o.not(o.literal(false))],
['externalTestIdentifier', o.importExpr(testDataIdentifier)],
['externalSrcIdentifier', o.importExpr(eventEmitterIdentifier)],
['externalEnumIdentifier', o.importExpr(enumIdentifier)],
['externalInstance', o.variable('externalInstance')],
['dynamicInstance', o.variable('dynamicInstance')],
['throwError', o.variable('throwError')],
['catchError', o.variable('catchError')],
[
'operators',
o.literalMap([
['==', createOperatorFn(o.BinaryOperator.Equals)],
['!=', createOperatorFn(o.BinaryOperator.NotEquals)],
['===', createOperatorFn(o.BinaryOperator.Identical)],
['!==', createOperatorFn(o.BinaryOperator.NotIdentical)],
['-', createOperatorFn(o.BinaryOperator.Minus)],
['+', createOperatorFn(o.BinaryOperator.Plus)],
['/', createOperatorFn(o.BinaryOperator.Divide)],
['*', createOperatorFn(o.BinaryOperator.Multiply)],
['%', createOperatorFn(o.BinaryOperator.Modulo)],
['&&', createOperatorFn(o.BinaryOperator.And)],
['||', createOperatorFn(o.BinaryOperator.Or)],
['<', createOperatorFn(o.BinaryOperator.Lower)],
['<=', createOperatorFn(o.BinaryOperator.LowerEquals)],
['>', createOperatorFn(o.BinaryOperator.Bigger)],
['>=', createOperatorFn(o.BinaryOperator.BiggerEquals)]
])
],
]))
];
export var codegenStmts: o.Statement[] = [
new o.CommentStmt('This is a comment'),
new o.ClassStmt(
'DynamicClass', o.importExpr(testDataIdentifier),
[
new o.ClassField('dynamicProp', o.DYNAMIC_TYPE),
new o.ClassField('dynamicChangeable', o.DYNAMIC_TYPE),
new o.ClassField('closure', o.FUNCTION_TYPE)
],
[
new o.ClassGetter('dynamicGetter',
[
new o.ReturnStatement(o.literalMap([
['data', o.THIS_EXPR.prop('data')],
['dynamicProp', o.THIS_EXPR.prop('dynamicProp')]
]))
],
new o.MapType(o.DYNAMIC_TYPE))
],
new o.ClassMethod(null,
[
new o.FnParam('dataParam', o.DYNAMIC_TYPE),
new o.FnParam('dynamicPropParam', o.DYNAMIC_TYPE)
],
[
o.SUPER_EXPR.callFn([o.variable('dataParam')])
.toStmt(),
o.THIS_EXPR.prop('dynamicProp')
.set(o.variable('dynamicPropParam'))
.toStmt(),
o.THIS_EXPR.prop('dynamicChangeable')
.set(o.variable('dynamicPropParam'))
.toStmt(),
o.THIS_EXPR.prop('closure')
.set(o.fn([new o.FnParam('param', o.DYNAMIC_TYPE)],
[
new o.ReturnStatement(o.literalMap([
['param', o.variable('param')],
['data', o.THIS_EXPR.prop('data')],
['dynamicProp', o.THIS_EXPR.prop('dynamicProp')]
]))
],
o.DYNAMIC_TYPE))
.toStmt(),
]),
[
new o.ClassMethod('dynamicMethod', [new o.FnParam('param', o.DYNAMIC_TYPE)],
[
new o.ReturnStatement(o.literalMap([
['param', o.variable('param')],
['data', o.THIS_EXPR.prop('data')],
['dynamicProp', o.THIS_EXPR.prop('dynamicProp')]
]))
],
o.DYNAMIC_TYPE)
]),
o.fn([], _getExpressionsStmts, o.DYNAMIC_TYPE).toDeclStmt('getExpressions')
];
function createOperatorFn(op: o.BinaryOperator) {
return o.fn(
[new o.FnParam('a'), new o.FnParam('b')],
[new o.ReturnStatement(new o.BinaryOperatorExpr(op, o.variable('a'), o.variable('b')))],
o.DYNAMIC_TYPE);
}
export class DynamicClassInstanceFactory implements InstanceFactory {
createInstance(superClass: any, clazz: any, args: any[], props: Map<string, any>,
getters: Map<string, Function>, methods: Map<string, Function>): any {
if (superClass === ExternalClass) {
return new _InterpretiveDynamicClass(args, clazz, props, getters, methods);
}
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
}
}
class _InterpretiveDynamicClass extends ExternalClass implements DynamicInstance {
constructor(args: any[], public clazz: any, public props: Map<string, any>,
public getters: Map<string, Function>, public methods: Map<string, Function>) {
super(args[0]);
}
childMethod(a) { return this.methods.get('childMethod')(a); }
}

View File

@ -0,0 +1,72 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {getImportModulePath, ImportEnv} from 'angular2/src/compiler/output/path_util';
export function main() {
describe('PathUtils', () => {
describe('getImportModulePath', () => {
it('should calculate relative paths for JS and Dart', () => {
expect(getImportModulePath('asset:somePkg/lib/modPath', 'asset:somePkg/lib/impPath',
ImportEnv.JS))
.toEqual('./impPath');
expect(getImportModulePath('asset:somePkg/lib/modPath', 'asset:somePkg/lib/impPath',
ImportEnv.Dart))
.toEqual('impPath');
});
it('should calculate relative paths for different constellations', () => {
expect(getImportModulePath('asset:somePkg/test/modPath', 'asset:somePkg/test/impPath',
ImportEnv.JS))
.toEqual('./impPath');
expect(getImportModulePath('asset:somePkg/lib/modPath', 'asset:somePkg/lib/dir2/impPath',
ImportEnv.JS))
.toEqual('./dir2/impPath');
expect(getImportModulePath('asset:somePkg/lib/dir1/modPath', 'asset:somePkg/lib/impPath',
ImportEnv.JS))
.toEqual('../impPath');
expect(getImportModulePath('asset:somePkg/lib/dir1/modPath',
'asset:somePkg/lib/dir2/impPath', ImportEnv.JS))
.toEqual('../dir2/impPath');
});
it('should calculate absolute paths for JS and Dart', () => {
expect(getImportModulePath('asset:somePkg/lib/modPath', 'asset:someOtherPkg/lib/impPath',
ImportEnv.JS))
.toEqual('someOtherPkg/impPath');
expect(getImportModulePath('asset:somePkg/lib/modPath', 'asset:someOtherPkg/lib/impPath',
ImportEnv.Dart))
.toEqual('package:someOtherPkg/impPath');
});
it('should not allow absolute imports of non lib modules', () => {
expect(() => getImportModulePath('asset:somePkg/lib/modPath', 'asset:somePkg/test/impPath',
ImportEnv.Dart))
.toThrowError(
`Can't import url asset:somePkg/test/impPath from asset:somePkg/lib/modPath`);
});
it('should not allow non asset urls as base url', () => {
expect(() => getImportModulePath('http:somePkg/lib/modPath', 'asset:somePkg/test/impPath',
ImportEnv.Dart))
.toThrowError(`Url http:somePkg/lib/modPath is not a valid asset: url`);
});
it('should allow non asset urls as import urls and pass them through', () => {
expect(getImportModulePath('asset:somePkg/lib/modPath', 'dart:html', ImportEnv.Dart))
.toEqual('dart:html');
});
});
});
}

View File

@ -0,0 +1,321 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {isBlank} from 'angular2/src/facade/lang';
import {TypeScriptEmitter} from 'angular2/src/compiler/output/ts_emitter';
import {CompileIdentifierMetadata} from 'angular2/src/compiler/compile_metadata';
import * as o from 'angular2/src/compiler/output/output_ast';
var someModuleUrl = 'asset:somePackage/lib/somePath';
var anotherModuleUrl = 'asset:somePackage/lib/someOtherPath';
var sameModuleIdentifier =
new CompileIdentifierMetadata({name: 'someLocalId', moduleUrl: someModuleUrl});
var externalModuleIdentifier =
new CompileIdentifierMetadata({name: 'someExternalId', moduleUrl: anotherModuleUrl});
export function main() {
// Note supported features of our OutputAstin TS:
// - real `const` like in Dart
// - final fields
describe('TypeScriptEmitter', () => {
var emitter: TypeScriptEmitter;
var someVar: o.ReadVarExpr;
beforeEach(() => {
emitter = new TypeScriptEmitter();
someVar = o.variable('someVar');
});
function emitStmt(stmt: o.Statement, exportedVars: string[] = null): string {
if (isBlank(exportedVars)) {
exportedVars = [];
}
return emitter.emitStatements(someModuleUrl, [stmt], exportedVars);
}
it('should declare variables', () => {
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt())).toEqual(`var someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(null, [o.StmtModifier.Final])))
.toEqual(`const someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(), ['someVar']))
.toEqual(`export var someVar = 1;`);
expect(emitStmt(someVar.set(o.literal(1)).toDeclStmt(o.INT_TYPE)))
.toEqual(`var someVar:number = 1;`);
});
it('should read and write variables', () => {
expect(emitStmt(someVar.toStmt())).toEqual(`someVar;`);
expect(emitStmt(someVar.set(o.literal(1)).toStmt())).toEqual(`someVar = 1;`);
expect(emitStmt(someVar.set(o.variable('someOtherVar').set(o.literal(1))).toStmt()))
.toEqual(`someVar = (someOtherVar = 1);`);
});
it('should read and write keys', () => {
expect(emitStmt(o.variable('someMap').key(o.variable('someKey')).toStmt()))
.toEqual(`someMap[someKey];`);
expect(emitStmt(o.variable('someMap').key(o.variable('someKey')).set(o.literal(1)).toStmt()))
.toEqual(`someMap[someKey] = 1;`);
});
it('should read and write properties', () => {
expect(emitStmt(o.variable('someObj').prop('someProp').toStmt()))
.toEqual(`someObj.someProp;`);
expect(emitStmt(o.variable('someObj').prop('someProp').set(o.literal(1)).toStmt()))
.toEqual(`someObj.someProp = 1;`);
});
it('should invoke functions and methods and constructors', () => {
expect(emitStmt(o.variable('someFn').callFn([o.literal(1)]).toStmt())).toEqual('someFn(1);');
expect(emitStmt(o.variable('someObj').callMethod('someMethod', [o.literal(1)]).toStmt()))
.toEqual('someObj.someMethod(1);');
expect(emitStmt(o.variable('SomeClass').instantiate([o.literal(1)]).toStmt()))
.toEqual('new SomeClass(1);');
});
it('should support builtin methods', () => {
expect(emitStmt(o.variable('arr1')
.callMethod(o.BuiltinMethod.ConcatArray, [o.variable('arr2')])
.toStmt()))
.toEqual('arr1.concat(arr2);');
expect(emitStmt(o.variable('observable')
.callMethod(o.BuiltinMethod.SubscribeObservable, [o.variable('listener')])
.toStmt()))
.toEqual('observable.subscribe(listener);');
});
it('should support literals', () => {
expect(emitStmt(o.literal(0).toStmt())).toEqual('0;');
expect(emitStmt(o.literal(true).toStmt())).toEqual('true;');
expect(emitStmt(o.literal('someStr').toStmt())).toEqual(`'someStr';`);
expect(emitStmt(o.literalArr([o.literal(1)]).toStmt())).toEqual(`[1];`);
expect(emitStmt(o.literalMap([['someKey', o.literal(1)]]).toStmt()))
.toEqual(`{'someKey': 1};`);
});
it('should support external identifiers', () => {
expect(emitStmt(o.importExpr(sameModuleIdentifier).toStmt())).toEqual('someLocalId;');
expect(emitStmt(o.importExpr(externalModuleIdentifier).toStmt()))
.toEqual([`import * as import0 from './someOtherPath';`, `import0.someExternalId;`].join(
'\n'));
});
it('should support operators', () => {
var lhs = o.variable('lhs');
var rhs = o.variable('rhs');
expect(emitStmt(someVar.cast(o.INT_TYPE).toStmt())).toEqual('(<number>someVar);');
expect(emitStmt(o.not(someVar).toStmt())).toEqual('!someVar;');
expect(
emitStmt(someVar.conditional(o.variable('trueCase'), o.variable('falseCase')).toStmt()))
.toEqual('someVar? trueCase: falseCase;');
expect(emitStmt(lhs.equals(rhs).toStmt())).toEqual('(lhs == rhs);');
expect(emitStmt(lhs.notEquals(rhs).toStmt())).toEqual('(lhs != rhs);');
expect(emitStmt(lhs.identical(rhs).toStmt())).toEqual('(lhs === rhs);');
expect(emitStmt(lhs.notIdentical(rhs).toStmt())).toEqual('(lhs !== rhs);');
expect(emitStmt(lhs.minus(rhs).toStmt())).toEqual('(lhs - rhs);');
expect(emitStmt(lhs.plus(rhs).toStmt())).toEqual('(lhs + rhs);');
expect(emitStmt(lhs.divide(rhs).toStmt())).toEqual('(lhs / rhs);');
expect(emitStmt(lhs.multiply(rhs).toStmt())).toEqual('(lhs * rhs);');
expect(emitStmt(lhs.modulo(rhs).toStmt())).toEqual('(lhs % rhs);');
expect(emitStmt(lhs.and(rhs).toStmt())).toEqual('(lhs && rhs);');
expect(emitStmt(lhs.or(rhs).toStmt())).toEqual('(lhs || rhs);');
expect(emitStmt(lhs.lower(rhs).toStmt())).toEqual('(lhs < rhs);');
expect(emitStmt(lhs.lowerEquals(rhs).toStmt())).toEqual('(lhs <= rhs);');
expect(emitStmt(lhs.bigger(rhs).toStmt())).toEqual('(lhs > rhs);');
expect(emitStmt(lhs.biggerEquals(rhs).toStmt())).toEqual('(lhs >= rhs);');
});
it('should support function expressions', () => {
expect(emitStmt(o.fn([], []).toStmt())).toEqual(['():void => {', '};'].join('\n'));
expect(emitStmt(o.fn([], [new o.ReturnStatement(o.literal(1))], o.INT_TYPE).toStmt()))
.toEqual(['():number => {', ' return 1;\n};'].join('\n'));
expect(emitStmt(o.fn([new o.FnParam('param1', o.INT_TYPE)], []).toStmt()))
.toEqual(['(param1:number):void => {', '};'].join('\n'));
});
it('should support function statements', () => {
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [])))
.toEqual(['function someFn():void {', '}'].join('\n'));
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], []), ['someFn']))
.toEqual(['export function someFn():void {', '}'].join('\n'));
expect(emitStmt(new o.DeclareFunctionStmt('someFn', [], [new o.ReturnStatement(o.literal(1))],
o.INT_TYPE)))
.toEqual(['function someFn():number {', ' return 1;', '}'].join('\n'));
expect(
emitStmt(new o.DeclareFunctionStmt('someFn', [new o.FnParam('param1', o.INT_TYPE)], [])))
.toEqual(['function someFn(param1:number):void {', '}'].join('\n'));
});
it('should support comments',
() => { expect(emitStmt(new o.CommentStmt('a\nb'))).toEqual(['// a', '// b'].join('\n')); });
it('should support if stmt', () => {
var trueCase = o.variable('trueCase').callFn([]).toStmt();
var falseCase = o.variable('falseCase').callFn([]).toStmt();
expect(emitStmt(new o.IfStmt(o.variable('cond'), [trueCase])))
.toEqual(['if (cond) { trueCase(); }'].join('\n'));
expect(emitStmt(new o.IfStmt(o.variable('cond'), [trueCase], [falseCase])))
.toEqual(['if (cond) {', ' trueCase();', '} else {', ' falseCase();', '}'].join('\n'));
});
it('should support try/catch', () => {
var bodyStmt = o.variable('body').callFn([]).toStmt();
var catchStmt = o.variable('catchFn').callFn([o.CATCH_ERROR_VAR, o.CATCH_STACK_VAR]).toStmt();
expect(emitStmt(new o.TryCatchStmt([bodyStmt], [catchStmt])))
.toEqual([
'try {',
' body();',
'} catch (error) {',
' const stack = error.stack;',
' catchFn(error,stack);',
'}'
].join('\n'));
});
it('should support support throwing',
() => { expect(emitStmt(new o.ThrowStmt(someVar))).toEqual('throw someVar;'); });
describe('classes', () => {
var callSomeMethod: o.Statement;
beforeEach(() => { callSomeMethod = o.THIS_EXPR.callMethod('someMethod', []).toStmt(); });
it('should support declaring classes', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null, [])))
.toEqual(['class SomeClass {', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null, []), ['SomeClass']))
.toEqual(['export class SomeClass {', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt('SomeClass', o.variable('SomeSuperClass'), [], [], null, [])))
.toEqual(['class SomeClass extends SomeSuperClass {', '}'].join('\n'));
});
it('should support declaring constructors', () => {
var superCall = o.SUPER_EXPR.callFn([o.variable('someParam')]).toStmt();
expect(emitStmt(
new o.ClassStmt('SomeClass', null, [], [], new o.ClassMethod(null, [], []), [])))
.toEqual(['class SomeClass {', ' constructor() {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt(
'SomeClass', null, [], [],
new o.ClassMethod(null, [new o.FnParam('someParam', o.INT_TYPE)], []), [])))
.toEqual(
['class SomeClass {', ' constructor(someParam:number) {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [],
new o.ClassMethod(null, [], [superCall]), [])))
.toEqual(['class SomeClass {', ' constructor() {', ' super(someParam);', ' }', '}']
.join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [],
new o.ClassMethod(null, [], [callSomeMethod]), [])))
.toEqual(
['class SomeClass {', ' constructor() {', ' this.someMethod();', ' }', '}']
.join('\n'));
});
it('should support declaring fields', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [new o.ClassField('someField')], [],
null, [])))
.toEqual(['class SomeClass {', ' someField;', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null,
[new o.ClassField('someField', o.INT_TYPE)], [], null, [])))
.toEqual(['class SomeClass {', ' someField:number;', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt(
'SomeClass', null,
[new o.ClassField('someField', o.INT_TYPE, [o.StmtModifier.Private])], [], null,
[])))
.toEqual(['class SomeClass {', ' private someField:number;', '}'].join('\n'));
});
it('should support declaring getters', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [])], null, [])))
.toEqual(['class SomeClass {', ' get someGetter() {', ' }', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [], o.INT_TYPE)], null, [])))
.toEqual(['class SomeClass {', ' get someGetter():number {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [],
[new o.ClassGetter('someGetter', [callSomeMethod])], null,
[])))
.toEqual(
['class SomeClass {', ' get someGetter() {', ' this.someMethod();', ' }', '}']
.join('\n'));
expect(
emitStmt(new o.ClassStmt(
'SomeClass', null, [],
[new o.ClassGetter('someGetter', [], null, [o.StmtModifier.Private])], null, [])))
.toEqual(['class SomeClass {', ' private get someGetter() {', ' }', '}'].join('\n'));
});
it('should support methods', () => {
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [])])))
.toEqual(['class SomeClass {', ' someMethod():void {', ' }', '}'].join('\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [], o.INT_TYPE)])))
.toEqual(['class SomeClass {', ' someMethod():number {', ' }', '}'].join('\n'));
expect(
emitStmt(new o.ClassStmt(
'SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [new o.FnParam('someParam', o.INT_TYPE)], [])])))
.toEqual(
['class SomeClass {', ' someMethod(someParam:number):void {', ' }', '}'].join(
'\n'));
expect(emitStmt(new o.ClassStmt('SomeClass', null, [], [], null,
[new o.ClassMethod('someMethod', [], [callSomeMethod])])))
.toEqual(
['class SomeClass {', ' someMethod():void {', ' this.someMethod();', ' }', '}']
.join('\n'));
});
});
it('should support builtin types', () => {
var writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(o.DYNAMIC_TYPE))).toEqual('var a:any = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.BOOL_TYPE))).toEqual('var a:boolean = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.INT_TYPE))).toEqual('var a:number = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.NUMBER_TYPE))).toEqual('var a:number = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.STRING_TYPE))).toEqual('var a:string = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.FUNCTION_TYPE))).toEqual('var a:Function = null;');
});
it('should support external types', () => {
var writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(o.importType(sameModuleIdentifier))))
.toEqual('var a:someLocalId = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(o.importType(externalModuleIdentifier))))
.toEqual([
`import * as import0 from './someOtherPath';`,
`var a:import0.someExternalId = null;`
].join('\n'));
});
it('should support combined types', () => {
var writeVarExpr = o.variable('a').set(o.NULL_EXPR);
expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(null))))
.toEqual('var a:any[] = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(new o.ArrayType(o.INT_TYPE))))
.toEqual('var a:number[] = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(null))))
.toEqual('var a:{[key: string]:any} = null;');
expect(emitStmt(writeVarExpr.toDeclStmt(new o.MapType(o.INT_TYPE))))
.toEqual('var a:{[key: string]:number} = null;');
});
});
}

View File

@ -1,57 +0,0 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {Component, provide} from 'angular2/core';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {SpyTemplateCompiler} from './spies';
import {TemplateCompiler} from 'angular2/src/compiler/compiler';
import {RuntimeCompiler, RuntimeCompiler_} from 'angular2/src/compiler/runtime_compiler';
import {HostViewFactory} from 'angular2/src/core/linker/view';
export function main() {
describe('RuntimeCompiler', () => {
var compiler: RuntimeCompiler_;
var templateCompilerSpy;
var someHostViewFactory;
beforeEachProviders(() => {
templateCompilerSpy = new SpyTemplateCompiler();
someHostViewFactory = new HostViewFactory(null, null);
templateCompilerSpy.spy('compileHostComponentRuntime')
.andReturn(PromiseWrapper.resolve(someHostViewFactory));
return [provide(TemplateCompiler, {useValue: templateCompilerSpy})];
});
beforeEach(inject([RuntimeCompiler], (_compiler) => { compiler = _compiler; }));
it('compileInHost should compile the template via TemplateCompiler',
inject([AsyncTestCompleter], (async) => {
compiler.compileInHost(SomeComponent)
.then((hostViewFactoryRef) => {
expect(hostViewFactoryRef.internalHostViewFactory).toBe(someHostViewFactory);
async.done();
});
}));
it('should clear the cache', () => {
compiler.clearCache();
expect(templateCompilerSpy.spy('clearCache')).toHaveBeenCalled();
});
});
}
@Component({selector: 'some-comp', template: ''})
class SomeComponent {
}

View File

@ -15,7 +15,7 @@ import {
import {stringify} from 'angular2/src/facade/lang';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/metadata/lifecycle_hooks';
import {
Component,
Directive,
@ -50,7 +50,6 @@ export function main() {
expect(meta.selector).toEqual('someSelector');
expect(meta.exportAs).toEqual('someExportAs');
expect(meta.isComponent).toBe(true);
expect(meta.dynamicLoadable).toBe(true);
expect(meta.type.runtime).toBe(ComponentWithEverything);
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
expect(meta.type.moduleUrl).toEqual(`package:someModuleId${MODULE_SUFFIX}`);
@ -72,7 +71,8 @@ export function main() {
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var value: string =
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
var expectedEndValue = IS_DART ? 'test/compiler/runtime_metadata_spec.dart' : './';
var expectedEndValue =
IS_DART ? 'test/compiler/runtime_metadata_spec.dart' : './ComponentWithoutModuleId';
expect(value.endsWith(expectedEndValue)).toBe(true);
}));

View File

@ -1,47 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
export function main() {
describe('SourceModule', () => {
describe('getSourceWithImports', () => {
it('should generate named imports for modules', () => {
var sourceWithImports =
new SourceModule('package:some/moda', `${moduleRef('package:some/modb')}A`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A');
expect(sourceWithImports.imports).toEqual([['package:some/modb', 'import0']]);
});
it('should dedupe imports', () => {
var sourceWithImports =
new SourceModule(
'package:some/moda',
`${moduleRef('package:some/modb')}A + ${moduleRef('package:some/modb')}B`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A + import0.B');
expect(sourceWithImports.imports).toEqual([['package:some/modb', 'import0']]);
});
it('should not use an import for the moduleUrl of the SourceModule', () => {
var sourceWithImports =
new SourceModule('package:some/moda', `${moduleRef('package:some/moda')}A`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('A');
expect(sourceWithImports.imports).toEqual([]);
});
});
});
}

View File

@ -2,10 +2,6 @@ library compiler.spies;
import 'package:angular2/src/compiler/xhr.dart';
import 'package:angular2/testing_internal.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
@proxy
class SpyXHR extends SpyObject implements XHR {}
@proxy
class SpyTemplateCompiler extends SpyObject implements TemplateCompiler {}

View File

@ -1,12 +1,7 @@
import {XHR} from 'angular2/src/compiler/xhr';
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
import {SpyObject, proxy} from 'angular2/testing_internal';
export class SpyXHR extends SpyObject {
constructor() { super(XHR); }
}
export class SpyTemplateCompiler extends SpyObject {
constructor() { super(TemplateCompiler); }
}

View File

@ -1,4 +0,0 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
// used by style_compiler_spec.ts
export const STYLES = CONST_EXPR(['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']);

View File

@ -1,4 +0,0 @@
import {CONST_EXPR} from 'angular2/src/facade/lang';
// used by style_compiler_spec.ts
export const STYLES = CONST_EXPR(['span {color: blue}']);

View File

@ -1,337 +0,0 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {provide} from 'angular2/src/core/di';
import {SpyXHR} from './spies';
import {XHR} from 'angular2/src/compiler/xhr';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {
CONST_EXPR,
isPresent,
isBlank,
StringWrapper,
isArray,
IS_DART
} from 'angular2/src/facade/lang';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {evalModule} from './eval_module';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
import {
CompileDirectiveMetadata,
CompileTemplateMetadata,
CompileTypeMetadata
} from 'angular2/src/compiler/directive_metadata';
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {TEST_PROVIDERS} from './test_bindings';
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
// Attention: These module names have to correspond to real modules!
var MODULE_URL = `package:angular2/test/compiler/style_compiler_spec${MODULE_SUFFIX}`;
var IMPORT_ABS_STYLESHEET_URL = `package:angular2/test/compiler/style_compiler_import.css`;
var IMPORT_REL_STYLESHEET_URL = './style_compiler_import.css';
// Note: Not a real module, only used via mocks.
var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
`package:angular2/test/compiler/style_compiler_transitive_import.css`;
export function main() {
// Dart's isolate support is broken, and these tests will be obsolote soon with
// https://github.com/angular/angular/issues/6270
if (IS_DART) {
return;
}
describe('StyleCompiler', () => {
var xhr: SpyXHR;
beforeEachProviders(() => {
xhr = <any>new SpyXHR();
return [TEST_PROVIDERS, provide(XHR, {useValue: xhr})];
});
var compiler: StyleCompiler;
beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
describe('compileComponentRuntime', () => {
var xhrUrlResults;
var xhrCount;
beforeEach(() => {
xhrCount = 0;
xhrUrlResults = {};
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: blue}';
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT] =
`a {color: green}@import ${IMPORT_REL_STYLESHEET_URL};`;
});
function compile(styles: string[], styleAbsUrls: string[],
encapsulation: ViewEncapsulation): Promise<string[]> {
// Note: Can't use MockXHR as the xhr is called recursively,
// so we can't trigger flush.
xhr.spy('get').andCallFake((url) => {
var response = xhrUrlResults[url];
xhrCount++;
if (isBlank(response)) {
throw new BaseException(`Unexpected url ${url}`);
}
return PromiseWrapper.resolve(response);
});
return compiler.compileComponentRuntime(new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
}
describe('no shim', () => {
var encapsulation = ViewEncapsulation.None;
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
async.done();
});
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
async.done();
});
}));
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT], encapsulation)
.then(styles => {
expect(styles)
.toEqual(['div {color: red}', ['a {color: green}', ['span {color: blue}']]]);
async.done();
});
}));
});
describe('with shim', () => {
var encapsulation = ViewEncapsulation.Emulated;
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
]);
async.done();
});
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
['span[_ngcontent-%COMP%] {color: blue}']
]);
async.done();
});
}));
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT],
encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
[
'a[_ngcontent-%COMP%] {color: green}',
['span[_ngcontent-%COMP%] {color: blue}']
]
]);
async.done();
});
}));
});
it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => {
PromiseWrapper.all([
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None),
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
])
.then((styleArrays) => {
expect(styleArrays[0]).toEqual([['span {color: blue}']]);
expect(styleArrays[1]).toEqual([['span {color: blue}']]);
expect(xhrCount).toBe(1);
async.done();
});
}));
it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => {
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((styles0) => {
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((styles1) => {
expect(styles0).toEqual([['span {color: blue}']]);
expect(styles1).toEqual([['span {color: blue}']]);
expect(xhrCount).toBe(1);
async.done();
});
});
}));
it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => {
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((_) => {
compiler.clearCache();
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None);
})
.then((styles) => {
expect(xhrCount).toBe(2);
expect(styles).toEqual([['span {color: black}']]);
async.done();
});
}));
});
describe('compileComponentCodeGen', () => {
function compile(styles: string[], styleAbsUrls: string[],
encapsulation: ViewEncapsulation): Promise<string[]> {
var sourceExpression = compiler.compileComponentCodeGen(new CompileTemplateMetadata(
{styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
};
describe('no shim', () => {
var encapsulation = ViewEncapsulation.None;
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
async.done();
});
}));
it('should compile css rules with newlines and quotes',
inject([AsyncTestCompleter], (async) => {
compile(['div\n{"color": \'red\'}'], [], encapsulation)
.then(styles => {
expect(styles).toEqual(['div\n{"color": \'red\'}']);
async.done();
});
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
async.done();
});
}), 1000);
});
describe('with shim', () => {
var encapsulation = ViewEncapsulation.Emulated;
it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => {
compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
]);
async.done();
});
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-%COMP%] {color: red}',
['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
]);
async.done();
});
}), 1000);
});
});
describe('compileStylesheetCodeGen', () => {
function compile(style: string): Promise<string[][]> {
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_URL, style);
return PromiseWrapper.all(sourceModules.map(sourceModule => {
var sourceWithImports = testableModule(sourceModule).getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
}));
}
it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
compile('div {color: red;}')
.then(stylesAndShimStyles => {
var expected = [['div {color: red;}'], ['div[_ngcontent-%COMP%] {color: red;}']];
compareStyles(stylesAndShimStyles[0], expected[0]);
compareStyles(stylesAndShimStyles[1], expected[1]);
async.done();
});
}));
it('should allow to import rules with relative paths',
inject([AsyncTestCompleter], (async) => {
compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
.then(stylesAndShimStyles => {
var expected = [
['div {color: red}', ['span {color: blue}']],
[
'div[_ngcontent-%COMP%] {color: red}',
['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
]
];
compareStyles(stylesAndShimStyles[0], expected[0]);
compareStyles(stylesAndShimStyles[1], expected[1]);
async.done();
});
}));
});
});
}
function testableExpression(source: SourceExpression): SourceModule {
var testableSource = `${source.declarations.join('\n')}
${codeGenValueFn(['_'], source.expression, '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(null, testableSource);
}
function testableModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenValueFn(['_'], 'STYLES', '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
// Needed for Android browsers which add an extra space at the end of some lines
function compareStyles(styles: Array<string | any[]>, expectedStyles: Array<string | any[]>) {
expect(styles.length).toEqual(expectedStyles.length);
for (var i = 0; i < styles.length; i++) {
var style = styles[i];
if (isArray(style)) {
compareStyles(<any[]>style, <any[]>expectedStyles[i]);
} else {
expect(StringWrapper.replaceAll(<string>style, /\s+\n/g, '\n')).toEqual(expectedStyles[i]);
}
}
}

View File

@ -1,509 +0,0 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {Type, isPresent, isBlank, stringify, isString, IS_DART} from 'angular2/src/facade/lang';
import {
MapWrapper,
SetWrapper,
ListWrapper,
StringMapWrapper
} from 'angular2/src/facade/collection';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {
TemplateCompiler,
NormalizedComponentWithViewDirectives
} from 'angular2/src/compiler/template_compiler';
import {CompileDirectiveMetadata} from 'angular2/src/compiler/directive_metadata';
import {evalModule} from './eval_module';
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
import {XHR} from 'angular2/src/compiler/xhr';
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
import {SpyRootRenderer, SpyRenderer, SpyAppViewManager} from '../core/spies';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {AppView, AppProtoView} from 'angular2/src/core/linker/view';
import {AppElement} from 'angular2/src/core/linker/element';
import {Locals, ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
import {Component, Directive, provide, RenderComponentType} from 'angular2/core';
import {TEST_PROVIDERS} from './test_bindings';
import {
codeGenValueFn,
codeGenFnHeader,
codeGenExportVariable,
MODULE_SUFFIX
} from 'angular2/src/compiler/util';
import {PipeTransform, WrappedValue, Injectable, Pipe} from 'angular2/core';
// Attention: This path has to point to this test file!
const THIS_MODULE_ID = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`);
var REFLECTOR_MODULE_REF =
moduleRef(`package:angular2/src/core/reflection/reflection${MODULE_SUFFIX}`);
var REFLECTION_CAPS_MODULE_REF =
moduleRef(`package:angular2/src/core/reflection/reflection_capabilities${MODULE_SUFFIX}`);
export function main() {
// Dart's isolate support is broken, and these tests will be obsolote soon with
// https://github.com/angular/angular/issues/6270
if (IS_DART) {
return;
}
describe('TemplateCompiler', () => {
var compiler: TemplateCompiler;
var runtimeMetadataResolver: RuntimeMetadataResolver;
beforeEachProviders(() => TEST_PROVIDERS);
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
(_compiler, _runtimeMetadataResolver) => {
compiler = _compiler;
runtimeMetadataResolver = _runtimeMetadataResolver;
}));
describe('compile templates', () => {
function runTests(compile: (components: Type[]) => Promise<any[]>) {
it('should throw for non components', inject([AsyncTestCompleter], (async) => {
PromiseWrapper.catchError(
PromiseWrapper.wrap(() => compile([NonComponent])), (error): any => {
expect(error.message)
.toEqual(
`Could not compile '${stringify(NonComponent)}' because it is not a component.`);
async.done();
});
}));
it('should compile host components', inject([AsyncTestCompleter], (async) => {
compile([CompWithBindingsAndStylesAndPipes])
.then((humanizedView) => {
expect(humanizedView['styles']).toEqual([]);
expect(humanizedView['elements']).toEqual(['<comp-a>']);
expect(humanizedView['pipes']).toEqual({});
expect(humanizedView['cd']).toEqual(['prop(title)=someHostValue']);
async.done();
});
}));
it('should compile nested components', inject([AsyncTestCompleter], (async) => {
compile([CompWithBindingsAndStylesAndPipes])
.then((humanizedView) => {
var componentView = humanizedView['componentViews'][0];
expect(componentView['styles']).toEqual(['div {color: red}']);
expect(componentView['elements']).toEqual(['<a>']);
expect(componentView['pipes']).toEqual({'uppercase': stringify(UpperCasePipe)});
expect(componentView['cd']).toEqual(['prop(href)=SOMECTXVALUE']);
async.done();
});
}));
it('should compile components at various nesting levels',
inject([AsyncTestCompleter], (async) => {
compile([CompWith2NestedComps, Comp1, Comp2])
.then((humanizedView) => {
expect(humanizedView['elements']).toEqual(['<comp-with-2nested>']);
expect(humanizedView['componentViews'][0]['elements'])
.toEqual(['<comp1>', '<comp2>']);
expect(humanizedView['componentViews'][0]['componentViews'][0]['elements'])
.toEqual(['<a>', '<comp2>']);
expect(humanizedView['componentViews'][0]['componentViews'][1]['elements'])
.toEqual(['<b>']);
async.done();
});
}));
it('should compile recursive components', inject([AsyncTestCompleter], (async) => {
compile([TreeComp])
.then((humanizedView) => {
expect(humanizedView['elements']).toEqual(['<tree>']);
expect(humanizedView['componentViews'][0]['embeddedViews'][0]['elements'])
.toEqual(['<tree>']);
expect(humanizedView['componentViews'][0]['embeddedViews'][0]['componentViews']
[0]['embeddedViews'][0]['elements'])
.toEqual(['<tree>']);
async.done();
});
}));
it('should compile embedded templates', inject([AsyncTestCompleter], (async) => {
compile([CompWithEmbeddedTemplate])
.then((humanizedView) => {
var embeddedView = humanizedView['componentViews'][0]['embeddedViews'][0];
expect(embeddedView['elements']).toEqual(['<a>']);
expect(embeddedView['cd']).toEqual(['prop(href)=someEmbeddedValue']);
async.done();
});
}));
it('should dedup directives', inject([AsyncTestCompleter], (async) => {
compile([CompWithDupDirectives, TreeComp])
.then((humanizedView) => {
expect(humanizedView['componentViews'][0]['componentViews'].length).toBe(1);
async.done();
});
}));
}
describe('compileHostComponentRuntime', () => {
function compile(components: Type[]): Promise<any[]> {
return compiler.compileHostComponentRuntime(components[0])
.then((compiledHostTemplate) =>
humanizeViewFactory(compiledHostTemplate.viewFactory));
}
describe('no jit', () => {
beforeEachProviders(() => [
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, false)})
]);
runTests(compile);
});
describe('jit', () => {
beforeEachProviders(() => [
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, true)})
]);
runTests(compile);
});
it('should cache components for parallel requests',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
// Expecting only one xhr...
xhr.expect('package:angular2/test/compiler/compUrl.html', '<a></a>');
PromiseWrapper.all([compile([CompWithTemplateUrl]), compile([CompWithTemplateUrl])])
.then((humanizedViews) => {
expect(humanizedViews[0]['componentViews'][0]['elements']).toEqual(['<a>']);
expect(humanizedViews[1]['componentViews'][0]['elements']).toEqual(['<a>']);
async.done();
});
xhr.flush();
}));
it('should cache components for sequential requests',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
// Expecting only one xhr...
xhr.expect('package:angular2/test/compiler/compUrl.html', '<a>');
compile([CompWithTemplateUrl])
.then((humanizedView0) => {
return compile([CompWithTemplateUrl])
.then((humanizedView1) => {
expect(humanizedView0['componentViews'][0]['elements']).toEqual(['<a>']);
expect(humanizedView1['componentViews'][0]['elements']).toEqual(['<a>']);
async.done();
});
});
xhr.flush();
}));
it('should allow to clear the cache',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('package:angular2/test/compiler/compUrl.html', '<a>');
compile([CompWithTemplateUrl])
.then((humanizedView) => {
compiler.clearCache();
xhr.expect('package:angular2/test/compiler/compUrl.html', '<b>');
var result = compile([CompWithTemplateUrl]);
xhr.flush();
return result;
})
.then((humanizedView) => {
expect(humanizedView['componentViews'][0]['elements']).toEqual(['<b>']);
async.done();
});
xhr.flush();
}));
});
describe('compileTemplatesCodeGen', () => {
function normalizeComponent(
component: Type): Promise<NormalizedComponentWithViewDirectives> {
var compAndViewDirMetas =
[runtimeMetadataResolver.getDirectiveMetadata(component)].concat(
runtimeMetadataResolver.getViewDirectivesMetadata(component));
var upperCasePipeMeta = runtimeMetadataResolver.getPipeMetadata(UpperCasePipe);
upperCasePipeMeta.type.moduleUrl = `package:${THIS_MODULE_ID}${MODULE_SUFFIX}`;
return PromiseWrapper.all(compAndViewDirMetas.map(
meta => compiler.normalizeDirectiveMetadata(meta)))
.then((normalizedCompAndViewDirMetas: CompileDirectiveMetadata[]) =>
new NormalizedComponentWithViewDirectives(
normalizedCompAndViewDirMetas[0],
normalizedCompAndViewDirMetas.slice(1), [upperCasePipeMeta]));
}
function compile(components: Type[]): Promise<any[]> {
return PromiseWrapper.all(components.map(normalizeComponent))
.then((normalizedCompWithViewDirMetas: NormalizedComponentWithViewDirectives[]) => {
var sourceModule = compiler.compileTemplatesCodeGen(normalizedCompWithViewDirMetas);
var sourceWithImports =
testableTemplateModule(sourceModule,
normalizedCompWithViewDirMetas[0].component)
.getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
});
}
runTests(compile);
});
});
describe('normalizeDirectiveMetadata', () => {
it('should return the given DirectiveMetadata for non components',
inject([AsyncTestCompleter], (async) => {
var meta = runtimeMetadataResolver.getDirectiveMetadata(NonComponent);
compiler.normalizeDirectiveMetadata(meta).then(normMeta => {
expect(normMeta).toBe(meta);
async.done();
});
}));
it('should normalize the template',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('package:angular2/test/compiler/compUrl.html', 'loadedTemplate');
compiler.normalizeDirectiveMetadata(
runtimeMetadataResolver.getDirectiveMetadata(CompWithTemplateUrl))
.then((meta: CompileDirectiveMetadata) => {
expect(meta.template.template).toEqual('loadedTemplate');
async.done();
});
xhr.flush();
}));
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
var meta =
runtimeMetadataResolver.getDirectiveMetadata(CompWithBindingsAndStylesAndPipes);
compiler.normalizeDirectiveMetadata(meta).then((normMeta: CompileDirectiveMetadata) => {
expect(normMeta.type).toEqual(meta.type);
expect(normMeta.isComponent).toEqual(meta.isComponent);
expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
expect(normMeta.selector).toEqual(meta.selector);
expect(normMeta.exportAs).toEqual(meta.exportAs);
expect(normMeta.changeDetection).toEqual(meta.changeDetection);
expect(normMeta.inputs).toEqual(meta.inputs);
expect(normMeta.outputs).toEqual(meta.outputs);
expect(normMeta.hostListeners).toEqual(meta.hostListeners);
expect(normMeta.hostProperties).toEqual(meta.hostProperties);
expect(normMeta.hostAttributes).toEqual(meta.hostAttributes);
expect(normMeta.lifecycleHooks).toEqual(meta.lifecycleHooks);
async.done();
});
}));
});
describe('compileStylesheetCodeGen', () => {
it('should compile stylesheets into code', inject([AsyncTestCompleter], (async) => {
var cssText = 'div {color: red}';
var sourceModule =
compiler.compileStylesheetCodeGen('package:someModuleUrl', cssText)[0];
var sourceWithImports = testableStylesModule(sourceModule).getSourceWithImports();
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
.then(loadedCssText => {
expect(loadedCssText).toEqual([cssText]);
async.done();
});
}));
});
});
}
@Pipe({name: 'uppercase'})
@Injectable()
export class UpperCasePipe implements PipeTransform {
transform(value: string, args: any[] = null): string { return value.toUpperCase(); }
}
@Component({
selector: 'comp-a',
host: {'[title]': '\'someHostValue\''},
moduleId: THIS_MODULE_ID,
exportAs: 'someExportAs',
template: '<a [href]="\'someCtxValue\' | uppercase"></a>',
styles: ['div {color: red}'],
encapsulation: ViewEncapsulation.None,
pipes: [UpperCasePipe]
})
export class CompWithBindingsAndStylesAndPipes {
}
@Component({
selector: 'tree',
moduleId: THIS_MODULE_ID,
template: '<template><tree></tree></template>',
directives: [TreeComp],
encapsulation: ViewEncapsulation.None
})
export class TreeComp {
}
@Component({
selector: 'comp-wit-dup-tpl',
moduleId: THIS_MODULE_ID,
template: '<tree></tree>',
directives: [TreeComp, TreeComp],
encapsulation: ViewEncapsulation.None
})
export class CompWithDupDirectives {
}
@Component({
selector: 'comp-url',
moduleId: THIS_MODULE_ID,
templateUrl: 'compUrl.html',
encapsulation: ViewEncapsulation.None
})
export class CompWithTemplateUrl {
}
@Component({
selector: 'comp-tpl',
moduleId: THIS_MODULE_ID,
template: '<template><a [href]="\'someEmbeddedValue\'"></a></template>',
encapsulation: ViewEncapsulation.None
})
export class CompWithEmbeddedTemplate {
}
@Directive({selector: 'plain'})
export class NonComponent {
}
@Component({
selector: 'comp2',
moduleId: THIS_MODULE_ID,
template: '<b></b>',
encapsulation: ViewEncapsulation.None
})
export class Comp2 {
}
@Component({
selector: 'comp1',
moduleId: THIS_MODULE_ID,
template: '<a></a>, <comp2></comp2>',
encapsulation: ViewEncapsulation.None,
directives: [Comp2]
})
export class Comp1 {
}
@Component({
selector: 'comp-with-2nested',
moduleId: THIS_MODULE_ID,
template: '<comp1></comp1>, <comp2></comp2>',
encapsulation: ViewEncapsulation.None,
directives: [Comp1, Comp2]
})
export class CompWith2NestedComps {
}
function testableTemplateModule(sourceModule: SourceModule,
normComp: CompileDirectiveMetadata): SourceModule {
var testableSource = `
${sourceModule.sourceWithModuleRefs}
${codeGenFnHeader(['_'], '_run')}{
${REFLECTOR_MODULE_REF}reflector.reflectionCapabilities = new ${REFLECTION_CAPS_MODULE_REF}ReflectionCapabilities();
return ${THIS_MODULE_REF}humanizeViewFactory(hostViewFactory_${normComp.type.name}.viewFactory);
}
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
function testableStylesModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenValueFn(['_'], 'STYLES', '_run')};
${codeGenExportVariable('run')}_run;`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
function humanizeView(view: AppView, cachedResults: Map<AppProtoView, any>): {[key: string]: any} {
var result = cachedResults.get(view.proto);
if (isPresent(result)) {
return result;
}
result = {};
// fill the cache early to break cycles.
cachedResults.set(view.proto, result);
view.changeDetector.detectChanges();
var pipes = {};
if (isPresent(view.proto.protoPipes)) {
StringMapWrapper.forEach(view.proto.protoPipes.config, (pipeProvider, pipeName) => {
pipes[pipeName] = stringify(pipeProvider.key.token);
});
}
var componentViews = [];
var embeddedViews = [];
view.appElements.forEach((appElement) => {
if (isPresent(appElement.componentView)) {
componentViews.push(humanizeView(appElement.componentView, cachedResults));
} else if (isPresent(appElement.embeddedViewFactory)) {
embeddedViews.push(
humanizeViewFactory(appElement.embeddedViewFactory, appElement, cachedResults));
}
});
result['styles'] = (<any>view.renderer).styles;
result['elements'] = (<any>view.renderer).elements;
result['pipes'] = pipes;
result['cd'] = (<any>view.renderer).props;
result['componentViews'] = componentViews;
result['embeddedViews'] = embeddedViews;
return result;
}
// Attention: read by eval!
export function humanizeViewFactory(
viewFactory: Function, containerAppElement: AppElement = null,
cachedResults: Map<AppProtoView, any> = null): {[key: string]: any} {
if (isBlank(cachedResults)) {
cachedResults = new Map<AppProtoView, any>();
}
var viewManager = new SpyAppViewManager();
viewManager.spy('createRenderComponentType')
.andCallFake((encapsulation: ViewEncapsulation, styles: Array<string | any[]>) => {
return new RenderComponentType('someId', encapsulation, styles);
});
var view: AppView = viewFactory(new RecordingRenderer([]), viewManager, containerAppElement, [],
null, null, null);
return humanizeView(view, cachedResults);
}
class RecordingRenderer extends SpyRenderer {
props: string[] = [];
elements: string[] = [];
constructor(public styles: string[]) {
super();
this.spy('renderComponent')
.andCallFake((componentProto) => new RecordingRenderer(componentProto.styles));
this.spy('setElementProperty')
.andCallFake((el, prop, value) => { this.props.push(`prop(${prop})=${value}`); });
this.spy('createElement')
.andCallFake((parent, elName) => { this.elements.push(`<${elName}>`); });
}
}

View File

@ -13,7 +13,7 @@ import {
import {provide} from 'angular2/src/core/di';
import {TEST_PROVIDERS} from './test_bindings';
import {isPresent} from 'angular2/src/facade/lang';
import {isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
import {
TemplateParser,
splitClasses,
@ -23,8 +23,12 @@ import {
CompileDirectiveMetadata,
CompilePipeMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
CompileTemplateMetadata,
CompileProviderMetadata,
CompileTokenMetadata,
CompileDiDependencyMetadata,
CompileQueryMetadata
} from 'angular2/src/compiler/compile_metadata';
import {
templateVisitAll,
TemplateAstVisitor,
@ -40,16 +44,19 @@ import {
BoundTextAst,
TextAst,
PropertyBindingType,
DirectiveAst
DirectiveAst,
ProviderAstType
} from 'angular2/src/compiler/template_ast';
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
import {MockSchemaRegistry} from './schema_registry_mock';
import {Unparser} from '../core/change_detection/parser/unparser';
import {Unparser} from './expression_parser/unparser';
var expressionUnparser = new Unparser();
var someModuleUrl = 'package:someModule';
var MOCK_SCHEMA_REGISTRY = [
provide(
ElementSchemaRegistry,
@ -62,15 +69,23 @@ export function main() {
function commonBeforeEach() {
beforeEach(inject([TemplateParser], (parser) => {
ngIf = CompileDirectiveMetadata.create(
{selector: '[ngIf]', type: new CompileTypeMetadata({name: 'NgIf'}), inputs: ['ngIf']});
var component = CompileDirectiveMetadata.create({
selector: 'root',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'Root'}),
isComponent: true
});
ngIf = CompileDirectiveMetadata.create({
selector: '[ngIf]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'NgIf'}),
inputs: ['ngIf']
});
parse = (template: string, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[] = null): TemplateAst[] => {
if (pipes === null) {
pipes = [];
}
return parser.parse(template, directives, pipes, 'TestComp');
return parser.parse(component, template, directives, pipes, 'TestComp');
};
}));
}
@ -260,7 +275,7 @@ export function main() {
var dirA = CompileDirectiveMetadata.create({
selector: 'template',
outputs: ['e'],
type: new CompileTypeMetadata({name: 'DirA'})
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('<template (e)="f"></template>', [dirA])))
.toEqual([
@ -297,16 +312,22 @@ export function main() {
describe('directives', () => {
it('should locate directives components first and ordered by the directives array in the View',
() => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'})});
var dirB = CompileDirectiveMetadata.create(
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
var dirC = CompileDirectiveMetadata.create(
{selector: '[c]', type: new CompileTypeMetadata({name: 'DirC'})});
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
var dirB = CompileDirectiveMetadata.create({
selector: '[b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
var dirC = CompileDirectiveMetadata.create({
selector: '[c]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirC'})
});
var comp = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({name: 'ZComp'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'ZComp'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(humanizeTplAst(parse('<div a c b>', [dirA, dirB, dirC, comp])))
@ -323,10 +344,14 @@ export function main() {
});
it('should locate directives in property bindings', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'})});
var dirB = CompileDirectiveMetadata.create(
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
var dirA = CompileDirectiveMetadata.create({
selector: '[a=b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
var dirB = CompileDirectiveMetadata.create({
selector: '[b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
expect(humanizeTplAst(parse('<div [a]="b">', [dirA, dirB])))
.toEqual([
[ElementAst, 'div'],
@ -336,8 +361,10 @@ export function main() {
});
it('should locate directives in event bindings', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirB'})});
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
expect(humanizeTplAst(parse('<div (a)="b">', [dirA])))
.toEqual(
@ -347,7 +374,7 @@ export function main() {
it('should parse directive host properties', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
host: {'[a]': 'expr'}
});
expect(humanizeTplAst(parse('<div></div>', [dirA])))
@ -361,7 +388,7 @@ export function main() {
it('should parse directive host listeners', () => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
host: {'(a)': 'expr'}
});
expect(humanizeTplAst(parse('<div></div>', [dirA])))
@ -370,8 +397,11 @@ export function main() {
});
it('should parse directive properties', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['aProp']});
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['aProp']
});
expect(humanizeTplAst(parse('<div [aProp]="expr"></div>', [dirA])))
.toEqual([
[ElementAst, 'div'],
@ -381,8 +411,11 @@ export function main() {
});
it('should parse renamed directive properties', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['b:a']});
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['b:a']
});
expect(humanizeTplAst(parse('<div [a]="expr"></div>', [dirA])))
.toEqual([
[ElementAst, 'div'],
@ -392,8 +425,11 @@ export function main() {
});
it('should parse literal directive properties', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['a']});
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
expect(humanizeTplAst(parse('<div a="literal"></div>', [dirA])))
.toEqual([
[ElementAst, 'div'],
@ -404,8 +440,11 @@ export function main() {
});
it('should favor explicit bound properties over literal properties', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['a']});
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
expect(humanizeTplAst(parse('<div a="literal" [a]="\'literal2\'"></div>', [dirA])))
.toEqual([
[ElementAst, 'div'],
@ -416,14 +455,284 @@ export function main() {
});
it('should support optional directive properties', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['a']});
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
expect(humanizeTplAst(parse('<div></div>', [dirA])))
.toEqual([[ElementAst, 'div'], [DirectiveAst, dirA]]);
});
});
describe('providers', () => {
var nextProviderId;
function createToken(value: string): CompileTokenMetadata {
var token;
if (value.startsWith('type:')) {
token = new CompileTokenMetadata({
identifier:
new CompileTypeMetadata({moduleUrl: someModuleUrl, name: value.substring(5)})
});
} else {
token = new CompileTokenMetadata({value: value});
}
return token;
}
function createDep(value: string): CompileDiDependencyMetadata {
var isOptional = false;
if (value.startsWith('optional:')) {
isOptional = true;
value = value.substring(9);
}
var isSelf = false;
if (value.startsWith('self:')) {
isSelf = true;
value = value.substring(5);
}
var isHost = false;
if (value.startsWith('host:')) {
isHost = true;
value = value.substring(5);
}
return new CompileDiDependencyMetadata(
{token: createToken(value), isOptional: isOptional, isSelf: isSelf, isHost: isHost});
}
function createProvider(
token: string, {multi = false, deps = CONST_EXPR([])}:
{multi?: boolean, deps?: string[]} = {}): CompileProviderMetadata {
return new CompileProviderMetadata({
token: createToken(token),
multi: multi,
useClass: new CompileTypeMetadata({name: `provider${nextProviderId++}`}),
deps: deps.map(createDep)
});
}
function createDir(selector: string, {providers = null, viewProviders = null,
deps = CONST_EXPR([]), queries = CONST_EXPR([])}: {
providers?: CompileProviderMetadata[],
viewProviders?: CompileProviderMetadata[],
deps?: string[],
queries?: string[]
} = {}): CompileDirectiveMetadata {
var isComponent = !selector.startsWith('[');
return CompileDirectiveMetadata.create({
selector: selector,
type: new CompileTypeMetadata(
{moduleUrl: someModuleUrl, name: selector, diDeps: deps.map(createDep)}),
isComponent: isComponent,
template: new CompileTemplateMetadata({ngContentSelectors: []}),
providers: providers,
viewProviders: viewProviders,
queries: queries.map((value) =>
new CompileQueryMetadata({selectors: [createToken(value)]}))
});
}
beforeEach(() => { nextProviderId = 0; });
it('should provide a component', () => {
var comp = createDir('my-comp');
var elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0];
expect(elAst.providers.length).toBe(1);
expect(elAst.providers[0].providerType).toBe(ProviderAstType.Component);
expect(elAst.providers[0].providers[0].useClass).toBe(comp.type);
});
it('should provide a directive', () => {
var dirA = createDir('[dirA]');
var elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
expect(elAst.providers.length).toBe(1);
expect(elAst.providers[0].providerType).toBe(ProviderAstType.Directive);
expect(elAst.providers[0].providers[0].useClass).toBe(dirA.type);
});
it('should use the public providers of a directive', () => {
var provider = createProvider('service');
var dirA = createDir('[dirA]', {providers: [provider]});
var elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
expect(elAst.providers.length).toBe(2);
expect(elAst.providers[1].providerType).toBe(ProviderAstType.PublicService);
expect(elAst.providers[1].providers).toEqual([provider]);
});
it('should use the private providers of a component', () => {
var provider = createProvider('service');
var comp = createDir('my-comp', {viewProviders: [provider]});
var elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0];
expect(elAst.providers.length).toBe(2);
expect(elAst.providers[1].providerType).toBe(ProviderAstType.PrivateService);
expect(elAst.providers[1].providers).toEqual([provider]);
});
it('should support multi providers', () => {
var provider0 = createProvider('service0', {multi: true});
var provider1 = createProvider('service1', {multi: true});
var provider2 = createProvider('service0', {multi: true});
var dirA = createDir('[dirA]', {providers: [provider0, provider1]});
var dirB = createDir('[dirB]', {providers: [provider2]});
var elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.providers[2].providers).toEqual([provider0, provider2]);
expect(elAst.providers[3].providers).toEqual([provider1]);
});
it('should overwrite non multi providers', () => {
var provider1 = createProvider('service0');
var provider2 = createProvider('service1');
var provider3 = createProvider('service0');
var dirA = createDir('[dirA]', {providers: [provider1, provider2]});
var dirB = createDir('[dirB]', {providers: [provider3]});
var elAst: ElementAst = <ElementAst>parse('<div dirA dirB>', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.providers[2].providers).toEqual([provider3]);
expect(elAst.providers[3].providers).toEqual([provider2]);
});
it('should overwrite component providers by directive providers', () => {
var compProvider = createProvider('service0');
var dirProvider = createProvider('service0');
var comp = createDir('my-comp', {providers: [compProvider]});
var dirA = createDir('[dirA]', {providers: [dirProvider]});
var elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[2].providers).toEqual([dirProvider]);
});
it('should overwrite view providers by directive providers', () => {
var viewProvider = createProvider('service0');
var dirProvider = createProvider('service0');
var comp = createDir('my-comp', {viewProviders: [viewProvider]});
var dirA = createDir('[dirA]', {providers: [dirProvider]});
var elAst: ElementAst = <ElementAst>parse('<my-comp dirA>', [dirA, comp])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[2].providers).toEqual([dirProvider]);
});
it('should overwrite directives by providers', () => {
var dirProvider = createProvider('type:my-comp');
var comp = createDir('my-comp', {providers: [dirProvider]});
var elAst: ElementAst = <ElementAst>parse('<my-comp>', [comp])[0];
expect(elAst.providers.length).toBe(1);
expect(elAst.providers[0].providers).toEqual([dirProvider]);
});
it('should throw if mixing multi and non multi providers', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service0', {multi: true});
var dirA = createDir('[dirA]', {providers: [provider0]});
var dirB = createDir('[dirB]', {providers: [provider1]});
expect(() => parse('<div dirA dirB>', [dirA, dirB]))
.toThrowError(
`Template parse errors:\n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0`);
});
it('should sort providers and directives by their DI order', () => {
var provider0 = createProvider('service0', {deps: ['type:[dir2]']});
var provider1 = createProvider('service1');
var dir2 = createDir('[dir2]', {deps: ['service1']});
var comp = createDir('my-comp', {providers: [provider0, provider1]});
var elAst: ElementAst = <ElementAst>parse('<my-comp dir2>', [comp, dir2])[0];
expect(elAst.providers.length).toBe(4);
expect(elAst.providers[0].providers[0].useClass).toEqual(comp.type);
expect(elAst.providers[1].providers).toEqual([provider1]);
expect(elAst.providers[2].providers[0].useClass).toEqual(dir2.type);
expect(elAst.providers[3].providers).toEqual([provider0]);
});
it('should mark directives and dependencies of directives as eager', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service1');
var dirA = createDir('[dirA]', {providers: [provider0, provider1], deps: ['service0']});
var elAst: ElementAst = <ElementAst>parse('<div dirA>', [dirA])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[0].providers).toEqual([provider0]);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[1].eager).toBe(true);
expect(elAst.providers[2].providers).toEqual([provider1]);
expect(elAst.providers[2].eager).toBe(false);
});
it('should mark dependencies on parent elements as eager', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service1');
var dirA = createDir('[dirA]', {providers: [provider0, provider1]});
var dirB = createDir('[dirB]', {deps: ['service0']});
var elAst: ElementAst =
<ElementAst>parse('<div dirA><div dirB></div></div>', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers).toEqual([provider0]);
expect(elAst.providers[1].eager).toBe(true);
expect(elAst.providers[2].providers).toEqual([provider1]);
expect(elAst.providers[2].eager).toBe(false);
});
it('should mark queried providers as eager', () => {
var provider0 = createProvider('service0');
var provider1 = createProvider('service1');
var dirA =
createDir('[dirA]', {providers: [provider0, provider1], queries: ['service0']});
var elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0];
expect(elAst.providers.length).toBe(3);
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers).toEqual([provider0]);
expect(elAst.providers[1].eager).toBe(true);
expect(elAst.providers[2].providers).toEqual([provider1]);
expect(elAst.providers[2].eager).toBe(false);
});
it('should not mark dependencies accross embedded views as eager', () => {
var provider0 = createProvider('service0');
var dirA = createDir('[dirA]', {providers: [provider0]});
var dirB = createDir('[dirB]', {deps: ['service0']});
var elAst: ElementAst =
<ElementAst>parse('<div dirA><div *ngIf dirB></div></div>', [dirA, dirB])[0];
expect(elAst.providers.length).toBe(2);
expect(elAst.providers[0].providers[0].useClass).toEqual(dirA.type);
expect(elAst.providers[0].eager).toBe(true);
expect(elAst.providers[1].providers).toEqual([provider0]);
expect(elAst.providers[1].eager).toBe(false);
});
it('should report missing @Self() deps as errors', () => {
var dirA = createDir('[dirA]', {deps: ['self:provider0']});
expect(() => parse('<div dirA></div>', [dirA]))
.toThrowErrorWith(
'No provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
});
it('should change missing @Self() that are optional to nulls', () => {
var dirA = createDir('[dirA]', {deps: ['optional:self:provider0']});
var elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0];
expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true);
expect(elAst.providers[0].providers[0].deps[0].value).toBe(null);
});
it('should report missing @Host() deps as errors', () => {
var dirA = createDir('[dirA]', {deps: ['host:provider0']});
expect(() => parse('<div dirA></div>', [dirA]))
.toThrowErrorWith(
'No provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0');
});
it('should change missing @Host() that are optional to nulls', () => {
var dirA = createDir('[dirA]', {deps: ['optional:host:provider0']});
var elAst: ElementAst = <ElementAst>parse('<div dirA></div>', [dirA])[0];
expect(elAst.providers[0].providers[0].deps[0].isValue).toBe(true);
expect(elAst.providers[0].providers[0].deps[0].value).toBe(null);
});
});
describe('variables', () => {
it('should parse variables via #... and not report them as attributes', () => {
@ -447,8 +756,11 @@ export function main() {
});
it('should assign variables to directives via exportAs', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'}), exportAs: 'dirA'});
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
exportAs: 'dirA'
});
expect(humanizeTplAst(parse('<div a #a="dirA"></div>', [dirA])))
.toEqual([
[ElementAst, 'div'],
@ -478,7 +790,7 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
isComponent: true,
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
exportAs: 'dirA',
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
@ -540,10 +852,15 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
describe('directives', () => {
it('should locate directives in property bindings', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['a']});
var dirB = CompileDirectiveMetadata.create(
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
var dirA = CompileDirectiveMetadata.create({
selector: '[a=b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['a']
});
var dirB = CompileDirectiveMetadata.create({
selector: '[b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
expect(humanizeTplAst(parse('<div template="a b" b>', [dirA, dirB])))
.toEqual([
[EmbeddedTemplateAst],
@ -556,10 +873,14 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
});
it('should locate directives in variable bindings', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a=b]', type: new CompileTypeMetadata({name: 'DirA'})});
var dirB = CompileDirectiveMetadata.create(
{selector: '[b]', type: new CompileTypeMetadata({name: 'DirB'})});
var dirA = CompileDirectiveMetadata.create({
selector: '[a=b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
var dirB = CompileDirectiveMetadata.create({
selector: '[b]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'})
});
expect(humanizeTplAst(parse('<div template="#a=b" b>', [dirA, dirB])))
.toEqual([
[EmbeddedTemplateAst],
@ -596,12 +917,16 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
});
describe('content projection', () => {
var compCounter;
beforeEach(() => { compCounter = 0; });
function createComp(selector: string,
ngContentSelectors: string[]): CompileDirectiveMetadata {
return CompileDirectiveMetadata.create({
selector: selector,
isComponent: true,
type: new CompileTypeMetadata({name: 'SomeComp'}),
type:
new CompileTypeMetadata({moduleUrl: someModuleUrl, name: `SomeComp${compCounter++}`}),
template: new CompileTemplateMetadata({ngContentSelectors: ngContentSelectors})
})
}
@ -750,7 +1075,7 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
() => {
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['invalidProp']
});
expect(() => parse('<div [invalid-prop]></div>', [dirA])).not.toThrow();
@ -760,13 +1085,13 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
var dirB = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({name: 'DirB'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
@ -778,7 +1103,7 @@ More than one component: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`);
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
isComponent: true,
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<template [a]="b" (e)="f"></template>', [dirA]))
@ -792,7 +1117,7 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
isComponent: true,
type: new CompileTypeMetadata({name: 'DirA'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<div *a="b"></div>', [dirA])).toThrowError(`Template parse errors:
@ -910,8 +1235,11 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
});
it('should support variables', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'}), exportAs: 'dirA'});
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
exportAs: 'dirA'
});
expect(humanizeTplAstSourceSpans(parse('<div a #a="dirA"></div>', [dirA])))
.toEqual([
[ElementAst, 'div', '<div a #a="dirA">'],
@ -955,12 +1283,14 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
});
it('should support directive', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: '[a]', type: new CompileTypeMetadata({name: 'DirA'})});
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
var comp = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({name: 'ZComp'}),
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'ZComp'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(humanizeTplAstSourceSpans(parse('<div a>', [dirA, comp])))
@ -973,10 +1303,14 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
});
it('should support directive in namespace', () => {
var tagSel = CompileDirectiveMetadata.create(
{selector: 'circle', type: new CompileTypeMetadata({name: 'elDir'})});
var attrSel = CompileDirectiveMetadata.create(
{selector: '[href]', type: new CompileTypeMetadata({name: 'attrDir'})});
var tagSel = CompileDirectiveMetadata.create({
selector: 'circle',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'elDir'})
});
var attrSel = CompileDirectiveMetadata.create({
selector: '[href]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'attrDir'})
});
expect(humanizeTplAstSourceSpans(
parse('<svg><circle /><use xlink:href="Port" /></svg>', [tagSel, attrSel])))
@ -991,8 +1325,11 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
});
it('should support directive property', () => {
var dirA = CompileDirectiveMetadata.create(
{selector: 'div', type: new CompileTypeMetadata({name: 'DirA'}), inputs: ['aProp']});
var dirA = CompileDirectiveMetadata.create({
selector: 'div',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
inputs: ['aProp']
});
expect(humanizeTplAstSourceSpans(parse('<div [aProp]="foo"></div>', [dirA])))
.toEqual([
[ElementAst, 'div', '<div [aProp]="foo">'],
@ -1005,8 +1342,10 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
describe('pipes', () => {
it('should allow pipes that have been defined as dependencies', () => {
var testPipe =
new CompilePipeMetadata({name: 'test', type: new CompileTypeMetadata({name: 'DirA'})});
var testPipe = new CompilePipeMetadata({
name: 'test',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(() => parse('{{a | test}}', [], [testPipe])).not.toThrow();
});
@ -1167,7 +1506,7 @@ class FooAstTransformer implements TemplateAstVisitor {
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; }
visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'div') return ast;
return new ElementAst('foo', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
return new ElementAst('foo', [], [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
}
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
@ -1184,6 +1523,6 @@ class FooAstTransformer implements TemplateAstVisitor {
class BarAstTransformer extends FooAstTransformer {
visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'foo') return ast;
return new ElementAst('bar', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
return new ElementAst('bar', [], [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
}
}

View File

@ -3,10 +3,13 @@ import {MockSchemaRegistry} from './schema_registry_mock';
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
import {XHR} from 'angular2/src/compiler/xhr';
import {UrlResolver, createWithoutPackagePrefix} from 'angular2/src/compiler/url_resolver';
import {
UrlResolver,
createUrlResolverWithoutPackagePrefix
} from 'angular2/src/compiler/url_resolver';
export var TEST_PROVIDERS = [
provide(ElementSchemaRegistry, {useValue: new MockSchemaRegistry({}, {})}),
provide(XHR, {useClass: MockXHR}),
provide(UrlResolver, {useFactory: createWithoutPackagePrefix})
provide(UrlResolver, {useFactory: createUrlResolverWithoutPackagePrefix})
];

View File

@ -10,7 +10,7 @@ import {
inject
} from 'angular2/testing_internal';
import {IS_DART} from 'angular2/src/facade/lang';
import {UrlResolver} from 'angular2/src/compiler/url_resolver';
import {UrlResolver, createOfflineCompileUrlResolver} from 'angular2/src/compiler/url_resolver';
export function main() {
describe('UrlResolver', () => {
@ -89,41 +89,51 @@ export function main() {
});
});
describe('packages',
() => {
it('should resolve a url based on the application package', () => {
resolver = new UrlResolver('my_packages_dir');
expect(resolver.resolve(null, 'package:some/dir/file.txt'))
.toEqual('my_packages_dir/some/dir/file.txt');
expect(resolver.resolve(null, 'some/dir/file.txt')).toEqual('some/dir/file.txt');
});
describe('packages', () => {
it('should resolve a url based on the application package', () => {
resolver = new UrlResolver('my_packages_dir');
expect(resolver.resolve(null, 'package:some/dir/file.txt'))
.toEqual('my_packages_dir/some/dir/file.txt');
expect(resolver.resolve(null, 'some/dir/file.txt')).toEqual('some/dir/file.txt');
});
it('should contain a default value of "/packages" when nothing is provided for DART',
inject([UrlResolver], (resolver: UrlResolver) => {
if (IS_DART) {
expect(resolver.resolve(null, 'package:file')).toEqual('/packages/file');
}
}));
it('should contain a default value of "/packages" when nothing is provided for DART',
inject([UrlResolver], (resolver: UrlResolver) => {
if (IS_DART) {
expect(resolver.resolve(null, 'package:file')).toEqual('/packages/file');
}
}));
it('should contain a default value of "/" when nothing is provided for TS/ESM',
inject([UrlResolver], (resolver: UrlResolver) => {
if (!IS_DART) {
expect(resolver.resolve(null, 'package:file')).toEqual('/file');
}
}));
it('should contain a default value of "/" when nothing is provided for TS/ESM',
inject([UrlResolver], (resolver: UrlResolver) => {
if (!IS_DART) {
expect(resolver.resolve(null, 'package:file')).toEqual('/file');
}
}));
it('should resolve a package value when present within the baseurl', () => {
resolver = new UrlResolver('/my_special_dir');
expect(resolver.resolve('package:some_dir/', 'matias.html'))
.toEqual('/my_special_dir/some_dir/matias.html');
});
})
it('should resolve a package value when present within the baseurl', () => {
resolver = new UrlResolver('/my_special_dir');
expect(resolver.resolve('package:some_dir/', 'matias.html'))
.toEqual('/my_special_dir/some_dir/matias.html');
});
});
describe('corner and error cases', () => {
it('should encode URLs before resolving', () => {
expect(resolver.resolve('foo/baz', `<p #p>Hello
</p>`)).toEqual('foo/%3Cp%20#p%3EHello%0A%20%20%20%20%20%20%20%20%3C/p%3E');
});
});
describe('asset urls', () => {
var resolver: UrlResolver;
beforeEach(() => { resolver = createOfflineCompileUrlResolver(); });
it('should resolve package: urls into asset: urls', () => {
expect(resolver.resolve(null, 'package:somePkg/somePath'))
.toEqual('asset:somePkg/lib/somePath');
});
});
describe('corner and error cases', () => {
it('should encode URLs before resolving', () => {
expect(resolver.resolve('foo/baz', `<p #p>Hello
</p>`))
.toEqual('foo/%3Cp%20#p%3EHello%0A%20%20%20%20%20%20%20%20%3C/p%3E');
});
});
});
}

View File

@ -1,61 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/testing_internal';
import {IS_DART} from 'angular2/src/facade/lang';
import {escapeSingleQuoteString, escapeDoubleQuoteString} from 'angular2/src/compiler/util';
export function main() {
describe('util', () => {
describe('escapeSingleQuoteString', () => {
it('should escape single quotes',
() => { expect(escapeSingleQuoteString(`'`)).toEqual(`'\\''`); });
it('should escape backslash',
() => { expect(escapeSingleQuoteString('\\')).toEqual(`'\\\\'`); });
it('should escape newlines',
() => { expect(escapeSingleQuoteString('\n')).toEqual(`'\\n'`); });
it('should escape carriage returns',
() => { expect(escapeSingleQuoteString('\r')).toEqual(`'\\r'`); });
if (IS_DART) {
it('should escape $', () => { expect(escapeSingleQuoteString('$')).toEqual(`'\\$'`); });
} else {
it('should not escape $', () => { expect(escapeSingleQuoteString('$')).toEqual(`'$'`); });
}
});
describe('escapeDoubleQuoteString', () => {
it('should escape double quotes',
() => { expect(escapeDoubleQuoteString(`"`)).toEqual(`"\\""`); });
it('should escape backslash',
() => { expect(escapeDoubleQuoteString('\\')).toEqual(`"\\\\"`); });
it('should escape newlines',
() => { expect(escapeDoubleQuoteString('\n')).toEqual(`"\\n"`); });
it('should escape carriage returns',
() => { expect(escapeDoubleQuoteString('\r')).toEqual(`"\\r"`); });
if (IS_DART) {
it('should escape $', () => { expect(escapeDoubleQuoteString('$')).toEqual(`"\\$"`); });
} else {
it('should not escape $', () => { expect(escapeDoubleQuoteString('$')).toEqual(`"$"`); });
}
});
});
}

View File

@ -0,0 +1,71 @@
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/testing_internal';
import {ViewResolver} from 'angular2/src/compiler/view_resolver';
import {Component, ViewMetadata} from 'angular2/src/core/metadata';
class SomeDir {}
class SomePipe {}
@Component({
selector: 'sample',
template: "some template",
directives: [SomeDir],
pipes: [SomePipe],
styles: ["some styles"]
})
class ComponentWithView {
}
@Component({
selector: 'sample',
template: "some template",
directives: [SomeDir],
pipes: [SomePipe],
styles: ["some styles"]
})
class ComponentWithTemplate {
}
@Component({selector: 'sample', template: "some template"})
class ComponentWithViewTemplate {
}
@Component({selector: 'sample', templateUrl: "some template url", template: "some template"})
class ComponentWithViewTemplateUrl {
}
@Component({selector: 'sample'})
class ComponentWithoutView {
}
class SimpleClass {}
export function main() {
describe("ViewResolver", () => {
var resolver: ViewResolver;
beforeEach(() => { resolver = new ViewResolver(); });
it('should read out the View metadata from the Component metadata', () => {
var viewMetadata = resolver.resolve(ComponentWithTemplate);
expect(viewMetadata)
.toEqual(new ViewMetadata({
template: "some template",
directives: [SomeDir],
pipes: [SomePipe],
styles: ["some styles"]
}));
});
it('should throw when Component has no View decorator and no template is set', () => {
expect(() => resolver.resolve(ComponentWithoutView))
.toThrowErrorWith(
"Component 'ComponentWithoutView' must have either 'template' or 'templateUrl' set");
});
it('should throw when simple class has no View decorator and no template is set', () => {
expect(() => resolver.resolve(SimpleClass))
.toThrowErrorWith("Could not compile 'SimpleClass' because it is not a component.");
});
});
}