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:
@ -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); }
|
||||
}
|
@ -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;
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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);
|
||||
});
|
||||
|
148
modules/angular2/test/compiler/directive_lifecycle_spec.dart
Normal file
148
modules/angular2/test/compiler/directive_lifecycle_spec.dart
Normal 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() {}
|
||||
}
|
149
modules/angular2/test/compiler/directive_lifecycle_spec.ts
Normal file
149
modules/angular2/test/compiler/directive_lifecycle_spec.ts
Normal 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() {}
|
||||
}
|
@ -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: []}),
|
209
modules/angular2/test/compiler/directive_resolver_spec.ts
Normal file
209
modules/angular2/test/compiler/directive_resolver_spec.ts
Normal 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")});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -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;
|
||||
});
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
`;
|
249
modules/angular2/test/compiler/expression_parser/lexer_spec.ts
Normal file
249
modules/angular2/test/compiler/expression_parser/lexer_spec.ts
Normal 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, '?.');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
465
modules/angular2/test/compiler/expression_parser/parser_spec.ts
Normal file
465
modules/angular2/test/compiler/expression_parser/parser_spec.ts
Normal 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"');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
196
modules/angular2/test/compiler/expression_parser/unparser.ts
Normal file
196
modules/angular2/test/compiler/expression_parser/unparser.ts
Normal 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); }
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
export const styles = CONST_EXPR([`.greenStyle[_ngcontent-a-1] { color: green; }`]);
|
88
modules/angular2/test/compiler/offline_compiler_spec.ts
Normal file
88
modules/angular2/test/compiler/offline_compiler_spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
67
modules/angular2/test/compiler/offline_compiler_util.ts
Normal file
67
modules/angular2/test/compiler/offline_compiler_util.ts
Normal 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;
|
||||
}
|
@ -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(`'$'`); });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
310
modules/angular2/test/compiler/output/dart_emitter_spec.ts
Normal file
310
modules/angular2/test/compiler/output/dart_emitter_spec.ts
Normal 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;');
|
||||
});
|
||||
});
|
||||
}
|
296
modules/angular2/test/compiler/output/js_emitter_spec.ts
Normal file
296
modules/angular2/test/compiler/output/js_emitter_spec.ts
Normal 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'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
197
modules/angular2/test/compiler/output/output_emitter_spec.ts
Normal file
197
modules/angular2/test/compiler/output/output_emitter_spec.ts
Normal 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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
267
modules/angular2/test/compiler/output/output_emitter_util.ts
Normal file
267
modules/angular2/test/compiler/output/output_emitter_util.ts
Normal 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); }
|
||||
}
|
72
modules/angular2/test/compiler/output/path_util_spec.ts
Normal file
72
modules/angular2/test/compiler/output/path_util_spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
321
modules/angular2/test/compiler/output/ts_emitter_spec.ts
Normal file
321
modules/angular2/test/compiler/output/ts_emitter_spec.ts
Normal 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;');
|
||||
});
|
||||
});
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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);
|
||||
}));
|
||||
|
||||
|
@ -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([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -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 {}
|
||||
|
@ -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); }
|
||||
}
|
@ -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}']);
|
@ -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}']);
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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}>`); });
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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})
|
||||
];
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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(`"$"`); });
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
71
modules/angular2/test/compiler/view_resolver_spec.ts
Normal file
71
modules/angular2/test/compiler/view_resolver_spec.ts
Normal 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.");
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user