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:

committed by
Miško Hevery

parent
1dc9be4b7d
commit
e4e9dbe33d
@ -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']);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user