/** * @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 {NgForOfContext} from '@angular/common'; import {ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, ɵɵpropertyInterpolate2, ɵɵpropertyInterpolate3, ɵɵpropertyInterpolate4, ɵɵpropertyInterpolate5, ɵɵpropertyInterpolate6, ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV} from '@angular/core/src/render3/instructions/all'; import {ɵɵdefineComponent} from '../../src/render3/definition'; import {RenderFlags, ɵɵbind, ɵɵelement, ɵɵelementAttribute, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵelementStylingMap, ɵɵinterpolation1, ɵɵproperty, ɵɵselect, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index'; import {AttributeMarker} from '../../src/render3/interfaces/node'; import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass'; import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization'; import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; import {StyleSanitizeFn} from '../../src/sanitization/style_sanitizer'; import {NgForOf} from './common_with_def'; import {ComponentFixture, TemplateFixture} from './render_util'; describe('instructions', () => { function createAnchor() { ɵɵelementStart(0, 'a'); ɵɵelementStyling(); ɵɵelementEnd(); } function createDiv( initialClasses?: string[] | null, classBindingNames?: string[] | null, initialStyles?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn) { const attrs: any[] = []; if (initialClasses) { attrs.push(AttributeMarker.Classes, ...initialClasses); } if (initialStyles) { attrs.push(AttributeMarker.Styles, ...initialStyles); } ɵɵelementStart(0, 'div', attrs); ɵɵelementStyling(classBindingNames || null, styleBindingNames || null, styleSanitizer); ɵɵelementEnd(); } function createScript() { ɵɵelement(0, 'script'); } describe('bind', () => { it('should update bindings when value changes', () => { const t = new TemplateFixture(createAnchor, () => {}, 1, 1); t.update(() => ɵɵelementProperty(0, 'title', ɵɵbind('Hello'))); expect(t.html).toEqual(''); t.update(() => ɵɵelementProperty(0, 'title', ɵɵbind('World'))); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for hostElement + 1 for the template under test tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2 }); }); it('should not update bindings when value does not change', () => { const idempotentUpdate = () => ɵɵelementProperty(0, 'title', ɵɵbind('Hello')); const t = new TemplateFixture(createAnchor, idempotentUpdate, 1, 1); t.update(); expect(t.html).toEqual(''); t.update(); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for hostElement + 1 for the template under test tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 1 }); }); }); describe('element', () => { it('should create an element', () => { const t = new TemplateFixture(() => { ɵɵelement(0, 'div', ['id', 'test', 'title', 'Hello']); }, () => {}, 1); const div = (t.hostElement as HTMLElement).querySelector('div') !; expect(div.id).toEqual('test'); expect(div.title).toEqual('Hello'); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, }); }); it('should allow setting namespaced attributes', () => { const t = new TemplateFixture(() => { ɵɵelement(0, 'div', [ // id="test" 'id', 'test', // test:foo="bar" AttributeMarker.NamespaceURI, 'http://someuri.com/2018/test', 'test:foo', 'bar', // title="Hello" 'title', 'Hello', ]); }, () => {}, 1); const div = (t.hostElement as HTMLElement).querySelector('div') !; const attrs: any = div.attributes; expect(attrs['id'].name).toEqual('id'); expect(attrs['id'].namespaceURI).toEqual(null); expect(attrs['id'].value).toEqual('test'); expect(attrs['test:foo'].name).toEqual('test:foo'); expect(attrs['test:foo'].namespaceURI).toEqual('http://someuri.com/2018/test'); expect(attrs['test:foo'].value).toEqual('bar'); expect(attrs['title'].name).toEqual('title'); expect(attrs['title'].namespaceURI).toEqual(null); expect(attrs['title'].value).toEqual('Hello'); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetAttribute: 3 }); }); }); describe('elementAttribute', () => { it('should use sanitizer function', () => { const t = new TemplateFixture(createDiv, () => {}, 1); t.update(() => ɵɵelementAttribute(0, 'title', 'javascript:true', ɵɵsanitizeUrl)); expect(t.html).toEqual('
'); t.update( () => ɵɵelementAttribute( 0, 'title', bypassSanitizationTrustUrl('javascript:true'), ɵɵsanitizeUrl)); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetAttribute: 2 }); }); }); describe('ɵɵselect', () => { it('should error in DevMode if index is out of range', () => { // Only one constant added, meaning only index `0` is valid. const t = new TemplateFixture(createDiv, () => {}, 1, 0); expect(() => { t.update(() => { ɵɵselect(-1); }); }).toThrow(); expect(() => { t.update(() => { ɵɵselect(1); }); }).toThrow(); expect(() => { t.update(() => { ɵɵselect(0); }); }).not.toThrow(); }); }); describe('property', () => { // TODO(benlesh): Replace with TestBed tests once the instruction is being generated. it('should set properties of the ɵɵselected element', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 1); t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'one'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'two'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); // TODO(benlesh): Replace with TestBed tests once the instruction is being generated. it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 2); t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'one')('accessKey', 'A'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'two')('accessKey', 'B'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); // TODO(benlesh): Replace with TestBed tests once the instruction is being generated. it('should diff value changes', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 2); t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'one')('accessKey', 'A'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'two')('accessKey', 'A'); // Notice: only changing the title. }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 3, }); }); it('should error in dev mode if ɵɵselect was not called prior', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 1); expect(() => { t.update(() => { ɵɵproperty('title', 'test'); }); }).toThrow(); expect(() => { t.update(() => { ɵɵselect(0); ɵɵproperty('title', 'test'); }); }).not.toThrow(); }); }); /** * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * TODO: REMOVE ALL OF THESE TemplateFixture TESTS FOR TestBed TESTS AFTER COMPILER IS UPDATED * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ describe('ɵɵpropertyInterpolate instructions', () => { describe('ɵɵpropertyInterpolate', () => { it('should interpolate one value', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 1); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate('title', 123); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate('title', 'abc'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 2); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate('title', 123)('accessKey', 'A'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate('title', 'abc')('accessKey', 'B'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 1); expect(() => { t.update(() => { ɵɵpropertyInterpolate('title', 123); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate('title', 123); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate1', () => { it('should interpolate one value', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 1); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate1('title', 'start', 123, 'end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate1('title', 'start', 'abc', 'end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 2); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate1('title', 'start', 123, 'end')('accessKey', 'start', 'A', 'end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate1('title', 'start', 'abc', 'end')('accessKey', 'start', 'B', 'end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 1); expect(() => { t.update(() => { ɵɵpropertyInterpolate1('title', 'start', 'whatever', 'end'); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate1('title', 'start', 'whatever', 'end'); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate2', () => { it('should interpolate two values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 2); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate2('title', 'start: ', 0, ', 1: ', 1, ', end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate2('title', 'start: ', 'A', ', 1: ', 'B', ', end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 4); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate2('title', 'start: ', 0, ', 1: ', 1, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate2('title', 'start: ', 'A', ', 1: ', 'B', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 2); expect(() => { t.update(() => { ɵɵpropertyInterpolate2('title', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate2('title', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate3', () => { it('should interpolate three values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 3); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate3('title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate3('title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 6); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate3('title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate3('title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 3); expect(() => { t.update(() => { ɵɵpropertyInterpolate3('title', '', '', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate3('title', '', '', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate4', () => { it('should interpolate four values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 4); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate4( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate4( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 8); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate4( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate4( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 4); expect(() => { t.update(() => { ɵɵpropertyInterpolate4('title', '', '', '', '', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate4('title', '', '', '', '', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate5', () => { it('should interpolate five values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 5); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate5( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate5( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 10); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate5( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate5( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 5); expect(() => { t.update(() => { ɵɵpropertyInterpolate5('title', '', '', '', '', '', '', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate5('title', '', '', '', '', '', '', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate6', () => { it('should interpolate six values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 6); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate6( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', end'); }); expect(t.html).toEqual(''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate6( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', end'); }); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 12); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate6( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate6( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 6); expect(() => { t.update(() => { ɵɵpropertyInterpolate6('title', '', '', '', '', '', '', '', '', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate6('title', '', '', '', '', '', '', '', '', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate7', () => { it('should interpolate seven values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 7); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate7( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate7( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 14); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate7( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate7( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 7); expect(() => { t.update(() => { ɵɵpropertyInterpolate7( 'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate7( 'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolate8', () => { it('should interpolate eight values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 8); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate8( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', 7: ', 7, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate8( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 16); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate8( 'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', 7: ', 7, ', end')( 'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', 7: ', 7, ', end'); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolate8( 'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', end')( 'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', end'); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 8); expect(() => { t.update(() => { ɵɵpropertyInterpolate8( 'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolate8( 'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); }); }).not.toThrow(); }); }); describe('ɵɵpropertyInterpolateV', () => { it('should interpolate eight or more values', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 9); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolateV('title', [ 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', 7: ', 7, ', 8: ', 8, ', end' ]); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolateV('title', [ 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', 8: ', 'I', ', end' ]); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 2, }); }); it('should chain', () => { // const t = new TemplateFixture(createDiv, () => {}, 1, 18); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolateV( 'title', [ 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', 7: ', 7, ', 8: ', 8, ', end' ])( 'accessKey', [ 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6, ', 7: ', 7, ', 8: ', 8, ', end' ]); }); expect(t.html).toEqual( ''); t.update(() => { ɵɵselect(0); ɵɵpropertyInterpolateV( 'title', [ 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', 8: ', 'I', ', end' ])( 'accessKey', [ 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', 8: ', 'I', ', end' ]); }); expect(t.html).toEqual( ''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 4, }); }); it('should error if called without ɵɵselect called first', () => { const t = new TemplateFixture(createDiv, () => {}, 1, 9); expect(() => { t.update(() => { ɵɵpropertyInterpolateV( 'title', ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']); }); }).toThrow(); expect(() => { ɵɵselect(0); t.update(() => { ɵɵpropertyInterpolateV( 'title', ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']); }); }).not.toThrow(); }); }); }); describe('elementProperty', () => { it('should use sanitizer function when available', () => { const t = new TemplateFixture(createDiv, () => {}, 1); t.update(() => ɵɵelementProperty(0, 'title', 'javascript:true', ɵɵsanitizeUrl)); expect(t.html).toEqual(''); t.update( () => ɵɵelementProperty( 0, 'title', bypassSanitizationTrustUrl('javascript:false'), ɵɵsanitizeUrl)); expect(t.html).toEqual(''); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, }); }); it('should not stringify non string values', () => { const t = new TemplateFixture(() => { ɵɵelement(0, 'input'); }, () => {}, 1); // Note: don't use 'hidden' here because IE10 does not support the hidden property t.update(() => ɵɵelementProperty(0, 'required', false)); // The required property would be true if `false` was stringified into `"false"`. expect((t.hostElement as HTMLElement).querySelector('input') !.required).toEqual(false); expect(ngDevMode).toHaveProperties({ firstTemplatePass: 1, tNode: 2, // 1 for div, 1 for host element tView: 2, // 1 for rootView + 1 for the template view rendererCreateElement: 1, rendererSetProperty: 1 }); }); }); describe('elementStyleProp', () => { it('should automatically sanitize unless a bypass operation is applied', () => { const t = new TemplateFixture(() => { return createDiv(null, null, null, ['background-image'], ɵɵdefaultStyleSanitizer); }, () => {}, 1); t.update(() => { ɵɵelementStyleProp(0, 0, 'url("http://server")'); ɵɵelementStylingApply(0); }); // nothing is set because sanitizer suppresses it. expect(t.html).toEqual(''); t.update(() => { ɵɵelementStyleProp(0, 0, bypassSanitizationTrustStyle('url("http://server2")')); ɵɵelementStylingApply(0); }); expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image')) .toEqual('url("http://server2")'); }); it('should not re-apply the style value even if it is a newly bypassed again', () => { const sanitizerInterceptor = new MockSanitizerInterceptor(); const t = createTemplateFixtureWithSanitizer( () => createDiv( null, null, null, ['background-image'], sanitizerInterceptor.getStyleSanitizer()), 1, sanitizerInterceptor); t.update(() => { ɵɵelementStyleProp(0, 0, bypassSanitizationTrustStyle('apple')); ɵɵelementStylingApply(0); }); expect(sanitizerInterceptor.lastValue !).toEqual('apple'); sanitizerInterceptor.lastValue = null; t.update(() => { ɵɵelementStyleProp(0, 0, bypassSanitizationTrustStyle('apple')); ɵɵelementStylingApply(0); }); expect(sanitizerInterceptor.lastValue).toEqual(null); }); }); describe('elementStyleMap', () => { function createDivWithStyle() { ɵɵelementStart(0, 'div', [AttributeMarker.Styles, 'height', '10px']); ɵɵelementStyling([], ['height']); ɵɵelementEnd(); } it('should add style', () => { const fixture = new TemplateFixture(createDivWithStyle, () => {}, 1); fixture.update(() => { ɵɵelementStylingMap(0, null, {'background-color': 'red'}); ɵɵelementStylingApply(0); }); expect(fixture.html).toEqual(''); }); it('should sanitize new styles that may contain `url` properties', () => { const detectedValues: string[] = []; const sanitizerInterceptor = new MockSanitizerInterceptor(value => { detectedValues.push(value); }); const fixture = createTemplateFixtureWithSanitizer( () => createDiv(null, null, null, null, sanitizerInterceptor.getStyleSanitizer()), 1, sanitizerInterceptor); fixture.update(() => { ɵɵelementStylingMap(0, null, { 'background-image': 'background-image', 'background': 'background', 'border-image': 'border-image', 'list-style': 'list-style', 'list-style-image': 'list-style-image', 'filter': 'filter', 'width': 'width' }); ɵɵelementStylingApply(0); }); const props = detectedValues.sort(); expect(props).toEqual([ 'background', 'background-image', 'border-image', 'filter', 'list-style', 'list-style-image' ]); }); }); describe('elementClass', () => { function createDivWithStyling() { ɵɵelementStart(0, 'div'); ɵɵelementStyling(); ɵɵelementEnd(); } it('should add class', () => { const fixture = new TemplateFixture(createDivWithStyling, () => {}, 1); fixture.update(() => { ɵɵelementStylingMap(0, 'multiple classes'); ɵɵelementStylingApply(0); }); expect(fixture.html).toEqual(''); }); }); describe('performance counters', () => { it('should create tViews only once for each nested level', () => { const _c0 = [AttributeMarker.Template, 'ngFor', 'ngForOf']; const _c1 = [AttributeMarker.Template, 'ngFor', 'ngForOf']; function ToDoAppComponent_NgForOf_Template_0(rf: RenderFlags, ctx0: NgForOfContext