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
This commit is contained in:
Misko Hevery 2020-09-28 17:39:56 -07:00 committed by Alex Rickabaugh
parent eb4c05d97a
commit 61d56be83e
11 changed files with 194 additions and 109 deletions

View File

@ -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

View File

@ -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 ?

View File

@ -176,8 +176,13 @@ export function viewAttachedToContainer(view: LView): boolean {
}
/** Returns a constant from `TConstants` instance. */
export function getConstant<T>(consts: TConstants|null, index: null|undefined): null;
export function getConstant<T>(consts: TConstants, index: number): T|null;
export function getConstant<T>(consts: TConstants|null, index: number|null|undefined): T|null;
export function getConstant<T>(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;
}
/**

View File

@ -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}`);
}
}

View File

@ -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(() => {
const fixture = new TemplateFixture({
create: () => {
ɵɵelementStart(0, 'div');
ɵɵi18nAttributes(index, 0);
ɵɵelementEnd();
}, null, nbConsts, index, [attrs]);
},
decls: nbDecls,
vars: index,
consts: [attrs],
});
const tView = fixture.hostView[TVIEW];
const opCodes = tView.data[index + HEADER_OFFSET] as I18nUpdateOpCodes;

View File

@ -205,14 +205,16 @@ describe('lView_debug', () => {
});
}
const fixture = new TemplateFixture(
() => {
const fixture = new TemplateFixture({
create: () => {
ɵɵelementStart(0, 'my-comp', 0);
ɵɵelement(1, 'my-child');
ɵɵelementEnd();
},
() => null, 2, 0, [MyComponent, MyDirective, MyChild], null, null, undefined,
[['my-dir', '']]);
decls: 2,
directives: [MyComponent, MyDirective, MyChild],
consts: [['my-dir', '']]
});
const lView = fixture.hostView;
const lViewDebug = lView.debug!;
const myCompNode = lViewDebug.nodes[0];

View File

@ -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('<a title="Hello"></a>');
@ -96,9 +97,14 @@ describe('instructions', () => {
describe('element', () => {
it('should create an element with the correct perf counters', () => {
const t = new TemplateFixture(() => {
const t = new TemplateFixture({
create: () => {
ɵɵelement(0, 'div', 0);
}, () => {}, 1, 0, null, null, null, undefined, [['id', 'test', 'title', 'Hello']]);
},
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', () => {
// <div [title]="title" [accesskey]="key"></div>
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(
() => {
const t = new TemplateFixture({
create: () => {
return createDiv();
},
() => {
update: () => {
ɵɵstyleProp('background-image', backgroundImage);
},
2, 2);
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, () => {
const fixture = new TemplateFixture({
create: createDivWithStyle,
update: () => {
ɵɵstyleMap({'background-color': 'red'});
}, 1, 2, null, null, null, undefined, attrs);
},
decls: 1,
vars: 2,
consts: attrs
});
fixture.update();
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
});
@ -206,9 +220,14 @@ describe('instructions', () => {
}
it('should add class', () => {
const fixture = new TemplateFixture(createDivWithStyling, () => {
const fixture = new TemplateFixture({
create: createDivWithStyling,
update: () => {
ɵɵclassMap('multiple classes');
}, 1, 2);
},
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} <!--sanitized-->`);
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 = '<header></header>';
const outputValue = '<header></header> <!--sanitized-->';
@ -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('<div onclick="alert(123)"></div>');
const outputValue = '<div onclick="alert(123)"></div>';
@ -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('<div onclick="alert(123)"></div>');
const outputValue = '<div onclick="alert(123)"></div>-ivy';

View File

@ -372,11 +372,16 @@ describe('event listeners', () => {
});
}
const fixture = new TemplateFixture(() => {
const fixture = new TemplateFixture({
create: () => {
ɵɵelementStart(0, 'button', 0);
ɵɵtext(1, 'Click');
ɵɵelementEnd();
}, () => {}, 2, 0, [HostListenerDir], null, null, undefined, [['hostListenerDir', '']]);
},
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(() => {
const fixture = new TemplateFixture({
create: () => {
ɵɵelement(0, 'div', 0);
}, () => {}, 1, 0, [GlobalHostListenerDir], null, null, undefined, [['hostListenerDir', '']]);
},
decls: 1,
directives: [GlobalHostListenerDir],
consts: [['hostListenerDir', '']]
});
const doc = fixture.hostElement.ownerDocument!;

View File

@ -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(
() => {
const fixture = new TemplateFixture({
create: () => {
ɵɵtext(0);
ɵɵpipe(1, 'sayHello');
},
() => {
update: () => {
ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), '');
},
2, 3, undefined, [SayHelloPipe]);
decls: 2,
vars: 3,
pipes: [SayHelloPipe]
});
expect(fixture.html).toBe('Hello there');
});

View File

@ -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);
}

View File

@ -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();
});