refactor(core): Add injector debug information to `LViewDebug` (#38707)
Extended the `LViewDebug` to display node-injector information for each node. PR Close #38707
This commit is contained in:
parent
9fb541787c
commit
b2579d43cd
|
@ -6,14 +6,14 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {assertDefined, assertEqual, throwError} from '../util/assert';
|
||||
|
||||
import {assertDefined, assertEqual, assertIndexInRange, assertNumber, throwError} from '../util/assert';
|
||||
import {getComponentDef, getNgModuleDef} from './definition';
|
||||
import {LContainer} from './interfaces/container';
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import { PARENT_INJECTOR } from './interfaces/injector';
|
||||
import {TNode} from './interfaces/node';
|
||||
import {isLContainer, isLView} from './interfaces/type_checks';
|
||||
import {LView, TVIEW, TView} from './interfaces/view';
|
||||
import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view';
|
||||
|
||||
// [Assert functions do not constraint type when they are guarded by a truthy
|
||||
// expression.](https://github.com/microsoft/TypeScript/issues/37295)
|
||||
|
@ -96,3 +96,55 @@ export function assertDirectiveDef<T>(obj: any): asserts obj is DirectiveDef<T>
|
|||
`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertIndexInDeclRange(lView: LView, index: number) {
|
||||
const tView = lView[1];
|
||||
assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
|
||||
}
|
||||
|
||||
export function assertIndexInVarsRange(lView: LView, index: number) {
|
||||
const tView = lView[1];
|
||||
assertBetween(
|
||||
tView.bindingStartIndex, (tView as any as {i18nStartIndex: number}).i18nStartIndex, index);
|
||||
}
|
||||
|
||||
export function assertIndexInI18nRange(lView: LView, index: number) {
|
||||
const tView = lView[1];
|
||||
assertBetween(
|
||||
(tView as any as {i18nStartIndex: number}).i18nStartIndex, tView.expandoStartIndex, index);
|
||||
}
|
||||
|
||||
export function assertIndexInExpandoRange(lView: LView, index: number) {
|
||||
const tView = lView[1];
|
||||
assertBetween(tView.expandoStartIndex, lView.length, index);
|
||||
}
|
||||
|
||||
export function assertBetween(lower: number, upper: number, index: number) {
|
||||
if (!(lower <= index && index < upper)) {
|
||||
throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
|
||||
* NodeInjector data structure.
|
||||
*
|
||||
* @param lView `LView` which should be checked.
|
||||
* @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
|
||||
*/
|
||||
export function assertNodeInjector(lView: LView, injectorIndex: number) {
|
||||
assertIndexInExpandoRange(lView, injectorIndex);
|
||||
assertIndexInExpandoRange(lView, injectorIndex + PARENT_INJECTOR);
|
||||
assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
|
||||
assertNumber(
|
||||
lView[injectorIndex + 8 /*PARENT_INJECTOR*/],
|
||||
'injectorIndex should point to parent injector');
|
||||
}
|
|
@ -32,7 +32,7 @@ import {DirectiveDef} from '../interfaces/definition';
|
|||
* ɵɵtextInterpolate(ctx.greeter.greet());
|
||||
* }
|
||||
* },
|
||||
* features: [ProvidersFeature([GreeterDE])]
|
||||
* features: [ɵɵProvidersFeature([GreeterDE])]
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
|
|
|
@ -6,21 +6,25 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injector, SchemaMetadata} from '../../core';
|
||||
import {Injector, SchemaMetadata, Type} from '../../core';
|
||||
import {Sanitizer} from '../../sanitization/sanitizer';
|
||||
import {KeyValueArray} from '../../util/array_utils';
|
||||
import {assertDefined} from '../../util/assert';
|
||||
import {createNamedArrayType} from '../../util/named_array_type';
|
||||
import {initNgDevMode} from '../../util/ng_dev_mode';
|
||||
import {assertNodeInjector} from '../assert';
|
||||
import {getInjectorIndex} from '../di';
|
||||
import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container';
|
||||
import {ComponentTemplate, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition';
|
||||
import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition';
|
||||
import {NO_PARENT_INJECTOR, PARENT_INJECTOR, TNODE} from '../interfaces/injector';
|
||||
import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString, TViewNode} from '../interfaces/node';
|
||||
import {SelectorFlags} from '../interfaces/projection';
|
||||
import {LQueries, TQueries} from '../interfaces/query';
|
||||
import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer';
|
||||
import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling';
|
||||
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DebugNode, DECLARATION_VIEW, DestroyHookData, ExpandoInstructions, FLAGS, HEADER_OFFSET, HookData, HOST, INJECTOR, LContainerDebug as ILContainerDebug, LView, LViewDebug as ILViewDebug, LViewDebugRange, LViewDebugRangeContent, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TView as ITView, TVIEW, TView, TViewType} from '../interfaces/view';
|
||||
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DebugNode, DECLARATION_VIEW, DestroyHookData, ExpandoInstructions, FLAGS, HEADER_OFFSET, HookData, HOST, INJECTOR, LContainerDebug as ILContainerDebug, LView, LViewDebug as ILViewDebug, LViewDebugRange, LViewDebugRangeContent, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, T_HOST, TData, TView as ITView, TVIEW, TView, TViewType, TViewTypeAsString} from '../interfaces/view';
|
||||
import {attachDebugObject} from '../util/debug_utils';
|
||||
import {getParentInjectorIndex, getParentInjectorView} from '../util/injector_utils';
|
||||
import {unwrapRNode} from '../util/view_utils';
|
||||
|
||||
const NG_DEV_MODE = ((typeof ngDevMode === 'undefined' || !!ngDevMode) && initNgDevMode());
|
||||
|
@ -152,6 +156,14 @@ export const TViewConstructor = class TView implements ITView {
|
|||
processTNodeChildren(this.firstChild, buf);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
get type_(): string {
|
||||
return TViewTypeAsString[this.type] || `TViewType.?${this.type}?`;
|
||||
}
|
||||
|
||||
get i18nStartIndex(): number {
|
||||
return HEADER_OFFSET + this._decls + this._vars;
|
||||
}
|
||||
};
|
||||
|
||||
class TNode implements ITNode {
|
||||
|
@ -189,23 +201,39 @@ class TNode implements ITNode {
|
|||
public styleBindings: TStylingRange, //
|
||||
) {}
|
||||
|
||||
get type_(): string {
|
||||
switch (this.type) {
|
||||
case TNodeType.Container:
|
||||
return 'TNodeType.Container';
|
||||
case TNodeType.Element:
|
||||
return 'TNodeType.Element';
|
||||
case TNodeType.ElementContainer:
|
||||
return 'TNodeType.ElementContainer';
|
||||
case TNodeType.IcuContainer:
|
||||
return 'TNodeType.IcuContainer';
|
||||
case TNodeType.Projection:
|
||||
return 'TNodeType.Projection';
|
||||
case TNodeType.View:
|
||||
return 'TNodeType.View';
|
||||
default:
|
||||
return 'TNodeType.???';
|
||||
/**
|
||||
* Return a human debug version of the set of `NodeInjector`s which will be consulted when
|
||||
* resolving tokens from this `TNode`.
|
||||
*
|
||||
* When debugging applications, it is often difficult to determine which `NodeInjector`s will be
|
||||
* consulted. This method shows a list of `DebugNode`s representing the `TNode`s which will be
|
||||
* consulted in order when resolving a token starting at this `TNode`.
|
||||
*
|
||||
* The original data is stored in `LView` and `TView` with a lot of offset indexes, and so it is
|
||||
* difficult to reason about.
|
||||
*
|
||||
* @param lView The `LView` instance for this `TNode`.
|
||||
*/
|
||||
debugNodeInjectorPath(lView: LView): DebugNode[] {
|
||||
const path: DebugNode[] = [];
|
||||
let injectorIndex = getInjectorIndex(this, lView);
|
||||
ngDevMode && assertNodeInjector(lView, injectorIndex);
|
||||
while (injectorIndex !== -1) {
|
||||
const tNode = lView[TVIEW].data[injectorIndex + TNODE] as TNode;
|
||||
path.push(buildDebugNode(tNode, lView));
|
||||
const parentLocation = lView[injectorIndex + PARENT_INJECTOR];
|
||||
if (parentLocation === NO_PARENT_INJECTOR) {
|
||||
injectorIndex = -1;
|
||||
} else {
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
get type_(): string {
|
||||
return TNodeTypeAsString[this.type] || `TNodeType.?${this.type}?`;
|
||||
}
|
||||
|
||||
get flags_(): string {
|
||||
|
@ -246,6 +274,14 @@ class TNode implements ITNode {
|
|||
get classBindings_(): DebugStyleBindings {
|
||||
return toDebugStyleBinding(this, true);
|
||||
}
|
||||
|
||||
get providerIndexStart_(): number {
|
||||
return this.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
}
|
||||
get providerIndexEnd_(): number {
|
||||
return this.providerIndexStart_ +
|
||||
(this.providerIndexes >>> TNodeProviderIndexes.CptViewProvidersCountShift);
|
||||
}
|
||||
}
|
||||
export const TNodeDebug = TNode;
|
||||
export type TNodeDebug = TNode;
|
||||
|
@ -462,21 +498,21 @@ export class LViewDebug implements ILViewDebug {
|
|||
}
|
||||
|
||||
get decls(): LViewDebugRange {
|
||||
const tView = this.tView as any as {_decls: number, _vars: number};
|
||||
const start = HEADER_OFFSET;
|
||||
return toLViewRange(this.tView, this._raw_lView, start, start + tView._decls);
|
||||
return toLViewRange(this.tView, this._raw_lView, HEADER_OFFSET, this.tView.bindingStartIndex);
|
||||
}
|
||||
|
||||
get vars(): LViewDebugRange {
|
||||
const tView = this.tView as any as {_decls: number, _vars: number};
|
||||
const start = HEADER_OFFSET + tView._decls;
|
||||
return toLViewRange(this.tView, this._raw_lView, start, start + tView._vars);
|
||||
const tView = this.tView;
|
||||
return toLViewRange(
|
||||
tView, this._raw_lView, tView.bindingStartIndex,
|
||||
(tView as any as {i18nStartIndex: number}).i18nStartIndex);
|
||||
}
|
||||
|
||||
get i18n(): LViewDebugRange {
|
||||
const tView = this.tView as any as {_decls: number, _vars: number};
|
||||
const start = HEADER_OFFSET + tView._decls + tView._vars;
|
||||
return toLViewRange(this.tView, this._raw_lView, start, this.tView.expandoStartIndex);
|
||||
const tView = this.tView;
|
||||
return toLViewRange(
|
||||
tView, this._raw_lView, (tView as any as {i18nStartIndex: number}).i18nStartIndex,
|
||||
tView.expandoStartIndex);
|
||||
}
|
||||
|
||||
get expando(): LViewDebugRange {
|
||||
|
@ -518,7 +554,7 @@ export function toDebugNodes(tNode: ITNode|null, lView: LView): DebugNode[] {
|
|||
const debugNodes: DebugNode[] = [];
|
||||
let tNodeCursor: ITNode|null = tNode;
|
||||
while (tNodeCursor) {
|
||||
debugNodes.push(buildDebugNode(tNodeCursor, lView, tNodeCursor.index));
|
||||
debugNodes.push(buildDebugNode(tNodeCursor, lView));
|
||||
tNodeCursor = tNodeCursor.next;
|
||||
}
|
||||
return debugNodes;
|
||||
|
@ -527,17 +563,75 @@ export function toDebugNodes(tNode: ITNode|null, lView: LView): DebugNode[] {
|
|||
}
|
||||
}
|
||||
|
||||
export function buildDebugNode(tNode: ITNode, lView: LView, nodeIndex: number): DebugNode {
|
||||
const rawValue = lView[nodeIndex];
|
||||
export function buildDebugNode(tNode: ITNode, lView: LView): DebugNode {
|
||||
const rawValue = lView[tNode.index];
|
||||
const native = unwrapRNode(rawValue);
|
||||
const factories: Type<any>[] = [];
|
||||
const instances: any[] = [];
|
||||
const tView = lView[TVIEW];
|
||||
for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
factories.push(def.type);
|
||||
instances.push(lView[i]);
|
||||
}
|
||||
return {
|
||||
html: toHtml(native),
|
||||
type: TNodeTypeAsString[tNode.type],
|
||||
native: native as any,
|
||||
children: toDebugNodes(tNode.child, lView),
|
||||
factories,
|
||||
instances,
|
||||
injector: buildNodeInjectorDebug(tNode, tView, lView)
|
||||
};
|
||||
}
|
||||
|
||||
function buildNodeInjectorDebug(tNode: ITNode, tView: ITView, lView: LView) {
|
||||
const viewProviders: Type<any>[] = [];
|
||||
for (let i = (tNode as TNode).providerIndexStart_; i < (tNode as TNode).providerIndexEnd_; i++) {
|
||||
viewProviders.push(tView.data[i] as Type<any>);
|
||||
}
|
||||
const providers: Type<any>[] = [];
|
||||
for (let i = (tNode as TNode).providerIndexEnd_; i < (tNode as TNode).directiveEnd; i++) {
|
||||
providers.push(tView.data[i] as Type<any>);
|
||||
}
|
||||
const nodeInjectorDebug = {
|
||||
bloom: toBloom(lView, tNode.injectorIndex),
|
||||
cumulativeBloom: toBloom(tView.data, tNode.injectorIndex),
|
||||
providers,
|
||||
viewProviders,
|
||||
parentInjectorIndex: lView[(tNode as TNode).providerIndexStart_ - 1],
|
||||
};
|
||||
return nodeInjectorDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a number at `idx` location in `array` into binary representation.
|
||||
*
|
||||
* @param array
|
||||
* @param idx
|
||||
*/
|
||||
function binary(array: any[], idx: number): string {
|
||||
const value = array[idx];
|
||||
// If not a number we print 8 `?` to retain alignment but let user know that it was called on
|
||||
// wrong type.
|
||||
if (typeof value !== 'number') return '????????';
|
||||
// We prefix 0s so that we have constant length number
|
||||
const text = '00000000' + value.toString(2);
|
||||
return text.substring(text.length - 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a bloom filter at location `idx` in `array` into binary representation.
|
||||
*
|
||||
* @param array
|
||||
* @param idx
|
||||
*/
|
||||
function toBloom(array: any[], idx: number): string {
|
||||
return `${binary(array, idx + 7)}_${binary(array, idx + 6)}_${binary(array, idx + 5)}_${
|
||||
binary(array, idx + 4)}_${binary(array, idx + 3)}_${binary(array, idx + 2)}_${
|
||||
binary(array, idx + 1)}_${binary(array, idx + 0)}`;
|
||||
}
|
||||
|
||||
export class LContainerDebug implements ILContainerDebug {
|
||||
constructor(private readonly _raw_lContainer: LContainer) {}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import {InjectionToken} from '../../di/injection_token';
|
||||
import {InjectFlags} from '../../di/interface/injector';
|
||||
import {Type} from '../../interface/type';
|
||||
import {assertDefined, assertEqual} from '../../util/assert';
|
||||
|
||||
import {TDirectiveHostNode} from './node';
|
||||
import {LView, TData} from './view';
|
||||
|
@ -239,6 +240,8 @@ export class NodeInjectorFactory {
|
|||
isViewProvider: boolean,
|
||||
injectImplementation: null|
|
||||
(<T>(token: Type<T>|InjectionToken<T>, flags?: InjectFlags) => T)) {
|
||||
ngDevMode && assertDefined(factory, 'Factory not specified');
|
||||
ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.');
|
||||
this.canSeeViewProviders = isViewProvider;
|
||||
this.injectImpl = injectImplementation;
|
||||
}
|
||||
|
|
|
@ -1021,4 +1021,48 @@ export interface DebugNode {
|
|||
* Child nodes
|
||||
*/
|
||||
children: DebugNode[];
|
||||
|
||||
/**
|
||||
* A list of Component/Directive types which need to be instantiated an this location.
|
||||
*/
|
||||
factories: Type<unknown>[];
|
||||
|
||||
/**
|
||||
* A list of Component/Directive instances which were instantiated an this location.
|
||||
*/
|
||||
instances: unknown[];
|
||||
|
||||
/**
|
||||
* NodeInjector information.
|
||||
*/
|
||||
injector: NodeInjectorDebug;
|
||||
}
|
||||
|
||||
interface NodeInjectorDebug {
|
||||
/**
|
||||
* Instance bloom. Does the current injector have a provider with a given bloom mask.
|
||||
*/
|
||||
bloom: string;
|
||||
|
||||
|
||||
/**
|
||||
* Cumulative bloom. Do any of the above injectors have a provider with a given bloom mask.
|
||||
*/
|
||||
cumulativeBloom: string;
|
||||
|
||||
/**
|
||||
* A list of providers associated with this injector.
|
||||
*/
|
||||
providers: (Type<unknown>|DirectiveDef<unknown>|ComponentDef<unknown>)[];
|
||||
|
||||
/**
|
||||
* A list of providers associated with this injector visible to the view of the component only.
|
||||
*/
|
||||
viewProviders: Type<unknown>[];
|
||||
|
||||
|
||||
/**
|
||||
* Location of the parent `TNode`.
|
||||
*/
|
||||
parentInjectorIndex: number;
|
||||
}
|
|
@ -389,7 +389,7 @@ export function getDebugNode(element: Element): DebugNode|null {
|
|||
// data. In this situation the TNode is not accessed at the same spot.
|
||||
const tNode = isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) :
|
||||
getTNode(lView[TVIEW], nodeIndex - HEADER_OFFSET);
|
||||
debugNode = buildDebugNode(tNode, lView, nodeIndex);
|
||||
debugNode = buildDebugNode(tNode, lView);
|
||||
}
|
||||
|
||||
return debugNode;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵProvidersFeature} from '@angular/core/src/core';
|
||||
import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '@angular/core/src/render3/instructions/element';
|
||||
import {TNodeDebug} from '@angular/core/src/render3/instructions/lview_debug';
|
||||
import {createTNode, createTView} from '@angular/core/src/render3/instructions/shared';
|
||||
import {TNodeType} from '@angular/core/src/render3/interfaces/node';
|
||||
|
@ -13,7 +15,7 @@ import {LView, TView, TViewType} from '@angular/core/src/render3/interfaces/view
|
|||
import {enterView, leaveView} from '@angular/core/src/render3/state';
|
||||
import {insertTStylingBinding} from '@angular/core/src/render3/styling/style_binding_list';
|
||||
import {KeyValueArray} from '@angular/core/src/util/array_utils';
|
||||
|
||||
import {TemplateFixture} from '../render_util';
|
||||
|
||||
describe('lView_debug', () => {
|
||||
const mockFirstUpdatePassLView: LView = [null, {firstUpdatePass: true}] as any;
|
||||
|
@ -151,4 +153,88 @@ describe('lView_debug', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('di', () => {
|
||||
it('should show basic information', () => {
|
||||
class DepA {
|
||||
static ɵfac = () => new DepA();
|
||||
}
|
||||
class DepB {
|
||||
static ɵfac = () => new DepB();
|
||||
}
|
||||
|
||||
const instances: any[] = [];
|
||||
class MyComponent {
|
||||
constructor(public depA: DepA, public depB: DepB) {
|
||||
instances.push(this);
|
||||
}
|
||||
static ɵfac = () => new MyComponent(ɵɵdirectiveInject(DepA), ɵɵdirectiveInject(DepB));
|
||||
static ɵcmp = ɵɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-comp']],
|
||||
decls: 1,
|
||||
vars: 0,
|
||||
template: function() {},
|
||||
features: [ɵɵProvidersFeature(
|
||||
[DepA, {provide: String, useValue: 'String'}],
|
||||
[DepB, {provide: Number, useValue: 123}])]
|
||||
});
|
||||
}
|
||||
|
||||
let myChild!: MyChild;
|
||||
class MyChild {
|
||||
constructor() {
|
||||
myChild = this;
|
||||
}
|
||||
static ɵfac = () => new MyChild();
|
||||
static ɵdir = ɵɵdefineDirective({
|
||||
type: MyChild,
|
||||
selectors: [['my-child']],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
class MyDirective {
|
||||
constructor(public myComp: MyComponent) {
|
||||
instances.push(this);
|
||||
}
|
||||
static ɵfac = () => new MyDirective(ɵɵdirectiveInject(MyComponent));
|
||||
static ɵdir = ɵɵdefineDirective({
|
||||
type: MyDirective,
|
||||
selectors: [['', 'my-dir', '']],
|
||||
});
|
||||
}
|
||||
|
||||
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 lView = fixture.hostView;
|
||||
const lViewDebug = lView.debug!;
|
||||
const myCompNode = lViewDebug.nodes[0];
|
||||
expect(myCompNode.factories).toEqual([MyComponent, MyDirective]);
|
||||
expect(myCompNode.instances).toEqual(instances);
|
||||
expect(myCompNode.injector).toEqual({
|
||||
bloom: jasmine.anything(),
|
||||
cumulativeBloom: jasmine.anything(),
|
||||
providers: [DepA, String, MyComponent.ɵcmp, MyDirective.ɵdir],
|
||||
viewProviders: [DepB, Number],
|
||||
parentInjectorIndex: -1,
|
||||
});
|
||||
const myChildNode = myCompNode.children[0];
|
||||
expect(myChildNode.factories).toEqual([MyChild]);
|
||||
expect(myChildNode.instances).toEqual([myChild]);
|
||||
expect(myChildNode.injector).toEqual({
|
||||
bloom: jasmine.anything(),
|
||||
cumulativeBloom: jasmine.anything(),
|
||||
providers: [MyChild.ɵdir],
|
||||
viewProviders: [],
|
||||
parentInjectorIndex: 22,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue