diff --git a/packages/core/test/acceptance/pure_function_spec.ts b/packages/core/test/acceptance/pure_function_spec.ts new file mode 100644 index 0000000000..01abb54b2e --- /dev/null +++ b/packages/core/test/acceptance/pure_function_spec.ts @@ -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: ` + + `, + }) + 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: ` + + `, + }) + 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: ` + + + `, + }) + 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: ` + + ` + }) + class ParentComp { + customName = 'Bess'; + + someFn(arr: string[]): string[] { + arr[0] = arr[0].toUpperCase(); + return arr; + } + } + + @Component({ + template: ` + + + ` + }) + 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: ` + + `, + }) + 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: ` + + + + + + + + + ` + }) + 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: ` + + + ` + }) + 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: '', + }) + 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: ` + + + `, + }) + 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: ` + + + `, + }) + 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}); + }); + }); +}); diff --git a/packages/core/test/render3/pure_function_spec.ts b/packages/core/test/render3/pure_function_spec.ts index 2824d8ee2a..42f7a17476 100644 --- a/packages/core/test/render3/pure_function_spec.ts +++ b/packages/core/test/render3/pure_function_spec.ts @@ -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']; - - /** */ - 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))); - } - } - - /** - * - */ - 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]; - - /** - * - * - */ - 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]; - - /** */ - 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]; - - /** */ - 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}`; - - /** - * - * - */ - 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}; }; - - /** */ - 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}; }; - - /** - * - * - */ - 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[] = [];