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';