test(ivy): pure function acceptance tests (#30406)

- Moves tests related to the pureFunction instructions from render3 to acceptance tests
- Leaves behind one test for in-template javascript that is not supported syntax yet

PR Close #30406
This commit is contained in:
Ben Lesh 2019-05-10 13:50:22 -07:00 committed by Alex Rickabaugh
parent 7dad3284e3
commit 7569a2e0d9
2 changed files with 489 additions and 474 deletions

View File

@ -0,0 +1,482 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CommonModule} from '@angular/common';
import {Component, Input} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
describe('components using pure function instructions internally', () => {
describe('with array literals', () => {
@Component({
selector: 'my-comp',
template: ``,
})
class MyComp {
@Input()
names: string[] = [];
}
it('should support an array literal with a binding', () => {
@Component({
template: `
<my-comp [names]="['Nancy', customName, 'Bess']"></my-comp>
`,
})
class App {
showing = true;
customName = 'Carson';
}
TestBed.configureTestingModule({
declarations: [App, MyComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const myComp = fixture.debugElement.query(By.directive(MyComp)).componentInstance;
const firstArray = myComp.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess']);
fixture.detectChanges();
expect(myComp.names).toEqual(['Nancy', 'Carson', 'Bess']);
expect(firstArray).toBe(myComp.names);
fixture.componentInstance.customName = 'Hannah';
fixture.detectChanges();
expect(myComp.names).toEqual(['Nancy', 'Hannah', 'Bess']);
// Identity must change if binding changes
expect(firstArray).not.toBe(myComp.names);
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
myComp.names = ['should not be overwritten'];
fixture.detectChanges();
expect(myComp !.names).toEqual(['should not be overwritten']);
});
it('should support array literals in dynamic views', () => {
@Component({
template: `
<my-comp *ngIf="showing" [names]="['Nancy', customName, 'Bess']"></my-comp>
`,
})
class App {
showing = true;
customName = 'Carson';
}
TestBed.configureTestingModule({
declarations: [App, MyComp],
imports: [CommonModule],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const myComp = fixture.debugElement.query(By.directive(MyComp)).componentInstance;
expect(myComp.names).toEqual(['Nancy', 'Carson', 'Bess']);
});
it('should support multiple array literals passed through to one node', () => {
@Component({
selector: 'many-prop-comp',
template: ``,
})
class ManyPropComp {
@Input()
names1: string[] = [];
@Input()
names2: string[] = [];
}
@Component({
template: `
<many-prop-comp [names1]="['Nancy', customName]" [names2]="[customName2]">
</many-prop-comp>
`,
})
class App {
showing = true;
customName = 'Carson';
customName2 = 'George';
}
TestBed.configureTestingModule({
declarations: [App, ManyPropComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const manyPropComp = fixture.debugElement.query(By.directive(ManyPropComp)).componentInstance;
expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']);
expect(manyPropComp !.names2).toEqual(['George']);
fixture.componentInstance.customName = 'George';
fixture.componentInstance.customName2 = 'Carson';
fixture.detectChanges();
expect(manyPropComp !.names1).toEqual(['Nancy', 'George']);
expect(manyPropComp !.names2).toEqual(['Carson']);
});
it('should support an array literals inside fn calls', () => {
@Component({
selector: 'parent-comp',
template: `
<my-comp [names]="someFn(['Nancy', customName])"></my-comp>
`
})
class ParentComp {
customName = 'Bess';
someFn(arr: string[]): string[] {
arr[0] = arr[0].toUpperCase();
return arr;
}
}
@Component({
template: `
<parent-comp></parent-comp>
<parent-comp></parent-comp>
`
})
class App {
}
TestBed.configureTestingModule({
declarations: [App, MyComp, ParentComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const myComps =
fixture.debugElement.queryAll(By.directive(MyComp)).map(f => f.componentInstance);
const firstArray = myComps[0].names;
const secondArray = myComps[1].names;
expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).not.toBe(secondArray);
fixture.detectChanges();
expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).toBe(myComps[0].names);
expect(secondArray).toBe(myComps[1].names);
});
it('should support an array literal with more than 1 binding', () => {
@Component({
template: `
<my-comp *ngIf="showing" [names]="['Nancy', customName, 'Bess', customName2]"></my-comp>
`,
})
class App {
showing = true;
customName = 'Carson';
customName2 = 'Hannah';
}
TestBed.configureTestingModule({
declarations: [App, MyComp],
imports: [CommonModule],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const myComp = fixture.debugElement.query(By.directive(MyComp)).componentInstance;
const firstArray = myComp.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
fixture.detectChanges();
expect(myComp.names).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
expect(firstArray).toBe(myComp.names);
fixture.componentInstance.customName = 'George';
fixture.detectChanges();
expect(myComp.names).toEqual(['Nancy', 'George', 'Bess', 'Hannah']);
expect(firstArray).not.toBe(myComp.names);
fixture.componentInstance.customName = 'Frank';
fixture.componentInstance.customName2 = 'Ned';
fixture.detectChanges();
expect(myComp.names).toEqual(['Nancy', 'Frank', 'Bess', 'Ned']);
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
myComp.names = ['should not be overwritten'];
fixture.detectChanges();
expect(myComp.names).toEqual(['should not be overwritten']);
});
it('should work up to 8 bindings', () => {
@Component({
template: `
<my-comp [names]="['a', 'b', 'c', 'd', 'e', 'f', 'g', v8]"></my-comp>
<my-comp [names]="['a', 'b', 'c', 'd', 'e', 'f', v7, v8]"></my-comp>
<my-comp [names]="['a', 'b', 'c', 'd', 'e', v6, v7, v8]"></my-comp>
<my-comp [names]="['a', 'b', 'c', 'd', v5, v6, v7, v8]"></my-comp>
<my-comp [names]="['a', 'b', 'c', v4, v5, v6, v7, v8]"></my-comp>
<my-comp [names]="['a', 'b', v3, v4, v5, v6, v7, v8]"></my-comp>
<my-comp [names]="['a', v2, v3, v4, v5, v6, v7, v8]"></my-comp>
<my-comp [names]="[v1, v2, v3, v4, v5, v6, v7, v8]"></my-comp>
`
})
class App {
v1 = 'a';
v2 = 'b';
v3 = 'c';
v4 = 'd';
v5 = 'e';
v6 = 'f';
v7 = 'g';
v8 = 'h';
}
TestBed.configureTestingModule({
declarations: [App, MyComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const myComps =
fixture.debugElement.queryAll(By.directive(MyComp)).map(f => f.componentInstance);
myComps.forEach(myComp => {
expect(myComp.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
});
const app = fixture.componentInstance;
app.v1 = 'a1';
app.v2 = 'b1';
app.v3 = 'c1';
app.v4 = 'd1';
app.v5 = 'e1';
app.v6 = 'f1';
app.v7 = 'g1';
app.v8 = 'h1';
fixture.detectChanges();
expect(myComps[0].names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h1']);
expect(myComps[1].names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g1', 'h1']);
expect(myComps[2].names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h1']);
expect(myComps[3].names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h1']);
expect(myComps[4].names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(myComps[5].names).toEqual(['a', 'b', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(myComps[6].names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(myComps[7].names).toEqual(['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
app.v8 = 'h2';
fixture.detectChanges();
expect(myComps[0].names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h2']);
expect(myComps[1].names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g1', 'h2']);
expect(myComps[2].names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h2']);
expect(myComps[3].names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h2']);
expect(myComps[4].names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h2']);
expect(myComps[5].names).toEqual(['a', 'b', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2']);
expect(myComps[6].names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2']);
expect(myComps[7].names).toEqual(['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2']);
});
it('should work with pureFunctionV for 9+ bindings', () => {
@Component({
template: `
<my-comp [names]="['start', v0, v1, v2, v3, 'modified_' + v4, v5, v6, v7, v8, 'end']">
</my-comp>
`
})
class App {
v0 = 'a';
v1 = 'b';
v2 = 'c';
v3 = 'd';
v4 = 'e';
v5 = 'f';
v6 = 'g';
v7 = 'h';
v8 = 'i';
}
TestBed.configureTestingModule({
declarations: [App, MyComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const myComp = fixture.debugElement.query(By.directive(MyComp)).componentInstance;
const app = fixture.componentInstance;
expect(myComp.names).toEqual([
'start', 'a', 'b', 'c', 'd', 'modified_e', 'f', 'g', 'h', 'i', 'end'
]);
app.v0 = 'a1';
fixture.detectChanges();
expect(myComp.names).toEqual([
'start', 'a1', 'b', 'c', 'd', 'modified_e', 'f', 'g', 'h', 'i', 'end'
]);
app.v4 = 'e5';
fixture.detectChanges();
expect(myComp.names).toEqual([
'start', 'a1', 'b', 'c', 'd', 'modified_e5', 'f', 'g', 'h', 'i', 'end'
]);
});
});
describe('with object literals', () => {
@Component({
selector: 'object-comp',
template: ``,
})
class ObjectComp {
@Input()
config: any = [];
}
it('should support an object literal', () => {
@Component({
template: '<object-comp [config]="{duration: 500, animation: name}"></object-comp>',
})
class App {
name = 'slide';
}
TestBed.configureTestingModule({
declarations: [App, ObjectComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const objectComp = fixture.debugElement.query(By.directive(ObjectComp)).componentInstance;
const firstObj = objectComp.config;
expect(objectComp.config).toEqual({duration: 500, animation: 'slide'});
fixture.detectChanges();
expect(objectComp.config).toEqual({duration: 500, animation: 'slide'});
expect(firstObj).toBe(objectComp.config);
fixture.componentInstance.name = 'tap';
fixture.detectChanges();
expect(objectComp.config).toEqual({duration: 500, animation: 'tap'});
// Identity must change if binding changes
expect(firstObj).not.toBe(objectComp.config);
});
it('should support expressions nested deeply in object/array literals', () => {
@Component({
template: `
<object-comp [config]="{animation: name, actions: [{ opacity: 0, duration: 0}, {opacity: 1,
duration: duration }]}">
</object-comp>
`,
})
class App {
name = 'slide';
duration = 100;
}
TestBed.configureTestingModule({
declarations: [App, ObjectComp],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const objectComp = fixture.debugElement.query(By.directive(ObjectComp)).componentInstance;
expect(objectComp.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
});
const firstConfig = objectComp.config;
fixture.detectChanges();
expect(objectComp.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
});
expect(objectComp.config).toBe(firstConfig);
fixture.componentInstance.duration = 50;
fixture.detectChanges();
expect(objectComp.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
});
expect(objectComp.config).not.toBe(firstConfig);
fixture.componentInstance.name = 'tap';
fixture.detectChanges();
expect(objectComp.config).toEqual({
animation: 'tap',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
});
fixture.componentInstance.name = 'drag';
fixture.componentInstance.duration = 500;
fixture.detectChanges();
expect(objectComp.config).toEqual({
animation: 'drag',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 500}]
});
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
objectComp.config = ['should not be overwritten'];
fixture.detectChanges();
expect(objectComp.config).toEqual(['should not be overwritten']);
});
it('should support multiple view instances with multiple bindings', () => {
@Component({
template: `
<object-comp *ngFor="let config of configs" [config]="config">
</object-comp>
`,
})
class App {
configs = [
{opacity: 0, duration: 500},
{opacity: 1, duration: 600},
];
}
TestBed.configureTestingModule({
declarations: [App, ObjectComp],
imports: [CommonModule],
});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const app = fixture.componentInstance;
const objectComps =
fixture.debugElement.queryAll(By.directive(ObjectComp)).map(f => f.componentInstance);
expect(objectComps[0].config).toEqual({opacity: 0, duration: 500});
expect(objectComps[1].config).toEqual({opacity: 1, duration: 600});
app.configs[0].duration = 1000;
fixture.detectChanges();
expect(objectComps[0].config).toEqual({opacity: 0, duration: 1000});
expect(objectComps[1].config).toEqual({opacity: 1, duration: 600});
});
});
});

View File

@ -5,381 +5,13 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AttributeMarker, ɵɵdefineComponent, ɵɵtemplate} from '../../src/render3/index';
import {ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵnextContext} from '../../src/render3/instructions/all';
import {ɵɵdefineComponent} from '../../src/render3/index';
import {ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart} from '../../src/render3/instructions/all';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {ɵɵpureFunction1, ɵɵpureFunction2, ɵɵpureFunction3, ɵɵpureFunction4, ɵɵpureFunction5, ɵɵpureFunction6, ɵɵpureFunction7, ɵɵpureFunction8, ɵɵpureFunctionV} from '../../src/render3/pure_function';
import {ComponentFixture, createComponent, getDirectiveOnNode, renderToHtml} from '../../test/render3/render_util';
import {NgIf} from './common_with_def';
describe('array literals', () => {
let myComp: MyComp;
class MyComp {
// TODO(issue/24571): remove '!'.
names !: string[];
static ngComponentDef = ɵɵdefineComponent({
type: MyComp,
selectors: [['my-comp']],
factory: function MyComp_Factory() { return myComp = new MyComp(); },
consts: 0,
vars: 0,
template: function MyComp_Template(rf: RenderFlags, ctx: MyComp) {},
inputs: {names: 'names'}
});
}
const directives = [MyComp];
it('should support an array literal with a binding', () => {
const e0_ff = (v: any) => ['Nancy', v, 'Bess'];
/** <my-comp [names]="['Nancy', customName, 'Bess']"></my-comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'my-comp');
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(0, 'names', ɵɵbind(ɵɵpureFunction1(1, e0_ff, ctx.customName)));
}
}, 1, 3, directives);
const fixture = new ComponentFixture(App);
fixture.component.customName = 'Carson';
fixture.update();
const firstArray = myComp !.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess']);
fixture.update();
expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']);
expect(firstArray).toBe(myComp !.names);
fixture.component.customName = 'Hannah';
fixture.update();
expect(myComp !.names).toEqual(['Nancy', 'Hannah', 'Bess']);
// Identity must change if binding changes
expect(firstArray).not.toBe(myComp !.names);
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
myComp !.names = ['should not be overwritten'];
fixture.update();
expect(myComp !.names).toEqual(['should not be overwritten']);
});
it('should support array literals in dynamic views', () => {
const e0_ff = (v: any) => ['Nancy', v, 'Bess'];
function IfTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'my-comp');
}
if (rf & RenderFlags.Update) {
const comp = ɵɵnextContext();
ɵɵelementProperty(0, 'names', ɵɵbind(ɵɵpureFunction1(1, e0_ff, comp.customName)));
}
}
/**
* <my-comp *ngIf="showing" [names]="['Nancy', customName, 'Bess']"></my-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵtemplate(
0, IfTemplate, 1, 3, 'my-comp',
[AttributeMarker.Bindings, 'names', AttributeMarker.Template, 'ngIf']);
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(0, 'ngIf', ɵɵbind(ctx.showing));
}
}, 1, 1, [MyComp, NgIf]);
const fixture = new ComponentFixture(App);
fixture.component.showing = true;
fixture.component.customName = 'Carson';
fixture.update();
expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']);
});
it('should support multiple array literals passed through to one node', () => {
let manyPropComp: ManyPropComp;
class ManyPropComp {
// TODO(issue/24571): remove '!'.
names1 !: string[];
// TODO(issue/24571): remove '!'.
names2 !: string[];
static ngComponentDef = ɵɵdefineComponent({
type: ManyPropComp,
selectors: [['many-prop-comp']],
factory: function ManyPropComp_Factory() { return manyPropComp = new ManyPropComp(); },
consts: 0,
vars: 0,
template: function ManyPropComp_Template(rf: RenderFlags, ctx: ManyPropComp) {},
inputs: {names1: 'names1', names2: 'names2'}
});
}
const e0_ff = (v: any) => ['Nancy', v];
const e0_ff_1 = (v: any) => [v];
/**
* <many-prop-comp [names1]="['Nancy', customName]" [names2]="[customName2]">
* </many-prop-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'many-prop-comp');
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(0, 'names1', ɵɵbind(ɵɵpureFunction1(2, e0_ff, ctx.customName)));
ɵɵelementProperty(0, 'names2', ɵɵbind(ɵɵpureFunction1(4, e0_ff_1, ctx.customName2)));
}
}, 1, 6, [ManyPropComp]);
const fixture = new ComponentFixture(App);
fixture.component.customName = 'Carson';
fixture.component.customName2 = 'George';
fixture.update();
expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']);
expect(manyPropComp !.names2).toEqual(['George']);
fixture.component.customName = 'George';
fixture.component.customName2 = 'Carson';
fixture.update();
expect(manyPropComp !.names1).toEqual(['Nancy', 'George']);
expect(manyPropComp !.names2).toEqual(['Carson']);
});
it('should support an array literals inside fn calls', () => {
let myComps: MyComp[] = [];
const e0_ff = (v: any) => ['Nancy', v];
/** <my-comp [names]="someFn(['Nancy', customName])"></my-comp> */
class ParentComp {
customName = 'Bess';
someFn(arr: string[]): string[] {
arr[0] = arr[0].toUpperCase();
return arr;
}
static ngComponentDef = ɵɵdefineComponent({
type: ParentComp,
selectors: [['parent-comp']],
factory: () => new ParentComp(),
consts: 1,
vars: 3,
template: function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'my-comp');
myComps.push(getDirectiveOnNode(0));
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(
0, 'names', ɵɵbind(ctx.someFn(ɵɵpureFunction1(1, e0_ff, ctx.customName))));
}
},
directives: directives
});
}
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'parent-comp');
ɵɵelement(1, 'parent-comp');
}
}, 2, 0, [ParentComp]);
const fixture = new ComponentFixture(App);
const firstArray = myComps[0].names;
const secondArray = myComps[1].names;
expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).not.toBe(secondArray);
fixture.update();
expect(firstArray).toEqual(['NANCY', 'Bess']);
expect(secondArray).toEqual(['NANCY', 'Bess']);
expect(firstArray).toBe(myComps[0].names);
expect(secondArray).toBe(myComps[1].names);
});
it('should support an array literal with more than 1 binding', () => {
const e0_ff = (v1: any, v2: any) => ['Nancy', v1, 'Bess', v2];
/** <my-comp [names]="['Nancy', customName, 'Bess', customName2]"></my-comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'my-comp');
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(
0, 'names', ɵɵbind(ɵɵpureFunction2(1, e0_ff, ctx.customName, ctx.customName2)));
}
}, 1, 4, directives);
const fixture = new ComponentFixture(App);
fixture.component.customName = 'Carson';
fixture.component.customName2 = 'Hannah';
fixture.update();
const firstArray = myComp !.names;
expect(firstArray).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
fixture.update();
expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess', 'Hannah']);
expect(firstArray).toBe(myComp !.names);
fixture.component.customName = 'George';
fixture.update();
expect(myComp !.names).toEqual(['Nancy', 'George', 'Bess', 'Hannah']);
expect(firstArray).not.toBe(myComp !.names);
fixture.component.customName = 'Frank';
fixture.component.customName2 = 'Ned';
fixture.update();
expect(myComp !.names).toEqual(['Nancy', 'Frank', 'Bess', 'Ned']);
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
myComp !.names = ['should not be overwritten'];
fixture.update();
expect(myComp !.names).toEqual(['should not be overwritten']);
});
it('should work up to 8 bindings', () => {
let f3Comp: MyComp;
let f4Comp: MyComp;
let f5Comp: MyComp;
let f6Comp: MyComp;
let f7Comp: MyComp;
let f8Comp: MyComp;
import {ɵɵpureFunction2} from '../../src/render3/pure_function';
import {getDirectiveOnNode, renderToHtml} from '../../test/render3/render_util';
const e0_ff = (v1: any, v2: any, v3: any) => ['a', 'b', 'c', 'd', 'e', v1, v2, v3];
const e2_ff = (v1: any, v2: any, v3: any, v4: any) => ['a', 'b', 'c', 'd', v1, v2, v3, v4];
const e4_ff =
(v1: any, v2: any, v3: any, v4: any, v5: any) => ['a', 'b', 'c', v1, v2, v3, v4, v5];
const e6_ff =
(v1: any, v2: any, v3: any, v4: any, v5: any,
v6: any) => ['a', 'b', v1, v2, v3, v4, v5, v6];
const e8_ff =
(v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any) => ['a', v1, v2, v3, v4, v5, v6, v7];
const e10_ff =
(v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any,
v8: any) => [v1, v2, v3, v4, v5, v6, v7, v8];
function Template(rf: RenderFlags, c: any) {
if (rf & RenderFlags.Create) {
ɵɵelementStart(0, 'my-comp');
f3Comp = getDirectiveOnNode(0);
ɵɵelementEnd();
ɵɵelementStart(1, 'my-comp');
f4Comp = getDirectiveOnNode(1);
ɵɵelementEnd();
ɵɵelementStart(2, 'my-comp');
f5Comp = getDirectiveOnNode(2);
ɵɵelementEnd();
ɵɵelementStart(3, 'my-comp');
f6Comp = getDirectiveOnNode(3);
ɵɵelementEnd();
ɵɵelementStart(4, 'my-comp');
f7Comp = getDirectiveOnNode(4);
ɵɵelementEnd();
ɵɵelementStart(5, 'my-comp');
f8Comp = getDirectiveOnNode(5);
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(0, 'names', ɵɵbind(ɵɵpureFunction3(6, e0_ff, c[5], c[6], c[7])));
ɵɵelementProperty(1, 'names', ɵɵbind(ɵɵpureFunction4(10, e2_ff, c[4], c[5], c[6], c[7])));
ɵɵelementProperty(
2, 'names', ɵɵbind(ɵɵpureFunction5(15, e4_ff, c[3], c[4], c[5], c[6], c[7])));
ɵɵelementProperty(
3, 'names', ɵɵbind(ɵɵpureFunction6(21, e6_ff, c[2], c[3], c[4], c[5], c[6], c[7])));
ɵɵelementProperty(
4, 'names',
ɵɵbind(ɵɵpureFunction7(28, e8_ff, c[1], c[2], c[3], c[4], c[5], c[6], c[7])));
ɵɵelementProperty(
5, 'names',
ɵɵbind(ɵɵpureFunction8(36, e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7])));
}
}
renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], 6, 45, directives);
expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f6Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f7Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
expect(f8Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
renderToHtml(
Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1', 'i1'], 6, 45, directives);
expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h1']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h1']);
expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(f6Comp !.names).toEqual(['a', 'b', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(f7Comp !.names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
expect(f8Comp !.names).toEqual(['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h1']);
renderToHtml(
Template, ['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2', 'i1'], 6, 45, directives);
expect(f3Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e', 'f1', 'g1', 'h2']);
expect(f4Comp !.names).toEqual(['a', 'b', 'c', 'd', 'e1', 'f1', 'g1', 'h2']);
expect(f5Comp !.names).toEqual(['a', 'b', 'c', 'd1', 'e1', 'f1', 'g1', 'h2']);
expect(f6Comp !.names).toEqual(['a', 'b', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2']);
expect(f7Comp !.names).toEqual(['a', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2']);
expect(f8Comp !.names).toEqual(['a1', 'b1', 'c1', 'd1', 'e1', 'f1', 'g1', 'h2']);
});
it('should work with pureFunctionV for 9+ bindings', () => {
const e0_ff =
(v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any,
v8: any) => ['start', v0, v1, v2, v3, v4, v5, v6, v7, v8, 'end'];
const e0_ff_1 = (v: any) => `modified_${v}`;
/**
* <my-comp [names]="['start', v0, v1, v2, v3, `modified_${v4}`, v5, v6, v7, v8, 'end']">
* </my-comp>
*/
function Template(rf: RenderFlags, c: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'my-comp');
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(
0, 'names', ɵɵbind(ɵɵpureFunctionV(3, e0_ff, [
c[0], c[1], c[2], c[3], ɵɵpureFunction1(1, e0_ff_1, c[4]), c[5], c[6], c[7], c[8]
])));
}
}
renderToHtml(Template, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], 1, 13, directives);
expect(myComp !.names).toEqual([
'start', 'a', 'b', 'c', 'd', 'modified_e', 'f', 'g', 'h', 'i', 'end'
]);
renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], 1, 13, directives);
expect(myComp !.names).toEqual([
'start', 'a1', 'b', 'c', 'd', 'modified_e', 'f', 'g', 'h', 'i', 'end'
]);
renderToHtml(Template, ['a1', 'b', 'c', 'd', 'e5', 'f', 'g', 'h', 'i'], 1, 13, directives);
expect(myComp !.names).toEqual([
'start', 'a1', 'b', 'c', 'd', 'modified_e5', 'f', 'g', 'h', 'i', 'end'
]);
});
});
describe('object literals', () => {
let objectComp: ObjectComp;
@ -393,114 +25,15 @@ describe('object literals', () => {
factory: function ObjectComp_Factory() { return objectComp = new ObjectComp(); },
consts: 0,
vars: 1,
template: function ObjectComp_Template(rf: RenderFlags, ctx: ObjectComp) {},
template: function ObjectComp_Template() {},
inputs: {config: 'config'}
});
}
const defs = [ObjectComp];
it('should support an object literal', () => {
const e0_ff = (v: any) => { return {duration: 500, animation: v}; };
/** <object-comp [config]="{duration: 500, animation: name}"></object-comp> */
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'object-comp');
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(0, 'config', ɵɵbind(ɵɵpureFunction1(1, e0_ff, ctx.name)));
}
}, 1, 3, defs);
const fixture = new ComponentFixture(App);
fixture.component.name = 'slide';
fixture.update();
const firstObj = objectComp !.config;
expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'});
fixture.update();
expect(objectComp !.config).toEqual({duration: 500, animation: 'slide'});
expect(firstObj).toBe(objectComp !.config);
fixture.component.name = 'tap';
fixture.update();
expect(objectComp !.config).toEqual({duration: 500, animation: 'tap'});
// Identity must change if binding changes
expect(firstObj).not.toBe(objectComp !.config);
});
it('should support expressions nested deeply in object/array literals', () => {
const e0_ff = (v1: any, v2: any) => { return {animation: v1, actions: v2}; };
const e0_ff_1 = (v: any) => [{opacity: 0, duration: 0}, v];
const e0_ff_2 = (v: any) => { return {opacity: 1, duration: v}; };
/**
* <object-comp [config]="{animation: name, actions: [{ opacity: 0, duration: 0}, {opacity: 1,
* duration: duration }]}">
* </object-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ɵɵelement(0, 'object-comp');
}
if (rf & RenderFlags.Update) {
ɵɵelementProperty(
0, 'config',
ɵɵbind(ɵɵpureFunction2(
5, e0_ff, ctx.name,
ɵɵpureFunction1(3, e0_ff_1, ɵɵpureFunction1(1, e0_ff_2, ctx.duration)))));
}
}, 1, 8, defs);
const fixture = new ComponentFixture(App);
fixture.component.name = 'slide';
fixture.component.duration = 100;
fixture.update();
expect(objectComp !.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
});
const firstConfig = objectComp !.config;
fixture.update();
expect(objectComp !.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 100}]
});
expect(objectComp !.config).toBe(firstConfig);
fixture.component.duration = 50;
fixture.update();
expect(objectComp !.config).toEqual({
animation: 'slide',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
});
expect(objectComp !.config).not.toBe(firstConfig);
fixture.component.name = 'tap';
fixture.update();
expect(objectComp !.config).toEqual({
animation: 'tap',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 50}]
});
fixture.component.name = 'drag';
fixture.component.duration = 500;
fixture.update();
expect(objectComp !.config).toEqual({
animation: 'drag',
actions: [{opacity: 0, duration: 0}, {opacity: 1, duration: 500}]
});
// The property should not be set if the exp value is the same, so artificially
// setting the property to ensure it's not overwritten.
objectComp !.config = ['should not be overwritten'];
fixture.update();
expect(objectComp !.config).toEqual(['should not be overwritten']);
});
// NOTE: This test cannot be ported to acceptance tests with TestBed because
// the syntax is still unsupported.
it('should support multiple view instances with multiple bindings', () => {
let objectComps: ObjectComp[] = [];