fix(ivy): querying should be possible on any debug element (#29534)
PR Close #29534
This commit is contained in:
parent
50f7ab2a06
commit
303eae918d
|
@ -391,7 +391,7 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walk the TNode tree to find matches for the predicate, skipping the parent element.
|
* Walk the TNode tree to find matches for the predicate.
|
||||||
*
|
*
|
||||||
* @param parentElement the element from which the walk is started
|
* @param parentElement the element from which the walk is started
|
||||||
* @param predicate the predicate to match
|
* @param predicate the predicate to match
|
||||||
|
@ -403,9 +403,8 @@ function _queryAllR3(
|
||||||
elementsOnly: boolean) {
|
elementsOnly: boolean) {
|
||||||
const context = loadLContext(parentElement.nativeNode) !;
|
const context = loadLContext(parentElement.nativeNode) !;
|
||||||
const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode;
|
const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode;
|
||||||
const lView = getComponentViewByIndex(parentTNode.index, context.lView);
|
_queryNodeChildrenR3(
|
||||||
const tNode = lView[TVIEW].firstChild !;
|
parentTNode, context.lView, predicate, matches, elementsOnly, parentElement.nativeNode);
|
||||||
_queryNodeChildrenR3(tNode, lView, predicate, matches, elementsOnly);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -416,39 +415,44 @@ function _queryAllR3(
|
||||||
* @param predicate the predicate to match
|
* @param predicate the predicate to match
|
||||||
* @param matches the list of positive matches
|
* @param matches the list of positive matches
|
||||||
* @param elementsOnly whether only elements should be searched
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
* @param rootNativeNode the root native node on which prediccate shouold not be matched
|
||||||
*/
|
*/
|
||||||
function _queryNodeChildrenR3(
|
function _queryNodeChildrenR3(
|
||||||
tNode: TNode, lView: LView, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
tNode: TNode, lView: LView, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
||||||
elementsOnly: boolean) {
|
elementsOnly: boolean, rootNativeNode: any) {
|
||||||
// For each type of TNode, specific logic is executed.
|
// For each type of TNode, specific logic is executed.
|
||||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
||||||
// Case 1: the TNode is an element
|
// Case 1: the TNode is an element
|
||||||
// The native node has to be checked.
|
// The native node has to be checked.
|
||||||
_addQueryMatchR3(getNativeByTNode(tNode, lView), predicate, matches, elementsOnly);
|
_addQueryMatchR3(
|
||||||
|
getNativeByTNode(tNode, lView), predicate, matches, elementsOnly, rootNativeNode);
|
||||||
if (isComponent(tNode)) {
|
if (isComponent(tNode)) {
|
||||||
// If the element is the host of a component, then all nodes in its view have to be processed.
|
// If the element is the host of a component, then all nodes in its view have to be processed.
|
||||||
// Note: the component's content (tNode.child) will be processed from the insertion points.
|
// Note: the component's content (tNode.child) will be processed from the insertion points.
|
||||||
const componentView = getComponentViewByIndex(tNode.index, lView);
|
const componentView = getComponentViewByIndex(tNode.index, lView);
|
||||||
if (componentView && componentView[TVIEW].firstChild)
|
if (componentView && componentView[TVIEW].firstChild)
|
||||||
_queryNodeChildrenR3(
|
_queryNodeChildrenR3(
|
||||||
componentView[TVIEW].firstChild !, componentView, predicate, matches, elementsOnly);
|
componentView[TVIEW].firstChild !, componentView, predicate, matches, elementsOnly,
|
||||||
|
rootNativeNode);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, its children have to be processed.
|
// Otherwise, its children have to be processed.
|
||||||
if (tNode.child) _queryNodeChildrenR3(tNode.child, lView, predicate, matches, elementsOnly);
|
if (tNode.child)
|
||||||
|
_queryNodeChildrenR3(tNode.child, lView, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
// In all cases, if a dynamic container exists for this node, each view inside it has to be
|
// In all cases, if a dynamic container exists for this node, each view inside it has to be
|
||||||
// processed.
|
// processed.
|
||||||
const nodeOrContainer = lView[tNode.index];
|
const nodeOrContainer = lView[tNode.index];
|
||||||
if (isLContainer(nodeOrContainer)) {
|
if (isLContainer(nodeOrContainer)) {
|
||||||
_queryNodeChildrenInContainerR3(nodeOrContainer, predicate, matches, elementsOnly);
|
_queryNodeChildrenInContainerR3(
|
||||||
|
nodeOrContainer, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
} else if (tNode.type === TNodeType.Container) {
|
} else if (tNode.type === TNodeType.Container) {
|
||||||
// Case 2: the TNode is a container
|
// Case 2: the TNode is a container
|
||||||
// The native node has to be checked.
|
// The native node has to be checked.
|
||||||
const lContainer = lView[tNode.index];
|
const lContainer = lView[tNode.index];
|
||||||
_addQueryMatchR3(lContainer[NATIVE], predicate, matches, elementsOnly);
|
_addQueryMatchR3(lContainer[NATIVE], predicate, matches, elementsOnly, rootNativeNode);
|
||||||
// Each view inside the container has to be processed.
|
// Each view inside the container has to be processed.
|
||||||
_queryNodeChildrenInContainerR3(lContainer, predicate, matches, elementsOnly);
|
_queryNodeChildrenInContainerR3(lContainer, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
} else if (tNode.type === TNodeType.Projection) {
|
} else if (tNode.type === TNodeType.Projection) {
|
||||||
// Case 3: the TNode is a projection insertion point (i.e. a <ng-content>).
|
// Case 3: the TNode is a projection insertion point (i.e. a <ng-content>).
|
||||||
// The nodes projected at this location all need to be processed.
|
// The nodes projected at this location all need to be processed.
|
||||||
|
@ -459,26 +463,27 @@ function _queryNodeChildrenR3(
|
||||||
|
|
||||||
if (Array.isArray(head)) {
|
if (Array.isArray(head)) {
|
||||||
for (let nativeNode of head) {
|
for (let nativeNode of head) {
|
||||||
_addQueryMatchR3(nativeNode, predicate, matches, elementsOnly);
|
_addQueryMatchR3(nativeNode, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (head) {
|
if (head) {
|
||||||
const nextLView = componentView[PARENT] !as LView;
|
const nextLView = componentView[PARENT] !as LView;
|
||||||
const nextTNode = nextLView[TVIEW].data[head.index] as TNode;
|
const nextTNode = nextLView[TVIEW].data[head.index] as TNode;
|
||||||
_queryNodeChildrenR3(nextTNode, nextLView, predicate, matches, elementsOnly);
|
_queryNodeChildrenR3(
|
||||||
|
nextTNode, nextLView, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Case 4: the TNode is a view.
|
// Case 4: the TNode is a view.
|
||||||
if (tNode.child) {
|
if (tNode.child) {
|
||||||
_queryNodeChildrenR3(tNode.child, lView, predicate, matches, elementsOnly);
|
_queryNodeChildrenR3(tNode.child, lView, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// To determine the next node to be processed, we need to use the next or the projectionNext link,
|
// To determine the next node to be processed, we need to use the next or the projectionNext link,
|
||||||
// depending on whether the current node has been projected.
|
// depending on whether the current node has been projected.
|
||||||
const nextTNode = (tNode.flags & TNodeFlags.isProjected) ? tNode.projectionNext : tNode.next;
|
const nextTNode = (tNode.flags & TNodeFlags.isProjected) ? tNode.projectionNext : tNode.next;
|
||||||
if (nextTNode) {
|
if (nextTNode) {
|
||||||
_queryNodeChildrenR3(nextTNode, lView, predicate, matches, elementsOnly);
|
_queryNodeChildrenR3(nextTNode, lView, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,13 +494,15 @@ function _queryNodeChildrenR3(
|
||||||
* @param predicate the predicate to match
|
* @param predicate the predicate to match
|
||||||
* @param matches the list of positive matches
|
* @param matches the list of positive matches
|
||||||
* @param elementsOnly whether only elements should be searched
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
* @param rootNativeNode the root native node on which prediccate shouold not be matched
|
||||||
*/
|
*/
|
||||||
function _queryNodeChildrenInContainerR3(
|
function _queryNodeChildrenInContainerR3(
|
||||||
lContainer: LContainer, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
lContainer: LContainer, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
||||||
elementsOnly: boolean) {
|
elementsOnly: boolean, rootNativeNode: any) {
|
||||||
for (let i = 0; i < lContainer[VIEWS].length; i++) {
|
for (let i = 0; i < lContainer[VIEWS].length; i++) {
|
||||||
const childView = lContainer[VIEWS][i];
|
const childView = lContainer[VIEWS][i];
|
||||||
_queryNodeChildrenR3(childView[TVIEW].node !, childView, predicate, matches, elementsOnly);
|
_queryNodeChildrenR3(
|
||||||
|
childView[TVIEW].node !, childView, predicate, matches, elementsOnly, rootNativeNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,14 +513,18 @@ function _queryNodeChildrenInContainerR3(
|
||||||
* @param predicate the predicate to match
|
* @param predicate the predicate to match
|
||||||
* @param matches the list of positive matches
|
* @param matches the list of positive matches
|
||||||
* @param elementsOnly whether only elements should be searched
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
* @param rootNativeNode the root native node on which prediccate shouold not be matched
|
||||||
*/
|
*/
|
||||||
function _addQueryMatchR3(
|
function _addQueryMatchR3(
|
||||||
nativeNode: any, predicate: Predicate<DebugNode>, matches: DebugNode[], elementsOnly: boolean) {
|
nativeNode: any, predicate: Predicate<DebugNode>, matches: DebugNode[], elementsOnly: boolean,
|
||||||
|
rootNativeNode: any) {
|
||||||
|
if (rootNativeNode !== nativeNode) {
|
||||||
const debugNode = getDebugNode(nativeNode);
|
const debugNode = getDebugNode(nativeNode);
|
||||||
if (debugNode && (elementsOnly ? debugNode instanceof DebugElement__POST_R3__ : true) &&
|
if (debugNode && (elementsOnly ? debugNode instanceof DebugElement__POST_R3__ : true) &&
|
||||||
predicate(debugNode)) {
|
predicate(debugNode)) {
|
||||||
matches.push(debugNode);
|
matches.push(debugNode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -644,5 +644,16 @@ class TestCmptWithViewContainerRef {
|
||||||
expect(divEl).not.toBeNull();
|
expect(divEl).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support querying on any debug element', () => {
|
||||||
|
TestBed.overrideTemplate(TestCmpt, `<span><div id="a"><div id="b"></div></div></span>`);
|
||||||
|
fixture = TestBed.createComponent(TestCmpt);
|
||||||
|
|
||||||
|
const divA = fixture.debugElement.query(By.css('div'));
|
||||||
|
expect(divA.nativeElement.getAttribute('id')).toBe('a');
|
||||||
|
|
||||||
|
const divB = divA.query(By.css('div'));
|
||||||
|
expect(divB.nativeElement.getAttribute('id')).toBe('b');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue