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:
parent
eb4c05d97a
commit
61d56be83e
|
@ -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
|
||||
|
|
|
@ -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 ?
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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(() => {
|
||||
ɵɵ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', () => {
|
||||
// <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(
|
||||
() => {
|
||||
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('<div style="background-color: red; height: 10px;"></div>');
|
||||
});
|
||||
|
@ -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} <!--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';
|
||||
|
||||
|
|
|
@ -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!;
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue