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
|
* 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
|
* 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 {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
|
||||||
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TView} from '../interfaces/view';
|
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TView} from '../interfaces/view';
|
||||||
import {getLView, getSelectedIndex, getTView, isInCheckNoChangesMode, setSelectedIndex} from '../state';
|
import {getLView, getSelectedIndex, getTView, isInCheckNoChangesMode, setSelectedIndex} from '../state';
|
||||||
|
@ -41,8 +42,7 @@ export function ɵɵadvance(delta: number): void {
|
||||||
|
|
||||||
export function selectIndexInternal(
|
export function selectIndexInternal(
|
||||||
tView: TView, lView: LView, index: number, checkNoChangesMode: boolean) {
|
tView: TView, lView: LView, index: number, checkNoChangesMode: boolean) {
|
||||||
ngDevMode && assertGreaterThan(index, -1, 'Invalid index');
|
ngDevMode && assertIndexInDeclRange(lView, index + HEADER_OFFSET);
|
||||||
ngDevMode && assertIndexInRange(lView, index + HEADER_OFFSET);
|
|
||||||
|
|
||||||
// Flush the initial hooks for elements in the view that have been added up to this point.
|
// 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
|
// 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 {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 {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer';
|
||||||
import {SanitizerFn} from '../interfaces/sanitization';
|
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 {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 {assertNodeNotOfTypes, assertNodeOfPossibleTypes} from '../node_assert';
|
||||||
import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher';
|
import {isInlineTemplate, isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||||
|
@ -822,6 +822,7 @@ export function createTNode(
|
||||||
export function createTNode(
|
export function createTNode(
|
||||||
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number,
|
tView: TView, tParent: TElementNode|TContainerNode|null, type: TNodeType, adjustedIndex: number,
|
||||||
tagName: string|null, attrs: TAttributes|null): TNode {
|
tagName: string|null, attrs: TAttributes|null): TNode {
|
||||||
|
ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
|
||||||
ngDevMode && ngDevMode.tNode++;
|
ngDevMode && ngDevMode.tNode++;
|
||||||
let injectorIndex = tParent ? tParent.injectorIndex : -1;
|
let injectorIndex = tParent ? tParent.injectorIndex : -1;
|
||||||
const tNode = ngDevMode ?
|
const tNode = ngDevMode ?
|
||||||
|
|
|
@ -176,8 +176,13 @@ export function viewAttachedToContainer(view: LView): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a constant from `TConstants` instance. */
|
/** 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 {
|
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) {
|
export function assertIndexInRange(arr: any[], index: number) {
|
||||||
const maxLen = arr ? arr.length : 0;
|
assertDefined(arr, 'Array must be defined.');
|
||||||
assertLessThan(index, maxLen, `Index expected to be less than ${maxLen} but got ${index}`);
|
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(
|
function getOpCodes(
|
||||||
messageOrAtrs: string|string[], createTemplate: () => void, updateTemplate: (() => void)|null,
|
messageOrAtrs: string|string[], createTemplate: () => void, updateTemplate: (() => void)|null,
|
||||||
nbConsts: number, index: number): TI18n|I18nUpdateOpCodes {
|
nbDecls: number, index: number): TI18n|I18nUpdateOpCodes {
|
||||||
const fixture =
|
const fixture = new TemplateFixture({
|
||||||
prepareFixture(createTemplate, updateTemplate, nbConsts, undefined, [messageOrAtrs]);
|
create: createTemplate,
|
||||||
|
update: updateTemplate || undefined,
|
||||||
|
decls: nbDecls,
|
||||||
|
consts: [messageOrAtrs]
|
||||||
|
});
|
||||||
const tView = fixture.hostView[TVIEW];
|
const tView = fixture.hostView[TVIEW];
|
||||||
return tView.data[index + HEADER_OFFSET] as TI18n;
|
return tView.data[index + HEADER_OFFSET] as TI18n;
|
||||||
}
|
}
|
||||||
|
@ -435,13 +431,18 @@ describe('Runtime i18n', () => {
|
||||||
it('for text', () => {
|
it('for text', () => {
|
||||||
const message = `Hello world!`;
|
const message = `Hello world!`;
|
||||||
const attrs = ['title', message];
|
const attrs = ['title', message];
|
||||||
const nbConsts = 2;
|
const nbDecls = 2;
|
||||||
const index = 1;
|
const index = 1;
|
||||||
const fixture = prepareFixture(() => {
|
const fixture = new TemplateFixture({
|
||||||
|
create: () => {
|
||||||
ɵɵelementStart(0, 'div');
|
ɵɵelementStart(0, 'div');
|
||||||
ɵɵi18nAttributes(index, 0);
|
ɵɵi18nAttributes(index, 0);
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}, null, nbConsts, index, [attrs]);
|
},
|
||||||
|
decls: nbDecls,
|
||||||
|
vars: index,
|
||||||
|
consts: [attrs],
|
||||||
|
});
|
||||||
const tView = fixture.hostView[TVIEW];
|
const tView = fixture.hostView[TVIEW];
|
||||||
const opCodes = tView.data[index + HEADER_OFFSET] as I18nUpdateOpCodes;
|
const opCodes = tView.data[index + HEADER_OFFSET] as I18nUpdateOpCodes;
|
||||||
|
|
||||||
|
|
|
@ -205,14 +205,16 @@ describe('lView_debug', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixture = new TemplateFixture(
|
const fixture = new TemplateFixture({
|
||||||
() => {
|
create: () => {
|
||||||
ɵɵelementStart(0, 'my-comp', 0);
|
ɵɵelementStart(0, 'my-comp', 0);
|
||||||
ɵɵelement(1, 'my-child');
|
ɵɵelement(1, 'my-child');
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
},
|
},
|
||||||
() => null, 2, 0, [MyComponent, MyDirective, MyChild], null, null, undefined,
|
decls: 2,
|
||||||
[['my-dir', '']]);
|
directives: [MyComponent, MyDirective, MyChild],
|
||||||
|
consts: [['my-dir', '']]
|
||||||
|
});
|
||||||
const lView = fixture.hostView;
|
const lView = fixture.hostView;
|
||||||
const lViewDebug = lView.debug!;
|
const lViewDebug = lView.debug!;
|
||||||
const myCompNode = lViewDebug.nodes[0];
|
const myCompNode = lViewDebug.nodes[0];
|
||||||
|
|
|
@ -26,7 +26,7 @@ describe('instructions', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDiv() {
|
function createDiv() {
|
||||||
ɵɵelement(0, 'div', 0);
|
ɵɵelement(0, 'div');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createScript() {
|
function createScript() {
|
||||||
|
@ -36,7 +36,7 @@ describe('instructions', () => {
|
||||||
describe('ɵɵadvance', () => {
|
describe('ɵɵadvance', () => {
|
||||||
it('should error in DevMode if index is out of range', () => {
|
it('should error in DevMode if index is out of range', () => {
|
||||||
// Only one constant added, meaning only index `0` is valid.
|
// 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(() => {
|
expect(() => {
|
||||||
t.update(() => {
|
t.update(() => {
|
||||||
ɵɵadvance(-1);
|
ɵɵadvance(-1);
|
||||||
|
@ -52,7 +52,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
describe('bind', () => {
|
describe('bind', () => {
|
||||||
it('should update bindings when value changes with the correct perf counters', () => {
|
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(() => {
|
t.update(() => {
|
||||||
ɵɵproperty('title', 'Hello');
|
ɵɵproperty('title', 'Hello');
|
||||||
|
@ -77,7 +77,8 @@ describe('instructions', () => {
|
||||||
const idempotentUpdate = () => {
|
const idempotentUpdate = () => {
|
||||||
ɵɵproperty('title', 'Hello');
|
ɵɵ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();
|
t.update();
|
||||||
expect(t.html).toEqual('<a title="Hello"></a>');
|
expect(t.html).toEqual('<a title="Hello"></a>');
|
||||||
|
@ -96,9 +97,14 @@ describe('instructions', () => {
|
||||||
|
|
||||||
describe('element', () => {
|
describe('element', () => {
|
||||||
it('should create an element with the correct perf counters', () => {
|
it('should create an element with the correct perf counters', () => {
|
||||||
const t = new TemplateFixture(() => {
|
const t = new TemplateFixture({
|
||||||
|
create: () => {
|
||||||
ɵɵelement(0, 'div', 0);
|
ɵɵ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')!;
|
const div = (t.hostElement as HTMLElement).querySelector('div')!;
|
||||||
expect(div.id).toEqual('test');
|
expect(div.id).toEqual('test');
|
||||||
|
@ -114,7 +120,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
describe('attribute', () => {
|
describe('attribute', () => {
|
||||||
it('should use sanitizer function', () => {
|
it('should use sanitizer function', () => {
|
||||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
const t = new TemplateFixture({create: createDiv, decls: 1, vars: 1});
|
||||||
|
|
||||||
t.update(() => {
|
t.update(() => {
|
||||||
ɵɵattribute('title', 'javascript:true', ɵɵsanitizeUrl);
|
ɵɵattribute('title', 'javascript:true', ɵɵsanitizeUrl);
|
||||||
|
@ -143,7 +149,7 @@ describe('instructions', () => {
|
||||||
*/
|
*/
|
||||||
it('should chain', () => {
|
it('should chain', () => {
|
||||||
// <div [title]="title" [accesskey]="key"></div>
|
// <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(() => {
|
t.update(() => {
|
||||||
ɵɵproperty('title', 'one')('accessKey', 'A');
|
ɵɵproperty('title', 'one')('accessKey', 'A');
|
||||||
});
|
});
|
||||||
|
@ -165,14 +171,16 @@ describe('instructions', () => {
|
||||||
describe('styleProp', () => {
|
describe('styleProp', () => {
|
||||||
it('should allow values even if a bypass operation is applied', () => {
|
it('should allow values even if a bypass operation is applied', () => {
|
||||||
let backgroundImage: string|SafeValue = 'url("http://server")';
|
let backgroundImage: string|SafeValue = 'url("http://server")';
|
||||||
const t = new TemplateFixture(
|
const t = new TemplateFixture({
|
||||||
() => {
|
create: () => {
|
||||||
return createDiv();
|
return createDiv();
|
||||||
},
|
},
|
||||||
() => {
|
update: () => {
|
||||||
ɵɵstyleProp('background-image', backgroundImage);
|
ɵɵstyleProp('background-image', backgroundImage);
|
||||||
},
|
},
|
||||||
2, 2);
|
decls: 2,
|
||||||
|
vars: 2
|
||||||
|
});
|
||||||
// nothing is set because sanitizer suppresses it.
|
// nothing is set because sanitizer suppresses it.
|
||||||
expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image'))
|
expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image'))
|
||||||
.toEqual('url("http://server")');
|
.toEqual('url("http://server")');
|
||||||
|
@ -192,9 +200,15 @@ describe('instructions', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should add style', () => {
|
it('should add style', () => {
|
||||||
const fixture = new TemplateFixture(createDivWithStyle, () => {
|
const fixture = new TemplateFixture({
|
||||||
|
create: createDivWithStyle,
|
||||||
|
update: () => {
|
||||||
ɵɵstyleMap({'background-color': 'red'});
|
ɵɵstyleMap({'background-color': 'red'});
|
||||||
}, 1, 2, null, null, null, undefined, attrs);
|
},
|
||||||
|
decls: 1,
|
||||||
|
vars: 2,
|
||||||
|
consts: attrs
|
||||||
|
});
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
|
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
|
||||||
});
|
});
|
||||||
|
@ -206,9 +220,14 @@ describe('instructions', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should add class', () => {
|
it('should add class', () => {
|
||||||
const fixture = new TemplateFixture(createDivWithStyling, () => {
|
const fixture = new TemplateFixture({
|
||||||
|
create: createDivWithStyling,
|
||||||
|
update: () => {
|
||||||
ɵɵclassMap('multiple classes');
|
ɵɵclassMap('multiple classes');
|
||||||
}, 1, 2);
|
},
|
||||||
|
decls: 1,
|
||||||
|
vars: 2
|
||||||
|
});
|
||||||
const div = fixture.containerElement.querySelector('div.multiple')!;
|
const div = fixture.containerElement.querySelector('div.multiple')!;
|
||||||
expect(getSortedClassName(div)).toEqual('classes multiple');
|
expect(getSortedClassName(div)).toEqual('classes multiple');
|
||||||
});
|
});
|
||||||
|
@ -284,7 +303,7 @@ describe('instructions', () => {
|
||||||
describe('sanitization injection compatibility', () => {
|
describe('sanitization injection compatibility', () => {
|
||||||
it('should work for url sanitization', () => {
|
it('should work for url sanitization', () => {
|
||||||
const s = new LocalMockSanitizer(value => `${value}-sanitized`);
|
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 inputValue = 'http://foo';
|
||||||
const outputValue = 'http://foo-sanitized';
|
const outputValue = 'http://foo-sanitized';
|
||||||
|
|
||||||
|
@ -297,7 +316,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should bypass url sanitization if marked by the service', () => {
|
it('should bypass url sanitization if marked by the service', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = s.bypassSecurityTrustUrl('http://foo');
|
||||||
const outputValue = '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', () => {
|
it('should bypass ivy-level url sanitization if a custom sanitizer is used', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = bypassSanitizationTrustUrl('http://foo');
|
||||||
const outputValue = 'http://foo-ivy';
|
const outputValue = 'http://foo-ivy';
|
||||||
|
|
||||||
|
@ -323,7 +342,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should work for style sanitization', () => {
|
it('should work for style sanitization', () => {
|
||||||
const s = new LocalMockSanitizer(value => `color:blue`);
|
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 inputValue = 'color:red';
|
||||||
const outputValue = 'color:blue';
|
const outputValue = 'color:blue';
|
||||||
|
|
||||||
|
@ -336,7 +355,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should bypass style sanitization if marked by the service', () => {
|
it('should bypass style sanitization if marked by the service', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = s.bypassSecurityTrustStyle('color:maroon');
|
||||||
const outputValue = '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', () => {
|
it('should bypass ivy-level style sanitization if a custom sanitizer is used', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = bypassSanitizationTrustStyle('font-family:foo');
|
||||||
const outputValue = 'font-family:foo-ivy';
|
const outputValue = 'font-family:foo-ivy';
|
||||||
|
|
||||||
|
@ -362,7 +381,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should work for resourceUrl sanitization', () => {
|
it('should work for resourceUrl sanitization', () => {
|
||||||
const s = new LocalMockSanitizer(value => `${value}-sanitized`);
|
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 inputValue = 'http://resource';
|
||||||
const outputValue = 'http://resource-sanitized';
|
const outputValue = 'http://resource-sanitized';
|
||||||
|
|
||||||
|
@ -375,7 +394,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should bypass resourceUrl sanitization if marked by the service', () => {
|
it('should bypass resourceUrl sanitization if marked by the service', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = s.bypassSecurityTrustResourceUrl('file://all-my-secrets.pdf');
|
||||||
const outputValue = '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', () => {
|
it('should bypass ivy-level resourceUrl sanitization if a custom sanitizer is used', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = bypassSanitizationTrustResourceUrl('file://all-my-secrets.pdf');
|
||||||
const outputValue = 'file://all-my-secrets.pdf-ivy';
|
const outputValue = 'file://all-my-secrets.pdf-ivy';
|
||||||
|
|
||||||
|
@ -401,7 +420,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should work for script sanitization', () => {
|
it('should work for script sanitization', () => {
|
||||||
const s = new LocalMockSanitizer(value => `${value} //sanitized`);
|
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 inputValue = 'fn();';
|
||||||
const outputValue = 'fn(); //sanitized';
|
const outputValue = 'fn(); //sanitized';
|
||||||
|
|
||||||
|
@ -414,7 +433,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should bypass script sanitization if marked by the service', () => {
|
it('should bypass script sanitization if marked by the service', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = s.bypassSecurityTrustScript('alert("bar")');
|
||||||
const outputValue = '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', () => {
|
it('should bypass ivy-level script sanitization if a custom sanitizer is used', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = bypassSanitizationTrustScript('alert("bar")');
|
||||||
const outputValue = 'alert("bar")-ivy';
|
const outputValue = 'alert("bar")-ivy';
|
||||||
|
|
||||||
|
@ -440,7 +459,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should work for html sanitization', () => {
|
it('should work for html sanitization', () => {
|
||||||
const s = new LocalMockSanitizer(value => `${value} <!--sanitized-->`);
|
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 inputValue = '<header></header>';
|
||||||
const outputValue = '<header></header> <!--sanitized-->';
|
const outputValue = '<header></header> <!--sanitized-->';
|
||||||
|
|
||||||
|
@ -453,7 +472,7 @@ describe('instructions', () => {
|
||||||
|
|
||||||
it('should bypass html sanitization if marked by the service', () => {
|
it('should bypass html sanitization if marked by the service', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = s.bypassSecurityTrustHtml('<div onclick="alert(123)"></div>');
|
||||||
const outputValue = '<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', () => {
|
it('should bypass ivy-level script sanitization if a custom sanitizer is used', () => {
|
||||||
const s = new LocalMockSanitizer(value => '');
|
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 inputValue = bypassSanitizationTrustHtml('<div onclick="alert(123)"></div>');
|
||||||
const outputValue = '<div onclick="alert(123)"></div>-ivy';
|
const outputValue = '<div onclick="alert(123)"></div>-ivy';
|
||||||
|
|
||||||
|
|
|
@ -372,11 +372,16 @@ describe('event listeners', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixture = new TemplateFixture(() => {
|
const fixture = new TemplateFixture({
|
||||||
|
create: () => {
|
||||||
ɵɵelementStart(0, 'button', 0);
|
ɵɵelementStart(0, 'button', 0);
|
||||||
ɵɵtext(1, 'Click');
|
ɵɵtext(1, 'Click');
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}, () => {}, 2, 0, [HostListenerDir], null, null, undefined, [['hostListenerDir', '']]);
|
},
|
||||||
|
decls: 2,
|
||||||
|
directives: [HostListenerDir],
|
||||||
|
consts: [['hostListenerDir', '']]
|
||||||
|
});
|
||||||
|
|
||||||
const button = fixture.hostElement.querySelector('button')!;
|
const button = fixture.hostElement.querySelector('button')!;
|
||||||
|
|
||||||
|
@ -388,9 +393,14 @@ describe('event listeners', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support global host listeners on directives', () => {
|
it('should support global host listeners on directives', () => {
|
||||||
const fixture = new TemplateFixture(() => {
|
const fixture = new TemplateFixture({
|
||||||
|
create: () => {
|
||||||
ɵɵelement(0, 'div', 0);
|
ɵɵelement(0, 'div', 0);
|
||||||
}, () => {}, 1, 0, [GlobalHostListenerDir], null, null, undefined, [['hostListenerDir', '']]);
|
},
|
||||||
|
decls: 1,
|
||||||
|
directives: [GlobalHostListenerDir],
|
||||||
|
consts: [['hostListenerDir', '']]
|
||||||
|
});
|
||||||
|
|
||||||
const doc = fixture.hostElement.ownerDocument!;
|
const doc = fixture.hostElement.ownerDocument!;
|
||||||
|
|
||||||
|
|
|
@ -52,14 +52,24 @@ describe('pipe', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should unwrap', () => {
|
it('should unwrap', () => {
|
||||||
const fixture =
|
const fixture = new TemplateFixture({
|
||||||
new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]);
|
create: createTemplate,
|
||||||
|
update: updateTemplate,
|
||||||
|
decls: 2,
|
||||||
|
vars: 3,
|
||||||
|
pipes: [WrappingPipe]
|
||||||
|
});
|
||||||
expect(fixture.html).toEqual('Bar');
|
expect(fixture.html).toEqual('Bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should force change detection', () => {
|
it('should force change detection', () => {
|
||||||
const fixture =
|
const fixture = new TemplateFixture({
|
||||||
new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]);
|
create: createTemplate,
|
||||||
|
update: updateTemplate,
|
||||||
|
decls: 2,
|
||||||
|
vars: 3,
|
||||||
|
pipes: [WrappingPipe]
|
||||||
|
});
|
||||||
expect(fixture.html).toEqual('Bar');
|
expect(fixture.html).toEqual('Bar');
|
||||||
|
|
||||||
fixture.hostElement.childNodes[0]!.textContent = 'Foo';
|
fixture.hostElement.childNodes[0]!.textContent = 'Foo';
|
||||||
|
@ -101,15 +111,18 @@ describe('pipe', () => {
|
||||||
static ɵpipe = ɵɵdefinePipe({name: 'sayHello', type: SayHelloPipe, pure: true});
|
static ɵpipe = ɵɵdefinePipe({name: 'sayHello', type: SayHelloPipe, pure: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixture = new TemplateFixture(
|
const fixture = new TemplateFixture({
|
||||||
() => {
|
create: () => {
|
||||||
ɵɵtext(0);
|
ɵɵtext(0);
|
||||||
ɵɵpipe(1, 'sayHello');
|
ɵɵpipe(1, 'sayHello');
|
||||||
},
|
},
|
||||||
() => {
|
update: () => {
|
||||||
ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), '');
|
ɵɵtextInterpolate1('', ɵɵpipeBind1(1, 1, null), '');
|
||||||
},
|
},
|
||||||
2, 3, undefined, [SayHelloPipe]);
|
decls: 2,
|
||||||
|
vars: 3,
|
||||||
|
pipes: [SayHelloPipe]
|
||||||
|
});
|
||||||
|
|
||||||
expect(fixture.html).toBe('Hello there');
|
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 {createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, renderComponentOrTemplate} from '@angular/core/src/render3/instructions/shared';
|
||||||
import {TConstants, TNodeType} from '@angular/core/src/render3/interfaces/node';
|
import {TConstants, TNodeType} from '@angular/core/src/render3/interfaces/node';
|
||||||
import {enterView, getLView} from '@angular/core/src/render3/state';
|
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 {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';
|
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 _pipeDefs: PipeDefList|null;
|
||||||
private _sanitizer: Sanitizer|null;
|
private _sanitizer: Sanitizer|null;
|
||||||
private _rendererFactory: RendererFactory3;
|
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:
|
* @param updateBlock Optional instructions which go into the update block:
|
||||||
* `if (rf & RenderFlags.Update) { __here__ }`.
|
* `if (rf & RenderFlags.Update) { __here__ }`.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor({
|
||||||
private createBlock: () => void, private updateBlock: () => void = noop, decls: number = 0,
|
create = noop,
|
||||||
private vars: number = 0, directives?: DirectiveTypesOrFactory|null,
|
update = noop,
|
||||||
pipes?: PipeTypesOrFactory|null, sanitizer?: Sanitizer|null,
|
decls = 0,
|
||||||
rendererFactory?: RendererFactory3, private _consts?: TConstants) {
|
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();
|
super();
|
||||||
|
this._consts = consts;
|
||||||
|
this._vars = vars;
|
||||||
|
this.createBlock = create;
|
||||||
|
this.updateBlock = update;
|
||||||
this._directiveDefs = toDefs(directives, extractDirectiveDef);
|
this._directiveDefs = toDefs(directives, extractDirectiveDef);
|
||||||
this._pipeDefs = toDefs(pipes, extractPipeDef);
|
this._pipeDefs = toDefs(pipes, extractPipeDef);
|
||||||
this._sanitizer = sanitizer || null;
|
this._sanitizer = sanitizer;
|
||||||
this._rendererFactory = rendererFactory || domRendererFactory3;
|
this._rendererFactory = rendererFactory;
|
||||||
this.hostView = renderTemplate(
|
this.hostView = renderTemplate(
|
||||||
this.hostElement,
|
this.hostElement,
|
||||||
(rf: RenderFlags, ctx: any) => {
|
(rf: RenderFlags, ctx: any) => {
|
||||||
|
@ -136,7 +161,7 @@ export class TemplateFixture extends BaseFixture {
|
||||||
*/
|
*/
|
||||||
update(updateBlock?: () => void): void {
|
update(updateBlock?: () => void): void {
|
||||||
renderTemplate(
|
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._rendererFactory, this.hostView, this._directiveDefs, this._pipeDefs, this._sanitizer,
|
||||||
this._consts);
|
this._consts);
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,9 +232,12 @@ describe('ViewContainerRef', () => {
|
||||||
ɵɵelement(1, 'footer');
|
ɵɵelement(1, 'footer');
|
||||||
}
|
}
|
||||||
|
|
||||||
new TemplateFixture(
|
new TemplateFixture({
|
||||||
createTemplate, undefined, 2, 0, [DirectiveWithVCRef], null, null, undefined,
|
create: createTemplate,
|
||||||
[['vcref', '']]);
|
decls: 2,
|
||||||
|
directives: [DirectiveWithVCRef],
|
||||||
|
consts: [['vcref', '']]
|
||||||
|
});
|
||||||
|
|
||||||
expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase())
|
expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase())
|
||||||
.toEqual('header');
|
.toEqual('header');
|
||||||
|
@ -253,9 +256,12 @@ describe('ViewContainerRef', () => {
|
||||||
ɵɵelement(1, 'footer');
|
ɵɵelement(1, 'footer');
|
||||||
}
|
}
|
||||||
|
|
||||||
new TemplateFixture(
|
new TemplateFixture({
|
||||||
createTemplate, undefined, 2, 0, [HeaderComponent, DirectiveWithVCRef], null, null,
|
create: createTemplate,
|
||||||
undefined, [['vcref', '']]);
|
decls: 2,
|
||||||
|
directives: [HeaderComponent, DirectiveWithVCRef],
|
||||||
|
consts: [['vcref', '']]
|
||||||
|
});
|
||||||
|
|
||||||
expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase())
|
expect(directiveInstance!.vcref.element.nativeElement.tagName.toLowerCase())
|
||||||
.toEqual('header-cmp');
|
.toEqual('header-cmp');
|
||||||
|
@ -430,10 +436,10 @@ describe('ViewContainerRef', () => {
|
||||||
fixture.update();
|
fixture.update();
|
||||||
|
|
||||||
// Destroying the parent view will also destroy all of its children views and call their
|
// 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.
|
// onDestroy hooks. Here, our child view attempts to destroy itself *again* in its
|
||||||
// This test exists to verify that no errors are thrown when doing this. We want the test
|
// onDestroy. This test exists to verify that no errors are thrown when doing this. We want
|
||||||
// component to destroy its own view in onDestroy because the destroy hooks happen as a
|
// the test component to destroy its own view in onDestroy because the destroy hooks happen
|
||||||
// *part of* view destruction. We also ensure that the test component has at least one
|
// 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.
|
// listener so that it runs the event listener cleanup code path.
|
||||||
fixture.destroy();
|
fixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue