feat(compiler): integrate compiler with view engine - change detection tests work (#14412)

Included refactoring:
- make ViewData.parentIndex point to component provider index
- split NodeType.Provider into Provider / Directive / Pipe
- make purePipe take the real pipe as argument to detect changes
- order change detection:
  1) directive props
  2) renderer props

Part of #14013

PR Close #14412
This commit is contained in:
Tobias Bosch
2017-02-09 14:59:57 -08:00
committed by Miško Hevery
parent 1dc9be4b7d
commit e4e9dbe33d
39 changed files with 942 additions and 768 deletions

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
@ -19,6 +20,19 @@ import {MockSchemaRegistry} from '../../../compiler/testing/index';
import {EventEmitter} from '../../src/facade/async';
export function main() {
describe('Current compiler', () => { createTests({viewEngine: false}); });
describe('View Engine compiler', () => {
beforeEach(() => {
TestBed.configureCompiler(
{useJit: true, providers: [{provide: USE_VIEW_ENGINE, useValue: true}]});
});
createTests({viewEngine: true});
});
}
function createTests({viewEngine}: {viewEngine: boolean}) {
let elSchema: MockSchemaRegistry;
let renderLog: RenderLog;
let directiveLog: DirectiveLog;
@ -1080,7 +1094,8 @@ export function main() {
ctx.destroy();
expect(directiveLog.filter(['ngOnDestroy'])).toEqual([
// We don't care about the exact order in this test.
expect(directiveLog.filter(['ngOnDestroy']).sort()).toEqual([
'dir.ngOnDestroy', 'injectable.ngOnDestroy'
]);
}));
@ -1092,8 +1107,9 @@ export function main() {
it('should throw when a record gets changed after it has been checked', fakeAsync(() => {
const ctx = createCompFixture('<div [someProp]="a"></div>', TestData);
ctx.componentInstance.a = 1;
expect(() => ctx.checkNoChanges())
.toThrowError(/:0:5[\s\S]*Expression has changed after it was checked./g);
.toThrowError(/Expression has changed after it was checked./g);
}));
it('should warn when the view has been created in a cd hook', fakeAsync(() => {
@ -1216,26 +1232,28 @@ export function main() {
expect(renderLog.loggedValues).toEqual(['Tom']);
});
it('should recurse into nested view containers even if there are no bindings in the component view',
() => {
@Component({template: '<template #vc>{{name}}</template>'})
class Comp {
name = 'Tom';
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
@ViewChild(TemplateRef) template: TemplateRef<any>;
}
// TODO(tbosch): ViewQueries don't work yet with the view engine...
viewEngine ||
it('should recurse into nested view containers even if there are no bindings in the component view',
() => {
@Component({template: '<template #vc>{{name}}</template>'})
class Comp {
name = 'Tom';
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
@ViewChild(TemplateRef) template: TemplateRef<any>;
}
TestBed.configureTestingModule({declarations: [Comp]});
initHelpers();
TestBed.configureTestingModule({declarations: [Comp]});
initHelpers();
const ctx = TestBed.createComponent(Comp);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual([]);
const ctx = TestBed.createComponent(Comp);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual([]);
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
});
});
}

View File

@ -265,7 +265,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
});
describe('pipes', () => {
viewEngine || it('should support pipes in bindings', () => {
it('should support pipes in bindings', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]});
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>';
TestBed.overrideComponent(MyComp, {set: {template}});
@ -545,7 +545,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
});
describe('variables', () => {
viewEngine || it('should allow to use variables in a for loop', () => {
it('should allow to use variables in a for loop', () => {
TestBed.configureTestingModule({declarations: [MyComp, ChildCompNoTemplate]});
const template =
'<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>';
@ -670,31 +670,29 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
});
if (getDOM().supportsDOMEvents()) {
viewEngine ||
it('should be checked when an async pipe requests a check', fakeAsync(() => {
TestBed.configureTestingModule(
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>';
TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp);
it('should be checked when an async pipe requests a check', fakeAsync(() => {
TestBed.configureTestingModule(
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>';
TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp);
tick();
tick();
const cmp: PushCmpWithAsyncPipe =
fixture.debugElement.children[0].references['cmp'];
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references['cmp'];
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
fixture.detectChanges();
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
fixture.detectChanges();
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
cmp.resolve(2);
tick();
cmp.resolve(2);
tick();
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(2);
}));
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(2);
}));
}
});
@ -897,26 +895,24 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
expect(tc.nativeElement.id).toEqual('newId');
});
viewEngine ||
it('should not use template variables for expressions in hostProperties', () => {
@Directive(
{selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
class DirectiveWithHostProps {
id = 'one';
}
it('should not use template variables for expressions in hostProperties', () => {
@Directive({selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
class DirectiveWithHostProps {
id = 'one';
}
const fixture =
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
.overrideComponent(MyComp, {
set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}
})
.createComponent(MyComp);
fixture.detectChanges();
const fixture =
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
.overrideComponent(
MyComp,
{set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}})
.createComponent(MyComp);
fixture.detectChanges();
const tc = fixture.debugElement.children[0];
expect(tc.properties['id']).toBe('one');
expect(tc.properties['title']).toBe(undefined);
});
const tc = fixture.debugElement.children[0];
expect(tc.properties['id']).toBe('one');
expect(tc.properties['title']).toBe(undefined);
});
it('should not allow pipes in hostProperties', () => {
@Directive({selector: '[host-properties]', host: {'[id]': 'id | uppercase'}})
@ -930,8 +926,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
.toThrowError(/Host binding expression cannot contain pipes/);
});
// TODO: literal array
viewEngine || it('should not use template variables for expressions in hostListeners', () => {
it('should not use template variables for expressions in hostListeners', () => {
@Directive({selector: '[host-listener]', host: {'(click)': 'doIt(id, unknownProp)'}})
class DirectiveWithHostListener {
id = 'one';

View File

@ -16,8 +16,9 @@ import {createRootView, isBrowser} from './helper';
export function main() {
describe(`View Anchor`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(

View File

@ -16,9 +16,9 @@ import {createRootView, isBrowser, removeNodes} from './helper';
export function main() {
describe(`Component Views`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
@ -119,7 +119,7 @@ export function main() {
directiveDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
[
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
], update
], null, update
)),
]));
const compView = asProviderData(view, 1).componentView;
@ -194,7 +194,7 @@ export function main() {
[
elementDef(NodeFlags.None, null, null, 0, 'span', null, null, ['click']),
],
update, null, ViewFlags.OnPush)),
update, null, null, ViewFlags.OnPush)),
],
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
@ -246,7 +246,7 @@ export function main() {
[
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
],
update)),
null, update)),
]));
const compView = asProviderData(view, 1).componentView;

View File

@ -17,9 +17,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, re
export function main() {
describe(`View Elements`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(
@ -85,7 +85,7 @@ export function main() {
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
]),
],
(check, view) => {
null, (check, view) => {
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
}));
@ -112,7 +112,7 @@ export function main() {
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
]),
],
(check, view) => {
null, (check, view) => {
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
}));
@ -159,7 +159,7 @@ export function main() {
[BindingType.ElementStyle, 'color', null]
]),
],
(check, view) => {
null, (check, view) => {
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [10, 'red']);
}));
@ -172,42 +172,6 @@ export function main() {
});
});
describe('general binding behavior', () => {
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
let bindingValue: any;
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(
NodeFlags.None, null, null, 0, 'input', null,
[
[BindingType.ElementProperty, 'someProp', SecurityContext.NONE],
]),
],
(check, view) => {
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
}));
const setterSpy = jasmine.createSpy('set');
Object.defineProperty(rootNodes[0], 'someProp', {set: setterSpy});
bindingValue = 'v1';
Services.checkAndUpdateView(view);
expect(setterSpy).toHaveBeenCalledWith('v1');
setterSpy.calls.reset();
Services.checkAndUpdateView(view);
expect(setterSpy).not.toHaveBeenCalled();
setterSpy.calls.reset();
bindingValue = WrappedValue.wrap('v1');
Services.checkAndUpdateView(view);
expect(setterSpy).toHaveBeenCalledWith('v1');
});
});
});
if (isBrowser()) {
describe('listen to DOM events', () => {
function createAndAttachAndGetRootNodes(viewDef: ViewDefinition):
@ -228,7 +192,7 @@ export function main() {
spyOn(HTMLElement.prototype, 'removeEventListener').and.callThrough();
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'])], null,
handleEventSpy));
null, handleEventSpy));
rootNodes[0].click();
@ -252,7 +216,7 @@ export function main() {
[elementDef(
NodeFlags.None, null, null, 0, 'button', null, null,
[['window', 'windowClick']])],
null, handleEventSpy));
null, null, handleEventSpy));
expect(addListenerSpy).toHaveBeenCalled();
expect(addListenerSpy.calls.mostRecent().args[0]).toBe('windowClick');
@ -278,7 +242,7 @@ export function main() {
[elementDef(
NodeFlags.None, null, null, 0, 'button', null, null,
[['document', 'documentClick']])],
null, handleEventSpy));
null, null, handleEventSpy));
expect(addListenerSpy).toHaveBeenCalled();
expect(addListenerSpy.calls.mostRecent().args[0]).toBe('documentClick');
@ -302,7 +266,7 @@ export function main() {
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'])], null,
(view, index, eventName, event) => {
null, (view, index, eventName, event) => {
preventDefaultSpy = spyOn(event, 'preventDefault').and.callThrough();
return eventHandlerResult;
}));
@ -328,7 +292,7 @@ export function main() {
const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'])], null,
() => { throw new Error('Test'); }));
null, () => { throw new Error('Test'); }));
let err: any;
try {

View File

@ -16,9 +16,9 @@ import {createRootView, isBrowser} from './helper';
export function main() {
describe(`Embedded Views`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {

View File

@ -16,9 +16,9 @@ import {createRootView, isBrowser} from './helper';
export function main() {
describe(`View NgContent`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {

View File

@ -17,10 +17,11 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} fr
export function main() {
describe(`View Providers`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(
viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []);
viewFlags, nodes, updateDirectives, updateRenderer, handleEvent, 'someCompId',
ViewEncapsulation.None, []);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
@ -59,7 +60,8 @@ export function main() {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 2, 'span'),
directiveDef(NodeFlags.LazyProvider, null, 0, LazyService, []),
providerDef(
NodeFlags.LazyProvider, null, ProviderType.Class, LazyService, LazyService, []),
directiveDef(NodeFlags.None, null, 0, SomeService, [Injector])
]));
@ -330,37 +332,6 @@ export function main() {
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
});
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
let bindingValue: any;
let setterSpy = jasmine.createSpy('set');
class SomeService {
set a(value: any) { setterSpy(value); }
}
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
],
(check, view) => {
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
}));
bindingValue = 'v1';
Services.checkAndUpdateView(view);
expect(setterSpy).toHaveBeenCalledWith('v1');
setterSpy.calls.reset();
Services.checkAndUpdateView(view);
expect(setterSpy).not.toHaveBeenCalled();
setterSpy.calls.reset();
bindingValue = WrappedValue.wrap('v1');
Services.checkAndUpdateView(view);
expect(setterSpy).toHaveBeenCalledWith('v1');
});
});
});
@ -388,7 +359,7 @@ export function main() {
directiveDef(
NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
],
null, handleEvent));
null, null, handleEvent));
emitter.emit('someEventInstance');
expect(handleEvent).toHaveBeenCalledWith(view, 0, 'someEventName', 'someEventInstance');
@ -410,7 +381,7 @@ export function main() {
directiveDef(
NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
],
null, () => { throw new Error('Test'); }));
null, null, () => { throw new Error('Test'); }));
let err: any;
try {

View File

@ -7,7 +7,7 @@
*/
import {Injector, PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
import {ArgumentType, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, directiveDef, elementDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {ArgumentType, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, directiveDef, elementDef, nodeValue, pipeDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing';
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView} from './helper';
@ -15,9 +15,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView} from './helpe
export function main() {
describe(`View Pure Expressions`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
@ -64,32 +64,6 @@ export function main() {
expect(arr1).toEqual([3, 2]);
});
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
let bindingValue: any;
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, null, 1, 'span'),
pureArrayDef(1),
],
(check, view) => {
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
}));
const exprData = asPureExpressionData(view, 1);
bindingValue = 'v1';
Services.checkAndUpdateView(view);
const v1Arr = exprData.value;
expect(v1Arr).toEqual(['v1']);
Services.checkAndUpdateView(view);
expect(exprData.value).toBe(v1Arr);
bindingValue = WrappedValue.wrap('v1');
Services.checkAndUpdateView(view);
expect(exprData.value).not.toBe(v1Arr);
expect(exprData.value).toEqual(['v1']);
});
});
});
@ -127,32 +101,6 @@ export function main() {
expect(obj1).toEqual({a: 3, b: 2});
});
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
let bindingValue: any;
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, null, 1, 'span'),
pureObjectDef(['a']),
],
(check, view) => {
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
}));
const exprData = asPureExpressionData(view, 1);
bindingValue = 'v1';
Services.checkAndUpdateView(view);
const v1Obj = exprData.value;
expect(v1Obj).toEqual({'a': 'v1'});
Services.checkAndUpdateView(view);
expect(exprData.value).toBe(v1Obj);
bindingValue = WrappedValue.wrap('v1');
Services.checkAndUpdateView(view);
expect(exprData.value).not.toBe(v1Obj);
expect(exprData.value).toEqual({'a': 'v1'});
});
});
});
@ -168,11 +116,12 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, null, 3, 'span'),
directiveDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
pipeDef(NodeFlags.None, SomePipe, []), purePipeDef(2),
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
],
(check, view) => {
const pureValue = checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, values);
const pureValue = checkNodeInlineOrDynamic(
check, view, 2, inlineDynamic, [nodeValue(view, 1)].concat(values));
checkNodeInlineOrDynamic(check, view, 3, inlineDynamic, [pureValue]);
}));
const service = asProviderData(view, 3).instance;
@ -194,36 +143,6 @@ export function main() {
expect(obj1).toEqual([13, 22]);
});
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
let bindingValue: any;
let transformSpy = jasmine.createSpy('transform');
class SomePipe implements PipeTransform {
transform = transformSpy;
}
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
elementDef(NodeFlags.None, null, null, 2, 'span'),
directiveDef(NodeFlags.None, null, 0, SomePipe, []),
purePipeDef(SomePipe, 1),
],
(check, view) => {
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [bindingValue]);
}));
bindingValue = 'v1';
Services.checkAndUpdateView(view);
expect(transformSpy).toHaveBeenCalledWith('v1');
transformSpy.calls.reset();
Services.checkAndUpdateView(view);
expect(transformSpy).not.toHaveBeenCalled();
bindingValue = WrappedValue.wrap('v1');
Services.checkAndUpdateView(view);
expect(transformSpy).toHaveBeenCalledWith('v1');
});
});
});
});

View File

@ -17,9 +17,9 @@ import {createRootView} from './helper';
export function main() {
describe(`Query Views`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {

View File

@ -16,9 +16,9 @@ import {createRootView, isBrowser} from './helper';
export function main() {
describe('View Services', () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(

View File

@ -16,9 +16,9 @@ import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} fr
export function main() {
describe(`View Text`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(
@ -67,7 +67,7 @@ export function main() {
[
textDef(null, ['0', '1', '2']),
],
(check, view) => {
null, (check, view) => {
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['a', 'b']);
}));
@ -77,41 +77,6 @@ export function main() {
expect(getDOM().getText(rootNodes[0])).toBe('0a1b2');
});
if (isBrowser()) {
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
let bindingValue: any;
const setterSpy = jasmine.createSpy('set');
class FakeTextNode {
set nodeValue(value: any) { setterSpy(value); }
}
spyOn(document, 'createTextNode').and.returnValue(new FakeTextNode());
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[
textDef(null, ['', '']),
],
(check, view) => {
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
}));
Object.defineProperty(rootNodes[0], 'nodeValue', {set: setterSpy});
bindingValue = 'v1';
Services.checkAndUpdateView(view);
expect(setterSpy).toHaveBeenCalledWith('v1');
setterSpy.calls.reset();
Services.checkAndUpdateView(view);
expect(setterSpy).not.toHaveBeenCalled();
setterSpy.calls.reset();
bindingValue = WrappedValue.wrap('v1');
Services.checkAndUpdateView(view);
expect(setterSpy).toHaveBeenCalledWith('v1');
});
}
});
});