feat(core): speed up view creation via code gen for view factories.
BREAKING CHANGE: - Platform pipes can only contain types and arrays of types, but no bindings any more. - When using transformers, platform pipes need to be specified explicitly in the pubspec.yaml via the new config option `platform_pipes`. - `Compiler.compileInHost` now returns a `HostViewFactoryRef` - Component view is not yet created when component constructor is called. -> use `onInit` lifecycle callback to access the view of a component - `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef` - `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access the root nodes of an embedded view - `renderer.setElementProperty`, `..setElementStyle`, `..setElementAttribute` now take a native element instead of an ElementRef - `Renderer` interface now operates on plain native nodes, instead of `RenderElementRef`s or `RenderViewRef`s Closes #5993
This commit is contained in:
@@ -18,7 +18,6 @@ import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/col
|
||||
import {Component, View, provide} from 'angular2/core';
|
||||
import {NgFor} from 'angular2/common';
|
||||
import {NgClass} from 'angular2/src/common/directives/ng_class';
|
||||
import {APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/linker/view_pool';
|
||||
|
||||
function detectChangesAndCheck(fixture: ComponentFixture, classes: string, elIndex: number = 0) {
|
||||
fixture.detectChanges();
|
||||
@@ -30,8 +29,6 @@ export function main() {
|
||||
describe('binding to CSS class list', () => {
|
||||
|
||||
describe('viewpool support', () => {
|
||||
beforeEachProviders(() => { return [provide(APP_VIEW_POOL_CAPACITY, {useValue: 100})]; });
|
||||
|
||||
it('should clean up when the directive is destroyed',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div *ngFor="var item of items" [ngClass]="item"></div>';
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import {ChangeDetectorRef_} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {SpyObject, proxy} from 'angular2/testing_internal';
|
||||
|
||||
export class SpyChangeDetectorRef extends SpyObject {
|
||||
constructor() { super(ChangeDetectorRef_); }
|
||||
constructor() {
|
||||
super(ChangeDetectorRef);
|
||||
this.spy('markForCheck');
|
||||
}
|
||||
}
|
||||
|
||||
export class SpyNgControl extends SpyObject {}
|
||||
|
||||
@@ -47,7 +47,6 @@ export function main() {
|
||||
var directive: TestDirective;
|
||||
var locals: Locals;
|
||||
var pipes: Pipes;
|
||||
var eventLocals: Locals;
|
||||
|
||||
beforeEach(inject([TemplateParser], (_templateParser) => {
|
||||
parser = _templateParser;
|
||||
@@ -55,7 +54,6 @@ export function main() {
|
||||
directive = new TestDirective();
|
||||
dispatcher = new TestDispatcher([directive], []);
|
||||
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||
eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
|
||||
pipes = new TestPipes();
|
||||
}));
|
||||
|
||||
@@ -65,9 +63,9 @@ export function main() {
|
||||
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
|
||||
ChangeDetectionStrategy.Default,
|
||||
new ChangeDetectorGenConfig(true, false, false),
|
||||
parser.parse(template, directives, 'TestComp'))
|
||||
parser.parse(template, directives, [], 'TestComp'))
|
||||
.map(definition => new DynamicProtoChangeDetector(definition));
|
||||
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
|
||||
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate();
|
||||
changeDetector.hydrate(context, locals, dispatcher, pipes);
|
||||
return changeDetector;
|
||||
}
|
||||
@@ -91,8 +89,7 @@ export function main() {
|
||||
it('should handle events on regular elements', () => {
|
||||
var changeDetector = createChangeDetector('<div on-click="onEvent($event)">', [], 0);
|
||||
|
||||
eventLocals.set('$event', 'click');
|
||||
changeDetector.handleEvent('click', 0, eventLocals);
|
||||
changeDetector.handleEvent('click', 0, 'click');
|
||||
expect(context.eventLog).toEqual(['click']);
|
||||
});
|
||||
|
||||
@@ -105,16 +102,14 @@ export function main() {
|
||||
var changeDetector =
|
||||
createChangeDetector('<template on-click="onEvent($event)">', [dirMeta], 0);
|
||||
|
||||
eventLocals.set('$event', 'click');
|
||||
changeDetector.handleEvent('click', 0, eventLocals);
|
||||
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);
|
||||
|
||||
eventLocals.set('$event', 'click');
|
||||
changeDetector.handleEvent('window:click', 0, eventLocals);
|
||||
changeDetector.handleEvent('window:click', 0, 'click');
|
||||
expect(context.eventLog).toEqual(['click']);
|
||||
});
|
||||
|
||||
@@ -177,8 +172,7 @@ export function main() {
|
||||
|
||||
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
||||
|
||||
eventLocals.set('$event', 'click');
|
||||
changeDetector.handleEvent('click', 0, eventLocals);
|
||||
changeDetector.handleEvent('click', 0, 'click');
|
||||
expect(directive.eventLog).toEqual(['click']);
|
||||
});
|
||||
|
||||
|
||||
@@ -56,10 +56,18 @@ var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
|
||||
|
||||
export function main() {
|
||||
describe('ChangeDetectorCompiler', () => {
|
||||
beforeEachProviders(() => TEST_PROVIDERS);
|
||||
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;
|
||||
@@ -71,35 +79,16 @@ export function main() {
|
||||
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||
var type =
|
||||
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
|
||||
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||
var parsedTemplate = parser.parse(template, directives, [], 'TestComp');
|
||||
var factories =
|
||||
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||
return testChangeDetector(factories[0]);
|
||||
}
|
||||
|
||||
describe('no jit', () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, false, false)})
|
||||
]);
|
||||
it('should watch element properties', () => {
|
||||
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
|
||||
.toEqual(['elementProperty(elProp)=someValue']);
|
||||
});
|
||||
it('should watch element properties', () => {
|
||||
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
|
||||
.toEqual(['elementProperty(elProp)=someValue']);
|
||||
});
|
||||
|
||||
describe('jit', () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, false, true)})
|
||||
]);
|
||||
it('should watch element properties', () => {
|
||||
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
|
||||
.toEqual(['elementProperty(elProp)=someValue']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('compileComponentCodeGen', () => {
|
||||
@@ -108,7 +97,7 @@ export function main() {
|
||||
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 parsedTemplate = parser.parse(template, directives, [], 'TestComp');
|
||||
var sourceExpressions =
|
||||
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
|
||||
@@ -140,7 +129,7 @@ function createTestableModule(source: SourceExpressions,
|
||||
|
||||
export function testChangeDetector(changeDetectorFactory: Function): string[] {
|
||||
var dispatcher = new TestDispatcher([], []);
|
||||
var cd = changeDetectorFactory(dispatcher);
|
||||
var cd = changeDetectorFactory();
|
||||
var ctx = new SomeComponent();
|
||||
ctx.someProp = 'someValue';
|
||||
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {
|
||||
ProtoChangeDetector,
|
||||
ChangeDetector,
|
||||
ChangeDispatcher,
|
||||
DirectiveIndex,
|
||||
BindingTarget
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
export class TestDirective {
|
||||
eventLog: string[] = [];
|
||||
dirProp: string;
|
||||
click: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
onEvent(value: string) { this.eventLog.push(value); }
|
||||
}
|
||||
@@ -17,7 +19,7 @@ export class TestDirective {
|
||||
export class TestDispatcher implements ChangeDispatcher {
|
||||
log: string[];
|
||||
|
||||
constructor(public directives: any[], public detectors: ProtoChangeDetector[]) { this.clear(); }
|
||||
constructor(public directives: any[], public detectors: ChangeDetector[]) { this.clear(); }
|
||||
|
||||
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
|
||||
|
||||
@@ -34,7 +36,9 @@ export class TestDispatcher implements ChangeDispatcher {
|
||||
notifyAfterContentChecked() {}
|
||||
notifyAfterViewChecked() {}
|
||||
|
||||
getDebugContext(a, b) { return null; }
|
||||
notifyOnDestroy() {}
|
||||
|
||||
getDebugContext(a, b, c) { return null; }
|
||||
|
||||
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
|
||||
}
|
||||
|
||||
@@ -1,597 +0,0 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachProviders
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {
|
||||
CONST_EXPR,
|
||||
stringify,
|
||||
isType,
|
||||
Type,
|
||||
isBlank,
|
||||
serializeEnum,
|
||||
IS_DART
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||
import {
|
||||
CommandVisitor,
|
||||
TextCmd,
|
||||
NgContentCmd,
|
||||
BeginElementCmd,
|
||||
BeginComponentCmd,
|
||||
EmbeddedTemplateCmd,
|
||||
TemplateCmd,
|
||||
visitAllCommands,
|
||||
CompiledComponentTemplate
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||
import {
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {evalModule} from './eval_module';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
codeGenValueFn,
|
||||
codeGenExportVariable,
|
||||
codeGenConstConstructorCall,
|
||||
MODULE_SUFFIX
|
||||
} from 'angular2/src/compiler/util';
|
||||
import {TEST_PROVIDERS} from './test_bindings';
|
||||
|
||||
const BEGIN_ELEMENT = 'BEGIN_ELEMENT';
|
||||
const END_ELEMENT = 'END_ELEMENT';
|
||||
const BEGIN_COMPONENT = 'BEGIN_COMPONENT';
|
||||
const END_COMPONENT = 'END_COMPONENT';
|
||||
const TEXT = 'TEXT';
|
||||
const NG_CONTENT = 'NG_CONTENT';
|
||||
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
|
||||
|
||||
// Attention: These module names have to correspond to real modules!
|
||||
var THIS_MODULE_URL = `package:angular2/test/compiler/command_compiler_spec${MODULE_SUFFIX}`;
|
||||
var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
|
||||
var TEMPLATE_COMMANDS_MODULE_REF =
|
||||
moduleRef(`package:angular2/src/core/linker/template_commands${MODULE_SUFFIX}`);
|
||||
|
||||
// Attention: read by eval!
|
||||
export class RootComp {}
|
||||
export class SomeDir {}
|
||||
export class AComp {}
|
||||
|
||||
var RootCompTypeMeta =
|
||||
new CompileTypeMetadata({name: 'RootComp', runtime: RootComp, moduleUrl: THIS_MODULE_URL});
|
||||
var SomeDirTypeMeta =
|
||||
new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleUrl: THIS_MODULE_URL});
|
||||
var ACompTypeMeta =
|
||||
new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleUrl: THIS_MODULE_URL});
|
||||
var compTypeTemplateId: Map<CompileTypeMetadata, string> = MapWrapper.createFromPairs(
|
||||
[[RootCompTypeMeta, 'rootCompId'], [SomeDirTypeMeta, 'someDirId'], [ACompTypeMeta, 'aCompId']]);
|
||||
|
||||
export function main() {
|
||||
describe('CommandCompiler', () => {
|
||||
beforeEachProviders(() => TEST_PROVIDERS);
|
||||
|
||||
var parser: TemplateParser;
|
||||
var commandCompiler: CommandCompiler;
|
||||
var componentTemplateFactory: Function;
|
||||
|
||||
beforeEach(inject([TemplateParser, CommandCompiler], (_templateParser, _commandCompiler) => {
|
||||
parser = _templateParser;
|
||||
commandCompiler = _commandCompiler;
|
||||
}));
|
||||
|
||||
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
|
||||
type?: CompileTypeMetadata,
|
||||
selector?: string,
|
||||
template?: string,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
ngContentSelectors?: string[]
|
||||
}): CompileDirectiveMetadata {
|
||||
if (isBlank(encapsulation)) {
|
||||
encapsulation = ViewEncapsulation.None;
|
||||
}
|
||||
if (isBlank(selector)) {
|
||||
selector = 'root';
|
||||
}
|
||||
if (isBlank(ngContentSelectors)) {
|
||||
ngContentSelectors = [];
|
||||
}
|
||||
if (isBlank(template)) {
|
||||
template = '';
|
||||
}
|
||||
return CompileDirectiveMetadata.create({
|
||||
selector: selector,
|
||||
isComponent: true,
|
||||
type: type,
|
||||
template: new CompileTemplateMetadata({
|
||||
template: template,
|
||||
ngContentSelectors: ngContentSelectors,
|
||||
encapsulation: encapsulation
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function createDirective(type: CompileTypeMetadata, selector: string,
|
||||
exportAs: string = null): CompileDirectiveMetadata {
|
||||
return CompileDirectiveMetadata.create(
|
||||
{selector: selector, exportAs: exportAs, isComponent: false, type: type});
|
||||
}
|
||||
|
||||
|
||||
function createTests(run: Function) {
|
||||
describe('text', () => {
|
||||
|
||||
it('should create unbound text commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({type: RootCompTypeMeta, template: 'a'});
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([[TEXT, 'a', false, null]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create bound text commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({type: RootCompTypeMeta, template: '{{a}}'});
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([[TEXT, null, true, null]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('elements', () => {
|
||||
|
||||
it('should create unbound element commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({type: RootCompTypeMeta, template: '<div a="b">'});
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[BEGIN_ELEMENT, 'div', ['a', 'b'], [], [], [], false, null],
|
||||
[END_ELEMENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create bound element commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<div a="b" #someVar (click)="someHandler" (window:scroll)="scrollTo()">'
|
||||
});
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_ELEMENT,
|
||||
'div',
|
||||
['a', 'b'],
|
||||
[null, 'click', 'window', 'scroll'],
|
||||
['someVar', null],
|
||||
[],
|
||||
true,
|
||||
null
|
||||
],
|
||||
[END_ELEMENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create element commands with directives',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp =
|
||||
createComp({type: RootCompTypeMeta, template: '<div a #someVar="someExport">'});
|
||||
var dir = CompileDirectiveMetadata.create({
|
||||
selector: '[a]',
|
||||
exportAs: 'someExport',
|
||||
isComponent: false,
|
||||
type: SomeDirTypeMeta,
|
||||
host: {'(click)': 'doIt()', '(window:scroll)': 'doIt()', 'role': 'button'}
|
||||
});
|
||||
run(rootComp, [dir])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_ELEMENT,
|
||||
'div',
|
||||
['a', '', 'role', 'button'],
|
||||
[null, 'click', 'window', 'scroll'],
|
||||
['someVar', 0],
|
||||
['SomeDirType'],
|
||||
true,
|
||||
null
|
||||
],
|
||||
[END_ELEMENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should merge element attributes with host attributes',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<div class="origclass" style="color: red;" role="origrole" attr1>'
|
||||
});
|
||||
var dir = CompileDirectiveMetadata.create({
|
||||
selector: 'div',
|
||||
isComponent: false,
|
||||
type: SomeDirTypeMeta,
|
||||
host: {'class': 'newclass', 'style': 'newstyle', 'role': 'newrole', 'attr2': ''}
|
||||
});
|
||||
run(rootComp, [dir])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_ELEMENT,
|
||||
'div',
|
||||
[
|
||||
'attr1',
|
||||
'',
|
||||
'attr2',
|
||||
'',
|
||||
'class',
|
||||
'origclass newclass',
|
||||
'role',
|
||||
'newrole',
|
||||
'style',
|
||||
'color: red; newstyle'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
['SomeDirType'],
|
||||
true,
|
||||
null
|
||||
],
|
||||
[END_ELEMENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create nested nodes', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({type: RootCompTypeMeta, template: '<div>a</div>'});
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[BEGIN_ELEMENT, 'div', [], [], [], [], false, null],
|
||||
[TEXT, 'a', false, null],
|
||||
[END_ELEMENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
|
||||
it('should create component commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp(
|
||||
{type: RootCompTypeMeta, template: '<a a="b" #someVar (click)="someHandler">'});
|
||||
var comp = createComp({type: ACompTypeMeta, selector: 'a'});
|
||||
run(rootComp, [comp])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_COMPONENT,
|
||||
'a',
|
||||
['a', 'b'],
|
||||
[null, 'click'],
|
||||
['someVar', 0],
|
||||
['ACompType'],
|
||||
serializeEnum(ViewEncapsulation.None),
|
||||
null,
|
||||
'aCompId'
|
||||
],
|
||||
[END_COMPONENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should store viewEncapsulation', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({type: RootCompTypeMeta, template: '<a></a>'});
|
||||
var comp = createComp(
|
||||
{type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Native});
|
||||
run(rootComp, [comp])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_COMPONENT,
|
||||
'a',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
['ACompType'],
|
||||
serializeEnum(ViewEncapsulation.Native),
|
||||
null,
|
||||
'aCompId'
|
||||
],
|
||||
[END_COMPONENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create nested nodes and set ngContentIndex',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({type: RootCompTypeMeta, template: '<a>t</a>'});
|
||||
var comp = createComp({type: ACompTypeMeta, selector: 'a', ngContentSelectors: ['*']});
|
||||
run(rootComp, [comp])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_COMPONENT,
|
||||
'a',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
['ACompType'],
|
||||
serializeEnum(ViewEncapsulation.None),
|
||||
null,
|
||||
'aCompId'
|
||||
],
|
||||
[TEXT, 't', false, 0],
|
||||
[END_COMPONENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('embedded templates', () => {
|
||||
it('should create embedded template commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp =
|
||||
createComp({type: RootCompTypeMeta, template: '<template a="b"></template>'});
|
||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||
run(rootComp, [dir], 1)
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[EMBEDDED_TEMPLATE, ['a', 'b'], [], ['SomeDirType'], false, null, 'cd1', []]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should keep variable name and value for <template> elements',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<template #someVar="someValue" #someEmptyVar></template>'
|
||||
});
|
||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||
run(rootComp, [dir], 1)
|
||||
.then((data) => {
|
||||
expect(data[0][2])
|
||||
.toEqual(['someVar', 'someValue', 'someEmptyVar', '$implicit']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should keep variable name and value for template attributes',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<div template="var someVar=someValue; var someEmptyVar"></div>'
|
||||
});
|
||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||
run(rootComp, [dir], 1)
|
||||
.then((data) => {
|
||||
expect(data[0][2])
|
||||
.toEqual(['someVar', 'someValue', 'someEmptyVar', '$implicit']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp =
|
||||
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
|
||||
run(rootComp, [], 1)
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
EMBEDDED_TEMPLATE,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
false,
|
||||
null,
|
||||
'cd1',
|
||||
[[TEXT, 't', false, null]]
|
||||
]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should calculate wether the template is merged based on nested ng-content elements',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<template><ng-content></ng-content></template>'
|
||||
});
|
||||
run(rootComp, [], 1)
|
||||
.then((data) => {
|
||||
expect(data).toEqual(
|
||||
[[EMBEDDED_TEMPLATE, [], [], [], true, null, 'cd1', [[NG_CONTENT, null]]]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('ngContent', () => {
|
||||
it('should create ng-content commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp =
|
||||
createComp({type: RootCompTypeMeta, template: '<ng-content></ng-content>'});
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([[NG_CONTENT, null]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
describe('compileComponentRuntime', () => {
|
||||
beforeEach(() => {
|
||||
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
|
||||
return () => new CompiledComponentTemplate(compTypeTemplateId.get(directive.type), null,
|
||||
null, null);
|
||||
};
|
||||
});
|
||||
|
||||
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||
embeddedTemplateCount: number = 0): Promise<any[][]> {
|
||||
var changeDetectorFactories = [];
|
||||
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
|
||||
(function(i) { changeDetectorFactories.push((_) => `cd${i}`); })(i);
|
||||
}
|
||||
var parsedTemplate =
|
||||
parser.parse(component.template.template, directives, component.type.name);
|
||||
var commands = commandCompiler.compileComponentRuntime(
|
||||
component, parsedTemplate, changeDetectorFactories, componentTemplateFactory);
|
||||
return PromiseWrapper.resolve(humanize(commands));
|
||||
}
|
||||
|
||||
createTests(run);
|
||||
});
|
||||
|
||||
|
||||
describe('compileComponentCodeGen', () => {
|
||||
beforeEach(() => {
|
||||
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
|
||||
return `${directive.type.name}TemplateGetter`;
|
||||
};
|
||||
});
|
||||
|
||||
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||
embeddedTemplateCount: number = 0): Promise<any[][]> {
|
||||
var testDeclarations = [];
|
||||
var changeDetectorFactoryExpressions = [];
|
||||
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
|
||||
var fnName = `cd${i}`;
|
||||
testDeclarations.push(`${codeGenValueFn(['_'], ` 'cd${i}' `, fnName)};`);
|
||||
changeDetectorFactoryExpressions.push(fnName);
|
||||
}
|
||||
for (var i = 0; i < directives.length; i++) {
|
||||
var directive = directives[i];
|
||||
if (directive.isComponent) {
|
||||
var nestedTemplate =
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'CompiledComponentTemplate')}('${compTypeTemplateId.get(directive.type)}', null, null, null)`;
|
||||
var getterName = `${directive.type.name}TemplateGetter`;
|
||||
testDeclarations.push(`${codeGenValueFn([], nestedTemplate, getterName)};`)
|
||||
}
|
||||
}
|
||||
var parsedTemplate =
|
||||
parser.parse(component.template.template, directives, component.type.name);
|
||||
var sourceExpression = commandCompiler.compileComponentCodeGen(
|
||||
component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory);
|
||||
testDeclarations.forEach(decl => sourceExpression.declarations.push(decl));
|
||||
var testableModule = createTestableModule(sourceExpression).getSourceWithImports();
|
||||
return evalModule(testableModule.source, testableModule.imports, null);
|
||||
}
|
||||
|
||||
createTests(run);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Attention: read by eval!
|
||||
export function humanize(cmds: TemplateCmd[]): any[][] {
|
||||
var visitor = new CommandHumanizer();
|
||||
visitAllCommands(visitor, cmds);
|
||||
return visitor.result;
|
||||
}
|
||||
|
||||
function checkAndStringifyType(type: Type): string {
|
||||
expect(isType(type)).toBe(true);
|
||||
return `${stringify(type)}Type`;
|
||||
}
|
||||
|
||||
class CommandHumanizer implements CommandVisitor {
|
||||
result: any[][] = [];
|
||||
visitText(cmd: TextCmd, context: any): any {
|
||||
this.result.push([TEXT, cmd.value, cmd.isBound, cmd.ngContentIndex]);
|
||||
return null;
|
||||
}
|
||||
visitNgContent(cmd: NgContentCmd, context: any): any {
|
||||
this.result.push([NG_CONTENT, cmd.ngContentIndex]);
|
||||
return null;
|
||||
}
|
||||
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
||||
this.result.push([
|
||||
BEGIN_ELEMENT,
|
||||
cmd.name,
|
||||
cmd.attrNameAndValues,
|
||||
cmd.eventTargetAndNames,
|
||||
cmd.variableNameAndValues,
|
||||
cmd.directives.map(checkAndStringifyType),
|
||||
cmd.isBound,
|
||||
cmd.ngContentIndex
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
visitEndElement(context: any): any {
|
||||
this.result.push([END_ELEMENT]);
|
||||
return null;
|
||||
}
|
||||
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
|
||||
this.result.push([
|
||||
BEGIN_COMPONENT,
|
||||
cmd.name,
|
||||
cmd.attrNameAndValues,
|
||||
cmd.eventTargetAndNames,
|
||||
cmd.variableNameAndValues,
|
||||
cmd.directives.map(checkAndStringifyType),
|
||||
serializeEnum(cmd.encapsulation),
|
||||
cmd.ngContentIndex,
|
||||
cmd.templateId
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
visitEndComponent(context: any): any {
|
||||
this.result.push([END_COMPONENT]);
|
||||
return null;
|
||||
}
|
||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
|
||||
this.result.push([
|
||||
EMBEDDED_TEMPLATE,
|
||||
cmd.attrNameAndValues,
|
||||
cmd.variableNameAndValues,
|
||||
cmd.directives.map(checkAndStringifyType),
|
||||
cmd.isMerged,
|
||||
cmd.ngContentIndex,
|
||||
cmd.changeDetectorFactory(null),
|
||||
humanize(cmd.children)
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function createTestableModule(source: SourceExpression): SourceModule {
|
||||
var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`;
|
||||
var testableSource = `${source.declarations.join('\n')}
|
||||
${codeGenValueFn(['_'], resultExpression, '_run')};
|
||||
${codeGenExportVariable('run')}_run;
|
||||
`;
|
||||
return new SourceModule(null, testableSource);
|
||||
}
|
||||
@@ -15,73 +15,40 @@ import {
|
||||
|
||||
import {Component, View, provide} from 'angular2/core';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {SpyProtoViewFactory} from '../core/spies';
|
||||
import {
|
||||
CompiledHostTemplate,
|
||||
CompiledComponentTemplate,
|
||||
BeginComponentCmd
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {AppProtoView} from 'angular2/src/core/linker/view';
|
||||
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 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; }));
|
||||
|
||||
describe('compileInHost', () => {
|
||||
var protoViewFactorySpy;
|
||||
var someProtoView;
|
||||
|
||||
beforeEachProviders(() => {
|
||||
protoViewFactorySpy = new SpyProtoViewFactory();
|
||||
someProtoView = new AppProtoView(null, null, null, null, null, null, null);
|
||||
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
|
||||
return [provide(ProtoViewFactory, {useValue: protoViewFactorySpy})];
|
||||
});
|
||||
|
||||
it('should compile the template via TemplateCompiler',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var cht: CompiledHostTemplate;
|
||||
protoViewFactorySpy.spy('createHost')
|
||||
.andCallFake((_cht) => {
|
||||
cht = _cht;
|
||||
return someProtoView;
|
||||
});
|
||||
compiler.compileInHost(SomeComponent)
|
||||
.then((_) => {
|
||||
var beginComponentCmd = <BeginComponentCmd>cht.template.commands[0];
|
||||
expect(beginComponentCmd.name).toEqual('some-comp');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should cache the result', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper
|
||||
.all([compiler.compileInHost(SomeComponent), compiler.compileInHost(SomeComponent)])
|
||||
.then((protoViewRefs) => {
|
||||
expect(protoViewRefs[0]).toBe(protoViewRefs[1]);
|
||||
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',
|
||||
inject([AsyncTestCompleter], (async) => {compiler.compileInHost(SomeComponent)
|
||||
.then((protoViewRef1) => {
|
||||
compiler.clearCache();
|
||||
compiler.compileInHost(SomeComponent)
|
||||
.then((protoViewRef2) => {
|
||||
expect(protoViewRef1)
|
||||
.not.toBe(protoViewRef2);
|
||||
async.done();
|
||||
});
|
||||
})}));
|
||||
|
||||
it('should clear the cache', () => {
|
||||
compiler.clearCache();
|
||||
expect(templateCompilerSpy.spy('clearCache')).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export function main() {
|
||||
describe('getMetadata', () => {
|
||||
it('should read metadata',
|
||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||
var meta = resolver.getMetadata(ComponentWithEverything);
|
||||
var meta = resolver.getDirectiveMetadata(ComponentWithEverything);
|
||||
expect(meta.selector).toEqual('someSelector');
|
||||
expect(meta.exportAs).toEqual('someExportAs');
|
||||
expect(meta.isComponent).toBe(true);
|
||||
@@ -70,7 +70,8 @@ export function main() {
|
||||
|
||||
it('should use the moduleUrl from the reflector if none is given',
|
||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||
var value: string = resolver.getMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
||||
var value: string =
|
||||
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
||||
var expectedEndValue =
|
||||
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec.dart' : './';
|
||||
expect(value.endsWith(expectedEndValue)).toBe(true);
|
||||
@@ -82,7 +83,7 @@ export function main() {
|
||||
it('should return the directive metadatas',
|
||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
||||
.toEqual([resolver.getMetadata(SomeDirective)]);
|
||||
.toEqual([resolver.getDirectiveMetadata(SomeDirective)]);
|
||||
}));
|
||||
|
||||
describe("platform directives", () => {
|
||||
@@ -91,7 +92,10 @@ export function main() {
|
||||
it('should include platform directives when available',
|
||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
||||
.toEqual([resolver.getMetadata(ADirective), resolver.getMetadata(SomeDirective)]);
|
||||
.toEqual([
|
||||
resolver.getDirectiveMetadata(ADirective),
|
||||
resolver.getDirectiveMetadata(SomeDirective)
|
||||
]);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
library core.spies;
|
||||
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 {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyTemplateCompiler extends SpyObject implements TemplateCompiler {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
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); }
|
||||
}
|
||||
@@ -15,7 +15,12 @@ import {
|
||||
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {Type, isPresent, isBlank, stringify, isString} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, SetWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
MapWrapper,
|
||||
SetWrapper,
|
||||
ListWrapper,
|
||||
StringMapWrapper
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||
import {
|
||||
TemplateCompiler,
|
||||
@@ -26,31 +31,31 @@ 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 {Locals} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {
|
||||
CommandVisitor,
|
||||
TextCmd,
|
||||
NgContentCmd,
|
||||
BeginElementCmd,
|
||||
BeginComponentCmd,
|
||||
EmbeddedTemplateCmd,
|
||||
TemplateCmd,
|
||||
visitAllCommands,
|
||||
CompiledComponentTemplate
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
|
||||
import {Component, View, Directive, provide} from 'angular2/core';
|
||||
import {Component, View, Directive, provide, RenderComponentType} from 'angular2/core';
|
||||
|
||||
import {TEST_PROVIDERS} from './test_bindings';
|
||||
import {TestDispatcher, TestPipes} from './change_detector_mocks';
|
||||
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
|
||||
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() {
|
||||
describe('TemplateCompiler', () => {
|
||||
@@ -77,23 +82,24 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should compile host components', inject([AsyncTestCompleter], (async) => {
|
||||
compile([CompWithBindingsAndStyles])
|
||||
.then((humanizedTemplate) => {
|
||||
expect(humanizedTemplate['styles']).toEqual([]);
|
||||
expect(humanizedTemplate['commands'][0]).toEqual('<comp-a>');
|
||||
expect(humanizedTemplate['cd']).toEqual(['elementProperty(title)=someDirValue']);
|
||||
|
||||
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([CompWithBindingsAndStyles])
|
||||
.then((humanizedTemplate) => {
|
||||
var nestedTemplate = humanizedTemplate['commands'][1];
|
||||
expect(nestedTemplate['styles']).toEqual(['div {color: red}']);
|
||||
expect(nestedTemplate['commands'][0]).toEqual('<a>');
|
||||
expect(nestedTemplate['cd']).toEqual(['elementProperty(href)=someCtxValue']);
|
||||
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();
|
||||
});
|
||||
@@ -101,23 +107,24 @@ export function main() {
|
||||
|
||||
it('should compile recursive components', inject([AsyncTestCompleter], (async) => {
|
||||
compile([TreeComp])
|
||||
.then((humanizedTemplate) => {
|
||||
expect(humanizedTemplate['commands'][0]).toEqual('<tree>');
|
||||
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<tree>');
|
||||
expect(humanizedTemplate['commands'][1]['commands'][1]['commands'][0])
|
||||
.toEqual('<tree>');
|
||||
.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 pass the right change detector to embedded templates',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
it('should compile embedded templates', inject([AsyncTestCompleter], (async) => {
|
||||
compile([CompWithEmbeddedTemplate])
|
||||
.then((humanizedTemplate) => {
|
||||
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<template>');
|
||||
expect(humanizedTemplate['commands'][1]['commands'][1]['cd'])
|
||||
.toEqual(['elementProperty(href)=someCtxValue']);
|
||||
.then((humanizedView) => {
|
||||
var embeddedView = humanizedView['componentViews'][0]['embeddedViews'][0];
|
||||
expect(embeddedView['elements']).toEqual(['<a>']);
|
||||
expect(embeddedView['cd']).toEqual(['prop(href)=someEmbeddedValue']);
|
||||
|
||||
async.done();
|
||||
});
|
||||
@@ -125,8 +132,8 @@ export function main() {
|
||||
|
||||
it('should dedup directives', inject([AsyncTestCompleter], (async) => {
|
||||
compile([CompWithDupDirectives, TreeComp])
|
||||
.then((humanizedTemplate) => {
|
||||
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual("<tree>");
|
||||
.then((humanizedView) => {
|
||||
expect(humanizedView['componentViews'][0]['componentViews'].length).toBe(1);
|
||||
async.done();
|
||||
|
||||
});
|
||||
@@ -136,18 +143,34 @@ export function main() {
|
||||
describe('compileHostComponentRuntime', () => {
|
||||
function compile(components: Type[]): Promise<any[]> {
|
||||
return compiler.compileHostComponentRuntime(components[0])
|
||||
.then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.template));
|
||||
.then((compiledHostTemplate) =>
|
||||
humanizeViewFactory(compiledHostTemplate.viewFactory));
|
||||
}
|
||||
|
||||
runTests(compile);
|
||||
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) => {
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
|
||||
// Expecting only one xhr...
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', '<a></a>');
|
||||
PromiseWrapper.all([compile([CompWithTemplateUrl]), compile([CompWithTemplateUrl])])
|
||||
.then((humanizedTemplates) => {
|
||||
expect(humanizedTemplates[0]['commands'][1]['commands']).toEqual(['#text(a)']);
|
||||
expect(humanizedTemplates[1]['commands'][1]['commands']).toEqual(['#text(a)']);
|
||||
.then((humanizedViews) => {
|
||||
expect(humanizedViews[0]['componentViews'][0]['elements']).toEqual(['<a>']);
|
||||
expect(humanizedViews[1]['componentViews'][0]['elements']).toEqual(['<a>']);
|
||||
|
||||
async.done();
|
||||
});
|
||||
@@ -156,15 +179,14 @@ export function main() {
|
||||
|
||||
it('should cache components for sequential requests',
|
||||
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
|
||||
// Expecting only one xhr...
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', '<a>');
|
||||
compile([CompWithTemplateUrl])
|
||||
.then((humanizedTemplate0) => {
|
||||
.then((humanizedView0) => {
|
||||
return compile([CompWithTemplateUrl])
|
||||
.then((humanizedTemplate1) => {
|
||||
expect(humanizedTemplate0['commands'][1]['commands'])
|
||||
.toEqual(['#text(a)']);
|
||||
expect(humanizedTemplate1['commands'][1]['commands'])
|
||||
.toEqual(['#text(a)']);
|
||||
.then((humanizedView1) => {
|
||||
expect(humanizedView0['componentViews'][0]['elements']).toEqual(['<a>']);
|
||||
expect(humanizedView1['componentViews'][0]['elements']).toEqual(['<a>']);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
@@ -173,17 +195,17 @@ export function main() {
|
||||
|
||||
it('should allow to clear the cache',
|
||||
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', '<a>');
|
||||
compile([CompWithTemplateUrl])
|
||||
.then((humanizedTemplate) => {
|
||||
.then((humanizedView) => {
|
||||
compiler.clearCache();
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', 'b');
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', '<b>');
|
||||
var result = compile([CompWithTemplateUrl]);
|
||||
xhr.flush();
|
||||
return result;
|
||||
})
|
||||
.then((humanizedTemplate) => {
|
||||
expect(humanizedTemplate['commands'][1]['commands']).toEqual(['#text(b)']);
|
||||
.then((humanizedView) => {
|
||||
expect(humanizedView['componentViews'][0]['elements']).toEqual(['<b>']);
|
||||
async.done();
|
||||
});
|
||||
xhr.flush();
|
||||
@@ -193,14 +215,17 @@ export function main() {
|
||||
describe('compileTemplatesCodeGen', () => {
|
||||
function normalizeComponent(
|
||||
component: Type): Promise<NormalizedComponentWithViewDirectives> {
|
||||
var compAndViewDirMetas = [runtimeMetadataResolver.getMetadata(component)].concat(
|
||||
runtimeMetadataResolver.getViewDirectivesMetadata(component));
|
||||
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)));
|
||||
normalizedCompAndViewDirMetas.slice(1), [upperCasePipeMeta]));
|
||||
}
|
||||
|
||||
function compile(components: Type[]): Promise<any[]> {
|
||||
@@ -223,7 +248,7 @@ export function main() {
|
||||
describe('normalizeDirectiveMetadata', () => {
|
||||
it('should return the given DirectiveMetadata for non components',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var meta = runtimeMetadataResolver.getMetadata(NonComponent);
|
||||
var meta = runtimeMetadataResolver.getDirectiveMetadata(NonComponent);
|
||||
compiler.normalizeDirectiveMetadata(meta).then(normMeta => {
|
||||
expect(normMeta).toBe(meta);
|
||||
async.done();
|
||||
@@ -234,7 +259,7 @@ export function main() {
|
||||
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
|
||||
xhr.expect('package:angular2/test/compiler/compUrl.html', 'loadedTemplate');
|
||||
compiler.normalizeDirectiveMetadata(
|
||||
runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
|
||||
runtimeMetadataResolver.getDirectiveMetadata(CompWithTemplateUrl))
|
||||
.then((meta: CompileDirectiveMetadata) => {
|
||||
expect(meta.template.template).toEqual('loadedTemplate');
|
||||
async.done();
|
||||
@@ -243,7 +268,8 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
|
||||
var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles);
|
||||
var meta =
|
||||
runtimeMetadataResolver.getDirectiveMetadata(CompWithBindingsAndStylesAndPipes);
|
||||
compiler.normalizeDirectiveMetadata(meta).then((normMeta: CompileDirectiveMetadata) => {
|
||||
expect(normMeta.type).toEqual(meta.type);
|
||||
expect(normMeta.isComponent).toEqual(meta.isComponent);
|
||||
@@ -279,23 +305,35 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@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]': 'someProp'},
|
||||
host: {'[title]': '\'someHostValue\''},
|
||||
moduleId: THIS_MODULE_ID,
|
||||
exportAs: 'someExportAs'
|
||||
})
|
||||
@View({
|
||||
template: '<a [href]="someProp"></a>',
|
||||
template: '<a [href]="\'someCtxValue\' | uppercase"></a>',
|
||||
styles: ['div {color: red}'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
pipes: [UpperCasePipe]
|
||||
})
|
||||
class CompWithBindingsAndStyles {
|
||||
export class CompWithBindingsAndStylesAndPipes {
|
||||
}
|
||||
|
||||
@Component({selector: 'tree', moduleId: THIS_MODULE_ID})
|
||||
@View({template: '<tree></tree>', directives: [TreeComp], encapsulation: ViewEncapsulation.None})
|
||||
class TreeComp {
|
||||
@View({
|
||||
template: '<template><tree></tree></template>',
|
||||
directives: [TreeComp],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class TreeComp {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp-wit-dup-tpl', moduleId: THIS_MODULE_ID})
|
||||
@@ -304,34 +342,37 @@ class TreeComp {
|
||||
directives: [TreeComp, TreeComp],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
class CompWithDupDirectives {
|
||||
export class CompWithDupDirectives {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp-url', moduleId: THIS_MODULE_ID})
|
||||
@View({templateUrl: 'compUrl.html', encapsulation: ViewEncapsulation.None})
|
||||
class CompWithTemplateUrl {
|
||||
export class CompWithTemplateUrl {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp-tpl', moduleId: THIS_MODULE_ID})
|
||||
@View({
|
||||
template: '<template><a [href]="someProp"></a></template>',
|
||||
template: '<template><a [href]="\'someEmbeddedValue\'"></a></template>',
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
class CompWithEmbeddedTemplate {
|
||||
export class CompWithEmbeddedTemplate {
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'plain'})
|
||||
@View({template: ''})
|
||||
class NonComponent {
|
||||
export class NonComponent {
|
||||
}
|
||||
|
||||
|
||||
function testableTemplateModule(sourceModule: SourceModule,
|
||||
normComp: CompileDirectiveMetadata): SourceModule {
|
||||
var resultExpression =
|
||||
`${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template.template)`;
|
||||
var testableSource = `${sourceModule.sourceWithModuleRefs}
|
||||
${codeGenValueFn(['_'], resultExpression, '_run')};
|
||||
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);
|
||||
}
|
||||
@@ -343,75 +384,71 @@ function testableStylesModule(sourceModule: SourceModule): SourceModule {
|
||||
return new SourceModule(sourceModule.moduleUrl, testableSource);
|
||||
}
|
||||
|
||||
// Attention: read by eval!
|
||||
export function humanizeTemplate(
|
||||
template: CompiledComponentTemplate,
|
||||
humanizedTemplates: Map<string, {[key: string]: any}> = null): {[key: string]: any} {
|
||||
if (isBlank(humanizedTemplates)) {
|
||||
humanizedTemplates = new Map<string, {[key: string]: any}>();
|
||||
}
|
||||
var result = humanizedTemplates.get(template.id);
|
||||
function humanizeView(view: AppView, cachedResults: Map<AppProtoView, any>): {[key: string]: any} {
|
||||
var result = cachedResults.get(view.proto);
|
||||
if (isPresent(result)) {
|
||||
return result;
|
||||
}
|
||||
var commands = [];
|
||||
result = {
|
||||
'styles': template.styles,
|
||||
'commands': commands,
|
||||
'cd': testChangeDetector(template.changeDetectorFactory)
|
||||
};
|
||||
humanizedTemplates.set(template.id, result);
|
||||
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), template.commands);
|
||||
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;
|
||||
}
|
||||
|
||||
class TestContext implements CompWithBindingsAndStyles, TreeComp, CompWithTemplateUrl,
|
||||
CompWithEmbeddedTemplate, CompWithDupDirectives {
|
||||
someProp: string;
|
||||
// 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[] = [];
|
||||
|
||||
function testChangeDetector(changeDetectorFactory: Function): string[] {
|
||||
var ctx = new TestContext();
|
||||
ctx.someProp = 'someCtxValue';
|
||||
var dir1 = new TestContext();
|
||||
dir1.someProp = 'someDirValue';
|
||||
|
||||
var dispatcher = new TestDispatcher([dir1], []);
|
||||
var cd = changeDetectorFactory(dispatcher);
|
||||
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
|
||||
cd.detectChanges();
|
||||
return dispatcher.log;
|
||||
}
|
||||
|
||||
|
||||
class CommandHumanizer implements CommandVisitor {
|
||||
constructor(private result: any[],
|
||||
private humanizedTemplates: Map<string, {[key: string]: any}>) {}
|
||||
visitText(cmd: TextCmd, context: any): any {
|
||||
this.result.push(`#text(${cmd.value})`);
|
||||
return null;
|
||||
}
|
||||
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
|
||||
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
||||
this.result.push(`<${cmd.name}>`);
|
||||
return null;
|
||||
}
|
||||
visitEndElement(context: any): any {
|
||||
this.result.push('</>');
|
||||
return null;
|
||||
}
|
||||
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
|
||||
this.result.push(`<${cmd.name}>`);
|
||||
this.result.push(humanizeTemplate(cmd.templateGetter(), this.humanizedTemplates));
|
||||
return null;
|
||||
}
|
||||
visitEndComponent(context: any): any { return this.visitEndElement(context); }
|
||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
|
||||
this.result.push(`<template>`);
|
||||
this.result.push({'cd': testChangeDetector(cmd.changeDetectorFactory)});
|
||||
this.result.push(`</template>`);
|
||||
return null;
|
||||
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}>`); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from 'angular2/src/compiler/template_parser';
|
||||
import {
|
||||
CompileDirectiveMetadata,
|
||||
CompilePipeMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
@@ -69,8 +70,12 @@ export function main() {
|
||||
{selector: '[ngIf]', type: new CompileTypeMetadata({name: 'NgIf'}), inputs: ['ngIf']});
|
||||
}));
|
||||
|
||||
function parse(template: string, directives: CompileDirectiveMetadata[]): TemplateAst[] {
|
||||
return parser.parse(template, directives, 'TestComp');
|
||||
function parse(template: string, directives: CompileDirectiveMetadata[],
|
||||
pipes: CompilePipeMetadata[] = null): TemplateAst[] {
|
||||
if (pipes === null) {
|
||||
pipes = [];
|
||||
}
|
||||
return parser.parse(template, directives, pipes, 'TestComp');
|
||||
}
|
||||
|
||||
describe('template transform', () => {
|
||||
@@ -938,15 +943,29 @@ 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']});
|
||||
expect(humanizeTplAstSourceSpans(parse('<div [aProp]="foo | bar"></div>', [dirA])))
|
||||
expect(humanizeTplAstSourceSpans(parse('<div [aProp]="foo"></div>', [dirA])))
|
||||
.toEqual([
|
||||
[ElementAst, 'div', '<div [aProp]="foo | bar">'],
|
||||
[DirectiveAst, dirA, '<div [aProp]="foo | bar">'],
|
||||
[BoundDirectivePropertyAst, 'aProp', '(foo | bar)', '[aProp]="foo | bar"']
|
||||
[ElementAst, 'div', '<div [aProp]="foo">'],
|
||||
[DirectiveAst, dirA, '<div [aProp]="foo">'],
|
||||
[BoundDirectivePropertyAst, 'aProp', 'foo', '[aProp]="foo"']
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('pipes', () => {
|
||||
it('should allow pipes that have been defined as dependencies', () => {
|
||||
var testPipe =
|
||||
new CompilePipeMetadata({name: 'test', type: new CompileTypeMetadata({name: 'DirA'})});
|
||||
expect(() => parse('{{a | test}}', [], [testPipe])).not.toThrow();
|
||||
});
|
||||
|
||||
it('should report pipes as error that have not been defined as dependencies', () => {
|
||||
expect(() => parse('{{a | test}}', [])).toThrowError(`Template parse errors:
|
||||
The pipe 'test' could not be found ("[ERROR ->]{{a | test}}"): TestComp@0:0`);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -101,8 +101,9 @@ export function getDefinition(id: string): TestDefinition {
|
||||
|
||||
} else if (ListWrapper.indexOf(_availableHostEventDefinitions, id) >= 0) {
|
||||
var eventRecords = _createHostEventRecords(id, _DirectiveUpdating.basicRecords[0]);
|
||||
let cdDef = new ChangeDetectorDefinition(id, null, [], [], eventRecords,
|
||||
[_DirectiveUpdating.basicRecords[0]], genConfig);
|
||||
let cdDef = new ChangeDetectorDefinition(
|
||||
id, null, [], [], eventRecords,
|
||||
[_DirectiveUpdating.basicRecords[0], _DirectiveUpdating.basicRecords[1]], genConfig);
|
||||
testDef = new TestDefinition(id, cdDef, null);
|
||||
|
||||
} else if (id == "onPushObserveBinding") {
|
||||
@@ -286,7 +287,9 @@ class _DirectiveUpdating {
|
||||
callAfterContentInit: true,
|
||||
callAfterContentChecked: true,
|
||||
callAfterViewInit: true,
|
||||
callAfterViewChecked: true
|
||||
callAfterViewChecked: true,
|
||||
callOnDestroy: true,
|
||||
outputs: [['eventEmitter', 'host-event']]
|
||||
}),
|
||||
new DirectiveRecord({
|
||||
directiveIndex: new DirectiveIndex(0, 1),
|
||||
@@ -296,7 +299,9 @@ class _DirectiveUpdating {
|
||||
callAfterContentInit: true,
|
||||
callAfterContentChecked: true,
|
||||
callAfterViewInit: true,
|
||||
callAfterViewChecked: true
|
||||
callAfterViewChecked: true,
|
||||
callOnDestroy: true,
|
||||
outputs: [['eventEmitter', 'host-event']]
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
@@ -28,4 +28,4 @@ export function main() {
|
||||
expect(changeDetector.spy('detectChanges')).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
|
||||
import {
|
||||
ChangeDispatcher,
|
||||
@@ -53,6 +54,7 @@ import {getDefinition} from './change_detector_config';
|
||||
import {createObservableModel} from './change_detector_spec_util';
|
||||
import {getFactoryById} from './generated/change_detector_classes';
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
const _DEFAULT_CONTEXT = CONST_EXPR(new Object());
|
||||
|
||||
@@ -79,10 +81,10 @@ export function main() {
|
||||
switch (cdType) {
|
||||
case 'dynamic':
|
||||
var dynProto = new DynamicProtoChangeDetector(def);
|
||||
return (dispatcher) => dynProto.instantiate(dispatcher);
|
||||
return () => dynProto.instantiate();
|
||||
case 'JIT':
|
||||
var jitProto = new JitProtoChangeDetector(def);
|
||||
return (dispatcher) => jitProto.instantiate(dispatcher);
|
||||
return () => jitProto.instantiate();
|
||||
case 'Pregen':
|
||||
return getFactoryById(def.id);
|
||||
default:
|
||||
@@ -92,7 +94,7 @@ export function main() {
|
||||
|
||||
function _createWithoutHydrate(expression: string) {
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = _getChangeDetectorFactory(getDefinition(expression).cdDef)(dispatcher);
|
||||
var cd = _getChangeDetectorFactory(getDefinition(expression).cdDef)();
|
||||
return new _ChangeDetectorAndDispatcher(cd, dispatcher);
|
||||
}
|
||||
|
||||
@@ -101,8 +103,8 @@ export function main() {
|
||||
registry = null, dispatcher = null) {
|
||||
if (isBlank(dispatcher)) dispatcher = new TestDispatcher();
|
||||
var testDef = getDefinition(expression);
|
||||
var cd = _getChangeDetectorFactory(testDef.cdDef)(dispatcher);
|
||||
cd.hydrate(context, testDef.locals, null, registry);
|
||||
var cd = _getChangeDetectorFactory(testDef.cdDef)();
|
||||
cd.hydrate(context, testDef.locals, dispatcher, registry);
|
||||
return new _ChangeDetectorAndDispatcher(cd, dispatcher);
|
||||
}
|
||||
|
||||
@@ -361,7 +363,6 @@ export function main() {
|
||||
|
||||
it('should support interpolation', () => {
|
||||
var val = _createChangeDetector('interpolation', new TestData('value'));
|
||||
val.changeDetector.hydrate(new TestData('value'), null, null, null);
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
|
||||
@@ -369,8 +370,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should output empty strings for null values in interpolation', () => {
|
||||
var val = _createChangeDetector('interpolation', new TestData('value'));
|
||||
val.changeDetector.hydrate(new TestData(null), null, null, null);
|
||||
var val = _createChangeDetector('interpolation', new TestData(null));
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
|
||||
@@ -490,7 +490,7 @@ export function main() {
|
||||
|
||||
it('should happen directly, without invoking the dispatcher', () => {
|
||||
var val = _createWithoutHydrate('directNoDispatcher');
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []),
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []),
|
||||
null);
|
||||
val.changeDetector.detectChanges();
|
||||
expect(val.dispatcher.loggedValues).toEqual([]);
|
||||
@@ -501,7 +501,7 @@ export function main() {
|
||||
describe('ngOnChanges', () => {
|
||||
it('should notify the directive when a group of records changes', () => {
|
||||
var cd = _createWithoutHydrate('groupChanges').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1, directive2], []),
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1, directive2], []),
|
||||
null);
|
||||
cd.detectChanges();
|
||||
expect(directive1.changes).toEqual({'a': 1, 'b': 2});
|
||||
@@ -513,7 +513,7 @@ export function main() {
|
||||
it('should notify the directive when it is checked', () => {
|
||||
var cd = _createWithoutHydrate('directiveDoCheck').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
cd.detectChanges();
|
||||
|
||||
expect(directive1.ngDoCheckCalled).toBe(true);
|
||||
@@ -526,7 +526,7 @@ export function main() {
|
||||
it('should not call ngDoCheck in detectNoChanges', () => {
|
||||
var cd = _createWithoutHydrate('directiveDoCheck').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
|
||||
cd.checkNoChanges();
|
||||
|
||||
@@ -538,7 +538,7 @@ export function main() {
|
||||
it('should notify the directive after it has been checked the first time', () => {
|
||||
var cd = _createWithoutHydrate('directiveOnInit').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1, directive2], []),
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1, directive2], []),
|
||||
null);
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -555,7 +555,7 @@ export function main() {
|
||||
it('should not call ngOnInit in detectNoChanges', () => {
|
||||
var cd = _createWithoutHydrate('directiveOnInit').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
|
||||
cd.checkNoChanges();
|
||||
|
||||
@@ -565,7 +565,7 @@ export function main() {
|
||||
it('should not call ngOnInit again if it throws', () => {
|
||||
var cd = _createWithoutHydrate('directiveOnInit').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive3], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive3], []), null);
|
||||
var errored = false;
|
||||
// First pass fails, but ngOnInit should be called.
|
||||
try {
|
||||
@@ -590,7 +590,7 @@ export function main() {
|
||||
describe('ngAfterContentInit', () => {
|
||||
it('should be called after processing the content children', () => {
|
||||
var cd = _createWithoutHydrate('emptyWithDirectiveRecords').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1, directive2], []),
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1, directive2], []),
|
||||
null);
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -618,7 +618,7 @@ export function main() {
|
||||
it('should not be called when ngAfterContentInit is false', () => {
|
||||
var cd = _createWithoutHydrate('noCallbacks').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@@ -629,7 +629,7 @@ export function main() {
|
||||
describe('ngAfterContentChecked', () => {
|
||||
it('should be called after processing all the children', () => {
|
||||
var cd = _createWithoutHydrate('emptyWithDirectiveRecords').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1, directive2], []),
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1, directive2], []),
|
||||
null);
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -657,7 +657,7 @@ export function main() {
|
||||
it('should not be called when ngAfterContentChecked is false', () => {
|
||||
var cd = _createWithoutHydrate('noCallbacks').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@@ -673,7 +673,7 @@ export function main() {
|
||||
td1 = new TestDirective(() => ngOnChangesDoneCalls.push(td1));
|
||||
var td2;
|
||||
td2 = new TestDirective(() => ngOnChangesDoneCalls.push(td2));
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([td1, td2], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([td1, td2], []), null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@@ -694,10 +694,10 @@ export function main() {
|
||||
parentDirective =
|
||||
new TestDirective(() => { orderOfOperations.push(parentDirective); });
|
||||
|
||||
parent.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([parentDirective], []),
|
||||
parent.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([parentDirective], []),
|
||||
null);
|
||||
child.hydrate(_DEFAULT_CONTEXT, null,
|
||||
new FakeDirectives([directiveInShadowDom], []), null);
|
||||
new TestDispatcher([directiveInShadowDom], []), null);
|
||||
|
||||
parent.detectChanges();
|
||||
expect(orderOfOperations).toEqual([parentDirective, directiveInShadowDom]);
|
||||
@@ -708,7 +708,7 @@ export function main() {
|
||||
describe('ngAfterViewInit', () => {
|
||||
it('should be called after processing the view children', () => {
|
||||
var cd = _createWithoutHydrate('emptyWithDirectiveRecords').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1, directive2], []),
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1, directive2], []),
|
||||
null);
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -737,7 +737,7 @@ export function main() {
|
||||
it('should not be called when ngAfterViewInit is false', () => {
|
||||
var cd = _createWithoutHydrate('noCallbacks').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@@ -748,7 +748,7 @@ export function main() {
|
||||
describe('ngAfterViewChecked', () => {
|
||||
it('should be called after processing the view children', () => {
|
||||
var cd = _createWithoutHydrate('emptyWithDirectiveRecords').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1, directive2], []),
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1, directive2], []),
|
||||
null);
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -776,7 +776,7 @@ export function main() {
|
||||
it('should not be called when ngAfterViewChecked is false', () => {
|
||||
var cd = _createWithoutHydrate('noCallbacks').changeDetector;
|
||||
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive1], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([directive1], []), null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@@ -792,7 +792,7 @@ export function main() {
|
||||
td1 = new TestDirective(null, () => ngOnChangesDoneCalls.push(td1));
|
||||
var td2;
|
||||
td2 = new TestDirective(null, () => ngOnChangesDoneCalls.push(td2));
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([td1, td2], []), null);
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([td1, td2], []), null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@@ -813,15 +813,29 @@ export function main() {
|
||||
parentDirective =
|
||||
new TestDirective(null, () => { orderOfOperations.push(parentDirective); });
|
||||
|
||||
parent.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([parentDirective], []),
|
||||
parent.hydrate(_DEFAULT_CONTEXT, null, new TestDispatcher([parentDirective], []),
|
||||
null);
|
||||
child.hydrate(_DEFAULT_CONTEXT, null,
|
||||
new FakeDirectives([directiveInShadowDom], []), null);
|
||||
new TestDispatcher([directiveInShadowDom], []), null);
|
||||
|
||||
parent.detectChanges();
|
||||
expect(orderOfOperations).toEqual([directiveInShadowDom, parentDirective]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngOnDestroy', () => {
|
||||
it('should be called on dehydration', () => {
|
||||
var cd = _createChangeDetector('emptyWithDirectiveRecords', _DEFAULT_CONTEXT, null,
|
||||
new TestDispatcher([directive1, directive2], []))
|
||||
.changeDetector;
|
||||
|
||||
cd.dehydrate();
|
||||
|
||||
expect(directive1.destroyCalled).toBe(true);
|
||||
expect(directive2.destroyCalled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -836,9 +850,8 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should be called for directive updates in the dev mode', () => {
|
||||
var val = _createWithoutHydrate('directNoDispatcher');
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null,
|
||||
new FakeDirectives([new TestDirective()], []), null);
|
||||
var val = _createChangeDetector('directNoDispatcher', _DEFAULT_CONTEXT, null,
|
||||
new TestDispatcher([new TestDirective()], []));
|
||||
val.changeDetector.detectChanges();
|
||||
expect(val.dispatcher.debugLog).toEqual(["a=42"]);
|
||||
});
|
||||
@@ -857,9 +870,8 @@ export function main() {
|
||||
var directive = new TestDirective();
|
||||
directive.a = 'aaa';
|
||||
|
||||
var val = _createWithoutHydrate('readingDirectives');
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([directive], []),
|
||||
null);
|
||||
var val = _createChangeDetector('readingDirectives', _DEFAULT_CONTEXT, null,
|
||||
new TestDispatcher([directive], []));
|
||||
|
||||
val.changeDetector.detectChanges();
|
||||
|
||||
@@ -997,7 +1009,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should not check a detached change detector', () => {
|
||||
var val = _createChangeDetector('a', new TestData('value'));
|
||||
var val = _createChangeDetector('a', _DEFAULT_CONTEXT);
|
||||
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, null, null);
|
||||
val.changeDetector.mode = ChangeDetectionStrategy.Detached;
|
||||
@@ -1017,8 +1029,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should change CheckOnce to Checked', () => {
|
||||
var cd = _createChangeDetector('10').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, null, null);
|
||||
var cd = _createChangeDetector('10', _DEFAULT_CONTEXT).changeDetector;
|
||||
cd.mode = ChangeDetectionStrategy.CheckOnce;
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -1027,8 +1038,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should not change the CheckAlways', () => {
|
||||
var cd = _createChangeDetector('10').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, null, null);
|
||||
var cd = _createChangeDetector('10', _DEFAULT_CONTEXT).changeDetector;
|
||||
cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||
|
||||
cd.detectChanges();
|
||||
@@ -1052,7 +1062,7 @@ export function main() {
|
||||
childDirectiveDetectorOnPush.mode = ChangeDetectionStrategy.Checked;
|
||||
|
||||
directives =
|
||||
new FakeDirectives([new TestData(null), new TestData(null)],
|
||||
new TestDispatcher([new TestData(null), new TestData(null)],
|
||||
[childDirectiveDetectorRegular, childDirectiveDetectorOnPush]);
|
||||
});
|
||||
|
||||
@@ -1126,7 +1136,7 @@ export function main() {
|
||||
it('should mark OnPushObserve detectors as CheckOnce when an observable directive fires an event',
|
||||
fakeAsync(() => {
|
||||
var dir = createObservableModel();
|
||||
var directives = new FakeDirectives([dir], []);
|
||||
var directives = new TestDispatcher([dir], []);
|
||||
|
||||
var cd = _createWithoutHydrate('onPushObserveDirective').changeDetector;
|
||||
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
|
||||
@@ -1290,30 +1300,31 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('handleEvent', () => {
|
||||
var locals;
|
||||
var event;
|
||||
var d: TestDirective;
|
||||
|
||||
beforeEach(() => {
|
||||
locals = new Locals(null, MapWrapper.createFromStringMap({"$event": "EVENT"}));
|
||||
event = "EVENT";
|
||||
d = new TestDirective();
|
||||
});
|
||||
|
||||
it('should execute events', () => {
|
||||
var val = _createChangeDetector('(event)="onEvent($event)"', d, null);
|
||||
val.changeDetector.handleEvent("event", 0, locals);
|
||||
val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(d.event).toEqual("EVENT");
|
||||
});
|
||||
|
||||
it('should execute host events', () => {
|
||||
var val = _createWithoutHydrate('(host-event)="onEvent($event)"');
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null, new FakeDirectives([d], []), null);
|
||||
val.changeDetector.handleEvent("host-event", 0, locals);
|
||||
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null,
|
||||
new TestDispatcher([d, new TestDirective()], []), null);
|
||||
val.changeDetector.handleEvent("host-event", 0, event);
|
||||
expect(d.event).toEqual("EVENT");
|
||||
});
|
||||
|
||||
it('should support field assignments', () => {
|
||||
var val = _createChangeDetector('(event)="b=a=$event"', d, null);
|
||||
val.changeDetector.handleEvent("event", 0, locals);
|
||||
val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(d.a).toEqual("EVENT");
|
||||
expect(d.b).toEqual("EVENT");
|
||||
});
|
||||
@@ -1321,14 +1332,14 @@ export function main() {
|
||||
it('should support keyed assignments', () => {
|
||||
d.a = ["OLD"];
|
||||
var val = _createChangeDetector('(event)="a[0]=$event"', d, null);
|
||||
val.changeDetector.handleEvent("event", 0, locals);
|
||||
val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(d.a).toEqual(["EVENT"]);
|
||||
});
|
||||
|
||||
it('should support chains', () => {
|
||||
d.a = 0;
|
||||
var val = _createChangeDetector('(event)="a=a+1; a=a+1;"', d, null);
|
||||
val.changeDetector.handleEvent("event", 0, locals);
|
||||
val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(d.a).toEqual(2);
|
||||
});
|
||||
|
||||
@@ -1339,23 +1350,83 @@ export function main() {
|
||||
// }).toThrowError(new RegExp("Cannot reassign a variable binding"));
|
||||
// });
|
||||
|
||||
it('should return the prevent default value', () => {
|
||||
it('should return false if the event handler returned false', () => {
|
||||
var val = _createChangeDetector('(event)="false"', d, null);
|
||||
var res = val.changeDetector.handleEvent("event", 0, locals);
|
||||
expect(res).toBe(true);
|
||||
var res = val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(res).toBe(false);
|
||||
|
||||
val = _createChangeDetector('(event)="true"', d, null);
|
||||
res = val.changeDetector.handleEvent("event", 0, locals);
|
||||
expect(res).toBe(false);
|
||||
res = val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(res).toBe(true);
|
||||
});
|
||||
|
||||
it('should support short-circuiting', () => {
|
||||
d.a = 0;
|
||||
var val = _createChangeDetector('(event)="true ? a = a + 1 : a = a + 1"', d, null);
|
||||
val.changeDetector.handleEvent("event", 0, locals);
|
||||
val.changeDetector.handleEvent("event", 0, event);
|
||||
expect(d.a).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
if (DOM.supportsDOMEvents()) {
|
||||
describe('subscribe to EventEmitters', () => {
|
||||
it('should call handleEvent when an output of a directive fires', fakeAsync(() => {
|
||||
var directive1 = new TestDirective();
|
||||
var directive2 = new TestDirective();
|
||||
_createChangeDetector('(host-event)="onEvent(\$event)"', new Object(), null,
|
||||
new TestDispatcher([directive1, directive2]));
|
||||
ObservableWrapper.callEmit(directive2.eventEmitter, 'EVENT');
|
||||
|
||||
tick();
|
||||
|
||||
expect(directive1.event).toEqual('EVENT');
|
||||
}));
|
||||
|
||||
it('should ignore events when dehydrated', fakeAsync(() => {
|
||||
var directive1 = new TestDirective();
|
||||
var directive2 = new TestDirective();
|
||||
var cd = _createChangeDetector('(host-event)="onEvent(\$event)"', new Object(), null,
|
||||
new TestDispatcher([directive1, directive2]))
|
||||
.changeDetector;
|
||||
cd.dehydrate();
|
||||
ObservableWrapper.callEmit(directive2.eventEmitter, 'EVENT');
|
||||
|
||||
tick();
|
||||
|
||||
expect(directive1.event).toBeFalsy();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
describe('destroyRecursive', () => {
|
||||
var parent, child;
|
||||
var parentDispatcher, childDispatcher;
|
||||
|
||||
beforeEach(() => {
|
||||
parentDispatcher = new TestDispatcher();
|
||||
parent = _createChangeDetector('10', null, null, parentDispatcher).changeDetector;
|
||||
childDispatcher = new TestDispatcher();
|
||||
child = _createChangeDetector('"str"', null, null, childDispatcher).changeDetector;
|
||||
parent.addContentChild(child);
|
||||
});
|
||||
|
||||
it('should notify the dispatcher', () => {
|
||||
child.destroyRecursive();
|
||||
expect(childDispatcher.ngOnDestroyCalled).toBe(true);
|
||||
});
|
||||
|
||||
it('should dehydrate the change detector', () => {
|
||||
child.destroyRecursive();
|
||||
expect(child.hydrated()).toBe(false);
|
||||
});
|
||||
|
||||
it('should destroy children', () => {
|
||||
parent.destroyRecursive();
|
||||
expect(parentDispatcher.ngOnDestroyCalled).toBe(true);
|
||||
expect(childDispatcher.ngOnDestroyCalled).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1416,7 +1487,9 @@ class TestDirective {
|
||||
|
||||
ngAfterViewInitCalled = false;
|
||||
ngAfterViewCheckedCalled = false;
|
||||
destroyCalled: boolean = false;
|
||||
event;
|
||||
eventEmitter: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
constructor(public ngAfterContentCheckedSpy = null, public ngAfterViewCheckedSpy = null,
|
||||
public throwOnInit = false) {}
|
||||
@@ -1455,6 +1528,8 @@ class TestDirective {
|
||||
this.ngAfterViewCheckedSpy();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() { this.destroyCalled = true; }
|
||||
}
|
||||
|
||||
class Person {
|
||||
@@ -1518,23 +1593,24 @@ class TestData {
|
||||
constructor(public a: any) {}
|
||||
}
|
||||
|
||||
class FakeDirectives {
|
||||
constructor(public directives: Array<TestData | TestDirective>,
|
||||
public detectors: ProtoChangeDetector[]) {}
|
||||
|
||||
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
|
||||
|
||||
getDetectorFor(di: DirectiveIndex) { return this.detectors[di.directiveIndex]; }
|
||||
}
|
||||
|
||||
class TestDispatcher implements ChangeDispatcher {
|
||||
log: string[];
|
||||
debugLog: string[];
|
||||
loggedValues: any[];
|
||||
ngAfterContentCheckedCalled: boolean = false;
|
||||
ngAfterViewCheckedCalled: boolean = false;
|
||||
ngOnDestroyCalled: boolean = false;
|
||||
|
||||
constructor() { this.clear(); }
|
||||
constructor(public directives: Array<TestData | TestDirective> = null,
|
||||
public detectors: any[] = null) {
|
||||
if (isBlank(this.directives)) {
|
||||
this.directives = [];
|
||||
}
|
||||
if (isBlank(this.detectors)) {
|
||||
this.detectors = [];
|
||||
}
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.log = [];
|
||||
@@ -1543,6 +1619,10 @@ class TestDispatcher implements ChangeDispatcher {
|
||||
this.ngAfterContentCheckedCalled = true;
|
||||
}
|
||||
|
||||
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
|
||||
|
||||
getDetectorFor(di: DirectiveIndex) { return this.detectors[di.directiveIndex]; }
|
||||
|
||||
notifyOnBinding(target, value) {
|
||||
this.log.push(`${target.name}=${this._asString(value)}`);
|
||||
this.loggedValues.push(value);
|
||||
@@ -1553,7 +1633,9 @@ class TestDispatcher implements ChangeDispatcher {
|
||||
notifyAfterContentChecked() { this.ngAfterContentCheckedCalled = true; }
|
||||
notifyAfterViewChecked() { this.ngAfterViewCheckedCalled = true; }
|
||||
|
||||
getDebugContext(a, b) { return null; }
|
||||
notifyOnDestroy() { this.ngOnDestroyCalled = true; }
|
||||
|
||||
getDebugContext(a, b, c) { return null; }
|
||||
|
||||
_asString(value) {
|
||||
if (isNumber(value) && NumberWrapper.isNaN(value)) {
|
||||
|
||||
@@ -31,7 +31,7 @@ export function main() {
|
||||
() => { expect(() => locals.set('notPresent', 'bar')).toThrowError(); });
|
||||
|
||||
it('should clearValues', () => {
|
||||
locals.clearValues();
|
||||
locals.clearLocalValues();
|
||||
expect(locals.get('key')).toBe(null);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -111,8 +111,10 @@ export function main() {
|
||||
providers: dynamicProviders,
|
||||
strategyClass: InjectorDynamicStrategy
|
||||
}].forEach((context) => {
|
||||
function createInjector(providers: any[]) {
|
||||
return Injector.resolveAndCreate(providers.concat(context['providers']));
|
||||
function createInjector(providers: any[], parent: Injector = null, isHost: boolean = false) {
|
||||
return new Injector(ProtoInjector.fromResolvedProviders(
|
||||
Injector.resolve(providers.concat(context['providers']))),
|
||||
parent, isHost);
|
||||
}
|
||||
|
||||
describe(`injector ${context['strategy']}`, () => {
|
||||
@@ -317,7 +319,7 @@ export function main() {
|
||||
new ProviderWithVisibility(providers[0], Visibility.Public),
|
||||
new ProviderWithVisibility(providers[1], Visibility.Public)
|
||||
]);
|
||||
var injector = new Injector(proto, null, null);
|
||||
var injector = new Injector(proto);
|
||||
|
||||
try {
|
||||
injector.get(Car);
|
||||
@@ -339,8 +341,8 @@ export function main() {
|
||||
var protoChild =
|
||||
new ProtoInjector([new ProviderWithVisibility(carProvider, Visibility.Public)]);
|
||||
|
||||
var parent = new Injector(protoParent, null, null, () => "parentContext");
|
||||
var child = new Injector(protoChild, parent, null, () => "childContext");
|
||||
var parent = new Injector(protoParent, null, false, null, () => "parentContext");
|
||||
var child = new Injector(protoChild, parent, false, null, () => "childContext");
|
||||
|
||||
try {
|
||||
child.get(Car);
|
||||
@@ -379,7 +381,7 @@ export function main() {
|
||||
var providers = Injector.resolve([Car]);
|
||||
var proto =
|
||||
new ProtoInjector([new ProviderWithVisibility(providers[0], Visibility.Public)]);
|
||||
var injector = new Injector(proto, null, depProvider);
|
||||
var injector = new Injector(proto, null, false, depProvider);
|
||||
|
||||
expect(injector.get(Car).engine).toEqual(e);
|
||||
expect(depProvider.spy("getDependency"))
|
||||
@@ -489,11 +491,9 @@ export function main() {
|
||||
new ProtoInjector([new ProviderWithVisibility(engine, Visibility.Private)]);
|
||||
var parent = new Injector(protoParent);
|
||||
|
||||
var child = Injector.resolveAndCreate([
|
||||
provide(Car, {useFactory: (e) => new Car(e), deps: [[Engine, new HostMetadata()]]})
|
||||
]);
|
||||
|
||||
child.internalStrategy.attach(parent, true); // host
|
||||
var child = createInjector(
|
||||
[provide(Car, {useFactory: (e) => new Car(e), deps: [[Engine, new HostMetadata()]]})],
|
||||
parent, true); // host
|
||||
|
||||
expect(child.get(Car)).toBeAnInstanceOf(Car);
|
||||
});
|
||||
@@ -504,11 +504,9 @@ export function main() {
|
||||
new ProtoInjector([new ProviderWithVisibility(engine, Visibility.Public)]);
|
||||
var parent = new Injector(protoParent);
|
||||
|
||||
var child = Injector.resolveAndCreate([
|
||||
provide(Car, {useFactory: (e) => new Car(e), deps: [[Engine, new HostMetadata()]]})
|
||||
]);
|
||||
|
||||
child.internalStrategy.attach(parent, true); // host
|
||||
var child = createInjector(
|
||||
[provide(Car, {useFactory: (e) => new Car(e), deps: [[Engine, new HostMetadata()]]})],
|
||||
parent, true); // host
|
||||
|
||||
expect(() => child.get(Car))
|
||||
.toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`);
|
||||
@@ -532,12 +530,13 @@ export function main() {
|
||||
new ProtoInjector([new ProviderWithVisibility(engine, Visibility.Private)]);
|
||||
var parent = new Injector(protoParent);
|
||||
|
||||
var child = Injector.resolveAndCreate([
|
||||
provide(Engine, {useClass: BrokenEngine}),
|
||||
provide(Car,
|
||||
{useFactory: (e) => new Car(e), deps: [[Engine, new SkipSelfMetadata()]]})
|
||||
]);
|
||||
child.internalStrategy.attach(parent, true); // boundary
|
||||
var child = createInjector(
|
||||
[
|
||||
provide(Engine, {useClass: BrokenEngine}),
|
||||
provide(Car,
|
||||
{useFactory: (e) => new Car(e), deps: [[Engine, new SkipSelfMetadata()]]})
|
||||
],
|
||||
parent, true); // boundary
|
||||
|
||||
expect(child.get(Car)).toBeAnInstanceOf(Car);
|
||||
});
|
||||
@@ -548,12 +547,13 @@ export function main() {
|
||||
new ProtoInjector([new ProviderWithVisibility(engine, Visibility.Public)]);
|
||||
var parent = new Injector(protoParent);
|
||||
|
||||
var child = Injector.resolveAndCreate([
|
||||
provide(Engine, {useClass: BrokenEngine}),
|
||||
provide(Car,
|
||||
{useFactory: (e) => new Car(e), deps: [[Engine, new SkipSelfMetadata()]]})
|
||||
]);
|
||||
child.internalStrategy.attach(parent, true); // boundary
|
||||
var child = createInjector(
|
||||
[
|
||||
provide(Engine, {useClass: BrokenEngine}),
|
||||
provide(Car,
|
||||
{useFactory: (e) => new Car(e), deps: [[Engine, new SkipSelfMetadata()]]})
|
||||
],
|
||||
parent, true); // boundary
|
||||
|
||||
expect(child.get(Car)).toBeAnInstanceOf(Car);
|
||||
});
|
||||
@@ -564,12 +564,13 @@ export function main() {
|
||||
new ProtoInjector([new ProviderWithVisibility(engine, Visibility.Private)]);
|
||||
var parent = new Injector(protoParent);
|
||||
|
||||
var child = Injector.resolveAndCreate([
|
||||
provide(Engine, {useClass: BrokenEngine}),
|
||||
provide(Car,
|
||||
{useFactory: (e) => new Car(e), deps: [[Engine, new SkipSelfMetadata()]]})
|
||||
]);
|
||||
child.internalStrategy.attach(parent, false);
|
||||
var child = createInjector(
|
||||
[
|
||||
provide(Engine, {useClass: BrokenEngine}),
|
||||
provide(Car,
|
||||
{useFactory: (e) => new Car(e), deps: [[Engine, new SkipSelfMetadata()]]})
|
||||
],
|
||||
parent, false);
|
||||
|
||||
expect(() => child.get(Car))
|
||||
.toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`);
|
||||
|
||||
@@ -13,54 +13,36 @@ import {
|
||||
beforeEachProviders
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {Component, View, provide} from 'angular2/core';
|
||||
import {SpyProtoViewFactory} from '../spies';
|
||||
import {
|
||||
CompiledHostTemplate,
|
||||
CompiledComponentTemplate,
|
||||
BeginComponentCmd
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {provide} from 'angular2/core';
|
||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {reflector, ReflectionInfo} from 'angular2/src/core/reflection/reflection';
|
||||
import {AppProtoView} from 'angular2/src/core/linker/view';
|
||||
import {Compiler_} from "angular2/src/core/linker/compiler";
|
||||
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||
|
||||
export function main() {
|
||||
describe('Compiler', () => {
|
||||
var compiler: Compiler;
|
||||
var protoViewFactorySpy;
|
||||
var someProtoView;
|
||||
var cht: CompiledHostTemplate;
|
||||
var someHostViewFactory;
|
||||
|
||||
beforeEachProviders(() => {
|
||||
protoViewFactorySpy = new SpyProtoViewFactory();
|
||||
someProtoView = new AppProtoView(null, null, null, null, null, null, null);
|
||||
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
|
||||
var factory = provide(ProtoViewFactory, {useValue: protoViewFactorySpy});
|
||||
var classProvider = provide(Compiler, {useClass: Compiler_});
|
||||
var providers = [factory, classProvider];
|
||||
return providers;
|
||||
});
|
||||
beforeEachProviders(() => [provide(Compiler, {useClass: Compiler_})]);
|
||||
|
||||
beforeEach(inject([Compiler], (_compiler) => {
|
||||
compiler = _compiler;
|
||||
cht = new CompiledHostTemplate(new CompiledComponentTemplate('aCompId', null, null, null));
|
||||
reflector.registerType(SomeComponent, new ReflectionInfo([cht]));
|
||||
someHostViewFactory = new HostViewFactory(null, null);
|
||||
reflector.registerType(SomeComponent, new ReflectionInfo([someHostViewFactory]));
|
||||
}));
|
||||
|
||||
it('should read the template from an annotation', inject([AsyncTestCompleter], (async) => {
|
||||
it('should read the template from an annotation',
|
||||
inject([AsyncTestCompleter, Compiler], (async, compiler) => {
|
||||
compiler.compileInHost(SomeComponent)
|
||||
.then((_) => {
|
||||
expect(protoViewFactorySpy.spy('createHost')).toHaveBeenCalledWith(cht);
|
||||
.then((hostViewFactoryRef) => {
|
||||
expect(hostViewFactoryRef.internalHostViewFactory).toBe(someHostViewFactory);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should clear the cache', () => {
|
||||
compiler.clearCache();
|
||||
expect(protoViewFactorySpy.spy('clearCache')).toHaveBeenCalled();
|
||||
});
|
||||
it('should clear the cache', inject([Compiler], (compiler) => {
|
||||
// Nothing to assert for now...
|
||||
compiler.clearCache();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {ComponentFixture_} from "angular2/src/testing/test_component_builder";
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('DynamicComponentLoader', function() {
|
||||
@@ -164,6 +165,44 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to pass projectable nodes',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp,
|
||||
new ViewMetadata({template: '<div #loc></div>', directives: []}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadIntoLocation(DynamicallyLoadedWithNgContent,
|
||||
tc.debugElement.elementRef, 'loc', null,
|
||||
[[DOM.createTextNode('hello')]])
|
||||
.then(ref => {
|
||||
tc.detectChanges();
|
||||
expect(tc.nativeElement).toHaveText('dynamic(hello)');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw if not enough projectable nodes are passed in',
|
||||
inject(
|
||||
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp,
|
||||
new ViewMetadata({template: '<div #loc></div>', directives: []}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
PromiseWrapper.catchError(
|
||||
loader.loadIntoLocation(DynamicallyLoadedWithNgContent,
|
||||
tc.debugElement.elementRef, 'loc', null, []),
|
||||
(e) => {
|
||||
expect(e.message).toContain(
|
||||
`The component ${stringify(DynamicallyLoadedWithNgContent)} has 1 <ng-content> elements, but only 0 slots were provided`);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe("loading next to a location", () => {
|
||||
@@ -248,17 +287,37 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to pass projectable nodes',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: '', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadNextToLocation(DynamicallyLoadedWithNgContent,
|
||||
tc.debugElement.elementRef, null,
|
||||
[[DOM.createTextNode('hello')]])
|
||||
.then(ref => {
|
||||
tc.detectChanges();
|
||||
var newlyInsertedElement =
|
||||
DOM.nextSibling(tc.debugElement.nativeElement);
|
||||
expect(newlyInsertedElement).toHaveText('dynamic(hello)');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('loadAsRoot', () => {
|
||||
it('should allow to create, update and destroy components',
|
||||
inject([AsyncTestCompleter, DynamicComponentLoader, DOCUMENT, Injector],
|
||||
(async, loader, doc, injector) => {
|
||||
var rootEl = el('<child-cmp></child-cmp>');
|
||||
var rootEl = createRootElement(doc, 'child-cmp');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
loader.loadAsRoot(ChildComp, null, injector)
|
||||
.then((componentRef) => {
|
||||
var el = new ComponentFixture_(componentRef);
|
||||
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
|
||||
el.detectChanges();
|
||||
@@ -279,11 +338,35 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to pass projectable nodes',
|
||||
inject([AsyncTestCompleter, DynamicComponentLoader, DOCUMENT, Injector],
|
||||
(async, loader, doc, injector) => {
|
||||
var rootEl = createRootElement(doc, 'dummy');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
loader.loadAsRoot(DynamicallyLoadedWithNgContent, null, injector, null,
|
||||
[[DOM.createTextNode('hello')]])
|
||||
.then((_) => {
|
||||
expect(rootEl).toHaveText('dynamic(hello)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function createRootElement(doc: any, name: string): any {
|
||||
var nodes = DOM.querySelectorAll(doc, name);
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.remove(nodes[i]);
|
||||
}
|
||||
var rootEl = el(`<${name}></${name}>`);
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
return rootEl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-cmp',
|
||||
})
|
||||
@@ -335,6 +418,14 @@ class DynamicallyLoadedWithHostProps {
|
||||
constructor() { this.id = "default"; }
|
||||
}
|
||||
|
||||
@Component({selector: 'dummy'})
|
||||
@View({template: "dynamic(<ng-content></ng-content>)"})
|
||||
class DynamicallyLoadedWithNgContent {
|
||||
id: string;
|
||||
|
||||
constructor() { this.id = "default"; }
|
||||
}
|
||||
|
||||
@Component({selector: 'location'})
|
||||
@View({template: "Location;"})
|
||||
class Location {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,843 @@
|
||||
// TODO(tbosch): clang-format screws this up, see https://github.com/angular/clang-format/issues/11.
|
||||
// Enable clang-format here again when this is fixed.
|
||||
// clang-format off
|
||||
import {
|
||||
describe,
|
||||
ddescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
xdescribe,
|
||||
expect,
|
||||
beforeEach,
|
||||
beforeEachBindings,
|
||||
inject,
|
||||
AsyncTestCompleter,
|
||||
el,
|
||||
containsRegexp
|
||||
} from 'angular2/testing_internal';
|
||||
import {SpyView, SpyElementRef, SpyDirectiveResolver, SpyProtoView, SpyChangeDetector, SpyAppViewManager} from '../spies';
|
||||
import {isBlank, isPresent, stringify, Type} from 'angular2/src/facade/lang';
|
||||
import {ResolvedProvider} from 'angular2/src/core/di';
|
||||
import {
|
||||
ListWrapper,
|
||||
MapWrapper,
|
||||
StringMapWrapper,
|
||||
iterateListLike
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
AppProtoElement,
|
||||
AppElement,
|
||||
DirectiveProvider
|
||||
} from 'angular2/src/core/linker/element';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {
|
||||
Attribute,
|
||||
Query,
|
||||
ViewQuery,
|
||||
ComponentMetadata,
|
||||
DirectiveMetadata,
|
||||
ViewEncapsulation
|
||||
} from 'angular2/src/core/metadata';
|
||||
import {OnDestroy, Directive} from 'angular2/core';
|
||||
import {provide, Injector, Provider, Optional, Inject, Injectable, Self, SkipSelf, InjectMetadata, Host, HostMetadata, SkipSelfMetadata} from 'angular2/core';
|
||||
import {ViewContainerRef, ViewContainerRef_} from 'angular2/src/core/linker/view_container_ref';
|
||||
import {TemplateRef, TemplateRef_} from 'angular2/src/core/linker/template_ref';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {DynamicChangeDetector, ChangeDetectorRef, Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ChangeDetectorRef_} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {QueryList} from 'angular2/src/core/linker/query_list';
|
||||
import {AppView, AppProtoView} from "angular2/src/core/linker/view";
|
||||
import {ViewType} from "angular2/src/core/linker/view_type";
|
||||
|
||||
@Directive({selector: ''})
|
||||
class SimpleDirective {}
|
||||
|
||||
class SimpleService {}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class SomeOtherDirective {}
|
||||
|
||||
var _constructionCount;
|
||||
@Directive({selector: ''})
|
||||
class CountingDirective {
|
||||
count: number;
|
||||
constructor() {
|
||||
this.count = _constructionCount;
|
||||
_constructionCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class FancyCountingDirective extends CountingDirective {
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsDirective {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Self() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class OptionallyNeedsDirective {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Self() @Optional() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeeedsDirectiveFromHost {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Host() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsDirectiveFromHostShadowDom {
|
||||
dependency: SimpleDirective;
|
||||
constructor(dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsService {
|
||||
service: any;
|
||||
constructor(@Inject("service") service) { this.service = service; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsServiceFromHost {
|
||||
service: any;
|
||||
constructor(@Host() @Inject("service") service) { this.service = service; }
|
||||
}
|
||||
|
||||
class HasEventEmitter {
|
||||
emitter;
|
||||
constructor() { this.emitter = "emitter"; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsAttribute {
|
||||
typeAttribute;
|
||||
titleAttribute;
|
||||
fooAttribute;
|
||||
constructor(@Attribute('type') typeAttribute: String, @Attribute('title') titleAttribute: String,
|
||||
@Attribute('foo') fooAttribute: String) {
|
||||
this.typeAttribute = typeAttribute;
|
||||
this.titleAttribute = titleAttribute;
|
||||
this.fooAttribute = fooAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsAttributeNoType {
|
||||
fooAttribute;
|
||||
constructor(@Attribute('foo') fooAttribute) { this.fooAttribute = fooAttribute; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsQuery {
|
||||
query: QueryList<CountingDirective>;
|
||||
constructor(@Query(CountingDirective) query: QueryList<CountingDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsViewQuery {
|
||||
query: QueryList<CountingDirective>;
|
||||
constructor(@ViewQuery(CountingDirective) query: QueryList<CountingDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsQueryByVarBindings {
|
||||
query: QueryList<any>;
|
||||
constructor(@Query("one,two") query: QueryList<any>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsTemplateRefQuery {
|
||||
query: QueryList<TemplateRef>;
|
||||
constructor(@Query(TemplateRef) query: QueryList<TemplateRef>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsElementRef {
|
||||
elementRef;
|
||||
constructor(ref: ElementRef) { this.elementRef = ref; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsViewContainer {
|
||||
viewContainer;
|
||||
constructor(vc: ViewContainerRef) { this.viewContainer = vc; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsTemplateRef {
|
||||
templateRef;
|
||||
constructor(ref: TemplateRef) { this.templateRef = ref; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class OptionallyInjectsTemplateRef {
|
||||
templateRef;
|
||||
constructor(@Optional() ref: TemplateRef) { this.templateRef = ref; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class DirectiveNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class ComponentNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class PipeNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
class A_Needs_B {
|
||||
constructor(dep) {}
|
||||
}
|
||||
|
||||
class B_Needs_A {
|
||||
constructor(dep) {}
|
||||
}
|
||||
|
||||
class DirectiveWithDestroy implements OnDestroy {
|
||||
ngOnDestroyCounter: number;
|
||||
|
||||
constructor() { this.ngOnDestroyCounter = 0; }
|
||||
|
||||
ngOnDestroy() { this.ngOnDestroyCounter++; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class D0 {}
|
||||
@Directive({selector: ''})
|
||||
class D1 {}
|
||||
@Directive({selector: ''})
|
||||
class D2 {}
|
||||
@Directive({selector: ''})
|
||||
class D3 {}
|
||||
@Directive({selector: ''})
|
||||
class D4 {}
|
||||
@Directive({selector: ''})
|
||||
class D5 {}
|
||||
@Directive({selector: ''})
|
||||
class D6 {}
|
||||
@Directive({selector: ''})
|
||||
class D7 {}
|
||||
@Directive({selector: ''})
|
||||
class D8 {}
|
||||
@Directive({selector: ''})
|
||||
class D9 {}
|
||||
@Directive({selector: ''})
|
||||
class D10 {}
|
||||
@Directive({selector: ''})
|
||||
class D11 {}
|
||||
@Directive({selector: ''})
|
||||
class D12 {}
|
||||
@Directive({selector: ''})
|
||||
class D13 {}
|
||||
@Directive({selector: ''})
|
||||
class D14 {}
|
||||
@Directive({selector: ''})
|
||||
class D15 {}
|
||||
@Directive({selector: ''})
|
||||
class D16 {}
|
||||
@Directive({selector: ''})
|
||||
class D17 {}
|
||||
@Directive({selector: ''})
|
||||
class D18 {}
|
||||
@Directive({selector: ''})
|
||||
class D19 {}
|
||||
|
||||
export function main() {
|
||||
// An injector with more than 10 providers will switch to the dynamic strategy
|
||||
var dynamicStrategyDirectives = [D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19];
|
||||
var resolvedMetadataCache:ResolvedMetadataCache;
|
||||
var mockDirectiveMeta:Map<Type, DirectiveMetadata>;
|
||||
var directiveResolver:SpyDirectiveResolver;
|
||||
var dummyView:AppView;
|
||||
var dummyViewFactory:Function;
|
||||
|
||||
function createView(type: ViewType, containerAppElement:AppElement = null, imperativelyCreatedProviders: ResolvedProvider[] = null, rootInjector: Injector = null, pipes: Type[] = null):AppView {
|
||||
if (isBlank(pipes)) {
|
||||
pipes = [];
|
||||
}
|
||||
var proto = AppProtoView.create(resolvedMetadataCache, type, pipes, {});
|
||||
var cd = new SpyChangeDetector();
|
||||
cd.prop('ref', new ChangeDetectorRef_(<any>cd));
|
||||
|
||||
var view = new AppView(proto, null, <any>new SpyAppViewManager(), [], containerAppElement, imperativelyCreatedProviders, rootInjector, <any> cd);
|
||||
view.init([], [], [], []);
|
||||
return view;
|
||||
}
|
||||
|
||||
function protoAppElement(index, directives: Type[], attributes: {[key:string]:string} = null, dirVariableBindings:{[key:string]:number} = null) {
|
||||
return AppProtoElement.create(resolvedMetadataCache, index, attributes, directives, dirVariableBindings);
|
||||
}
|
||||
|
||||
function appElement(parent: AppElement, directives: Type[],
|
||||
view: AppView = null, embeddedViewFactory: Function = null, attributes: {[key:string]:string} = null, dirVariableBindings:{[key:string]:number} = null) {
|
||||
if (isBlank(view)) {
|
||||
view = dummyView;
|
||||
}
|
||||
var proto = protoAppElement(0, directives, attributes, dirVariableBindings);
|
||||
var el = new AppElement(proto, view, parent, null, embeddedViewFactory);
|
||||
view.appElements.push(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function parentChildElements(parentDirectives: Type[], childDirectives:Type[], view: AppView = null) {
|
||||
if (isBlank(view)) {
|
||||
view = dummyView;
|
||||
}
|
||||
var parent = appElement(null, parentDirectives, view);
|
||||
var child = appElement(parent, childDirectives, view);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
function hostShadowElement(hostDirectives: Type[],
|
||||
viewDirectives: Type[]): AppElement {
|
||||
var host = appElement(null, hostDirectives);
|
||||
var view = createView(ViewType.COMPONENT, host);
|
||||
host.attachComponentView(view);
|
||||
|
||||
return appElement(null, viewDirectives, view);
|
||||
}
|
||||
|
||||
function init() {
|
||||
beforeEachBindings(() => {
|
||||
var delegateDirectiveResolver = new DirectiveResolver();
|
||||
directiveResolver = new SpyDirectiveResolver();
|
||||
directiveResolver.spy('resolve').andCallFake( (directiveType) => {
|
||||
var result = mockDirectiveMeta.get(directiveType);
|
||||
if (isBlank(result)) {
|
||||
result = delegateDirectiveResolver.resolve(directiveType);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return [
|
||||
provide(DirectiveResolver, {useValue: directiveResolver})
|
||||
];
|
||||
});
|
||||
beforeEach(inject([ResolvedMetadataCache], (_metadataCache) => {
|
||||
mockDirectiveMeta = new Map<Type, DirectiveMetadata>();
|
||||
resolvedMetadataCache = _metadataCache;
|
||||
dummyView = createView(ViewType.HOST);
|
||||
dummyViewFactory = () => {};
|
||||
_constructionCount = 0;
|
||||
}));
|
||||
}
|
||||
|
||||
describe("ProtoAppElement", () => {
|
||||
init();
|
||||
|
||||
describe('inline strategy', () => {
|
||||
it("should allow for direct access using getProviderAtIndex", () => {
|
||||
var proto = protoAppElement(0, [SimpleDirective]);
|
||||
|
||||
expect(proto.getProviderAtIndex(0)).toBeAnInstanceOf(DirectiveProvider);
|
||||
expect(() => proto.getProviderAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.');
|
||||
expect(() => proto.getProviderAtIndex(10)).toThrowError('Index 10 is out-of-bounds.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic strategy', () => {
|
||||
it("should allow for direct access using getProviderAtIndex", () => {
|
||||
var proto = protoAppElement(0, dynamicStrategyDirectives);
|
||||
|
||||
expect(proto.getProviderAtIndex(0)).toBeAnInstanceOf(DirectiveProvider);
|
||||
expect(() => proto.getProviderAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.');
|
||||
expect(() => proto.getProviderAtIndex(dynamicStrategyDirectives.length - 1)).not.toThrow();
|
||||
expect(() => proto.getProviderAtIndex(dynamicStrategyDirectives.length))
|
||||
.toThrowError(`Index ${dynamicStrategyDirectives.length} is out-of-bounds.`);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".create", () => {
|
||||
it("should collect providers from all directives", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new DirectiveMetadata({providers: [provide('injectable1', {useValue: 'injectable1'})]}));
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new DirectiveMetadata({
|
||||
providers: [provide('injectable2', {useValue: 'injectable2'})]
|
||||
}));
|
||||
var pel = protoAppElement( 0, [
|
||||
SimpleDirective,
|
||||
SomeOtherDirective
|
||||
]);
|
||||
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(SimpleDirective);
|
||||
expect(pel.getProviderAtIndex(1).key.token).toBe(SomeOtherDirective);
|
||||
expect(pel.getProviderAtIndex(2).key.token).toEqual("injectable1");
|
||||
expect(pel.getProviderAtIndex(3).key.token).toEqual("injectable2");
|
||||
});
|
||||
|
||||
it("should collect view providers from the component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('injectable1', {useValue: 'injectable1'})]
|
||||
}));
|
||||
var pel = protoAppElement(0, [SimpleDirective]);
|
||||
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(SimpleDirective);
|
||||
expect(pel.getProviderAtIndex(1).key.token).toEqual("injectable1");
|
||||
});
|
||||
|
||||
it("should flatten nested arrays in viewProviders and providers", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [[[provide('view', {useValue: 'view'})]]],
|
||||
providers: [[[provide('host', {useValue: 'host'})]]]
|
||||
}));
|
||||
var pel = protoAppElement(0, [SimpleDirective]);
|
||||
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(SimpleDirective);
|
||||
expect(pel.getProviderAtIndex(1).key.token).toEqual("view");
|
||||
expect(pel.getProviderAtIndex(2).key.token).toEqual("host");
|
||||
});
|
||||
|
||||
it('should support an arbitrary number of providers', () => {
|
||||
var pel = protoAppElement(0, dynamicStrategyDirectives);
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(D0);
|
||||
expect(pel.getProviderAtIndex(19).key.token).toBe(D19);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AppElement", () => {
|
||||
init();
|
||||
|
||||
[{ strategy: 'inline', directives: [] }, { strategy: 'dynamic',
|
||||
directives: dynamicStrategyDirectives }].forEach((context) => {
|
||||
|
||||
var extraDirectives = context['directives'];
|
||||
describe(`${context['strategy']} strategy`, () => {
|
||||
|
||||
describe("injection", () => {
|
||||
it("should instantiate directives that have no dependencies", () => {
|
||||
var directives = ListWrapper.concat([SimpleDirective], extraDirectives);
|
||||
var el = appElement(null, directives);
|
||||
expect(el.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on an arbitrary number of directives", () => {
|
||||
var directives = ListWrapper.concat([SimpleDirective, NeedsDirective], extraDirectives);
|
||||
var el = appElement(null, directives);
|
||||
|
||||
var d = el.get(NeedsDirective);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedsDirective);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should instantiate providers that have dependencies with set visibility",
|
||||
function() {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
providers: [provide('injectable1', {useValue: 'injectable1'})]
|
||||
}));
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new ComponentMetadata({
|
||||
providers: [
|
||||
provide('injectable1', {useValue:'new-injectable1'}),
|
||||
provide('injectable2', {useFactory:
|
||||
(val) => `${val}-injectable2`,
|
||||
deps: [[new InjectMetadata('injectable1'), new SkipSelfMetadata()]]})
|
||||
]
|
||||
}));
|
||||
var childInj = parentChildElements(
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
[SomeOtherDirective]
|
||||
);
|
||||
expect(childInj.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
});
|
||||
|
||||
it("should instantiate providers that have dependencies", () => {
|
||||
var providers = [
|
||||
provide('injectable1', {useValue: 'injectable1'}),
|
||||
provide('injectable2', {useFactory:
|
||||
(val) => `${val}-injectable2`,
|
||||
deps: ['injectable1']})
|
||||
];
|
||||
mockDirectiveMeta.set(SimpleDirective, new DirectiveMetadata({providers: providers}));
|
||||
var el = appElement(null, ListWrapper.concat(
|
||||
[SimpleDirective], extraDirectives));
|
||||
|
||||
expect(el.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
});
|
||||
|
||||
it("should instantiate viewProviders that have dependencies", () => {
|
||||
var viewProviders = [
|
||||
provide('injectable1', {useValue: 'injectable1'}),
|
||||
provide('injectable2', {useFactory:
|
||||
(val) => `${val}-injectable2`,
|
||||
deps: ['injectable1']})
|
||||
];
|
||||
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: viewProviders}));
|
||||
var el = appElement(null, ListWrapper.concat(
|
||||
[SimpleDirective], extraDirectives));
|
||||
expect(el.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
});
|
||||
|
||||
it("should instantiate components that depend on viewProviders providers", () => {
|
||||
mockDirectiveMeta.set(NeedsService, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useValue: 'service'})]
|
||||
}));
|
||||
var el = appElement(null,
|
||||
ListWrapper.concat([NeedsService], extraDirectives));
|
||||
expect(el.get(NeedsService).service).toEqual('service');
|
||||
});
|
||||
|
||||
it("should instantiate providers lazily", () => {
|
||||
var created = false;
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
providers: [provide('service', {useFactory: () => created = true})]
|
||||
}));
|
||||
var el = appElement(null,
|
||||
ListWrapper.concat([SimpleDirective],
|
||||
extraDirectives));
|
||||
|
||||
expect(created).toBe(false);
|
||||
|
||||
el.get('service');
|
||||
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
it("should instantiate view providers lazily", () => {
|
||||
var created = false;
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useFactory: () => created = true})]
|
||||
}));
|
||||
var el = appElement(null,
|
||||
ListWrapper.concat([SimpleDirective],
|
||||
extraDirectives));
|
||||
|
||||
expect(created).toBe(false);
|
||||
|
||||
el.get('service');
|
||||
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
it("should not instantiate other directives that depend on viewProviders providers",
|
||||
() => {
|
||||
mockDirectiveMeta.set(SimpleDirective,
|
||||
new ComponentMetadata({
|
||||
viewProviders: [provide("service", {useValue: "service"})]
|
||||
}));
|
||||
expect(() => { appElement(null, ListWrapper.concat([SimpleDirective, NeedsService], extraDirectives)); })
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for service! (${stringify(NeedsService) } -> service)`));
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on providers of other directives", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
providers: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
var shadowInj = hostShadowElement(
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsService], extraDirectives)
|
||||
);
|
||||
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on view providers of a component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
var shadowInj = hostShadowElement(
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsService], extraDirectives)
|
||||
);
|
||||
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should instantiate directives in a root embedded view that depend on view providers of a component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
var host = appElement(null, ListWrapper.concat([SimpleDirective], extraDirectives));
|
||||
var componenetView = createView(ViewType.COMPONENT, host);
|
||||
host.attachComponentView(componenetView);
|
||||
|
||||
var anchor = appElement(null, [], componenetView);
|
||||
var embeddedView = createView(ViewType.EMBEDDED, anchor);
|
||||
|
||||
var rootEmbeddedEl = appElement(null, ListWrapper.concat([NeedsService], extraDirectives), embeddedView);
|
||||
expect(rootEmbeddedEl.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on imperatively created injector (bootstrap)", () => {
|
||||
var rootInjector = Injector.resolveAndCreate([
|
||||
provide("service", {useValue: 'appService'})
|
||||
]);
|
||||
var view = createView(ViewType.HOST, null, null, rootInjector);
|
||||
expect(appElement(null, [NeedsService], view).get(NeedsService).service).toEqual('appService');
|
||||
|
||||
expect(() => appElement(null, [NeedsServiceFromHost], view)).toThrowError();
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on imperatively created providers (root injector)", () => {
|
||||
var imperativelyCreatedProviders = Injector.resolve([
|
||||
provide("service", {useValue: 'appService'})
|
||||
]);
|
||||
var containerAppElement = appElement(null, []);
|
||||
var view = createView(ViewType.HOST, containerAppElement, imperativelyCreatedProviders, null);
|
||||
expect(appElement(null, [NeedsService], view).get(NeedsService).service).toEqual('appService');
|
||||
expect(appElement(null, [NeedsServiceFromHost], view).get(NeedsServiceFromHost).service).toEqual('appService');
|
||||
});
|
||||
|
||||
it("should not instantiate a directive in a view that has a host dependency on providers"+
|
||||
" of the component", () => {
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new DirectiveMetadata({
|
||||
providers: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
expect(() => {
|
||||
hostShadowElement(
|
||||
ListWrapper.concat([SomeOtherDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsServiceFromHost], extraDirectives)
|
||||
);
|
||||
}).toThrowError(new RegExp("No provider for service!"));
|
||||
});
|
||||
|
||||
it("should not instantiate a directive in a view that has a host dependency on providers"+
|
||||
" of a decorator directive", () => {
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new DirectiveMetadata({
|
||||
providers: [provide('service', {useValue: 'hostService'})]}));
|
||||
expect(() => {
|
||||
hostShadowElement(
|
||||
ListWrapper.concat([SimpleDirective, SomeOtherDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsServiceFromHost], extraDirectives)
|
||||
);
|
||||
}).toThrowError(new RegExp("No provider for service!"));
|
||||
});
|
||||
|
||||
it("should get directives", () => {
|
||||
var child = hostShadowElement(
|
||||
ListWrapper.concat([SomeOtherDirective, SimpleDirective], extraDirectives),
|
||||
[NeedsDirectiveFromHostShadowDom]);
|
||||
|
||||
var d = child.get(NeedsDirectiveFromHostShadowDom);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedsDirectiveFromHostShadowDom);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should get directives from the host", () => {
|
||||
var child = parentChildElements(ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
[NeeedsDirectiveFromHost]);
|
||||
|
||||
var d = child.get(NeeedsDirectiveFromHost);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeeedsDirectiveFromHost);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should throw when a dependency cannot be resolved", () => {
|
||||
expect(() => appElement(null, ListWrapper.concat([NeeedsDirectiveFromHost], extraDirectives)))
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for ${stringify(SimpleDirective) }! (${stringify(NeeedsDirectiveFromHost) } -> ${stringify(SimpleDirective) })`));
|
||||
});
|
||||
|
||||
it("should inject null when an optional dependency cannot be resolved", () => {
|
||||
var el = appElement(null, ListWrapper.concat([OptionallyNeedsDirective], extraDirectives));
|
||||
var d = el.get(OptionallyNeedsDirective);
|
||||
expect(d.dependency).toEqual(null);
|
||||
});
|
||||
|
||||
it("should allow for direct access using getDirectiveAtIndex", () => {
|
||||
var providers =
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives);
|
||||
|
||||
var el = appElement(null, providers);
|
||||
|
||||
var firsIndexOut = providers.length > 10 ? providers.length : 10;
|
||||
|
||||
expect(el.getDirectiveAtIndex(0)).toBeAnInstanceOf(SimpleDirective);
|
||||
expect(() => el.getDirectiveAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.');
|
||||
expect(() => el.getDirectiveAtIndex(firsIndexOut))
|
||||
.toThrowError(`Index ${firsIndexOut} is out-of-bounds.`);
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on the containing component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata());
|
||||
var shadow = hostShadowElement(ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
[NeeedsDirectiveFromHost]);
|
||||
|
||||
var d = shadow.get(NeeedsDirectiveFromHost);
|
||||
expect(d).toBeAnInstanceOf(NeeedsDirectiveFromHost);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should not instantiate directives that depend on other directives in the containing component's ElementInjector",
|
||||
() => {
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new ComponentMetadata());
|
||||
expect(() =>
|
||||
{
|
||||
hostShadowElement(
|
||||
ListWrapper.concat([SomeOtherDirective, SimpleDirective], extraDirectives),
|
||||
[NeedsDirective]);
|
||||
})
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`));
|
||||
});
|
||||
});
|
||||
|
||||
describe('static attributes', () => {
|
||||
it('should be injectable', () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsAttribute], extraDirectives), null, null, {
|
||||
'type': 'text',
|
||||
'title': ''
|
||||
});
|
||||
var needsAttribute = el.get(NeedsAttribute);
|
||||
|
||||
expect(needsAttribute.typeAttribute).toEqual('text');
|
||||
expect(needsAttribute.titleAttribute).toEqual('');
|
||||
expect(needsAttribute.fooAttribute).toEqual(null);
|
||||
});
|
||||
|
||||
it('should be injectable without type annotation', () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsAttributeNoType], extraDirectives), null,
|
||||
null, {'foo': 'bar'});
|
||||
var needsAttribute = el.get(NeedsAttributeNoType);
|
||||
|
||||
expect(needsAttribute.fooAttribute).toEqual('bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe("refs", () => {
|
||||
it("should inject ElementRef", () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsElementRef], extraDirectives));
|
||||
expect(el.get(NeedsElementRef).elementRef).toBe(el.ref);
|
||||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the component's view into the component via a proxy", () => {
|
||||
mockDirectiveMeta.set(ComponentNeedsChangeDetectorRef, new ComponentMetadata());
|
||||
var host = appElement(null, ListWrapper.concat([ComponentNeedsChangeDetectorRef], extraDirectives));
|
||||
var view = createView(ViewType.COMPONENT, host);
|
||||
host.attachComponentView(view);
|
||||
host.get(ComponentNeedsChangeDetectorRef).changeDetectorRef.markForCheck();
|
||||
expect((<any>view.changeDetector).spy('markPathToRootAsCheckOnce')).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the containing component into directives", () => {
|
||||
mockDirectiveMeta.set(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata());
|
||||
var view = createView(ViewType.HOST);
|
||||
var el = appElement(null, ListWrapper.concat([DirectiveNeedsChangeDetectorRef], extraDirectives), view);
|
||||
expect(el.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef).toBe(view.changeDetector.ref);
|
||||
});
|
||||
|
||||
it('should inject ViewContainerRef', () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsViewContainer], extraDirectives));
|
||||
expect(el.get(NeedsViewContainer).viewContainer).toBeAnInstanceOf(ViewContainerRef_);
|
||||
});
|
||||
|
||||
it("should inject TemplateRef", () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsTemplateRef], extraDirectives), null, dummyViewFactory);
|
||||
expect(el.get(NeedsTemplateRef).templateRef.elementRef).toBe(el.ref);
|
||||
});
|
||||
|
||||
it("should throw if there is no TemplateRef", () => {
|
||||
expect(() => appElement(null, ListWrapper.concat([NeedsTemplateRef], extraDirectives)))
|
||||
.toThrowError(
|
||||
`No provider for TemplateRef! (${stringify(NeedsTemplateRef) } -> TemplateRef)`);
|
||||
});
|
||||
|
||||
it('should inject null if there is no TemplateRef when the dependency is optional', () => {
|
||||
var el = appElement(null, ListWrapper.concat([OptionallyInjectsTemplateRef], extraDirectives));
|
||||
var instance = el.get(OptionallyInjectsTemplateRef);
|
||||
expect(instance.templateRef).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('queries', () => {
|
||||
function expectDirectives(query: QueryList<any>, type, expectedIndex) {
|
||||
var currentCount = 0;
|
||||
expect(query.length).toEqual(expectedIndex.length);
|
||||
iterateListLike(query, (i) => {
|
||||
expect(i).toBeAnInstanceOf(type);
|
||||
expect(i.count).toBe(expectedIndex[currentCount]);
|
||||
currentCount += 1;
|
||||
});
|
||||
}
|
||||
|
||||
it('should be injectable', () => {
|
||||
var el =
|
||||
appElement(null, ListWrapper.concat([NeedsQuery], extraDirectives));
|
||||
expect(el.get(NeedsQuery).query).toBeAnInstanceOf(QueryList);
|
||||
});
|
||||
|
||||
it('should contain directives on the same injector', () => {
|
||||
var el = appElement(null, ListWrapper.concat([
|
||||
NeedsQuery,
|
||||
CountingDirective
|
||||
], extraDirectives));
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
expectDirectives(el.get(NeedsQuery).query, CountingDirective, [0]);
|
||||
});
|
||||
|
||||
it('should contain TemplateRefs on the same injector', () => {
|
||||
var el = appElement(null, ListWrapper.concat([
|
||||
NeedsTemplateRefQuery
|
||||
], extraDirectives), null, dummyViewFactory);
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
expect(el.get(NeedsTemplateRefQuery).query.first).toBeAnInstanceOf(TemplateRef_);
|
||||
});
|
||||
|
||||
it('should contain the element when no directives are bound to the var provider', () => {
|
||||
var dirs:Type[] = [NeedsQueryByVarBindings];
|
||||
|
||||
var dirVariableBindings:{[key:string]:number} = {
|
||||
"one": null // element
|
||||
};
|
||||
|
||||
var el = appElement(null, dirs.concat(extraDirectives), null, null, null, dirVariableBindings);
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
expect(el.get(NeedsQueryByVarBindings).query.first).toBe(el.ref);
|
||||
});
|
||||
|
||||
it('should contain directives on the same injector when querying by variable providers' +
|
||||
'in the order of var providers specified in the query', () => {
|
||||
var dirs:Type[] = [NeedsQueryByVarBindings, NeedsDirective, SimpleDirective];
|
||||
|
||||
var dirVariableBindings:{[key:string]:number} = {
|
||||
"one": 2, // 2 is the index of SimpleDirective
|
||||
"two": 1 // 1 is the index of NeedsDirective
|
||||
};
|
||||
|
||||
var el = appElement(null, dirs.concat(extraDirectives), null, null, null, dirVariableBindings);
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
// NeedsQueryByVarBindings queries "one,two", so SimpleDirective should be before NeedsDirective
|
||||
expect(el.get(NeedsQueryByVarBindings).query.first).toBeAnInstanceOf(SimpleDirective);
|
||||
expect(el.get(NeedsQueryByVarBindings).query.last).toBeAnInstanceOf(NeedsDirective);
|
||||
});
|
||||
|
||||
it('should contain directives on the same and a child injector in construction order', () => {
|
||||
var parent = appElement(null, [NeedsQuery, CountingDirective]);
|
||||
appElement(parent, ListWrapper.concat([CountingDirective], extraDirectives));
|
||||
|
||||
parent.ngAfterContentChecked();
|
||||
|
||||
expectDirectives(parent.get(NeedsQuery).query, CountingDirective, [0, 1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContextWithHandler {
|
||||
handler;
|
||||
constructor(handler) { this.handler = handler; }
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import {EventConfig} from 'angular2/src/core/linker/event_config';
|
||||
import {ddescribe, describe, expect, it} from 'angular2/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('EventConfig', () => {
|
||||
describe('parse', () => {
|
||||
it('should handle short form events', () => {
|
||||
var eventConfig = EventConfig.parse('shortForm');
|
||||
expect(eventConfig.fieldName).toEqual('shortForm');
|
||||
expect(eventConfig.eventName).toEqual('shortForm');
|
||||
expect(eventConfig.isLongForm).toEqual(false);
|
||||
});
|
||||
it('should handle long form events', () => {
|
||||
var eventConfig = EventConfig.parse('fieldName: eventName');
|
||||
expect(eventConfig.fieldName).toEqual('fieldName');
|
||||
expect(eventConfig.eventName).toEqual('eventName');
|
||||
expect(eventConfig.isLongForm).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('getFullName', () => {
|
||||
it('should handle short form events', () => {
|
||||
var eventConfig = new EventConfig('shortForm', 'shortForm', false);
|
||||
expect(eventConfig.getFullName()).toEqual('shortForm');
|
||||
});
|
||||
it('should handle long form events', () => {
|
||||
var eventConfig = new EventConfig('fieldName', 'eventName', true);
|
||||
expect(eventConfig.getFullName()).toEqual('fieldName:eventName');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -86,18 +86,40 @@ import {
|
||||
import {QueryList} from 'angular2/src/core/linker/query_list';
|
||||
|
||||
import {ViewContainerRef} from 'angular2/src/core/linker/view_container_ref';
|
||||
import {ViewRef, ViewRef_} from 'angular2/src/core/linker/view_ref';
|
||||
import {EmbeddedViewRef} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {TemplateRef} from 'angular2/src/core/linker/template_ref';
|
||||
|
||||
import {DomRenderer} from 'angular2/src/platform/dom/dom_renderer';
|
||||
import {Renderer} from 'angular2/src/core/render';
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
|
||||
const ANCHOR_ELEMENT = CONST_EXPR(new OpaqueToken('AnchorElement'));
|
||||
|
||||
export function main() {
|
||||
if (IS_DART) {
|
||||
declareTests();
|
||||
} else {
|
||||
describe('no jit', () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, false, false)})
|
||||
]);
|
||||
declareTests();
|
||||
});
|
||||
|
||||
describe('jit', () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, false, true)})
|
||||
]);
|
||||
declareTests();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function declareTests() {
|
||||
describe('integration tests', function() {
|
||||
|
||||
beforeEachProviders(() => [provide(ANCHOR_ELEMENT, {useValue: el('<div></div>')})]);
|
||||
@@ -151,7 +173,6 @@ export function main() {
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.ctxProp = 'Initial aria label';
|
||||
fixture.detectChanges();
|
||||
expect(
|
||||
@@ -306,17 +327,16 @@ export function main() {
|
||||
|
||||
it('should consume directive watch expression change.',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var tpl = '<div>' +
|
||||
var tpl = '<span>' +
|
||||
'<div my-dir [elprop]="ctxProp"></div>' +
|
||||
'<div my-dir elprop="Hi there!"></div>' +
|
||||
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
|
||||
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
|
||||
'</div>';
|
||||
'</span>';
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: tpl, directives: [MyDir]}))
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.ctxProp = 'Hello World!';
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -677,7 +697,6 @@ export function main() {
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
// Get the element at index 2, since index 0 is the <template>.
|
||||
expect(DOM.childNodes(fixture.debugElement.nativeElement)[2])
|
||||
.toHaveText("1-hello");
|
||||
@@ -1085,6 +1104,9 @@ export function main() {
|
||||
dispatchEvent(DOM.getGlobalEventTarget("window"), 'domEvent');
|
||||
expect(globalCounter).toEqual(2);
|
||||
|
||||
// need to destroy to release all remaining global event listeners
|
||||
fixture.destroy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -1850,8 +1872,8 @@ class MyService {
|
||||
class SimpleImperativeViewComponent {
|
||||
done;
|
||||
|
||||
constructor(self: ElementRef, renderer: DomRenderer) {
|
||||
var hostElement = renderer.getNativeElementSync(self);
|
||||
constructor(self: ElementRef, renderer: Renderer) {
|
||||
var hostElement = self.nativeElement;
|
||||
DOM.appendChild(hostElement, el('hello imp view'));
|
||||
}
|
||||
}
|
||||
@@ -2332,10 +2354,10 @@ class ChildConsumingEventBus {
|
||||
@Directive({selector: '[someImpvp]', inputs: ['someImpvp']})
|
||||
@Injectable()
|
||||
class SomeImperativeViewport {
|
||||
view: ViewRef;
|
||||
view: EmbeddedViewRef;
|
||||
anchor;
|
||||
constructor(public vc: ViewContainerRef, public templateRef: TemplateRef,
|
||||
public renderer: DomRenderer, @Inject(ANCHOR_ELEMENT) anchor) {
|
||||
@Inject(ANCHOR_ELEMENT) anchor) {
|
||||
this.view = null;
|
||||
this.anchor = anchor;
|
||||
}
|
||||
@@ -2347,7 +2369,7 @@ class SomeImperativeViewport {
|
||||
}
|
||||
if (value) {
|
||||
this.view = this.vc.createEmbeddedView(this.templateRef);
|
||||
var nodes = this.renderer.getRootNodes((<ViewRef_>this.view).renderFragment);
|
||||
var nodes = this.view.rootNodes;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.appendChild(this.anchor, nodes[i]);
|
||||
}
|
||||
|
||||
@@ -34,9 +34,12 @@ import {
|
||||
View,
|
||||
ViewContainerRef,
|
||||
ViewEncapsulation,
|
||||
ViewMetadata
|
||||
ViewMetadata,
|
||||
Scope
|
||||
} from 'angular2/core';
|
||||
import {By} from 'angular2/platform/common_dom';
|
||||
import {
|
||||
By,
|
||||
} from 'angular2/platform/common_dom';
|
||||
|
||||
export function main() {
|
||||
describe('projection', () => {
|
||||
@@ -439,6 +442,7 @@ export function main() {
|
||||
var childNodes = DOM.childNodes(main.debugElement.nativeElement);
|
||||
expect(childNodes[0]).toHaveText('div {color: red}SIMPLE1(A)');
|
||||
expect(childNodes[1]).toHaveText('div {color: blue}SIMPLE2(B)');
|
||||
main.destroy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -521,6 +525,47 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should project filled view containers into a view container',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<template manual class="left">B</template>' +
|
||||
'<div class="left">C</div>' +
|
||||
'<div>D</div>' +
|
||||
'</conditional-content>',
|
||||
directives: [ConditionalContentComponent, ManualViewportDirective]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
var conditionalComp =
|
||||
main.debugElement.query(By.directive(ConditionalContentComponent));
|
||||
var viewViewportDir =
|
||||
conditionalComp.query(By.directive(ManualViewportDirective), Scope.view)
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
var contentViewportDir =
|
||||
conditionalComp.query(By.directive(ManualViewportDirective), Scope.light)
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, D)');
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, D)');
|
||||
// first show content viewport, then the view viewport,
|
||||
// i.e. projection needs to take create of already
|
||||
// created views
|
||||
contentViewportDir.show();
|
||||
viewViewportDir.show();
|
||||
expect(main.debugElement.nativeElement).toHaveText('(ABC, D)');
|
||||
|
||||
// hide view viewport, and test that it also hides
|
||||
// the content viewport's views
|
||||
viewViewportDir.hide();
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, D)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
xdescribe,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewFactory', () => {
|
||||
// TODO
|
||||
|
||||
});
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
AfterViewChecked
|
||||
} from 'angular2/core';
|
||||
import {NgIf, NgFor} from 'angular2/common';
|
||||
import {asNativeElements} from 'angular2/core';
|
||||
import {asNativeElements, ViewContainerRef} from 'angular2/core';
|
||||
|
||||
export function main() {
|
||||
describe('Query API', () => {
|
||||
@@ -99,6 +99,8 @@ export function main() {
|
||||
view.debugElement.componentInstance.shouldShow = false;
|
||||
view.detectChanges();
|
||||
|
||||
// TODO: this fails right now!
|
||||
// -> queries are not dirtied!
|
||||
expect(q.log).toEqual([
|
||||
["setter", "foo"],
|
||||
["init", "foo"],
|
||||
@@ -250,8 +252,11 @@ export function main() {
|
||||
view.detectChanges();
|
||||
var needsTpl: NeedsTpl =
|
||||
view.debugElement.componentViewChildren[0].inject(NeedsTpl);
|
||||
expect(needsTpl.query.first.hasLocal('light')).toBe(true);
|
||||
expect(needsTpl.viewQuery.first.hasLocal('shadow')).toBe(true);
|
||||
|
||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).hasLocal('light'))
|
||||
.toBe(true);
|
||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.viewQuery.first).hasLocal('shadow'))
|
||||
.toBe(true);
|
||||
|
||||
async.done();
|
||||
});
|
||||
@@ -892,7 +897,7 @@ class NeedsTpl {
|
||||
viewQuery: QueryList<TemplateRef>;
|
||||
query: QueryList<TemplateRef>;
|
||||
constructor(@ViewQuery(TemplateRef) viewQuery: QueryList<TemplateRef>,
|
||||
@Query(TemplateRef) query: QueryList<TemplateRef>) {
|
||||
@Query(TemplateRef) query: QueryList<TemplateRef>, public vc: ViewContainerRef) {
|
||||
this.viewQuery = viewQuery;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {SpyView, SpyAppViewManager} from '../spies';
|
||||
import {AppView, AppViewContainer} from 'angular2/src/core/linker/view';
|
||||
import {ViewContainerRef, ViewContainerRef_} from 'angular2/src/core/linker/view_container_ref';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
import {ViewRef, ViewRef_} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('ViewContainerRef', () => {
|
||||
var location;
|
||||
var view;
|
||||
var viewManager;
|
||||
|
||||
function createViewContainer() { return new ViewContainerRef_(viewManager, location); }
|
||||
|
||||
beforeEach(() => {
|
||||
viewManager = new SpyAppViewManager();
|
||||
view = new SpyView();
|
||||
view.prop("viewContainers", [null]);
|
||||
location = new ElementRef_(new ViewRef_(view), 0, null);
|
||||
});
|
||||
|
||||
describe('length', () => {
|
||||
|
||||
it('should return a 0 length if there is no underlying AppViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
expect(vc.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return the size of the underlying AppViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
var appVc = new AppViewContainer();
|
||||
view.prop("viewContainers", [appVc]);
|
||||
appVc.views = [<any>new SpyView()];
|
||||
expect(vc.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// TODO: add missing tests here!
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener, SpyProtoViewFactory} from '../spies';
|
||||
import {Injector, provide} from 'angular2/core';
|
||||
|
||||
import {AppProtoView, AppView, AppViewContainer, ViewType} from 'angular2/src/core/linker/view';
|
||||
import {
|
||||
ProtoViewRef,
|
||||
ProtoViewRef_,
|
||||
ViewRef,
|
||||
ViewRef_,
|
||||
internalView
|
||||
} from 'angular2/src/core/linker/view_ref';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {TemplateRef, TemplateRef_} from 'angular2/src/core/linker/template_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
RenderProtoViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {AppViewManager, AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/linker/view_manager_utils';
|
||||
|
||||
import {
|
||||
createHostPv,
|
||||
createComponentPv,
|
||||
createEmbeddedPv,
|
||||
createEmptyElBinder,
|
||||
createNestedElBinder,
|
||||
createProtoElInjector
|
||||
} from './view_manager_utils_spec';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('AppViewManager', () => {
|
||||
var renderer;
|
||||
var utils: AppViewManagerUtils;
|
||||
var viewListener;
|
||||
var viewPool;
|
||||
var linker;
|
||||
var manager: AppViewManager;
|
||||
var createdRenderViews: RenderViewWithFragments[];
|
||||
|
||||
function wrapPv(protoView: AppProtoView): ProtoViewRef { return new ProtoViewRef_(protoView); }
|
||||
|
||||
function wrapView(view: AppView): ViewRef { return new ViewRef_(view); }
|
||||
|
||||
function resetSpies() {
|
||||
viewListener.spy('onViewCreated').reset();
|
||||
viewListener.spy('onViewDestroyed').reset();
|
||||
renderer.spy('createView').reset();
|
||||
renderer.spy('destroyView').reset();
|
||||
renderer.spy('createRootHostView').reset();
|
||||
renderer.spy('setEventDispatcher').reset();
|
||||
renderer.spy('hydrateView').reset();
|
||||
renderer.spy('dehydrateView').reset();
|
||||
viewPool.spy('returnView').reset();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
renderer = new SpyRenderer();
|
||||
utils = new AppViewManagerUtils();
|
||||
viewListener = new SpyAppViewListener();
|
||||
viewPool = new SpyAppViewPool();
|
||||
linker = new SpyProtoViewFactory();
|
||||
manager = new AppViewManager_(viewPool, viewListener, utils, renderer, linker);
|
||||
createdRenderViews = [];
|
||||
|
||||
renderer.spy('createRootHostView')
|
||||
.andCallFake((_a, renderFragmentCount, _b) => {
|
||||
var fragments = [];
|
||||
for (var i = 0; i < renderFragmentCount; i++) {
|
||||
fragments.push(new RenderFragmentRef());
|
||||
}
|
||||
var rv = new RenderViewWithFragments(new RenderViewRef(), fragments);
|
||||
createdRenderViews.push(rv);
|
||||
return rv;
|
||||
});
|
||||
renderer.spy('createView')
|
||||
.andCallFake((_a, renderFragmentCount) => {
|
||||
var fragments = [];
|
||||
for (var i = 0; i < renderFragmentCount; i++) {
|
||||
fragments.push(new RenderFragmentRef());
|
||||
}
|
||||
var rv = new RenderViewWithFragments(new RenderViewRef(), fragments);
|
||||
createdRenderViews.push(rv);
|
||||
return rv;
|
||||
});
|
||||
viewPool.spy('returnView').andReturn(true);
|
||||
});
|
||||
|
||||
describe('createRootHostView', () => {
|
||||
|
||||
var hostProtoView: AppProtoView;
|
||||
beforeEach(
|
||||
() => { hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]); });
|
||||
|
||||
it('should initialize the ProtoView', () => {
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, null);
|
||||
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(hostProtoView);
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
var rootView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(rootView.proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('onViewCreated')).toHaveBeenCalledWith(rootView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = Injector.resolveAndCreate([]);
|
||||
var rootView = internalView(
|
||||
<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, injector));
|
||||
expect(rootView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(rootView.render);
|
||||
});
|
||||
|
||||
it('should create and set the render view using the component selector', () => {
|
||||
var rootView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.render,
|
||||
hostProtoView.mergeInfo.embeddedViewCount + 1, 'someComponent');
|
||||
expect(rootView.render).toBe(createdRenderViews[0].viewRef);
|
||||
expect(rootView.renderFragment).toBe(createdRenderViews[0].fragmentRefs[0]);
|
||||
});
|
||||
|
||||
it('should allow to override the selector', () => {
|
||||
var selector = 'someOtherSelector';
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), selector, null));
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.render,
|
||||
hostProtoView.mergeInfo.embeddedViewCount + 1, selector);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
var rootView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(rootView.render, rootView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyRootHostView', () => {
|
||||
var hostProtoView: AppProtoView;
|
||||
var hostView: AppView;
|
||||
var hostRenderViewRef: RenderViewRef;
|
||||
beforeEach(() => {
|
||||
hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
hostView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(hostView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
it('should destroy the render view', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostRenderViewRef);
|
||||
expect(viewListener.spy('onViewDestroyed')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createEmbeddedViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should initialize the ProtoView', () => {
|
||||
manager.createEmbeddedViewInContainer(vcRef, 0, templateRef);
|
||||
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(childProtoView);
|
||||
});
|
||||
|
||||
describe('create the first view', () => {
|
||||
|
||||
it('should create an AppViewContainer if not yet existing', () => {
|
||||
manager.createEmbeddedViewInContainer(vcRef, 0, templateRef);
|
||||
expect(hostView.viewContainers[1]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should use an existing nested view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('onViewCreated')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the fragment', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(hostView.viewContainers[1].views.length).toBe(1);
|
||||
expect(hostView.viewContainers[1].views[0]).toBe(childView);
|
||||
expect(renderer.spy('attachFragmentAfterElement'))
|
||||
.toHaveBeenCalledWith(vcRef, childView.renderFragment);
|
||||
});
|
||||
|
||||
it('should hydrate the view but not the render view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not set the EventDispatcher', () => {
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(renderer.spy('setEventDispatcher')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create the second view', () => {
|
||||
var firstChildView;
|
||||
beforeEach(() => {
|
||||
firstChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should create a new view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).not.toBe(firstChildView);
|
||||
expect(viewListener.spy('onViewCreated')).toHaveBeenCalledWith(childView);
|
||||
expect(renderer.spy('createView'))
|
||||
.toHaveBeenCalledWith(childProtoView.render,
|
||||
childProtoView.mergeInfo.embeddedViewCount + 1);
|
||||
expect(childView.render).toBe(createdRenderViews[1].viewRef);
|
||||
expect(childView.renderFragment).toBe(createdRenderViews[1].fragmentRefs[0]);
|
||||
});
|
||||
|
||||
it('should attach the fragment', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(hostView.viewContainers[1].views[1]).toBe(childView);
|
||||
expect(renderer.spy('attachFragmentAfterFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment, childView.renderFragment);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(childView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should set the EventDispatcher', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(renderer.spy('setEventDispatcher'))
|
||||
.toHaveBeenCalledWith(childView.render, childView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create another view when the first view has been returned', () => {
|
||||
beforeEach(() => {
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should use an existing nested view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('onViewCreated')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create a host view', () => {
|
||||
|
||||
it('should initialize the ProtoView', () => {
|
||||
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
manager.createHostViewInContainer(vcRef, 0, wrapPv(newHostPv), null);
|
||||
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(newHostPv);
|
||||
});
|
||||
|
||||
it('should always create a new view and not use the embedded view', () => {
|
||||
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
var newHostView = internalView(
|
||||
<ViewRef>manager.createHostViewInContainer(vcRef, 0, wrapPv(newHostPv), null));
|
||||
expect(newHostView.proto).toBe(newHostPv);
|
||||
expect(newHostView).not.toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('onViewCreated')).toHaveBeenCalledWith(newHostView);
|
||||
expect(renderer.spy('createView'))
|
||||
.toHaveBeenCalledWith(newHostPv.render, newHostPv.mergeInfo.embeddedViewCount + 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroyViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
var firstChildView: AppView;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
firstChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
describe('destroy the first view', () => {
|
||||
it('should dehydrate the app view but not the render view', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(firstChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(hostView.viewContainers[1].views).toEqual([]);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('destroy another view', () => {
|
||||
var secondChildView;
|
||||
beforeEach(() => {
|
||||
secondChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(secondChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(secondChildView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(hostView.viewContainers[1].views[0]).toBe(firstChildView);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(secondChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(secondChildView);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('recursively destroy views in ViewContainers', () => {
|
||||
|
||||
describe('destroy child views when a component is destroyed', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
var firstChildView: AppView;
|
||||
var secondChildView: AppView;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(
|
||||
<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
firstChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
secondChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(firstChildView.hydrated()).toBe(false);
|
||||
expect(secondChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(secondChildView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(hostView.viewContainers[1].views).toEqual([]);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(secondChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalledWith(firstChildView);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(secondChildView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('destroy child views over multiple levels', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var nestedChildProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
var nestedVcRefs: ElementRef[];
|
||||
var childViews: AppView[];
|
||||
var nestedChildViews: AppView[];
|
||||
beforeEach(() => {
|
||||
nestedChildProtoView = createEmbeddedPv();
|
||||
childProtoView = createEmbeddedPv([
|
||||
createNestedElBinder(
|
||||
createComponentPv([createNestedElBinder(nestedChildProtoView)]))
|
||||
]);
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(
|
||||
<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
nestedChildViews = [];
|
||||
childViews = [];
|
||||
nestedVcRefs = [];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var view = internalView(manager.createEmbeddedViewInContainer(vcRef, i, templateRef));
|
||||
childViews.push(view);
|
||||
var nestedVcRef = view.elementRefs[view.elementOffset];
|
||||
nestedVcRefs.push(nestedVcRef);
|
||||
for (var j = 0; j < 2; j++) {
|
||||
var nestedView = internalView(
|
||||
manager.createEmbeddedViewInContainer(nestedVcRef, j, templateRef));
|
||||
nestedChildViews.push(nestedView);
|
||||
}
|
||||
}
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate all child views', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
childViews.forEach((childView) => expect(childView.hydrated()).toBe(false));
|
||||
nestedChildViews.forEach((childView) => expect(childView.hydrated()).toBe(false));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('detachViewInContainer', () => {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit,
|
||||
Log,
|
||||
SpyObject
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {
|
||||
SpyChangeDetector,
|
||||
SpyProtoElementInjector,
|
||||
SpyElementInjector,
|
||||
SpyPreBuiltObjects
|
||||
} from '../spies';
|
||||
|
||||
import {Injector, provide} from 'angular2/core';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
AppProtoView,
|
||||
AppView,
|
||||
AppProtoViewMergeInfo,
|
||||
ViewType
|
||||
} from 'angular2/src/core/linker/view';
|
||||
import {ElementBinder} from 'angular2/src/core/linker/element_binder';
|
||||
import {
|
||||
DirectiveProvider,
|
||||
ElementInjector,
|
||||
PreBuiltObjects,
|
||||
ProtoElementInjector
|
||||
} from 'angular2/src/core/linker/element_injector';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {Component} from 'angular2/src/core/metadata';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/linker/view_manager_utils';
|
||||
import {RenderViewWithFragments} from 'angular2/core';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add more tests here!
|
||||
|
||||
describe('AppViewManagerUtils', () => {
|
||||
|
||||
var utils: AppViewManagerUtils;
|
||||
|
||||
beforeEach(() => { utils = new AppViewManagerUtils(); });
|
||||
|
||||
function createViewWithChildren(pv: AppProtoView): AppView {
|
||||
var renderViewWithFragments = new RenderViewWithFragments(null, [null, null]);
|
||||
return utils.createView(pv, renderViewWithFragments, null, null);
|
||||
}
|
||||
|
||||
describe('shared hydrate functionality', () => {
|
||||
|
||||
it("should hydrate the change detector after hydrating element injectors", () => {
|
||||
var log = new Log();
|
||||
|
||||
var componentProtoView = createComponentPv([createEmptyElBinder()]);
|
||||
var hostView =
|
||||
createViewWithChildren(createHostPv([createNestedElBinder(componentProtoView)]));
|
||||
var componentView = hostView.views[1];
|
||||
|
||||
var spyEi = <any>componentView.elementInjectors[0];
|
||||
spyEi.spy('hydrate').andCallFake(log.fn('hydrate'));
|
||||
|
||||
var spyCd = <any>componentView.changeDetector;
|
||||
spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD'));
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(log.result()).toEqual('hydrate; hydrateCD');
|
||||
});
|
||||
|
||||
it("should set up event listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv =
|
||||
createHostPv([createNestedElBinder(createComponentPv()), createEmptyElBinder()]);
|
||||
var hostView = createViewWithChildren(hostPv);
|
||||
var spyEventAccessor1 = SpyObject.stub({"subscribe": null});
|
||||
SpyObject.stub(
|
||||
hostView.elementInjectors[0],
|
||||
{'getEventEmitterAccessors': [[spyEventAccessor1]], 'getDirectiveAtIndex': dir});
|
||||
var spyEventAccessor2 = SpyObject.stub({"subscribe": null});
|
||||
SpyObject.stub(
|
||||
hostView.elementInjectors[1],
|
||||
{'getEventEmitterAccessors': [[spyEventAccessor2]], 'getDirectiveAtIndex': dir});
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
|
||||
it("should not hydrate element injectors of component views inside of embedded fragments",
|
||||
() => {
|
||||
var hostView = createViewWithChildren(createHostPv([
|
||||
createNestedElBinder(createComponentPv([
|
||||
createNestedElBinder(createEmbeddedPv(
|
||||
[createNestedElBinder(createComponentPv([createEmptyElBinder()]))]))
|
||||
]))
|
||||
]));
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
expect(hostView.elementInjectors.length).toBe(4);
|
||||
expect((<any>hostView.elementInjectors[3]).spy('hydrate')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
var parentView, contextView, childView;
|
||||
|
||||
function createViews(numInj = 1) {
|
||||
var childPv = createEmbeddedPv([createEmptyElBinder()]);
|
||||
childView = createViewWithChildren(childPv);
|
||||
|
||||
var parentPv = createHostPv([createEmptyElBinder()]);
|
||||
parentView = createViewWithChildren(parentPv);
|
||||
|
||||
var binders = [];
|
||||
for (var i = 0; i < numInj; i++) {
|
||||
binders.push(createEmptyElBinder(i > 0 ? binders[i - 1] : null))
|
||||
}
|
||||
var contextPv = createHostPv(binders);
|
||||
contextView = createViewWithChildren(contextPv);
|
||||
}
|
||||
|
||||
it('should not modify the rootElementInjectors at the given context view', () => {
|
||||
createViews();
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||
expect(contextView.rootElementInjectors.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should link the views rootElementInjectors after the elementInjector at the given context',
|
||||
() => {
|
||||
createViews(2);
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 1, 0, childView);
|
||||
expect(childView.rootElementInjectors[0].spy('link'))
|
||||
.toHaveBeenCalledWith(contextView.elementInjectors[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hydrateViewInContainer', () => {
|
||||
var parentView, contextView, childView;
|
||||
|
||||
function createViews() {
|
||||
var parentPv = createHostPv([createEmptyElBinder()]);
|
||||
parentView = createViewWithChildren(parentPv);
|
||||
|
||||
var contextPv = createHostPv([createEmptyElBinder()]);
|
||||
contextView = createViewWithChildren(contextPv);
|
||||
|
||||
var childPv = createEmbeddedPv([createEmptyElBinder()]);
|
||||
childView = createViewWithChildren(childPv);
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||
}
|
||||
|
||||
it("should instantiate the elementInjectors with the host of the context's elementInjector",
|
||||
() => {
|
||||
createViews();
|
||||
|
||||
utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null);
|
||||
expect(childView.rootElementInjectors[0].spy('hydrate'))
|
||||
.toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(),
|
||||
childView.preBuiltObjects[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hydrateRootHostView', () => {
|
||||
var hostView;
|
||||
|
||||
function createViews() {
|
||||
var hostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
hostView = createViewWithChildren(hostPv);
|
||||
}
|
||||
|
||||
it("should instantiate the elementInjectors with the given injector and an empty host element injector",
|
||||
() => {
|
||||
var injector = createInjector();
|
||||
createViews();
|
||||
|
||||
utils.hydrateRootHostView(hostView, injector);
|
||||
expect(hostView.rootElementInjectors[0].spy('hydrate'))
|
||||
.toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function createInjector() {
|
||||
return Injector.resolveAndCreate([]);
|
||||
}
|
||||
|
||||
function createElementInjector(parent = null) {
|
||||
var host = new SpyElementInjector();
|
||||
var elementInjector = new SpyElementInjector();
|
||||
var _preBuiltObjects = null;
|
||||
var res = SpyObject.stub(elementInjector, {
|
||||
'isExportingComponent': false,
|
||||
'isExportingElement': false,
|
||||
'getEventEmitterAccessors': [],
|
||||
'getHostActionAccessors': [],
|
||||
'getComponent': new Object(),
|
||||
'getHost': host
|
||||
});
|
||||
res.spy('getNestedView').andCallFake(() => _preBuiltObjects.nestedView);
|
||||
res.spy('hydrate')
|
||||
.andCallFake((mperativelyCreatedInjector: Injector, host: ElementInjector,
|
||||
preBuiltObjects: PreBuiltObjects) => { _preBuiltObjects = preBuiltObjects; });
|
||||
res.prop('parent', parent);
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createProtoElInjector(parent: ProtoElementInjector = null): ProtoElementInjector {
|
||||
var pei = new SpyProtoElementInjector();
|
||||
pei.prop("parent", parent);
|
||||
pei.prop("index", 0);
|
||||
pei.spy('instantiate').andCallFake((parentEli) => createElementInjector(parentEli));
|
||||
return <any>pei;
|
||||
}
|
||||
|
||||
export function createEmptyElBinder(parent: ElementBinder = null) {
|
||||
var parentPeli = isPresent(parent) ? parent.protoElementInjector : null;
|
||||
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null, null);
|
||||
}
|
||||
|
||||
export function createNestedElBinder(nestedProtoView: AppProtoView) {
|
||||
var componentProvider = null;
|
||||
if (nestedProtoView.type === ViewType.COMPONENT) {
|
||||
var annotation = new DirectiveResolver().resolve(SomeComponent);
|
||||
componentProvider = DirectiveProvider.createFromType(SomeComponent, annotation);
|
||||
}
|
||||
return new ElementBinder(0, null, 0, createProtoElInjector(), componentProvider, nestedProtoView);
|
||||
}
|
||||
|
||||
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, [], type, true, (_) => new SpyChangeDetector(),
|
||||
new Map<string, any>(), null);
|
||||
var mergedElementCount = 0;
|
||||
var mergedEmbeddedViewCount = 0;
|
||||
var mergedViewCount = 1;
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
var binder = binders[i];
|
||||
binder.protoElementInjector.index = i;
|
||||
mergedElementCount++;
|
||||
var nestedPv = binder.nestedProtoView;
|
||||
if (isPresent(nestedPv)) {
|
||||
mergedElementCount += nestedPv.mergeInfo.elementCount;
|
||||
mergedEmbeddedViewCount += nestedPv.mergeInfo.embeddedViewCount;
|
||||
mergedViewCount += nestedPv.mergeInfo.viewCount;
|
||||
if (nestedPv.type === ViewType.EMBEDDED) {
|
||||
mergedEmbeddedViewCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
var mergeInfo =
|
||||
new AppProtoViewMergeInfo(mergedEmbeddedViewCount, mergedElementCount, mergedViewCount);
|
||||
res.init(null, binders, 0, mergeInfo, new Map<string, number>());
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createHostPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.HOST, binders);
|
||||
}
|
||||
|
||||
export function createComponentPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.COMPONENT, binders);
|
||||
}
|
||||
|
||||
export function createEmbeddedPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.EMBEDDED, binders);
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'someComponent'})
|
||||
class SomeComponent {
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/testing_internal';
|
||||
import {AppViewPool} from 'angular2/src/core/linker/view_pool';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/linker/view';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('AppViewPool', () => {
|
||||
|
||||
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||
|
||||
function createProtoView() {
|
||||
return new AppProtoView(null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function createView(pv) {
|
||||
return new AppView(null, pv, null, null, null, new Map<string, any>(), null, null, null);
|
||||
}
|
||||
|
||||
it('should support multiple AppProtoViews', () => {
|
||||
var vf = createViewPool({capacity: 2});
|
||||
var pv1 = createProtoView();
|
||||
var pv2 = createProtoView();
|
||||
var view1 = createView(pv1);
|
||||
var view2 = createView(pv2);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv1)).toBe(view1);
|
||||
expect(vf.getView(pv2)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should reuse the newest view that has been returned', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewPool({capacity: 2});
|
||||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should not add views when the capacity has been reached', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewPool({capacity: 2});
|
||||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
var view3 = createView(pv);
|
||||
expect(vf.returnView(view1)).toBe(true);
|
||||
expect(vf.returnView(view2)).toBe(true);
|
||||
expect(vf.returnView(view3)).toBe(false);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
expect(vf.getView(pv)).toBe(view1);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1,740 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
stringifyElement
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import * as appCmds from 'angular2/src/core/linker/template_commands';
|
||||
import {
|
||||
createRenderView,
|
||||
encapsulateStyles,
|
||||
NodeFactory
|
||||
} from 'angular2/src/core/render/view_factory';
|
||||
import {
|
||||
RenderTemplateCmd,
|
||||
RenderBeginElementCmd,
|
||||
RenderComponentTemplate
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {SpyRenderEventDispatcher} from '../spies';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
|
||||
function beginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
isBound: boolean, ngContentIndex: number): RenderBeginElementCmd {
|
||||
return new appCmds.BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, [], [], isBound,
|
||||
ngContentIndex)
|
||||
}
|
||||
|
||||
function endElement() {
|
||||
return new appCmds.EndElementCmd();
|
||||
}
|
||||
|
||||
function text(value: string, isBound: boolean, ngContentIndex: number) {
|
||||
return new appCmds.TextCmd(value, isBound, ngContentIndex);
|
||||
}
|
||||
|
||||
function embeddedTemplate(attrNameAndValues: string[], isMerged: boolean, ngContentIndex: number,
|
||||
children: any[]) {
|
||||
return new appCmds.EmbeddedTemplateCmd(attrNameAndValues, [], [], isMerged, ngContentIndex, null,
|
||||
children);
|
||||
}
|
||||
|
||||
function beginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
ngContentIndex: number, templateId: string) {
|
||||
return new appCmds.BeginComponentCmd(
|
||||
name, attrNameAndValues, eventTargetAndNames, [], [], null, ngContentIndex,
|
||||
() => new appCmds.CompiledComponentTemplate(templateId, null, null, null));
|
||||
}
|
||||
|
||||
function endComponent() {
|
||||
return new appCmds.EndComponentCmd();
|
||||
}
|
||||
|
||||
function ngContent(index: number, ngContentIndex: number) {
|
||||
return new appCmds.NgContentCmd(index, ngContentIndex);
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('createRenderView', () => {
|
||||
var nodeFactory: DomNodeFactory;
|
||||
var eventDispatcher: SpyRenderEventDispatcher;
|
||||
var componentTemplates = new Map<string, RenderComponentTemplate | RenderTemplateCmd[]>();
|
||||
var defaultCmpTpl: RenderComponentTemplate;
|
||||
|
||||
beforeEach(() => {
|
||||
nodeFactory = new DomNodeFactory(componentTemplates);
|
||||
eventDispatcher = new SpyRenderEventDispatcher();
|
||||
defaultCmpTpl =
|
||||
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.None, [], []);
|
||||
});
|
||||
|
||||
describe('primitives', () => {
|
||||
|
||||
it('should create elements with attributes', () => {
|
||||
var view = createRenderView(
|
||||
defaultCmpTpl,
|
||||
[beginElement('div', ['attr1', 'value1'], [], false, null), endElement()], null,
|
||||
nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div attr1="value1"></div>');
|
||||
});
|
||||
|
||||
it('should create host elements with attributes', () => {
|
||||
componentTemplates.set('0', []);
|
||||
var view = createRenderView(
|
||||
defaultCmpTpl,
|
||||
[beginComponent('a-comp', ['attr1', 'value1'], [], null, '0'), endElement()], null,
|
||||
nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<a-comp attr1="value1"></a-comp>');
|
||||
});
|
||||
|
||||
it('should create embedded templates with attributes', () => {
|
||||
componentTemplates.set('0', []);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[embeddedTemplate(['attr1', 'value1'], false, null, [])], null,
|
||||
nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<template attr1="value1"></template>');
|
||||
});
|
||||
|
||||
it('should store bound elements', () => {
|
||||
componentTemplates.set('0', []);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('div', ['id', '1'], [], false, null),
|
||||
endElement(),
|
||||
beginElement('span', ['id', '2'], [], true, null),
|
||||
endElement(),
|
||||
beginComponent('a-comp', ['id', '3'], [], null, '0'),
|
||||
endElement(),
|
||||
embeddedTemplate(['id', '4'], false, null, [])
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(mapAttrs(view.boundElements, 'id')).toEqual(['2', '3', '4']);
|
||||
});
|
||||
|
||||
it('should use the inplace element for the first create element', () => {
|
||||
var el = DOM.createElement('span');
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('div', ['attr1', 'value1'], [], false, null),
|
||||
endElement(),
|
||||
beginElement('div', [], [], false, null),
|
||||
endElement()
|
||||
],
|
||||
el, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<span attr1="value1"></span><div></div>');
|
||||
});
|
||||
|
||||
it('should create text nodes', () => {
|
||||
var view =
|
||||
createRenderView(defaultCmpTpl, [text('someText', false, null)], null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('someText');
|
||||
});
|
||||
|
||||
it('should store bound text nodes', () => {
|
||||
var view = createRenderView(defaultCmpTpl, [text('1', false, null), text('2', true, null)],
|
||||
null, nodeFactory);
|
||||
expect(stringifyElement(view.boundTextNodes[0])).toEqual('2');
|
||||
});
|
||||
|
||||
it('should register element event listeners', () => {
|
||||
componentTemplates.set('0', []);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('div', [], [null, 'click'], true, null),
|
||||
endElement(),
|
||||
beginComponent('a-comp', [], [null, 'click'], null, '0'),
|
||||
endElement(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
view.setEventDispatcher(<any>eventDispatcher);
|
||||
var event = {};
|
||||
nodeFactory.triggerLocalEvent(view.boundElements[0], 'click', event);
|
||||
nodeFactory.triggerLocalEvent(view.boundElements[1], 'click', event);
|
||||
expect(eventDispatcher.spy('dispatchRenderEvent'))
|
||||
.toHaveBeenCalledWith(0, 'click', MapWrapper.createFromStringMap({'$event': event}));
|
||||
expect(eventDispatcher.spy('dispatchRenderEvent'))
|
||||
.toHaveBeenCalledWith(1, 'click', MapWrapper.createFromStringMap({'$event': event}));
|
||||
});
|
||||
|
||||
it('should register element global event listeners', () => {
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('div', [], ['window', 'scroll'], true, null),
|
||||
endElement(),
|
||||
beginComponent('a-comp', [], ['window', 'scroll'], null, '0'),
|
||||
endElement(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
view.hydrate();
|
||||
view.setEventDispatcher(<any>eventDispatcher);
|
||||
var event = {};
|
||||
nodeFactory.triggerGlobalEvent('window', 'scroll', event);
|
||||
expect(eventDispatcher.spy('dispatchRenderEvent'))
|
||||
.toHaveBeenCalledWith(0, 'window:scroll',
|
||||
MapWrapper.createFromStringMap({'$event': event}));
|
||||
expect(eventDispatcher.spy('dispatchRenderEvent'))
|
||||
.toHaveBeenCalledWith(1, 'window:scroll',
|
||||
MapWrapper.createFromStringMap({'$event': event}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('nested nodes', () => {
|
||||
it('should create nested node', () => {
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('a', [], [], false, null),
|
||||
beginElement('b', [], [], false, null),
|
||||
text('someText', false, null),
|
||||
endElement(),
|
||||
endElement(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<a><b>someText</b></a>');
|
||||
});
|
||||
|
||||
it('should store bound elements in depth first order', () => {
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('a', ['id', '1'], [], false, null),
|
||||
endElement(),
|
||||
beginElement('a', ['id', '2'], [], true, null),
|
||||
beginElement('a', ['id', '3'], [], false, null),
|
||||
endElement(),
|
||||
beginElement('a', ['id', '4'], [], true, null),
|
||||
endElement(),
|
||||
endElement(),
|
||||
beginElement('a', ['id', '5'], [], false, null),
|
||||
endElement(),
|
||||
beginElement('a', ['id', '6'], [], true, null),
|
||||
endElement(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(mapAttrs(view.boundElements, 'id')).toEqual(['2', '4', '6']);
|
||||
});
|
||||
|
||||
it('should store bound text nodes in depth first order', () => {
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
text('1', false, null),
|
||||
text('2', true, null),
|
||||
beginElement('a', [], [], false, null),
|
||||
text('3', false, null),
|
||||
text('4', true, null),
|
||||
endElement(),
|
||||
text('5', false, null),
|
||||
text('6', true, null),
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(mapText(view.boundTextNodes)).toEqual(['2', '4', '6']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('merged embedded templates',
|
||||
() => {
|
||||
it('should create separate fragments', () => {
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
embeddedTemplate(['attr1', 'value1'], true, null,
|
||||
[text('someText', false, null)])
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(view.fragments.length).toBe(2);
|
||||
expect(stringifyFragment(view.fragments[1].nodes)).toEqual('someText');
|
||||
});
|
||||
|
||||
it('should store bound elements after the bound elements of earlier fragments',
|
||||
() => {
|
||||
var view =
|
||||
createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('a', ['id', '1.1'], [], true, null),
|
||||
endElement(),
|
||||
embeddedTemplate(['id', '1.2'], true, null,
|
||||
[
|
||||
embeddedTemplate(['id', '2.1'], true, null,
|
||||
[
|
||||
beginElement('a', ['id', '3.1'],
|
||||
[], true, null),
|
||||
endElement()
|
||||
]),
|
||||
beginElement('a', ['id', '2.2'], [], true, null),
|
||||
endElement(),
|
||||
]),
|
||||
beginElement('a', ['id', '1.3'], [], true, null),
|
||||
endElement(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(mapAttrs(view.boundElements, 'id'))
|
||||
.toEqual(['1.1', '1.2', '1.3', '2.1', '2.2', '3.1']);
|
||||
});
|
||||
|
||||
it('should store bound text nodes after the bound text nodes of earlier fragments',
|
||||
() => {
|
||||
var view =
|
||||
createRenderView(defaultCmpTpl,
|
||||
[
|
||||
text('1.1', true, null),
|
||||
embeddedTemplate(['id', '1.2'], true, null,
|
||||
[
|
||||
text('2.1', true, null),
|
||||
embeddedTemplate(['id', '2.1'], true, null,
|
||||
[
|
||||
text('3.1', true, null),
|
||||
]),
|
||||
text('2.2', true, null),
|
||||
]),
|
||||
text('1.2', true, null),
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(mapText(view.boundTextNodes))
|
||||
.toEqual(['1.1', '1.2', '2.1', '2.2', '3.1']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('non merged embedded templates', () => {
|
||||
it('should only create the anchor element', () => {
|
||||
var view =
|
||||
createRenderView(defaultCmpTpl,
|
||||
[
|
||||
embeddedTemplate(['id', '1.1'], false, null,
|
||||
[
|
||||
text('someText', true, null),
|
||||
beginElement('a', ['id', '2.1'], [], true, null),
|
||||
endElement()
|
||||
])
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(view.fragments.length).toBe(1);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<template id="1.1"></template>');
|
||||
expect(view.boundTextNodes.length).toBe(0);
|
||||
expect(mapAttrs(view.boundElements, 'id')).toEqual(['1.1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
it('should store the component template in the same fragment', () => {
|
||||
componentTemplates.set('0', [
|
||||
text('hello', false, null),
|
||||
]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[beginComponent('my-comp', [], [], null, '0'), endComponent()],
|
||||
null, nodeFactory);
|
||||
expect(view.fragments.length).toBe(1);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp>hello</my-comp>');
|
||||
});
|
||||
|
||||
it('should use native shadow DOM', () => {
|
||||
componentTemplates.set(
|
||||
'0', new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Native,
|
||||
[
|
||||
text('hello', false, null),
|
||||
],
|
||||
[]));
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[beginComponent('my-comp', [], [], null, '0'), endComponent()],
|
||||
null, nodeFactory);
|
||||
expect(view.fragments.length).toBe(1);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<my-comp><shadow-root>hello</shadow-root></my-comp>');
|
||||
});
|
||||
|
||||
it('should store bound elements after the bound elements of the main template', () => {
|
||||
componentTemplates.set('0', [
|
||||
beginComponent('b-comp', ['id', '2.1'], [], null, '1'),
|
||||
endComponent(),
|
||||
beginComponent('b-comp', ['id', '2.2'], [], null, '1'),
|
||||
endComponent(),
|
||||
]);
|
||||
componentTemplates.set('1',
|
||||
[beginElement('a', ['id', '3.1'], [], true, null), endElement()]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginElement('a', ['id', '1.1'], [], true, null),
|
||||
endElement(),
|
||||
beginComponent('a-comp', ['id', '1.2'], [], null, '0'),
|
||||
beginElement('a', ['id', '1.3'], [], true, null),
|
||||
endElement(),
|
||||
endComponent(),
|
||||
beginElement('a', ['id', '1.4'], [], true, null),
|
||||
endElement(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
|
||||
expect(mapAttrs(view.boundElements, 'id'))
|
||||
.toEqual(['1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '3.1', '3.1']);
|
||||
});
|
||||
|
||||
it('should store bound elements from the view before bound elements from content components',
|
||||
() => {
|
||||
componentTemplates.set('0', [
|
||||
beginElement('a', ['id', '2.1'], [], true, null),
|
||||
endElement(),
|
||||
]);
|
||||
componentTemplates.set('1', [
|
||||
beginElement('a', ['id', '3.1'], [], true, null),
|
||||
endElement(),
|
||||
]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginComponent('a-comp', ['id', '1.1'], [], null, '0'),
|
||||
beginComponent('b-comp', ['id', '1.2'], [], null, '1'),
|
||||
endComponent(),
|
||||
endComponent(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
|
||||
expect(mapAttrs(view.boundElements, 'id')).toEqual(['1.1', '1.2', '2.1', '3.1']);
|
||||
});
|
||||
|
||||
it('should process nested components in depth first order', () => {
|
||||
componentTemplates.set('0', [
|
||||
beginComponent('b11-comp', ['id', '2.1'], [], null, '2'),
|
||||
endComponent(),
|
||||
beginComponent('b12-comp', ['id', '2.2'], [], null, '3'),
|
||||
endComponent(),
|
||||
]);
|
||||
componentTemplates.set('1', [
|
||||
beginComponent('b21-comp', ['id', '3.1'], [], null, '4'),
|
||||
endComponent(),
|
||||
beginComponent('b22-comp', ['id', '3.2'], [], null, '5'),
|
||||
endComponent(),
|
||||
]);
|
||||
componentTemplates.set('2', [
|
||||
beginElement('b11', ['id', '4.11'], [], true, null),
|
||||
endElement(),
|
||||
]);
|
||||
componentTemplates.set('3', [
|
||||
beginElement('b12', ['id', '4.12'], [], true, null),
|
||||
endElement(),
|
||||
]);
|
||||
componentTemplates.set('4', [
|
||||
beginElement('b21', ['id', '4.21'], [], true, null),
|
||||
endElement(),
|
||||
]);
|
||||
componentTemplates.set('5', [
|
||||
beginElement('b22', ['id', '4.22'], [], true, null),
|
||||
endElement(),
|
||||
]);
|
||||
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginComponent('a1-comp', ['id', '1.1'], [], null, '0'),
|
||||
endComponent(),
|
||||
beginComponent('a2-comp', ['id', '1.2'], [], null, '1'),
|
||||
endComponent(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
|
||||
expect(mapAttrs(view.boundElements, 'id'))
|
||||
.toEqual(['1.1', '1.2', '2.1', '2.2', '4.11', '4.12', '3.1', '3.2', '4.21', '4.22']);
|
||||
});
|
||||
|
||||
|
||||
it('should store bound text nodes after the bound text nodes of the main template', () => {
|
||||
componentTemplates.set('0', [
|
||||
text('2.1', true, null),
|
||||
beginComponent('b-comp', [], [], null, '1'),
|
||||
endComponent(),
|
||||
beginComponent('b-comp', [], [], null, '1'),
|
||||
endComponent(),
|
||||
text('2.2', true, null),
|
||||
]);
|
||||
componentTemplates.set('1', [
|
||||
text('3.1', true, null),
|
||||
]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
text('1.1', true, null),
|
||||
beginComponent('a-comp', [], [], null, '0'),
|
||||
text('1.2', true, null),
|
||||
endComponent(),
|
||||
text('1.3', true, null),
|
||||
],
|
||||
null, nodeFactory);
|
||||
|
||||
expect(mapText(view.boundTextNodes))
|
||||
.toEqual(['1.1', '1.2', '1.3', '2.1', '2.2', '3.1', '3.1']);
|
||||
});
|
||||
});
|
||||
|
||||
it('should store bound text nodes from the view before bound text nodes from content components',
|
||||
() => {
|
||||
componentTemplates.set('0', [text('2.1', true, null)]);
|
||||
componentTemplates.set('1', [text('3.1', true, null)]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginComponent('a-comp', [], [], null, '0'),
|
||||
beginComponent('b-comp', [], [], null, '1'),
|
||||
endComponent(),
|
||||
endComponent(),
|
||||
],
|
||||
null, nodeFactory);
|
||||
|
||||
expect(mapText(view.boundTextNodes)).toEqual(['2.1', '3.1']);
|
||||
});
|
||||
|
||||
describe('content projection', () => {
|
||||
it('should remove non projected nodes', () => {
|
||||
componentTemplates.set('0', []);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginComponent('my-comp', [], [], null, '0'),
|
||||
text('hello', false, null),
|
||||
endComponent()
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp></my-comp>');
|
||||
});
|
||||
|
||||
it('should keep non projected nodes in the light dom when using native shadow dom', () => {
|
||||
componentTemplates.set('0', new RenderComponentTemplate('someId', 'shortid',
|
||||
ViewEncapsulation.Native, [], []));
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginComponent('my-comp', [], [], null, '0'),
|
||||
text('hello', false, null),
|
||||
endComponent()
|
||||
],
|
||||
null, nodeFactory);
|
||||
var rootEl = view.fragments[0].nodes[0];
|
||||
expect(stringifyElement(rootEl))
|
||||
.toEqual('<my-comp><shadow-root></shadow-root>hello</my-comp>');
|
||||
});
|
||||
|
||||
it('should project commands based on their ngContentIndex', () => {
|
||||
componentTemplates.set('0', [
|
||||
text('(', false, null),
|
||||
ngContent(0, null),
|
||||
text(',', false, null),
|
||||
ngContent(1, null),
|
||||
text(')', false, null)
|
||||
]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[
|
||||
beginComponent('my-comp', [], [], null, '0'),
|
||||
text('2', false, 1),
|
||||
text('1', false, 0),
|
||||
endComponent()
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<my-comp>(1,2)</my-comp>');
|
||||
});
|
||||
|
||||
it('should reproject nodes over multiple ng-content commands', () => {
|
||||
componentTemplates.set(
|
||||
'0', [beginComponent('b-comp', [], [], null, '1'), ngContent(0, 0), endComponent()]);
|
||||
componentTemplates.set(
|
||||
'1', [text('(', false, null), ngContent(0, null), text(')', false, null)]);
|
||||
var view = createRenderView(
|
||||
defaultCmpTpl,
|
||||
[beginComponent('a-comp', [], [], null, '0'), text('hello', false, 0), endComponent()],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<a-comp><b-comp>(hello)</b-comp></a-comp>');
|
||||
});
|
||||
|
||||
|
||||
it('should store content injection points for root component in a view', () => {
|
||||
componentTemplates.set('0', [ngContent(0, null)]);
|
||||
var view = createRenderView(defaultCmpTpl,
|
||||
[beginComponent('a-comp', [], [], null, '0'), endComponent()],
|
||||
DOM.createElement('root'), nodeFactory);
|
||||
expect(stringifyFragment(view.rootContentInsertionPoints))
|
||||
.toEqual('<root-content-insertion-point></root-content-insertion-point>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('view encapsulation', () => {
|
||||
it('should not add attributes to elements in template with ViewEncapsulation.None', () => {
|
||||
var tpl = new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.None, [], []);
|
||||
var view = createRenderView(tpl, [beginElement('div', [], [], false, null), endElement()],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div></div>');
|
||||
});
|
||||
|
||||
it('should not add attributes to elements in template with ViewEncapsulation.Native', () => {
|
||||
var tpl =
|
||||
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Native, [], []);
|
||||
var view = createRenderView(tpl, [beginElement('div', [], [], false, null), endElement()],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<div></div>');
|
||||
});
|
||||
|
||||
describe('ViewEncapsulation.Emulated', () => {
|
||||
var encapsulatedTpl;
|
||||
|
||||
beforeEach(() => {
|
||||
encapsulatedTpl =
|
||||
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Emulated, [], []);
|
||||
});
|
||||
|
||||
it('should add marker attributes to content elements', () => {
|
||||
var view = createRenderView(encapsulatedTpl,
|
||||
[beginElement('div', [], [], false, null), endElement()],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual('<div _ngcontent-shortid=""></div>');
|
||||
});
|
||||
|
||||
it('should add marker attributes to content elements in merged embedded templates', () => {
|
||||
var view = createRenderView(
|
||||
encapsulatedTpl,
|
||||
[
|
||||
embeddedTemplate([], true, null,
|
||||
[beginElement('div', [], [], false, null), endElement()])
|
||||
],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes)).toEqual('<template></template>');
|
||||
expect(stringifyFragment(view.fragments[1].nodes))
|
||||
.toEqual('<div _ngcontent-shortid=""></div>');
|
||||
});
|
||||
|
||||
it('should add marker attributes to host elements and content elements of nested components',
|
||||
() => {
|
||||
componentTemplates.set(
|
||||
'0', new RenderComponentTemplate(
|
||||
'innerComp', 'innerid', ViewEncapsulation.Emulated,
|
||||
[beginElement('div', [], [], false, null), endElement()], []));
|
||||
var view = createRenderView(
|
||||
defaultCmpTpl, [beginComponent('my-comp', [], [], null, '0'), endComponent()],
|
||||
null, nodeFactory);
|
||||
expect(stringifyFragment(view.fragments[0].nodes))
|
||||
.toEqual(
|
||||
'<my-comp _nghost-innerid=""><div _ngcontent-innerid=""></div></my-comp>');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('encapsulateStyles', () => {
|
||||
const input = 'div[%COMP%] {}';
|
||||
|
||||
it('should not change styles for ViewEncapsulation.Native', () => {
|
||||
var tpl =
|
||||
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Native, [], [input]);
|
||||
expect(encapsulateStyles(tpl)).toEqual([input]);
|
||||
});
|
||||
|
||||
it('should not change styles for ViewEncapsulation.None', () => {
|
||||
var tpl =
|
||||
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.None, [], [input]);
|
||||
expect(encapsulateStyles(tpl)).toEqual([input]);
|
||||
});
|
||||
|
||||
it('should change styles for ViewEncapsulation.Emulated', () => {
|
||||
var tpl =
|
||||
new RenderComponentTemplate('someId', 'shortid', ViewEncapsulation.Emulated, [], [input]);
|
||||
expect(encapsulateStyles(tpl)).toEqual(['div[shortid] {}']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class DomNodeFactory implements NodeFactory<Node> {
|
||||
private _globalEventListeners: GlobalEventListener[] = [];
|
||||
private _localEventListeners: LocalEventListener[] = [];
|
||||
|
||||
constructor(private _components: Map<string, RenderComponentTemplate | RenderTemplateCmd[]>) {}
|
||||
|
||||
triggerLocalEvent(el: Element, eventName: string, event: any) {
|
||||
this._localEventListeners.forEach(listener => {
|
||||
if (listener.eventName == eventName) {
|
||||
listener.callback(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
triggerGlobalEvent(target: string, eventName: string, event: any) {
|
||||
this._globalEventListeners.forEach(listener => {
|
||||
if (listener.eventName == eventName && listener.target == target) {
|
||||
listener.callback(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resolveComponentTemplate(templateId: string): RenderComponentTemplate {
|
||||
var data = this._components.get(templateId);
|
||||
if (data instanceof RenderComponentTemplate) {
|
||||
return data;
|
||||
} else {
|
||||
return new RenderComponentTemplate(templateId, templateId, ViewEncapsulation.None,
|
||||
<RenderTemplateCmd[]>data, []);
|
||||
}
|
||||
}
|
||||
createTemplateAnchor(attrNameAndValues: string[]): Node {
|
||||
var el = DOM.createElement('template');
|
||||
this._setAttributes(el, attrNameAndValues);
|
||||
return el;
|
||||
}
|
||||
createElement(name: string, attrNameAndValues: string[]): Node {
|
||||
var el = DOM.createElement(name);
|
||||
this._setAttributes(el, attrNameAndValues);
|
||||
return el;
|
||||
}
|
||||
mergeElement(existing: Node, attrNameAndValues: string[]) {
|
||||
DOM.clearNodes(existing);
|
||||
this._setAttributes(existing, attrNameAndValues);
|
||||
}
|
||||
private _setAttributes(el: Node, attrNameAndValues: string[]) {
|
||||
for (var attrIdx = 0; attrIdx < attrNameAndValues.length; attrIdx += 2) {
|
||||
DOM.setAttribute(el, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]);
|
||||
}
|
||||
}
|
||||
createShadowRoot(host: Node, templateId: string): Node {
|
||||
var root = DOM.createElement('shadow-root');
|
||||
DOM.appendChild(host, root);
|
||||
return root;
|
||||
}
|
||||
createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
|
||||
createRootContentInsertionPoint(): Node {
|
||||
return DOM.createElement('root-content-insertion-point');
|
||||
}
|
||||
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
|
||||
on(element: Node, eventName: string, callback: Function) {
|
||||
this._localEventListeners.push(new LocalEventListener(element, eventName, callback));
|
||||
}
|
||||
globalOn(target: string, eventName: string, callback: Function): Function {
|
||||
var listener = new GlobalEventListener(target, eventName, callback);
|
||||
this._globalEventListeners.push(listener);
|
||||
return () => {
|
||||
var index = this._globalEventListeners.indexOf(listener);
|
||||
if (index !== -1) {
|
||||
this._globalEventListeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LocalEventListener {
|
||||
constructor(public element: Node, public eventName: string, public callback: Function) {}
|
||||
}
|
||||
|
||||
class GlobalEventListener {
|
||||
constructor(public target: string, public eventName: string, public callback: Function) {}
|
||||
}
|
||||
|
||||
function stringifyFragment(nodes: Node[]) {
|
||||
return nodes.map(stringifyElement).join('');
|
||||
}
|
||||
|
||||
function mapAttrs(nodes: Node[], attrName): string[] {
|
||||
return nodes.map(node => DOM.getAttribute(node, attrName));
|
||||
}
|
||||
|
||||
function mapText(nodes: Node[]): string[] {
|
||||
return nodes.map(node => DOM.getText(node));
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {DefaultRenderView} from 'angular2/src/core/render/view';
|
||||
|
||||
export function main() {
|
||||
describe('DefaultRenderView', () => {
|
||||
describe('hydrate', () => {
|
||||
it('should register global event listeners', () => {
|
||||
var addCount = 0;
|
||||
var adder = () => { addCount++ };
|
||||
var view = new DefaultRenderView<Node>([], [], [], [], [adder], []);
|
||||
view.hydrate();
|
||||
expect(addCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dehydrate', () => {
|
||||
it('should deregister global event listeners', () => {
|
||||
var removeCount = 0;
|
||||
var adder = () => () => { removeCount++ };
|
||||
var view = new DefaultRenderView<Node>([], [], [], [], [adder], []);
|
||||
view.hydrate();
|
||||
view.dehydrate();
|
||||
expect(removeCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -8,10 +8,7 @@ import 'package:angular2/src/core/linker/directive_resolver.dart';
|
||||
import 'package:angular2/src/core/linker/view.dart';
|
||||
import 'package:angular2/src/core/linker/element_ref.dart';
|
||||
import 'package:angular2/src/core/linker/view_manager.dart';
|
||||
import 'package:angular2/src/core/linker/proto_view_factory.dart';
|
||||
import 'package:angular2/src/core/linker/view_pool.dart';
|
||||
import 'package:angular2/src/core/linker/view_listener.dart';
|
||||
import 'package:angular2/src/core/linker/element_injector.dart';
|
||||
import 'package:angular2/src/platform/dom/dom_adapter.dart';
|
||||
import 'package:angular2/testing_internal.dart';
|
||||
|
||||
@@ -52,12 +49,22 @@ class SpyView extends SpyObject implements AppView {
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyElementRef extends SpyObject implements ElementRef_ {
|
||||
class SpyProtoView extends SpyObject implements AppProtoView {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyAppViewManager extends SpyObject implements AppViewManager {
|
||||
class SpyHostViewFactory extends SpyObject implements HostViewFactory {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyElementRef extends SpyObject implements ElementRef {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyAppViewManager extends SpyObject implements AppViewManager_ {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@@ -67,7 +74,7 @@ class SpyRenderer extends SpyObject implements Renderer {
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyAppViewPool extends SpyObject implements AppViewPool {
|
||||
class SpyRootRenderer extends SpyObject implements RootRenderer {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@@ -76,34 +83,7 @@ class SpyAppViewListener extends SpyObject implements AppViewListener {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyProtoViewFactory extends SpyObject implements ProtoViewFactory {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyProtoElementInjector extends SpyObject
|
||||
implements ProtoElementInjector {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyElementInjector extends SpyObject implements ElementInjector {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyPreBuiltObjects extends SpyObject implements PreBuiltObjects {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyDomAdapter extends SpyObject implements DomAdapter {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
|
||||
@proxy
|
||||
class SpyRenderEventDispatcher extends SpyObject
|
||||
implements RenderEventDispatcher {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
||||
@@ -5,23 +5,14 @@ import {
|
||||
DynamicChangeDetector
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {Renderer, RenderEventDispatcher} from 'angular2/src/core/render/api';
|
||||
import {Renderer} from 'angular2/src/core/render/api';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
|
||||
import {AppView} from 'angular2/src/core/linker/view';
|
||||
import {AppView, AppProtoView, HostViewFactory} from 'angular2/src/core/linker/view';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
|
||||
import {AppViewPool} from 'angular2/src/core/linker/view_pool';
|
||||
import {AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
||||
import {AppViewListener} from 'angular2/src/core/linker/view_listener';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {DomAdapter} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {ClientMessageBroker} from 'angular2/src/web_workers/shared/client_message_broker';
|
||||
|
||||
import {
|
||||
ElementInjector,
|
||||
PreBuiltObjects,
|
||||
ProtoElementInjector
|
||||
} from 'angular2/src/core/linker/element_injector';
|
||||
|
||||
import {SpyObject, proxy} from 'angular2/testing_internal';
|
||||
|
||||
@@ -43,12 +34,20 @@ export class SpyView extends SpyObject {
|
||||
constructor() { super(AppView); }
|
||||
}
|
||||
|
||||
export class SpyProtoView extends SpyObject {
|
||||
constructor() { super(AppProtoView); }
|
||||
}
|
||||
|
||||
export class SpyHostViewFactory extends SpyObject {
|
||||
constructor() { super(HostViewFactory); }
|
||||
}
|
||||
|
||||
export class SpyElementRef extends SpyObject {
|
||||
constructor() { super(ElementRef); }
|
||||
}
|
||||
|
||||
export class SpyAppViewManager extends SpyObject {
|
||||
constructor() { super(AppViewManager); }
|
||||
constructor() { super(AppViewManager_); }
|
||||
}
|
||||
|
||||
export class SpyRenderer extends SpyObject {
|
||||
@@ -57,52 +56,42 @@ export class SpyRenderer extends SpyObject {
|
||||
// so we can't generates spy functions automatically
|
||||
// by inspecting the prototype...
|
||||
super(Renderer);
|
||||
this.spy('setEventDispatcher');
|
||||
this.spy('renderComponent');
|
||||
this.spy('selectRootElement');
|
||||
this.spy('createElement');
|
||||
this.spy('createViewRoot');
|
||||
this.spy('createTemplateAnchor');
|
||||
this.spy('createText');
|
||||
this.spy('projectNodes');
|
||||
this.spy('attachViewAfter');
|
||||
this.spy('detachView');
|
||||
this.spy('destroyView');
|
||||
this.spy('createView');
|
||||
this.spy('createProtoView');
|
||||
this.spy('hydrateView');
|
||||
this.spy('dehydrateView');
|
||||
this.spy('attachFragmentAfterElement');
|
||||
this.spy('attachFragmentAfterFragment');
|
||||
this.spy('detachFragment');
|
||||
this.spy('listen');
|
||||
this.spy('listenGlobal');
|
||||
this.spy('setElementProperty');
|
||||
this.spy('setElementAttribute');
|
||||
this.spy('setBindingDebugInfo');
|
||||
this.spy('setElementClass');
|
||||
this.spy('setElementStyle');
|
||||
this.spy('invokeElementMethod');
|
||||
this.spy('setText');
|
||||
}
|
||||
}
|
||||
|
||||
export class SpyAppViewPool extends SpyObject {
|
||||
constructor() { super(AppViewPool); }
|
||||
export class SpyRootRenderer extends SpyObject {
|
||||
constructor() {
|
||||
// Note: RootRenderer is an abstract class,
|
||||
// so we can't generates spy functions automatically
|
||||
// by inspecting the prototype...
|
||||
super(SpyRootRenderer);
|
||||
this.spy('renderComponent');
|
||||
}
|
||||
}
|
||||
|
||||
export class SpyAppViewListener extends SpyObject {
|
||||
constructor() { super(AppViewListener); }
|
||||
}
|
||||
|
||||
export class SpyProtoViewFactory extends SpyObject {
|
||||
constructor() { super(ProtoViewFactory); }
|
||||
}
|
||||
|
||||
export class SpyProtoElementInjector extends SpyObject {
|
||||
constructor() { super(ProtoElementInjector); }
|
||||
}
|
||||
|
||||
export class SpyElementInjector extends SpyObject {
|
||||
constructor() { super(ElementInjector); }
|
||||
}
|
||||
|
||||
export class SpyPreBuiltObjects extends SpyObject {
|
||||
constructor() { super(PreBuiltObjects); }
|
||||
}
|
||||
|
||||
export class SpyDomAdapter extends SpyObject {
|
||||
constructor() { super(DomAdapter); }
|
||||
}
|
||||
|
||||
export class SpyRenderEventDispatcher extends SpyObject {
|
||||
constructor() {
|
||||
// Note: RenderEventDispatcher is an interface,
|
||||
// so we can't pass it to super() and have to register
|
||||
// the spy methods on our own.
|
||||
super();
|
||||
this.spy('dispatchRenderEvent');
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
TestComponentBuilder,
|
||||
} from 'angular2/testing_internal';
|
||||
import {global} from 'angular2/src/facade/lang';
|
||||
import {APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/linker/view_pool';
|
||||
import {provide, Component, Directive, Injectable, View} from 'angular2/core';
|
||||
import {inspectNativeElement} from 'angular2/platform/browser';
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
@@ -28,8 +27,6 @@ class MyComp {
|
||||
|
||||
export function main() {
|
||||
describe('element probe', function() {
|
||||
beforeEachProviders(() => [provide(APP_VIEW_POOL_CAPACITY, {useValue: 0})]);
|
||||
|
||||
it('should return a TestElement from a dom element',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideTemplate(MyComp, '<div some-dir></div>')
|
||||
|
||||
@@ -594,6 +594,7 @@ var NG_COMPILER = [
|
||||
"SourceModule.moduleUrl=",
|
||||
"SourceModule.sourceWithModuleRefs",
|
||||
"SourceModule.sourceWithModuleRefs=",
|
||||
"SourceModule#getSourceWithoutImports()",
|
||||
"SourceWithImports.imports",
|
||||
"SourceWithImports.imports=",
|
||||
"SourceWithImports.source",
|
||||
@@ -690,9 +691,9 @@ var NG_CORE = [
|
||||
'AbstractProviderError.message=',
|
||||
'AbstractProviderError.stackTrace',
|
||||
'AppViewManager:js',
|
||||
'AppViewManager.getHostElement():js',
|
||||
/*
|
||||
Abstract methods
|
||||
'AppViewManager.getHostElement()',
|
||||
'AppViewManager.attachViewInContainer()',
|
||||
'AppViewManager.createEmbeddedViewInContainer()',
|
||||
'AppViewManager.createHostViewInContainer()',
|
||||
@@ -948,15 +949,14 @@ var NG_CORE = [
|
||||
'DynamicComponentLoader.loadNextToLocation()',
|
||||
*/
|
||||
'ElementRef',
|
||||
'ElementRef.boundElementIndex',
|
||||
'ElementRef.boundElementIndex=',
|
||||
'ElementRef.nativeElement',
|
||||
'ElementRef.parentView',
|
||||
'ElementRef.parentView=',
|
||||
'ElementRef.renderView',
|
||||
'ErrorHandlingFn:dart',
|
||||
'Output',
|
||||
'Output.bindingPropertyName',
|
||||
'EmbeddedViewRef:dart',
|
||||
'EmbeddedViewRef.changeDetectorRef',
|
||||
'EmbeddedViewRef.destroyed',
|
||||
'EmbeddedViewRef.rootNodes',
|
||||
'EventEmitter',
|
||||
/*
|
||||
Dart Stream/EventEmitter
|
||||
@@ -1026,6 +1026,7 @@ var NG_CORE = [
|
||||
'HostListenerMetadata.args',
|
||||
'HostListenerMetadata.eventName',
|
||||
'HostMetadata',
|
||||
'HostViewFactoryRef:dart',
|
||||
'Inject',
|
||||
'Inject.token',
|
||||
'InjectMetadata',
|
||||
@@ -1043,6 +1044,7 @@ var NG_CORE = [
|
||||
'Injector.get()',
|
||||
'Injector.getAt()',
|
||||
'Injector.getOptional()',
|
||||
'Injector.hostBoundary',
|
||||
'Injector.instantiateResolved()',
|
||||
'Injector.internalStrategy',
|
||||
'Injector.parent',
|
||||
@@ -1137,7 +1139,6 @@ var NG_CORE = [
|
||||
'Input.bindingPropertyName',
|
||||
'InputMetadata',
|
||||
'InputMetadata.bindingPropertyName',
|
||||
'ProtoViewRef',
|
||||
'Query',
|
||||
'Query.descendants',
|
||||
'Query.first',
|
||||
@@ -1185,36 +1186,15 @@ var NG_CORE = [
|
||||
'QueryMetadata.selector',
|
||||
'QueryMetadata.token',
|
||||
'QueryMetadata.varBindings',
|
||||
'RenderFragmentRef',
|
||||
'RenderProtoViewRef',
|
||||
'RenderViewRef',
|
||||
'RenderViewWithFragments',
|
||||
'RenderViewWithFragments.fragmentRefs',
|
||||
'RenderViewWithFragments.fragmentRefs=',
|
||||
'RenderViewWithFragments.viewRef',
|
||||
'RenderViewWithFragments.viewRef=',
|
||||
'Renderer',
|
||||
/*
|
||||
Abstract methods
|
||||
'Renderer.attachFragmentAfterElement()',
|
||||
'Renderer.attachFragmentAfterFragment()',
|
||||
'Renderer.createProtoView()',
|
||||
'Renderer.registerComponentTemplate()',
|
||||
'Renderer.createRootHostView()',
|
||||
'Renderer.createView()',
|
||||
'Renderer.dehydrateView()',
|
||||
'Renderer.destroyView()',
|
||||
'Renderer.detachFragment()',
|
||||
'Renderer.getNativeElementSync()',
|
||||
'Renderer.hydrateView()',
|
||||
'Renderer.invokeElementMethod()',
|
||||
'Renderer.setElementAttribute()',
|
||||
'Renderer.setElementClass()',
|
||||
'Renderer.setElementProperty()',
|
||||
'Renderer.setElementStyle()',
|
||||
'Renderer.setEventDispatcher()',
|
||||
'Renderer.setText()',
|
||||
*/
|
||||
'RootRenderer',
|
||||
'RenderComponentType',
|
||||
'RenderComponentType.encapsulation',
|
||||
'RenderComponentType.encapsulation=',
|
||||
'RenderComponentType.id',
|
||||
'RenderComponentType.id=',
|
||||
'RenderComponentType.styles',
|
||||
'RenderComponentType.styles=',
|
||||
'ResolvedBinding:dart',
|
||||
'ResolvedProvider:dart',
|
||||
'ResolvedProvider.key:dart',
|
||||
@@ -1318,7 +1298,6 @@ var NG_CORE = [
|
||||
'ViewContainerRef.detach()',
|
||||
*/
|
||||
'ViewContainerRef.element',
|
||||
'ViewContainerRef.element=',
|
||||
/*
|
||||
'ViewContainerRef.get()',
|
||||
'ViewContainerRef.indexOf()',
|
||||
@@ -1355,9 +1334,6 @@ var NG_CORE = [
|
||||
'ViewQueryMetadata.selector',
|
||||
'ViewQueryMetadata.token',
|
||||
'ViewQueryMetadata.varBindings',
|
||||
'ViewRef',
|
||||
'ViewRef.changeDetectorRef',
|
||||
'ViewRef.changeDetectorRef=',
|
||||
/*
|
||||
Abstract method
|
||||
'ViewRef.setLocal()',
|
||||
@@ -1391,7 +1367,8 @@ var NG_CORE = [
|
||||
'DoCheck:dart',
|
||||
'HostViewRef:dart',
|
||||
'HostViewRef.changeDetectorRef',
|
||||
'HostViewRef.changeDetectorRef=',
|
||||
'HostViewRef.destroyed',
|
||||
'HostViewRef.rootNodes',
|
||||
'IterableDifferFactory:dart',
|
||||
'IterableDiffer:dart',
|
||||
'KeyValueDifferFactory:dart',
|
||||
@@ -1401,56 +1378,6 @@ var NG_CORE = [
|
||||
'OnInit:dart',
|
||||
'PipeTransform:dart',
|
||||
'reflector',
|
||||
'RenderBeginCmd:dart',
|
||||
'RenderBeginCmd.isBound',
|
||||
'RenderBeginCmd.ngContentIndex',
|
||||
'RenderBeginComponentCmd:dart',
|
||||
'RenderBeginComponentCmd.attrNameAndValues',
|
||||
'RenderBeginComponentCmd.eventTargetAndNames',
|
||||
'RenderBeginComponentCmd.isBound',
|
||||
'RenderBeginComponentCmd.name',
|
||||
'RenderBeginComponentCmd.ngContentIndex',
|
||||
'RenderBeginComponentCmd.templateId',
|
||||
'RenderBeginElementCmd:dart',
|
||||
'RenderBeginElementCmd.attrNameAndValues',
|
||||
'RenderBeginElementCmd.isBound',
|
||||
'RenderBeginElementCmd.eventTargetAndNames',
|
||||
'RenderBeginElementCmd.name',
|
||||
'RenderBeginElementCmd.ngContentIndex',
|
||||
'RenderCommandVisitor:dart',
|
||||
'RenderEmbeddedTemplateCmd:dart',
|
||||
'RenderEmbeddedTemplateCmd.attrNameAndValues',
|
||||
'RenderEmbeddedTemplateCmd.children',
|
||||
'RenderEmbeddedTemplateCmd.eventTargetAndNames',
|
||||
'RenderEmbeddedTemplateCmd.isBound',
|
||||
'RenderEmbeddedTemplateCmd.isMerged',
|
||||
'RenderEmbeddedTemplateCmd.name',
|
||||
'RenderEmbeddedTemplateCmd.ngContentIndex',
|
||||
'RenderNgContentCmd:dart',
|
||||
'RenderNgContentCmd.ngContentIndex',
|
||||
'RenderTemplateCmd:dart',
|
||||
'RenderTextCmd:dart',
|
||||
'RenderTextCmd.isBound',
|
||||
'RenderTextCmd.ngContentIndex',
|
||||
'RenderTextCmd.value',
|
||||
'RenderElementRef:dart',
|
||||
'RenderElementRef.boundElementIndex',
|
||||
'RenderElementRef.boundElementIndex=',
|
||||
'RenderElementRef.renderView',
|
||||
'RenderElementRef.renderView=',
|
||||
'RenderEventDispatcher:dart',
|
||||
'RenderNgContentCmd.index',
|
||||
'RenderComponentTemplate',
|
||||
'RenderComponentTemplate.commands',
|
||||
'RenderComponentTemplate.commands=',
|
||||
'RenderComponentTemplate.encapsulation',
|
||||
'RenderComponentTemplate.encapsulation=',
|
||||
'RenderComponentTemplate.id',
|
||||
'RenderComponentTemplate.id=',
|
||||
'RenderComponentTemplate.shortId',
|
||||
'RenderComponentTemplate.shortId=',
|
||||
'RenderComponentTemplate.styles',
|
||||
'RenderComponentTemplate.styles=',
|
||||
'Stream:dart',
|
||||
'Stream.any():dart',
|
||||
'Stream.asBroadcastStream():dart',
|
||||
@@ -1532,7 +1459,10 @@ var NG_CORE = [
|
||||
'Reflector.registerType():dart',
|
||||
'Reflector.setter():dart',
|
||||
'Reflector.trackUsage():dart',
|
||||
'SetterFn:dart'
|
||||
'SetterFn:dart',
|
||||
'ViewRef:dart',
|
||||
'ViewRef.changeDetectorRef',
|
||||
'ViewRef.destroyed',
|
||||
];
|
||||
|
||||
var NG_INSTRUMENTATION = [
|
||||
|
||||
@@ -82,7 +82,6 @@ export function main() {
|
||||
tcb.createAsync(AppCmp).then((fixture) => {
|
||||
var router = fixture.debugElement.componentInstance.router;
|
||||
PromiseWrapper.catchError(router.navigateByUrl('/cause-error'), (error) => {
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { oh no }');
|
||||
expect(error).toContainError('oops!');
|
||||
async.done();
|
||||
});
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
ddescribe,
|
||||
beforeEach,
|
||||
it,
|
||||
expect
|
||||
} from "angular2/testing_internal";
|
||||
import {RenderProtoViewRef} from "angular2/src/core/render/api";
|
||||
import {RenderProtoViewRefStore} from "angular2/src/web_workers/shared/render_proto_view_ref_store";
|
||||
import {
|
||||
WebWorkerRenderProtoViewRef
|
||||
} from "angular2/src/web_workers/shared/render_proto_view_ref_store";
|
||||
|
||||
export function main() {
|
||||
describe("RenderProtoViewRefStore", () => {
|
||||
describe("on WebWorker", () => {
|
||||
var store: RenderProtoViewRefStore;
|
||||
beforeEach(() => { store = new RenderProtoViewRefStore(true); });
|
||||
|
||||
it("should allocate refs", () => {
|
||||
expect((<WebWorkerRenderProtoViewRef>store.allocate()).refNumber).toBe(0);
|
||||
expect((<WebWorkerRenderProtoViewRef>store.allocate()).refNumber).toBe(1);
|
||||
});
|
||||
|
||||
it("should be serializable", () => {
|
||||
var protoView = store.allocate();
|
||||
expect(store.deserialize(store.serialize(protoView))).toEqual(protoView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("on UI", () => {
|
||||
var store: RenderProtoViewRefStore;
|
||||
beforeEach(() => { store = new RenderProtoViewRefStore(false); });
|
||||
|
||||
it("should associate views with the correct references", () => {
|
||||
var renderProtoViewRef = new RenderProtoViewRef();
|
||||
|
||||
store.store(renderProtoViewRef, 100);
|
||||
expect(store.deserialize(100)).toBe(renderProtoViewRef);
|
||||
});
|
||||
|
||||
it("should be serializable", () => {
|
||||
var renderProtoViewRef = new RenderProtoViewRef();
|
||||
store.store(renderProtoViewRef, 0);
|
||||
|
||||
var deserialized = store.deserialize(store.serialize(renderProtoViewRef));
|
||||
expect(deserialized).toBe(renderProtoViewRef);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
ddescribe,
|
||||
beforeEach,
|
||||
it,
|
||||
expect
|
||||
} from "angular2/testing_internal";
|
||||
import {RenderStore} from "angular2/src/web_workers/shared/render_store";
|
||||
|
||||
export function main() {
|
||||
describe("RenderStoreSpec", () => {
|
||||
var store: RenderStore;
|
||||
beforeEach(() => { store = new RenderStore(); });
|
||||
|
||||
it('should allocate ids', () => {
|
||||
expect(store.allocateId()).toBe(0);
|
||||
expect(store.allocateId()).toBe(1);
|
||||
});
|
||||
|
||||
it('should serialize objects', () => {
|
||||
var id = store.allocateId();
|
||||
var obj = 'testObject';
|
||||
store.store(obj, id);
|
||||
expect(store.serialize(obj)).toBe(id);
|
||||
});
|
||||
|
||||
it('should deserialize objects', () => {
|
||||
var id = store.allocateId();
|
||||
var obj = 'testObject';
|
||||
store.store(obj, id);
|
||||
expect(store.deserialize(id)).toBe(obj);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect
|
||||
} from "angular2/testing_internal";
|
||||
import {
|
||||
RenderViewWithFragments,
|
||||
RenderViewRef,
|
||||
RenderFragmentRef
|
||||
} from "angular2/src/core/render/api";
|
||||
import {
|
||||
RenderViewWithFragmentsStore,
|
||||
WebWorkerRenderViewRef,
|
||||
WebWorkerRenderFragmentRef
|
||||
} from "angular2/src/web_workers/shared/render_view_with_fragments_store";
|
||||
import {ListWrapper} from "angular2/src/facade/collection";
|
||||
|
||||
export function main() {
|
||||
describe("RenderViewWithFragmentsStore", () => {
|
||||
describe("on WebWorker", () => {
|
||||
var store: RenderViewWithFragmentsStore;
|
||||
beforeEach(() => { store = new RenderViewWithFragmentsStore(true); });
|
||||
|
||||
it("should allocate fragmentCount + 1 refs", () => {
|
||||
var view: RenderViewWithFragments = store.allocate(10);
|
||||
|
||||
var viewRef: WebWorkerRenderViewRef = <WebWorkerRenderViewRef>view.viewRef;
|
||||
expect(viewRef.refNumber).toEqual(0);
|
||||
|
||||
var fragmentRefs: WebWorkerRenderFragmentRef[] =
|
||||
<WebWorkerRenderFragmentRef[]>view.fragmentRefs;
|
||||
expect(fragmentRefs.length).toEqual(10);
|
||||
|
||||
for (var i = 0; i < fragmentRefs.length; i++) {
|
||||
expect(fragmentRefs[i].refNumber).toEqual(i + 1);
|
||||
}
|
||||
});
|
||||
|
||||
it("should not reuse a reference", () => {
|
||||
store.allocate(10);
|
||||
var view = store.allocate(0);
|
||||
var viewRef = <WebWorkerRenderViewRef>view.viewRef;
|
||||
expect(viewRef.refNumber).toEqual(11);
|
||||
});
|
||||
|
||||
it("should be serializable", () => {
|
||||
var view = store.allocate(1);
|
||||
expect(store.deserializeViewWithFragments(store.serializeViewWithFragments(view)))
|
||||
.toEqual(view);
|
||||
});
|
||||
|
||||
it("should remove a view and all attached fragments", () => {
|
||||
const NUM_FRAGMENTS = 5;
|
||||
var view = store.allocate(NUM_FRAGMENTS);
|
||||
var viewRef = (<WebWorkerRenderViewRef>view.viewRef).refNumber;
|
||||
store.remove(view.viewRef);
|
||||
|
||||
expect(store.deserializeRenderViewRef(viewRef++)).toBeNull();
|
||||
for (var i = 0; i < NUM_FRAGMENTS; i++) {
|
||||
expect(store.deserializeRenderFragmentRef(viewRef++)).toBeNull();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("on UI", () => {
|
||||
var store: RenderViewWithFragmentsStore;
|
||||
beforeEach(() => { store = new RenderViewWithFragmentsStore(false); });
|
||||
function createMockRenderViewWithFragments(): RenderViewWithFragments {
|
||||
var view = new MockRenderViewRef();
|
||||
var fragments = ListWrapper.createGrowableSize(20);
|
||||
for (var i = 0; i < 20; i++) {
|
||||
fragments[i] = new MockRenderFragmentRef();
|
||||
}
|
||||
|
||||
return new RenderViewWithFragments(view, fragments);
|
||||
}
|
||||
it("should associate views with the correct references", () => {
|
||||
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||
|
||||
store.store(renderViewWithFragments, 100);
|
||||
expect(store.deserializeRenderViewRef(100)).toBe(renderViewWithFragments.viewRef);
|
||||
|
||||
for (var i = 0; i < renderViewWithFragments.fragmentRefs.length; i++) {
|
||||
expect(store.deserializeRenderFragmentRef(101 + i))
|
||||
.toBe(renderViewWithFragments.fragmentRefs[i]);
|
||||
}
|
||||
});
|
||||
|
||||
describe("RenderViewWithFragments", () => {
|
||||
it("should be serializable", () => {
|
||||
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||
store.store(renderViewWithFragments, 0);
|
||||
|
||||
var deserialized = store.deserializeViewWithFragments(
|
||||
store.serializeViewWithFragments(renderViewWithFragments));
|
||||
expect(deserialized.viewRef).toBe(renderViewWithFragments.viewRef);
|
||||
|
||||
expect(deserialized.fragmentRefs.length)
|
||||
.toEqual(renderViewWithFragments.fragmentRefs.length);
|
||||
|
||||
for (var i = 0; i < deserialized.fragmentRefs.length; i++) {
|
||||
var val = deserialized.fragmentRefs[i];
|
||||
expect(val).toBe(renderViewWithFragments.fragmentRefs[i]);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
describe("RenderViewRef", () => {
|
||||
it("should be serializable", () => {
|
||||
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||
store.store(renderViewWithFragments, 0);
|
||||
|
||||
var deserialized = store.deserializeRenderViewRef(
|
||||
store.serializeRenderViewRef(renderViewWithFragments.viewRef));
|
||||
expect(deserialized).toBe(renderViewWithFragments.viewRef);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RenderFragmentRef", () => {
|
||||
it("should be serializable", () => {
|
||||
var renderViewWithFragments = createMockRenderViewWithFragments();
|
||||
store.store(renderViewWithFragments, 0);
|
||||
|
||||
var serialized =
|
||||
store.serializeRenderFragmentRef(renderViewWithFragments.fragmentRefs[0]);
|
||||
var deserialized = store.deserializeRenderFragmentRef(serialized);
|
||||
|
||||
expect(deserialized).toBe(renderViewWithFragments.fragmentRefs[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class MockRenderViewRef extends RenderViewRef {
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
||||
class MockRenderFragmentRef extends RenderFragmentRef {
|
||||
constructor() { super(); }
|
||||
}
|
||||
@@ -18,10 +18,7 @@ import {
|
||||
import {ObservableWrapper, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {provide} from 'angular2/core';
|
||||
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
|
||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
||||
import {
|
||||
RenderViewWithFragmentsStore
|
||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
||||
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||
|
||||
export function main() {
|
||||
const CHANNEL = "UIMessageBroker Test Channel";
|
||||
@@ -31,11 +28,7 @@ export function main() {
|
||||
const RESULT = 20;
|
||||
const ID = "methodId";
|
||||
|
||||
beforeEachProviders(() => [
|
||||
provide(ON_WEB_WORKER, {useValue: true}),
|
||||
RenderProtoViewRefStore,
|
||||
RenderViewWithFragmentsStore
|
||||
]);
|
||||
beforeEachProviders(() => [provide(ON_WEB_WORKER, {useValue: true}), RenderStore]);
|
||||
|
||||
describe("UIMessageBroker", () => {
|
||||
var messageBuses;
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/testing_internal';
|
||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
|
||||
import {provide} from 'angular2/core';
|
||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
||||
import {
|
||||
RenderViewWithFragmentsStore,
|
||||
WebWorkerRenderViewRef
|
||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
||||
import {RenderEventDispatcher, RenderViewRef} from 'angular2/src/core/render/api';
|
||||
import {createPairedMessageBuses} from '../shared/web_worker_test_util';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||
|
||||
export function main() {
|
||||
describe("EventDispatcher", () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ON_WEB_WORKER, {useValue: true}),
|
||||
RenderProtoViewRefStore,
|
||||
RenderViewWithFragmentsStore
|
||||
]);
|
||||
|
||||
it("should dispatch events", inject([Serializer, AsyncTestCompleter], (serializer, async) => {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var webWorkerEventDispatcher =
|
||||
new WebWorkerEventDispatcher(messageBuses.worker, serializer);
|
||||
|
||||
var elementIndex = 15;
|
||||
var eventName = 'click';
|
||||
|
||||
var eventDispatcher = new SpyEventDispatcher((elementIndex, eventName, locals) => {
|
||||
expect(elementIndex).toEqual(elementIndex);
|
||||
expect(eventName).toEqual(eventName);
|
||||
async.done();
|
||||
});
|
||||
|
||||
var viewRef = new WebWorkerRenderViewRef(0);
|
||||
serializer.allocateRenderViews(0); // serialize the ref so it's in the store
|
||||
viewRef =
|
||||
serializer.deserialize(serializer.serialize(viewRef, RenderViewRef), RenderViewRef);
|
||||
webWorkerEventDispatcher.registerEventDispatcher(viewRef, eventDispatcher);
|
||||
|
||||
ObservableWrapper.callEmit(messageBuses.ui.to(EVENT_CHANNEL), {
|
||||
'viewRef': viewRef.serialize(),
|
||||
'elementIndex': elementIndex,
|
||||
'eventName': eventName,
|
||||
'locals': {'$event': {'target': {value: null}}}
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
class SpyEventDispatcher implements RenderEventDispatcher {
|
||||
constructor(private _callback: Function) {}
|
||||
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean {
|
||||
this._callback(elementIndex, eventName, locals);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
inject,
|
||||
ddescribe,
|
||||
describe,
|
||||
dispatchEvent,
|
||||
it,
|
||||
iit,
|
||||
expect,
|
||||
@@ -23,8 +24,9 @@ import {
|
||||
Injectable,
|
||||
ElementRef
|
||||
} from 'angular2/core';
|
||||
import {AppViewListener} from 'angular2/src/core/linker/view_listener';
|
||||
import {NgIf} from 'angular2/common';
|
||||
import {WebWorkerRenderer} from "angular2/src/web_workers/worker/renderer";
|
||||
import {WebWorkerRootRenderer} from "angular2/src/web_workers/worker/renderer";
|
||||
import {
|
||||
ClientMessageBrokerFactory,
|
||||
ClientMessageBrokerFactory_,
|
||||
@@ -32,38 +34,22 @@ import {
|
||||
FnArg
|
||||
} from "angular2/src/web_workers/shared/client_message_broker";
|
||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||
import {
|
||||
RenderProtoViewRef,
|
||||
RenderViewWithFragments,
|
||||
RenderViewRef,
|
||||
RenderFragmentRef,
|
||||
Renderer
|
||||
} from "angular2/src/core/render/api";
|
||||
import {DomRenderer, DomRenderer_} from 'angular2/src/platform/dom/dom_renderer';
|
||||
import {DefaultRenderView} from 'angular2/src/core/render/view';
|
||||
import {
|
||||
RenderProtoViewRefStore,
|
||||
WebWorkerRenderProtoViewRef
|
||||
} from "angular2/src/web_workers/shared/render_proto_view_ref_store";
|
||||
import {
|
||||
RenderViewWithFragmentsStore,
|
||||
WebWorkerRenderViewRef
|
||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
||||
import {RootRenderer} from "angular2/src/core/render/api";
|
||||
import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
|
||||
import {RenderStore} from "angular2/src/web_workers/shared/render_store";
|
||||
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
|
||||
import {createPairedMessageBuses, PairedMessageBuses} from '../shared/web_worker_test_util';
|
||||
import {
|
||||
ServiceMessageBrokerFactory,
|
||||
ServiceMessageBrokerFactory_
|
||||
} from 'angular2/src/web_workers/shared/service_message_broker';
|
||||
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
|
||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
|
||||
export function main() {
|
||||
function createWebWorkerBrokerFactory(
|
||||
messageBuses: PairedMessageBuses, workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
domRenderer: DomRenderer, uiRenderProtoViewStore: RenderProtoViewRefStore,
|
||||
uiRenderViewStore: RenderViewWithFragmentsStore): ClientMessageBrokerFactory {
|
||||
domRootRenderer: DomRootRenderer, uiRenderStore: RenderStore): ClientMessageBrokerFactory {
|
||||
var uiMessageBus = messageBuses.ui;
|
||||
var workerMessageBus = messageBuses.worker;
|
||||
|
||||
@@ -74,67 +60,62 @@ export function main() {
|
||||
// set up the ui side
|
||||
var uiMessageBrokerFactory = new ServiceMessageBrokerFactory_(uiMessageBus, uiSerializer);
|
||||
var renderer = new MessageBasedRenderer(uiMessageBrokerFactory, uiMessageBus, uiSerializer,
|
||||
uiRenderProtoViewStore, uiRenderViewStore, domRenderer);
|
||||
uiRenderStore, domRootRenderer);
|
||||
renderer.start();
|
||||
|
||||
return webWorkerBrokerFactory;
|
||||
}
|
||||
|
||||
function createWorkerRenderer(
|
||||
workerSerializer: Serializer, uiSerializer: Serializer, domRenderer: DomRenderer,
|
||||
uiRenderProtoViewStore: RenderProtoViewRefStore,
|
||||
uiRenderViewStore: RenderViewWithFragmentsStore,
|
||||
workerRenderProtoViewStore: RenderProtoViewRefStore,
|
||||
workerRenderViewStore: RenderViewWithFragmentsStore): WebWorkerRenderer {
|
||||
function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer,
|
||||
domRootRenderer: DomRootRenderer, uiRenderStore: RenderStore,
|
||||
workerRenderStore: RenderStore): WebWorkerRootRenderer {
|
||||
var messageBuses = createPairedMessageBuses();
|
||||
var brokerFactory =
|
||||
createWebWorkerBrokerFactory(messageBuses, workerSerializer, uiSerializer, domRenderer,
|
||||
uiRenderProtoViewStore, uiRenderViewStore);
|
||||
var workerEventDispatcher = new WebWorkerEventDispatcher(messageBuses.worker, workerSerializer);
|
||||
return new WebWorkerRenderer(brokerFactory, workerRenderProtoViewStore, workerRenderViewStore,
|
||||
workerEventDispatcher);
|
||||
var brokerFactory = createWebWorkerBrokerFactory(messageBuses, workerSerializer, uiSerializer,
|
||||
domRootRenderer, uiRenderStore);
|
||||
return new WebWorkerRootRenderer(brokerFactory, messageBuses.worker, workerSerializer,
|
||||
workerRenderStore);
|
||||
}
|
||||
|
||||
describe("Web Worker Renderer", () => {
|
||||
var uiInjector: Injector;
|
||||
var uiRenderViewStore: RenderViewWithFragmentsStore;
|
||||
var uiRenderStore: RenderStore;
|
||||
var workerRenderStore: RenderStore;
|
||||
|
||||
beforeEachProviders(() => {
|
||||
var uiRenderProtoViewStore = new RenderProtoViewRefStore(false);
|
||||
uiRenderViewStore = new RenderViewWithFragmentsStore(false);
|
||||
uiRenderStore = new RenderStore();
|
||||
var testInjector = new TestInjector();
|
||||
testInjector.addProviders([
|
||||
provide(RenderProtoViewRefStore, {useValue: uiRenderProtoViewStore}),
|
||||
provide(RenderViewWithFragmentsStore, {useValue: uiRenderViewStore}),
|
||||
provide(DomRenderer, {useClass: DomRenderer_}),
|
||||
provide(Renderer, {useExisting: DomRenderer})
|
||||
provide(RenderStore, {useValue: uiRenderStore}),
|
||||
provide(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||
provide(RootRenderer, {useExisting: DomRootRenderer})
|
||||
]);
|
||||
uiInjector = testInjector.createInjector();
|
||||
var uiSerializer = uiInjector.get(Serializer);
|
||||
var domRenderer = uiInjector.get(DomRenderer);
|
||||
var workerRenderProtoViewStore = new RenderProtoViewRefStore(true);
|
||||
var workerRenderViewStore = new RenderViewWithFragmentsStore(true);
|
||||
var domRootRenderer = uiInjector.get(DomRootRenderer);
|
||||
workerRenderStore = new RenderStore();
|
||||
return [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, true, false)}),
|
||||
provide(RenderProtoViewRefStore, {useValue: workerRenderProtoViewStore}),
|
||||
provide(RenderViewWithFragmentsStore, {useValue: workerRenderViewStore}),
|
||||
provide(Renderer,
|
||||
provide(RenderStore, {useValue: workerRenderStore}),
|
||||
provide(RootRenderer,
|
||||
{
|
||||
useFactory: (workerSerializer) => {
|
||||
return createWorkerRenderer(workerSerializer, uiSerializer, domRenderer,
|
||||
uiRenderProtoViewStore, uiRenderViewStore,
|
||||
workerRenderProtoViewStore, workerRenderViewStore);
|
||||
return createWorkerRenderer(workerSerializer, uiSerializer, domRootRenderer,
|
||||
uiRenderStore, workerRenderStore);
|
||||
},
|
||||
deps: [Serializer]
|
||||
})
|
||||
}),
|
||||
provide(AppViewListener, {useClass: AppViewListener})
|
||||
];
|
||||
});
|
||||
|
||||
function getRenderElement(elementRef: ElementRef) {
|
||||
var renderView = <DefaultRenderView<Node>>uiRenderViewStore.deserializeRenderViewRef(
|
||||
(<WebWorkerRenderViewRef>elementRef.renderView).refNumber);
|
||||
return renderView.boundElements[elementRef.boundElementIndex];
|
||||
var id = workerRenderStore.serialize(elementRef.nativeElement);
|
||||
return uiRenderStore.deserialize(id);
|
||||
}
|
||||
|
||||
function getRenderer(elementRef: ElementRef) {
|
||||
return (<any>elementRef).internalElement.parentView.renderer;
|
||||
}
|
||||
|
||||
it('should update text nodes',
|
||||
@@ -154,28 +135,28 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should update any element property/attributes/class/style independent of the compilation on the root element and other elements',
|
||||
inject([TestComponentBuilder, Renderer, AsyncTestCompleter], (tcb: TestComponentBuilder,
|
||||
renderer: Renderer, async) => {
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata(
|
||||
{template: '<input [title]="y" style="position:absolute">'}))
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
var checkSetters = (elr) => {
|
||||
var renderer = getRenderer(elr);
|
||||
var el = getRenderElement(elr);
|
||||
renderer.setElementProperty(elr, 'tabIndex', 1);
|
||||
renderer.setElementProperty(elr.nativeElement, 'tabIndex', 1);
|
||||
expect((<HTMLInputElement>el).tabIndex).toEqual(1);
|
||||
|
||||
renderer.setElementClass(elr, 'a', true);
|
||||
renderer.setElementClass(elr.nativeElement, 'a', true);
|
||||
expect(DOM.hasClass(el, 'a')).toBe(true);
|
||||
renderer.setElementClass(elr, 'a', false);
|
||||
renderer.setElementClass(elr.nativeElement, 'a', false);
|
||||
expect(DOM.hasClass(el, 'a')).toBe(false);
|
||||
|
||||
renderer.setElementStyle(elr, 'width', '10px');
|
||||
renderer.setElementStyle(elr.nativeElement, 'width', '10px');
|
||||
expect(DOM.getStyle(el, 'width')).toEqual('10px');
|
||||
renderer.setElementStyle(elr, 'width', null);
|
||||
renderer.setElementStyle(elr.nativeElement, 'width', null);
|
||||
expect(DOM.getStyle(el, 'width')).toEqual('');
|
||||
|
||||
renderer.setElementAttribute(elr, 'someattr', 'someValue');
|
||||
renderer.setElementAttribute(elr.nativeElement, 'someattr', 'someValue');
|
||||
expect(DOM.getAttribute(el, 'someattr')).toEqual('someValue');
|
||||
};
|
||||
|
||||
@@ -189,20 +170,19 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should update any template comment property/attributes',
|
||||
inject([TestComponentBuilder, Renderer, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, renderer: Renderer, async) => {
|
||||
var tpl = '<template [ngIf]="ctxBoolProp"></template>';
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: tpl, directives: [NgIf]}))
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var tpl = '<template [ngIf]="ctxBoolProp"></template>';
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: tpl, directives: [NgIf]}))
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
(<MyComp>fixture.debugElement.componentInstance).ctxBoolProp = true;
|
||||
fixture.detectChanges();
|
||||
var el = getRenderElement(fixture.debugElement.elementRef);
|
||||
expect(DOM.getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
(<MyComp>fixture.debugElement.componentInstance).ctxBoolProp = true;
|
||||
fixture.detectChanges();
|
||||
var el = getRenderElement(fixture.debugElement.elementRef);
|
||||
expect(DOM.getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove fragments',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
@@ -229,19 +209,35 @@ export function main() {
|
||||
}));
|
||||
|
||||
if (DOM.supportsDOMEvents()) {
|
||||
it('should call actions on the element independent of the compilation',
|
||||
inject([TestComponentBuilder, Renderer, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, renderer: Renderer, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: '<input [title]="y">'}))
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
var elRef = fixture.debugElement.componentViewChildren[0].elementRef;
|
||||
renderer.invokeElementMethod(elRef, 'setAttribute', ['a', 'b']);
|
||||
it('should call actions on the element',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: '<input [title]="y">'}))
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
var elRef = fixture.debugElement.componentViewChildren[0].elementRef;
|
||||
getRenderer(elRef)
|
||||
.invokeElementMethod(elRef.nativeElement, 'setAttribute', ['a', 'b']);
|
||||
|
||||
expect(DOM.getAttribute(getRenderElement(elRef), 'a')).toEqual('b');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
expect(DOM.getAttribute(getRenderElement(elRef), 'a')).toEqual('b');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should listen to events',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp,
|
||||
new ViewMetadata({template: '<input (change)="ctxNumProp = 1">'}))
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
var elRef = fixture.debugElement.componentViewChildren[0].elementRef;
|
||||
dispatchEvent(getRenderElement(elRef), 'change');
|
||||
expect(fixture.componentInstance.ctxNumProp).toBe(1);
|
||||
|
||||
fixture.destroy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user