From 61d56be83ef864b2ca3709bb60bdf0eaa175523e Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 28 Sep 2020 17:39:56 -0700 Subject: [PATCH] refactor(core): Change `TemplateFixture` to named parameters (#39233) `TemplateFixture` used to have positional parameters and many tests got hard to read as number of parameters reach 10+ with many of them `null`. This refactoring changes `TemplateFixture` to take named parameters which improves usability and readability in tests. PR Close #39233 --- .../core/src/render3/instructions/advance.ts | 6 +- .../core/src/render3/instructions/shared.ts | 3 +- packages/core/src/render3/util/view_utils.ts | 7 +- packages/core/src/util/assert.ts | 7 +- packages/core/test/render3/i18n_spec.ts | 35 +++---- .../render3/instructions/lview_debug_spec.ts | 18 ++-- .../core/test/render3/instructions_spec.ts | 95 +++++++++++-------- packages/core/test/render3/listeners_spec.ts | 26 +++-- packages/core/test/render3/pipe_spec.ts | 39 +++++--- packages/core/test/render3/render_util.ts | 41 ++++++-- .../test/render3/view_container_ref_spec.ts | 26 +++-- 11 files changed, 194 insertions(+), 109 deletions(-) diff --git a/packages/core/src/render3/instructions/advance.ts b/packages/core/src/render3/instructions/advance.ts index 0a037b18af..0aafa53159 100644 --- a/packages/core/src/render3/instructions/advance.ts +++ b/packages/core/src/render3/instructions/advance.ts @@ -5,7 +5,8 @@ * 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 {assertGreaterThan, assertIndexInRange} from '../../util/assert'; +import {assertGreaterThan} from '../../util/assert'; +import {assertIndexInDeclRange} from '../assert'; import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks'; import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TView} from '../interfaces/view'; import {getLView, getSelectedIndex, getTView, isInCheckNoChangesMode, setSelectedIndex} from '../state'; @@ -41,8 +42,7 @@ export function ɵɵadvance(delta: number): void { export function selectIndexInternal( tView: TView, lView: LView, index: number, checkNoChangesMode: boolean) { - ngDevMode && assertGreaterThan(index, -1, 'Invalid index'); - ngDevMode && assertIndexInRange(lView, index + HEADER_OFFSET); + ngDevMode && assertIndexInDeclRange(lView, index + HEADER_OFFSET); // Flush the initial hooks for elements in the view that have been added up to this point. // PERF WARNING: do NOT extract this to a separate function without running benchmarks diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index a234772125..863a3832f8 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -29,7 +29,7 @@ import {NodeInjectorFactory, NodeInjectorOffset} from '../interfaces/injector'; import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode} from '../interfaces/node'; import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; -import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; +import {isComponentDef, isComponentHost, isContentQueryHost, isRootView} from '../interfaces/type_checks'; import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, InitPhaseState, INJECTOR, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, T_HOST, TData, TRANSPLANTED_VIEWS_TO_REFRESH, TVIEW, TView, TViewType} from '../interfaces/view'; import {assertNodeNotOfTypes, assertNodeOfPossibleTypes} from '../node_assert'; import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher'; @@ -822,6 +822,7 @@ export function createTNode( export function createTNode( tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number, tagName: string|null, attrs: TAttributes|null): TNode { + ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\''); ngDevMode && ngDevMode.tNode++; let injectorIndex = tParent ? tParent.injectorIndex : -1; const tNode = ngDevMode ? diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index 5aa43295bc..574c3d2215 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -176,8 +176,13 @@ export function viewAttachedToContainer(view: LView): boolean { } /** Returns a constant from `TConstants` instance. */ +export function getConstant(consts: TConstants|null, index: null|undefined): null; +export function getConstant(consts: TConstants, index: number): T|null; +export function getConstant(consts: TConstants|null, index: number|null|undefined): T|null; export function getConstant(consts: TConstants|null, index: number|null|undefined): T|null { - return consts === null || index == null ? null : consts[index] as unknown as T; + if (index === null || index === undefined) return null; + ngDevMode && assertIndexInRange(consts!, index); + return consts![index] as unknown as T; } /** diff --git a/packages/core/src/util/assert.ts b/packages/core/src/util/assert.ts index 1627149d56..68d456a24a 100644 --- a/packages/core/src/util/assert.ts +++ b/packages/core/src/util/assert.ts @@ -111,6 +111,9 @@ export function assertDomNode(node: any): asserts node is Node { export function assertIndexInRange(arr: any[], index: number) { - const maxLen = arr ? arr.length : 0; - assertLessThan(index, maxLen, `Index expected to be less than ${maxLen} but got ${index}`); + assertDefined(arr, 'Array must be defined.'); + const maxLen = arr.length; + if (index < 0 || index > maxLen) { + throwError(`Index expected to be less than ${maxLen} but got ${index}`); + } } diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index d6352e4675..08d8cb889d 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -57,19 +57,15 @@ describe('Runtime i18n', () => { }); }); - function prepareFixture( - createTemplate: () => void, updateTemplate: (() => void)|null, nbConsts = 0, nbVars = 0, - consts: TConstants = []): TemplateFixture { - return new TemplateFixture( - createTemplate, updateTemplate || noop, nbConsts, nbVars, null, null, null, undefined, - consts); - } - function getOpCodes( messageOrAtrs: string|string[], createTemplate: () => void, updateTemplate: (() => void)|null, - nbConsts: number, index: number): TI18n|I18nUpdateOpCodes { - const fixture = - prepareFixture(createTemplate, updateTemplate, nbConsts, undefined, [messageOrAtrs]); + nbDecls: number, index: number): TI18n|I18nUpdateOpCodes { + const fixture = new TemplateFixture({ + create: createTemplate, + update: updateTemplate || undefined, + decls: nbDecls, + consts: [messageOrAtrs] + }); const tView = fixture.hostView[TVIEW]; return tView.data[index + HEADER_OFFSET] as TI18n; } @@ -435,13 +431,18 @@ describe('Runtime i18n', () => { it('for text', () => { const message = `Hello world!`; const attrs = ['title', message]; - const nbConsts = 2; + const nbDecls = 2; const index = 1; - const fixture = prepareFixture(() => { - ɵɵelementStart(0, 'div'); - ɵɵi18nAttributes(index, 0); - ɵɵelementEnd(); - }, null, nbConsts, index, [attrs]); + const fixture = new TemplateFixture({ + create: () => { + ɵɵelementStart(0, 'div'); + ɵɵi18nAttributes(index, 0); + ɵɵelementEnd(); + }, + decls: nbDecls, + vars: index, + consts: [attrs], + }); const tView = fixture.hostView[TVIEW]; const opCodes = tView.data[index + HEADER_OFFSET] as I18nUpdateOpCodes; diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index 5205e3e136..3098ea3587 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -205,14 +205,16 @@ describe('lView_debug', () => { }); } - const fixture = new TemplateFixture( - () => { - ɵɵelementStart(0, 'my-comp', 0); - ɵɵelement(1, 'my-child'); - ɵɵelementEnd(); - }, - () => null, 2, 0, [MyComponent, MyDirective, MyChild], null, null, undefined, - [['my-dir', '']]); + const fixture = new TemplateFixture({ + create: () => { + ɵɵelementStart(0, 'my-comp', 0); + ɵɵelement(1, 'my-child'); + ɵɵelementEnd(); + }, + decls: 2, + directives: [MyComponent, MyDirective, MyChild], + consts: [['my-dir', '']] + }); const lView = fixture.hostView; const lViewDebug = lView.debug!; const myCompNode = lViewDebug.nodes[0]; diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index 192158364f..4fd8a11d95 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -26,7 +26,7 @@ describe('instructions', () => { } function createDiv() { - ɵɵelement(0, 'div', 0); + ɵɵelement(0, 'div'); } function createScript() { @@ -36,7 +36,7 @@ describe('instructions', () => { describe('ɵɵadvance', () => { 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); + const t = new TemplateFixture({create: createDiv, decls: 1}); expect(() => { t.update(() => { ɵɵadvance(-1); @@ -52,7 +52,7 @@ describe('instructions', () => { describe('bind', () => { it('should update bindings when value changes with the correct perf counters', () => { - const t = new TemplateFixture(createAnchor, () => {}, 1, 1); + const t = new TemplateFixture({create: createAnchor, update: () => {}, decls: 1, vars: 1}); t.update(() => { ɵɵproperty('title', 'Hello'); @@ -77,7 +77,8 @@ describe('instructions', () => { const idempotentUpdate = () => { ɵɵproperty('title', 'Hello'); }; - const t = new TemplateFixture(createAnchor, idempotentUpdate, 1, 1); + const t = new TemplateFixture( + {create: createAnchor, update: idempotentUpdate, decls: 1, vars: 1}); t.update(); expect(t.html).toEqual(''); @@ -96,9 +97,14 @@ describe('instructions', () => { describe('element', () => { it('should create an element with the correct perf counters', () => { - const t = new TemplateFixture(() => { - ɵɵelement(0, 'div', 0); - }, () => {}, 1, 0, null, null, null, undefined, [['id', 'test', 'title', 'Hello']]); + const t = new TemplateFixture({ + create: () => { + ɵɵelement(0, 'div', 0); + }, + decls: 1, + vars: 0, + consts: [['id', 'test', 'title', 'Hello']] + }); const div = (t.hostElement as HTMLElement).querySelector('div')!; expect(div.id).toEqual('test'); @@ -114,7 +120,7 @@ describe('instructions', () => { describe('attribute', () => { it('should use sanitizer function', () => { - const t = new TemplateFixture(createDiv, () => {}, 1, 1); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1}); t.update(() => { ɵɵattribute('title', 'javascript:true', ɵɵsanitizeUrl); @@ -143,7 +149,7 @@ describe('instructions', () => { */ it('should chain', () => { //
- const t = new TemplateFixture(createDiv, () => {}, 1, 2); + const t = new TemplateFixture({create: createDiv, update: () => {}, decls: 1, vars: 2}); t.update(() => { ɵɵproperty('title', 'one')('accessKey', 'A'); }); @@ -165,14 +171,16 @@ describe('instructions', () => { describe('styleProp', () => { it('should allow values even if a bypass operation is applied', () => { let backgroundImage: string|SafeValue = 'url("http://server")'; - const t = new TemplateFixture( - () => { - return createDiv(); - }, - () => { - ɵɵstyleProp('background-image', backgroundImage); - }, - 2, 2); + const t = new TemplateFixture({ + create: () => { + return createDiv(); + }, + update: () => { + ɵɵstyleProp('background-image', backgroundImage); + }, + decls: 2, + vars: 2 + }); // nothing is set because sanitizer suppresses it. expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image')) .toEqual('url("http://server")'); @@ -192,9 +200,15 @@ describe('instructions', () => { } it('should add style', () => { - const fixture = new TemplateFixture(createDivWithStyle, () => { - ɵɵstyleMap({'background-color': 'red'}); - }, 1, 2, null, null, null, undefined, attrs); + const fixture = new TemplateFixture({ + create: createDivWithStyle, + update: () => { + ɵɵstyleMap({'background-color': 'red'}); + }, + decls: 1, + vars: 2, + consts: attrs + }); fixture.update(); expect(fixture.html).toEqual('
'); }); @@ -206,9 +220,14 @@ describe('instructions', () => { } it('should add class', () => { - const fixture = new TemplateFixture(createDivWithStyling, () => { - ɵɵclassMap('multiple classes'); - }, 1, 2); + const fixture = new TemplateFixture({ + create: createDivWithStyling, + update: () => { + ɵɵclassMap('multiple classes'); + }, + decls: 1, + vars: 2 + }); const div = fixture.containerElement.querySelector('div.multiple')!; expect(getSortedClassName(div)).toEqual('classes multiple'); }); @@ -284,7 +303,7 @@ describe('instructions', () => { describe('sanitization injection compatibility', () => { it('should work for url sanitization', () => { const s = new LocalMockSanitizer(value => `${value}-sanitized`); - const t = new TemplateFixture(createAnchor, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createAnchor, decls: 1, vars: 1, sanitizer: s}); const inputValue = 'http://foo'; const outputValue = 'http://foo-sanitized'; @@ -297,7 +316,7 @@ describe('instructions', () => { it('should bypass url sanitization if marked by the service', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createAnchor, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createAnchor, decls: 1, vars: 1, sanitizer: s}); const inputValue = s.bypassSecurityTrustUrl('http://foo'); const outputValue = 'http://foo'; @@ -310,7 +329,7 @@ describe('instructions', () => { it('should bypass ivy-level url sanitization if a custom sanitizer is used', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createAnchor, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createAnchor, decls: 1, vars: 1, sanitizer: s}); const inputValue = bypassSanitizationTrustUrl('http://foo'); const outputValue = 'http://foo-ivy'; @@ -323,7 +342,7 @@ describe('instructions', () => { it('should work for style sanitization', () => { const s = new LocalMockSanitizer(value => `color:blue`); - const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1, sanitizer: s}); const inputValue = 'color:red'; const outputValue = 'color:blue'; @@ -336,7 +355,7 @@ describe('instructions', () => { it('should bypass style sanitization if marked by the service', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1, sanitizer: s}); const inputValue = s.bypassSecurityTrustStyle('color:maroon'); const outputValue = 'color:maroon'; @@ -349,7 +368,7 @@ describe('instructions', () => { it('should bypass ivy-level style sanitization if a custom sanitizer is used', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1, sanitizer: s}); const inputValue = bypassSanitizationTrustStyle('font-family:foo'); const outputValue = 'font-family:foo-ivy'; @@ -362,7 +381,7 @@ describe('instructions', () => { it('should work for resourceUrl sanitization', () => { const s = new LocalMockSanitizer(value => `${value}-sanitized`); - const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createScript, decls: 1, vars: 1, sanitizer: s}); const inputValue = 'http://resource'; const outputValue = 'http://resource-sanitized'; @@ -375,7 +394,7 @@ describe('instructions', () => { it('should bypass resourceUrl sanitization if marked by the service', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createScript, decls: 1, vars: 1, sanitizer: s}); const inputValue = s.bypassSecurityTrustResourceUrl('file://all-my-secrets.pdf'); const outputValue = 'file://all-my-secrets.pdf'; @@ -388,7 +407,7 @@ describe('instructions', () => { it('should bypass ivy-level resourceUrl sanitization if a custom sanitizer is used', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createScript, decls: 1, vars: 1, sanitizer: s}); const inputValue = bypassSanitizationTrustResourceUrl('file://all-my-secrets.pdf'); const outputValue = 'file://all-my-secrets.pdf-ivy'; @@ -401,7 +420,7 @@ describe('instructions', () => { it('should work for script sanitization', () => { const s = new LocalMockSanitizer(value => `${value} //sanitized`); - const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createScript, decls: 1, vars: 1, sanitizer: s}); const inputValue = 'fn();'; const outputValue = 'fn(); //sanitized'; @@ -414,7 +433,7 @@ describe('instructions', () => { it('should bypass script sanitization if marked by the service', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createScript, decls: 1, vars: 1, sanitizer: s}); const inputValue = s.bypassSecurityTrustScript('alert("bar")'); const outputValue = 'alert("bar")'; @@ -427,7 +446,7 @@ describe('instructions', () => { it('should bypass ivy-level script sanitization if a custom sanitizer is used', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createScript, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createScript, decls: 1, vars: 1, sanitizer: s}); const inputValue = bypassSanitizationTrustScript('alert("bar")'); const outputValue = 'alert("bar")-ivy'; @@ -440,7 +459,7 @@ describe('instructions', () => { it('should work for html sanitization', () => { const s = new LocalMockSanitizer(value => `${value} `); - const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1, sanitizer: s}); const inputValue = '
'; const outputValue = '
'; @@ -453,7 +472,7 @@ describe('instructions', () => { it('should bypass html sanitization if marked by the service', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1, sanitizer: s}); const inputValue = s.bypassSecurityTrustHtml('
'); const outputValue = '
'; @@ -466,7 +485,7 @@ describe('instructions', () => { it('should bypass ivy-level script sanitization if a custom sanitizer is used', () => { const s = new LocalMockSanitizer(value => ''); - const t = new TemplateFixture(createDiv, undefined, 1, 1, null, null, s); + const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1, sanitizer: s}); const inputValue = bypassSanitizationTrustHtml('
'); const outputValue = '
-ivy'; diff --git a/packages/core/test/render3/listeners_spec.ts b/packages/core/test/render3/listeners_spec.ts index f40e23eb57..3fe1e5ac3c 100644 --- a/packages/core/test/render3/listeners_spec.ts +++ b/packages/core/test/render3/listeners_spec.ts @@ -372,11 +372,16 @@ describe('event listeners', () => { }); } - const fixture = new TemplateFixture(() => { - ɵɵelementStart(0, 'button', 0); - ɵɵtext(1, 'Click'); - ɵɵelementEnd(); - }, () => {}, 2, 0, [HostListenerDir], null, null, undefined, [['hostListenerDir', '']]); + const fixture = new TemplateFixture({ + create: () => { + ɵɵelementStart(0, 'button', 0); + ɵɵtext(1, 'Click'); + ɵɵelementEnd(); + }, + decls: 2, + directives: [HostListenerDir], + consts: [['hostListenerDir', '']] + }); const button = fixture.hostElement.querySelector('button')!; @@ -388,9 +393,14 @@ describe('event listeners', () => { }); it('should support global host listeners on directives', () => { - const fixture = new TemplateFixture(() => { - ɵɵelement(0, 'div', 0); - }, () => {}, 1, 0, [GlobalHostListenerDir], null, null, undefined, [['hostListenerDir', '']]); + const fixture = new TemplateFixture({ + create: () => { + ɵɵelement(0, 'div', 0); + }, + decls: 1, + directives: [GlobalHostListenerDir], + consts: [['hostListenerDir', '']] + }); const doc = fixture.hostElement.ownerDocument!; diff --git a/packages/core/test/render3/pipe_spec.ts b/packages/core/test/render3/pipe_spec.ts index 55c4290f36..4cf1f8e841 100644 --- a/packages/core/test/render3/pipe_spec.ts +++ b/packages/core/test/render3/pipe_spec.ts @@ -52,14 +52,24 @@ describe('pipe', () => { } it('should unwrap', () => { - const fixture = - new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]); + const fixture = new TemplateFixture({ + create: createTemplate, + update: updateTemplate, + decls: 2, + vars: 3, + pipes: [WrappingPipe] + }); expect(fixture.html).toEqual('Bar'); }); it('should force change detection', () => { - const fixture = - new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]); + const fixture = new TemplateFixture({ + create: createTemplate, + update: updateTemplate, + decls: 2, + vars: 3, + pipes: [WrappingPipe] + }); expect(fixture.html).toEqual('Bar'); fixture.hostElement.childNodes[0]!.textContent = 'Foo'; @@ -101,15 +111,18 @@ describe('pipe', () => { static ɵpipe = ɵɵdefinePipe({name: 'sayHello', type: SayHelloPipe, pure: true}); } - const fixture = new TemplateFixture( - () => { - ɵɵtext(0); - ɵɵpipe(1, 'sayHello'); - }, - () => { - ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); - }, - 2, 3, undefined, [SayHelloPipe]); + const fixture = new TemplateFixture({ + create: () => { + ɵɵtext(0); + ɵɵpipe(1, 'sayHello'); + }, + update: () => { + ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), ''); + }, + decls: 2, + vars: 3, + pipes: [SayHelloPipe] + }); expect(fixture.html).toBe('Hello there'); }); diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 9fb6e790b8..912f3bbac8 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -15,6 +15,7 @@ import {Renderer2} from '@angular/core/src/render/api'; import {createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, renderComponentOrTemplate} from '@angular/core/src/render3/instructions/shared'; import {TConstants, TNodeType} from '@angular/core/src/render3/interfaces/node'; import {enterView, getLView} from '@angular/core/src/render3/state'; +import {EMPTY_ARRAY} from '@angular/core/src/util/empty'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref'; @@ -97,6 +98,10 @@ export class TemplateFixture extends BaseFixture { private _pipeDefs: PipeDefList|null; private _sanitizer: Sanitizer|null; private _rendererFactory: RendererFactory3; + private _consts: TConstants; + private _vars: number; + private createBlock: () => void; + private updateBlock: () => void; /** * @@ -105,16 +110,36 @@ export class TemplateFixture extends BaseFixture { * @param updateBlock Optional instructions which go into the update block: * `if (rf & RenderFlags.Update) { __here__ }`. */ - constructor( - private createBlock: () => void, private updateBlock: () => void = noop, decls: number = 0, - private vars: number = 0, directives?: DirectiveTypesOrFactory|null, - pipes?: PipeTypesOrFactory|null, sanitizer?: Sanitizer|null, - rendererFactory?: RendererFactory3, private _consts?: TConstants) { + constructor({ + create = noop, + update = noop, + decls = 0, + vars = 0, + directives, + pipes, + sanitizer = null, + rendererFactory = domRendererFactory3, + consts = EMPTY_ARRAY + }: { + create?: (() => void), + update?: (() => void), + decls?: number, + vars?: number, + directives?: DirectiveTypesOrFactory, + pipes?: PipeTypesOrFactory, + sanitizer?: Sanitizer|null, + rendererFactory?: RendererFactory3, + consts?: TConstants + }) { super(); + this._consts = consts; + this._vars = vars; + this.createBlock = create; + this.updateBlock = update; this._directiveDefs = toDefs(directives, extractDirectiveDef); this._pipeDefs = toDefs(pipes, extractPipeDef); - this._sanitizer = sanitizer || null; - this._rendererFactory = rendererFactory || domRendererFactory3; + this._sanitizer = sanitizer; + this._rendererFactory = rendererFactory; this.hostView = renderTemplate( this.hostElement, (rf: RenderFlags, ctx: any) => { @@ -136,7 +161,7 @@ export class TemplateFixture extends BaseFixture { */ update(updateBlock?: () => void): void { renderTemplate( - this.hostElement, updateBlock || this.updateBlock, 0, this.vars, null!, + this.hostElement, updateBlock || this.updateBlock, 0, this._vars, null!, this._rendererFactory, this.hostView, this._directiveDefs, this._pipeDefs, this._sanitizer, this._consts); } diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 3dc159923b..10ee8debb0 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -232,9 +232,12 @@ describe('ViewContainerRef', () => { ɵɵelement(1, 'footer'); } - new TemplateFixture( - createTemplate, undefined, 2, 0, [DirectiveWithVCRef], null, null, undefined, - [['vcref', '']]); + new TemplateFixture({ + create: createTemplate, + decls: 2, + directives: [DirectiveWithVCRef], + consts: [['vcref', '']] + }); expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase()) .toEqual('header'); @@ -253,9 +256,12 @@ describe('ViewContainerRef', () => { ɵɵelement(1, 'footer'); } - new TemplateFixture( - createTemplate, undefined, 2, 0, [HeaderComponent, DirectiveWithVCRef], null, null, - undefined, [['vcref', '']]); + new TemplateFixture({ + create: createTemplate, + decls: 2, + directives: [HeaderComponent, DirectiveWithVCRef], + consts: [['vcref', '']] + }); expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase()) .toEqual('header-cmp'); @@ -430,10 +436,10 @@ describe('ViewContainerRef', () => { fixture.update(); // Destroying the parent view will also destroy all of its children views and call their - // onDestroy hooks. Here, our child view attempts to destroy itself *again* in its onDestroy. - // This test exists to verify that no errors are thrown when doing this. We want the test - // component to destroy its own view in onDestroy because the destroy hooks happen as a - // *part of* view destruction. We also ensure that the test component has at least one + // onDestroy hooks. Here, our child view attempts to destroy itself *again* in its + // onDestroy. This test exists to verify that no errors are thrown when doing this. We want + // the test component to destroy its own view in onDestroy because the destroy hooks happen + // as a *part of* view destruction. We also ensure that the test component has at least one // listener so that it runs the event listener cleanup code path. fixture.destroy(); });