fix(ivy): DebugNode.query should query nodes in the logical tree (#29480)
PR Close #29480
This commit is contained in:
parent
9724247ad8
commit
c412374854
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {fixmeIvy, ivyEnabled, obsoleteInIvy} from '@angular/private/testing';
|
import {ivyEnabled, obsoleteInIvy} from '@angular/private/testing';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as shx from 'shelljs';
|
import * as shx from 'shelljs';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {__NGTOOLS_PRIVATE_API_2 as NgTools_InternalApi_NG_2} from '@angular/compiler-cli';
|
import {__NGTOOLS_PRIVATE_API_2 as NgTools_InternalApi_NG_2} from '@angular/compiler-cli';
|
||||||
import {fixmeIvy, ivyEnabled} from '@angular/private/testing';
|
import {ivyEnabled} from '@angular/private/testing';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,16 @@
|
||||||
|
|
||||||
import {Injector} from '../di';
|
import {Injector} from '../di';
|
||||||
import {getViewComponent} from '../render3/global_utils_api';
|
import {getViewComponent} from '../render3/global_utils_api';
|
||||||
import {TNode} from '../render3/interfaces/node';
|
import {LContainer, NATIVE, VIEWS} from '../render3/interfaces/container';
|
||||||
|
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
|
||||||
import {StylingIndex} from '../render3/interfaces/styling';
|
import {StylingIndex} from '../render3/interfaces/styling';
|
||||||
import {LView, TData, TVIEW} from '../render3/interfaces/view';
|
import {LView, NEXT, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view';
|
||||||
import {getProp, getValue, isClassBasedValue} from '../render3/styling/class_and_style_bindings';
|
import {getProp, getValue, isClassBasedValue} from '../render3/styling/class_and_style_bindings';
|
||||||
import {getStylingContext} from '../render3/styling/util';
|
import {getStylingContext} from '../render3/styling/util';
|
||||||
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext, loadLContextFromNode} from '../render3/util/discovery_utils';
|
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext, loadLContextFromNode} from '../render3/util/discovery_utils';
|
||||||
import {INTERPOLATION_DELIMITER, isPropMetadataString, renderStringify} from '../render3/util/misc_utils';
|
import {INTERPOLATION_DELIMITER, isPropMetadataString, renderStringify} from '../render3/util/misc_utils';
|
||||||
|
import {findComponentView} from '../render3/util/view_traversal_utils';
|
||||||
|
import {getComponentViewByIndex, getNativeByTNode, isComponent, isLContainer} from '../render3/util/view_utils';
|
||||||
import {assertDomNode} from '../util/assert';
|
import {assertDomNode} from '../util/assert';
|
||||||
import {DebugContext} from '../view/index';
|
import {DebugContext} from '../view/index';
|
||||||
|
|
||||||
|
@ -368,13 +371,13 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||||
|
|
||||||
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
|
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
|
||||||
const matches: DebugElement[] = [];
|
const matches: DebugElement[] = [];
|
||||||
_queryNodeChildrenR3(this, predicate, matches, true);
|
_queryAllR3(this, predicate, matches, true);
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
|
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
|
||||||
const matches: DebugNode[] = [];
|
const matches: DebugNode[] = [];
|
||||||
_queryNodeChildrenR3(this, predicate, matches, false);
|
_queryAllR3(this, predicate, matches, false);
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,20 +390,130 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _queryNodeChildrenR3(
|
/**
|
||||||
parentNode: DebugNode, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
* Walk the TNode tree to find matches for the predicate, skipping the parent element.
|
||||||
|
*
|
||||||
|
* @param parentElement the element from which the walk is started
|
||||||
|
* @param predicate the predicate to match
|
||||||
|
* @param matches the list of positive matches
|
||||||
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
*/
|
||||||
|
function _queryAllR3(
|
||||||
|
parentElement: DebugElement, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
||||||
elementsOnly: boolean) {
|
elementsOnly: boolean) {
|
||||||
if (parentNode instanceof DebugElement__POST_R3__) {
|
const context = loadLContext(parentElement.nativeNode) !;
|
||||||
parentNode.childNodes.forEach(node => {
|
const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode;
|
||||||
if (predicate(node)) {
|
// This the fixture's debug element, so this is always a component view.
|
||||||
matches.push(node);
|
const lView = context.lView[parentTNode.index];
|
||||||
|
const tNode = lView[TVIEW].firstChild;
|
||||||
|
_queryNodeChildrenR3(tNode, lView, predicate, matches, elementsOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively match the current TNode against the predicate, and goes on with the next ones.
|
||||||
|
*
|
||||||
|
* @param tNode the current TNode
|
||||||
|
* @param lView the LView of this TNode
|
||||||
|
* @param predicate the predicate to match
|
||||||
|
* @param matches the list of positive matches
|
||||||
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
*/
|
||||||
|
function _queryNodeChildrenR3(
|
||||||
|
tNode: TNode, lView: LView, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
||||||
|
elementsOnly: boolean) {
|
||||||
|
// For each type of TNode, specific logic is executed.
|
||||||
|
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
||||||
|
// Case 1: the TNode is an element
|
||||||
|
// The native node has to be checked.
|
||||||
|
_addQueryMatchR3(getNativeByTNode(tNode, lView), predicate, matches, elementsOnly);
|
||||||
|
if (isComponent(tNode)) {
|
||||||
|
// 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.
|
||||||
|
const componentView = getComponentViewByIndex(tNode.index, lView);
|
||||||
|
if (componentView && componentView[TVIEW].firstChild)
|
||||||
|
_queryNodeChildrenR3(
|
||||||
|
componentView[TVIEW].firstChild !, componentView, predicate, matches, elementsOnly);
|
||||||
|
} else {
|
||||||
|
// Otherwise, its children have to be processed.
|
||||||
|
if (tNode.child) _queryNodeChildrenR3(tNode.child, lView, predicate, matches, elementsOnly);
|
||||||
}
|
}
|
||||||
if (node instanceof DebugElement__POST_R3__) {
|
// In all cases, if a dynamic container exists for this node, each view inside it has to be
|
||||||
if (elementsOnly ? node.nativeElement : true) {
|
// processed.
|
||||||
_queryNodeChildrenR3(node, predicate, matches, elementsOnly);
|
const nodeOrContainer = lView[tNode.index];
|
||||||
|
if (isLContainer(nodeOrContainer)) {
|
||||||
|
_queryNodeChildrenInContainerR3(nodeOrContainer, predicate, matches, elementsOnly);
|
||||||
|
}
|
||||||
|
} else if (tNode.type === TNodeType.Container) {
|
||||||
|
// Case 2: the TNode is a container
|
||||||
|
// The native node has to be checked.
|
||||||
|
const lContainer = lView[tNode.index];
|
||||||
|
_addQueryMatchR3(lContainer[NATIVE], predicate, matches, elementsOnly);
|
||||||
|
// Each view inside the container has to be processed.
|
||||||
|
_queryNodeChildrenInContainerR3(lContainer, predicate, matches, elementsOnly);
|
||||||
|
} else if (tNode.type === TNodeType.Projection) {
|
||||||
|
// 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.
|
||||||
|
const componentView = findComponentView(lView !);
|
||||||
|
const componentHost = componentView[T_HOST] as TElementNode;
|
||||||
|
const head: TNode|null =
|
||||||
|
(componentHost.projection as(TNode | null)[])[tNode.projection as number];
|
||||||
|
|
||||||
|
if (Array.isArray(head)) {
|
||||||
|
for (let nativeNode of head) {
|
||||||
|
_addQueryMatchR3(nativeNode, predicate, matches, elementsOnly);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (head) {
|
||||||
|
const nextLView = componentView[PARENT] !as LView;
|
||||||
|
const nextTNode = nextLView[TVIEW].data[head.index] as TNode;
|
||||||
|
_queryNodeChildrenR3(nextTNode, nextLView, predicate, matches, elementsOnly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
// Case 4: the TNode is a view.
|
||||||
|
if (tNode.child) {
|
||||||
|
_queryNodeChildrenR3(tNode.child, lView, predicate, matches, elementsOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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.
|
||||||
|
const nextTNode = (tNode.flags & TNodeFlags.isProjected) ? tNode.projectionNext : tNode.next;
|
||||||
|
if (nextTNode) {
|
||||||
|
_queryNodeChildrenR3(nextTNode, lView, predicate, matches, elementsOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process all TNodes in a given container.
|
||||||
|
*
|
||||||
|
* @param lContainer the container to be processed
|
||||||
|
* @param predicate the predicate to match
|
||||||
|
* @param matches the list of positive matches
|
||||||
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
*/
|
||||||
|
function _queryNodeChildrenInContainerR3(
|
||||||
|
lContainer: LContainer, predicate: Predicate<DebugNode>, matches: DebugNode[],
|
||||||
|
elementsOnly: boolean) {
|
||||||
|
for (let i = 0; i < lContainer[VIEWS].length; i++) {
|
||||||
|
const childView = lContainer[VIEWS][i];
|
||||||
|
_queryNodeChildrenR3(childView[TVIEW].node !, childView, predicate, matches, elementsOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match the current native node against the predicate.
|
||||||
|
*
|
||||||
|
* @param nativeNode the current native node
|
||||||
|
* @param predicate the predicate to match
|
||||||
|
* @param matches the list of positive matches
|
||||||
|
* @param elementsOnly whether only elements should be searched
|
||||||
|
*/
|
||||||
|
function _addQueryMatchR3(
|
||||||
|
nativeNode: any, predicate: Predicate<DebugNode>, matches: DebugNode[], elementsOnly: boolean) {
|
||||||
|
const debugNode = getDebugNode(nativeNode);
|
||||||
|
if (debugNode && (elementsOnly ? debugNode instanceof DebugElement__POST_R3__ : true) &&
|
||||||
|
predicate(debugNode)) {
|
||||||
|
matches.push(debugNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {CommonModule} from '@angular/common';
|
||||||
import {Component, HostBinding, ViewChild} from '@angular/core';
|
import {Component, HostBinding, ViewChild} from '@angular/core';
|
||||||
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {fixmeIvy, ivyEnabled} from '@angular/private/testing';
|
import {ivyEnabled} from '@angular/private/testing';
|
||||||
|
|
||||||
import {HostListener} from '../../src/metadata/directives';
|
import {HostListener} from '../../src/metadata/directives';
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/brow
|
||||||
import {Component, HostBinding} from '@angular/core';
|
import {Component, HostBinding} from '@angular/core';
|
||||||
import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {fixmeIvy} from '@angular/private/testing';
|
|
||||||
import {ActivatedRoute, Router, RouterOutlet} from '@angular/router';
|
import {ActivatedRoute, Router, RouterOutlet} from '@angular/router';
|
||||||
import {RouterTestingModule} from '@angular/router/testing';
|
import {RouterTestingModule} from '@angular/router/testing';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import {Component, Directive, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA} from '@angular/core';
|
import {Component, Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
@ -154,6 +154,15 @@ class BankAccount {
|
||||||
normalizedBankName !: string;
|
normalizedBankName !: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<div class="content" #content>Some content</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class SimpleContentComp {
|
||||||
|
@ViewChild('content') content !: ElementRef;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'test-app',
|
selector: 'test-app',
|
||||||
template: `
|
template: `
|
||||||
|
@ -202,6 +211,7 @@ class HostClassBindingCmp {
|
||||||
BankAccount,
|
BankAccount,
|
||||||
TestCmpt,
|
TestCmpt,
|
||||||
HostClassBindingCmp,
|
HostClassBindingCmp,
|
||||||
|
SimpleContentComp,
|
||||||
],
|
],
|
||||||
providers: [Logger],
|
providers: [Logger],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
|
@ -369,6 +379,122 @@ class HostClassBindingCmp {
|
||||||
expect(debugElement).toBeTruthy();
|
expect(debugElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should query re-projected child elements by directive', () => {
|
||||||
|
@Directive({selector: 'example-directive-a'})
|
||||||
|
class ExampleDirectiveA {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'proxy-component',
|
||||||
|
template: `
|
||||||
|
<ng-content></ng-content>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class ProxyComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'wrapper-component',
|
||||||
|
template: `
|
||||||
|
<proxy-component>
|
||||||
|
<ng-content select="div"></ng-content>
|
||||||
|
<ng-content select="example-directive-a"></ng-content>
|
||||||
|
</proxy-component>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class WrapperComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
ProxyComponent,
|
||||||
|
WrapperComponent,
|
||||||
|
ExampleDirectiveA,
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
TestBed.overrideTemplate(TestApp, `<wrapper-component>
|
||||||
|
<div></div>
|
||||||
|
<example-directive-a></example-directive-a>
|
||||||
|
</wrapper-component>`);
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(TestApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const debugElements = fixture.debugElement.queryAll(By.directive(ExampleDirectiveA));
|
||||||
|
expect(debugElements.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query directives on containers before directives in a view', () => {
|
||||||
|
@Directive({selector: '[text]'})
|
||||||
|
class TextDirective {
|
||||||
|
@Input() text: string|undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TextDirective]});
|
||||||
|
TestBed.overrideTemplate(
|
||||||
|
TestApp,
|
||||||
|
`<ng-template text="first" [ngIf]="true"><div text="second"></div></ng-template>`);
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(TestApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const debugNodes = fixture.debugElement.queryAllNodes(By.directive(TextDirective));
|
||||||
|
expect(debugNodes.length).toBe(2);
|
||||||
|
expect(debugNodes[0].injector.get(TextDirective).text).toBe('first');
|
||||||
|
expect(debugNodes[1].injector.get(TextDirective).text).toBe('second');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query directives on views moved in the DOM', () => {
|
||||||
|
@Directive({selector: '[text]'})
|
||||||
|
class TextDirective {
|
||||||
|
@Input() text: string|undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[moveView]'})
|
||||||
|
class ViewManipulatingDirective {
|
||||||
|
constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<any>) {}
|
||||||
|
|
||||||
|
insert() { this._vcRef.createEmbeddedView(this._tplRef); }
|
||||||
|
|
||||||
|
removeFromTheDom() {
|
||||||
|
const viewRef = this._vcRef.get(0) as EmbeddedViewRef<any>;
|
||||||
|
viewRef.rootNodes.forEach(rootNode => { getDOM().remove(rootNode); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TextDirective, ViewManipulatingDirective]});
|
||||||
|
TestBed.overrideTemplate(
|
||||||
|
TestApp, `<ng-template text="first" moveView><div text="second"></div></ng-template>`);
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(TestApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const viewMover =
|
||||||
|
fixture.debugElement.queryAllNodes(By.directive(ViewManipulatingDirective))[0]
|
||||||
|
.injector.get(ViewManipulatingDirective);
|
||||||
|
|
||||||
|
let debugNodes = fixture.debugElement.queryAllNodes(By.directive(TextDirective));
|
||||||
|
|
||||||
|
// we've got just one directive on <ng-template>
|
||||||
|
expect(debugNodes.length).toBe(1);
|
||||||
|
expect(debugNodes[0].injector.get(TextDirective).text).toBe('first');
|
||||||
|
|
||||||
|
// insert a view - now we expect to find 2 directive instances
|
||||||
|
viewMover.insert();
|
||||||
|
fixture.detectChanges();
|
||||||
|
debugNodes = fixture.debugElement.queryAllNodes(By.directive(TextDirective));
|
||||||
|
expect(debugNodes.length).toBe(2);
|
||||||
|
|
||||||
|
// remove a view from the DOM (equivalent to moving it around)
|
||||||
|
// the logical tree is the same but DOM has changed
|
||||||
|
viewMover.removeFromTheDom();
|
||||||
|
debugNodes = fixture.debugElement.queryAllNodes(By.directive(TextDirective));
|
||||||
|
expect(debugNodes.length).toBe(2);
|
||||||
|
expect(debugNodes[0].injector.get(TextDirective).text).toBe('first');
|
||||||
|
expect(debugNodes[1].injector.get(TextDirective).text).toBe('second');
|
||||||
|
});
|
||||||
|
|
||||||
it('should list providerTokens', () => {
|
it('should list providerTokens', () => {
|
||||||
fixture = TestBed.createComponent(ParentComp);
|
fixture = TestBed.createComponent(ParentComp);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -489,5 +615,21 @@ class HostClassBindingCmp {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to query for elements that are not in the same DOM tree anymore', () => {
|
||||||
|
fixture = TestBed.createComponent(SimpleContentComp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const parent = getDOM().parentElement(fixture.nativeElement) !;
|
||||||
|
const content = fixture.componentInstance.content.nativeElement;
|
||||||
|
|
||||||
|
// Move the content element outside the component
|
||||||
|
// so that it can't be reached via querySelector.
|
||||||
|
getDOM().appendChild(parent, content);
|
||||||
|
|
||||||
|
expect(fixture.debugElement.query(By.css('.content'))).toBeTruthy();
|
||||||
|
|
||||||
|
getDOM().remove(content);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {NgModuleData} from '@angular/core/src/view/types';
|
||||||
import {tokenKey} from '@angular/core/src/view/util';
|
import {tokenKey} from '@angular/core/src/view/util';
|
||||||
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {fixmeIvy, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
|
import {modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
import {InternalNgModuleRef, NgModuleFactory} from '../../src/linker/ng_module_factory';
|
import {InternalNgModuleRef, NgModuleFactory} from '../../src/linker/ng_module_factory';
|
||||||
import {clearModulesForTest} from '../../src/linker/ng_module_factory_loader';
|
import {clearModulesForTest} from '../../src/linker/ng_module_factory_loader';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {fixmeIvy, modifiedInIvy} from '@angular/private/testing';
|
import {modifiedInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('projection', () => {
|
describe('projection', () => {
|
||||||
beforeEach(() => TestBed.configureTestingModule({declarations: [MainComp, OtherComp, Simple]}));
|
beforeEach(() => TestBed.configureTestingModule({declarations: [MainComp, OtherComp, Simple]}));
|
||||||
|
@ -511,8 +511,7 @@ describe('projection', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fixmeIvy('FW-869: debugElement.queryAllNodes returns nodes in the wrong order')
|
it('should support nested conditionals that contain ng-contents', () => {
|
||||||
.it('should support nested conditionals that contain ng-contents', () => {
|
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule(
|
||||||
{declarations: [ConditionalTextComponent, ManualViewportDirective]});
|
{declarations: [ConditionalTextComponent, ManualViewportDirective]});
|
||||||
TestBed.overrideComponent(
|
TestBed.overrideComponent(
|
||||||
|
@ -521,8 +520,7 @@ describe('projection', () => {
|
||||||
|
|
||||||
expect(main.nativeElement).toHaveText('MAIN()');
|
expect(main.nativeElement).toHaveText('MAIN()');
|
||||||
|
|
||||||
let viewportElement =
|
let viewportElement = main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0];
|
||||||
main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0];
|
|
||||||
viewportElement.injector.get(ManualViewportDirective).show();
|
viewportElement.injector.get(ManualViewportDirective).show();
|
||||||
expect(main.nativeElement).toHaveText('MAIN(FIRST())');
|
expect(main.nativeElement).toHaveText('MAIN(FIRST())');
|
||||||
|
|
||||||
|
@ -639,8 +637,7 @@ describe('projection', () => {
|
||||||
expect(main.nativeElement).toHaveText('ABC');
|
expect(main.nativeElement).toHaveText('ABC');
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-869: debugElement.queryAllNodes returns nodes in the wrong order')
|
it('should project filled view containers into a view container', () => {
|
||||||
.it('should project filled view containers into a view container', () => {
|
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule(
|
||||||
{declarations: [ConditionalContentComponent, ManualViewportDirective]});
|
{declarations: [ConditionalContentComponent, ManualViewportDirective]});
|
||||||
TestBed.overrideComponent(MainComp, {
|
TestBed.overrideComponent(MainComp, {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit,
|
||||||
import {ElementRef} from '@angular/core/src/core';
|
import {ElementRef} from '@angular/core/src/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {fixmeIvy, ivyEnabled, modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
import {ivyEnabled, modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||||
import {Subject} from 'rxjs';
|
import {Subject} from 'rxjs';
|
||||||
|
|
||||||
import {stringify} from '../../src/util/stringify';
|
import {stringify} from '../../src/util/stringify';
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {Component, Directive, HostBinding, Input, NO_ERRORS_SCHEMA, ɵivyEnabled
|
||||||
import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
||||||
import {fixmeIvy, modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
{
|
{
|
||||||
if (ivyEnabled) {
|
if (ivyEnabled) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {CompilerFacade, ExportedCompilerFacade} from '@angular/core/src/compiler
|
||||||
import {getErrorLogger} from '@angular/core/src/errors';
|
import {getErrorLogger} from '@angular/core/src/errors';
|
||||||
import {resolveComponentResources} from '@angular/core/src/metadata/resource_loading';
|
import {resolveComponentResources} from '@angular/core/src/metadata/resource_loading';
|
||||||
import {TestBed, fakeAsync, tick} from '@angular/core/testing';
|
import {TestBed, fakeAsync, tick} from '@angular/core/testing';
|
||||||
import {fixmeIvy, modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('jit source mapping', () => {
|
describe('jit source mapping', () => {
|
||||||
let resourceLoader: MockResourceLoader;
|
let resourceLoader: MockResourceLoader;
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {NgIf} from './common_with_def';
|
import {NgIf} from './common_with_def';
|
||||||
import {ComponentFixture, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame} from './render_util';
|
import {ComponentFixture, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame} from './render_util';
|
||||||
import {fixmeIvy} from '@angular/private/testing';
|
|
||||||
|
|
||||||
describe('lifecycles', () => {
|
describe('lifecycles', () => {
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {Component, Injectable, NgZone, RendererFactory2, RendererType2, ViewChil
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {BrowserAnimationsModule, ɵAnimationRendererFactory as AnimationRendererFactory, ɵInjectableAnimationEngine as InjectableAnimationEngine} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule, ɵAnimationRendererFactory as AnimationRendererFactory, ɵInjectableAnimationEngine as InjectableAnimationEngine} from '@angular/platform-browser/animations';
|
||||||
import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer';
|
import {DomRendererFactory2} from '@angular/platform-browser/src/dom/dom_renderer';
|
||||||
import {fixmeIvy} from '@angular/private/testing';
|
|
||||||
|
|
||||||
import {el} from '../../testing/src/browser_util';
|
import {el} from '../../testing/src/browser_util';
|
||||||
|
|
||||||
|
|
|
@ -8,15 +8,6 @@
|
||||||
|
|
||||||
import {bazelDefineCompileValue} from './bazel_define_compile_value';
|
import {bazelDefineCompileValue} from './bazel_define_compile_value';
|
||||||
|
|
||||||
/**
|
|
||||||
* Set this constant to `true` to run all tests and report which of the tests marked with `fixmeIvy`
|
|
||||||
* are actually already passing.
|
|
||||||
*
|
|
||||||
* This is useful for locating already passing tests. The already passing tests should have their
|
|
||||||
* `fixmeIvy` removed.
|
|
||||||
*/
|
|
||||||
const FIND_PASSING_TESTS = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function to conditionally include a test or a block of tests only when tests run against Ivy.
|
* A function to conditionally include a test or a block of tests only when tests run against Ivy.
|
||||||
*
|
*
|
||||||
|
@ -35,30 +26,6 @@ const FIND_PASSING_TESTS = false;
|
||||||
*/
|
*/
|
||||||
export const ivyEnabled = 'aot' === (bazelDefineCompileValue as string);
|
export const ivyEnabled = 'aot' === (bazelDefineCompileValue as string);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function to conditionally skip the execution of tests that are yet to be fixed
|
|
||||||
* when running against Ivy.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* fixmeIvy('some reason').describe(...);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* or
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* fixmeIvy('some reason').it(...);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export function fixmeIvy(reason: string): JasmineMethods {
|
|
||||||
if (FIND_PASSING_TESTS) {
|
|
||||||
return ivyEnabled ? PASSTHROUGH : IGNORE;
|
|
||||||
} else {
|
|
||||||
return ivyEnabled ? IGNORE : PASSTHROUGH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function to conditionally skip the execution of tests that are not relevant when
|
* A function to conditionally skip the execution of tests that are not relevant when
|
||||||
* running against Ivy.
|
* running against Ivy.
|
||||||
|
@ -94,7 +61,7 @@ export function obsoleteInIvy(reason: string): JasmineMethods {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function onlyInIvy(reason: string): JasmineMethods {
|
export function onlyInIvy(reason: string): JasmineMethods {
|
||||||
return ivyEnabled && !FIND_PASSING_TESTS ? PASSTHROUGH : IGNORE;
|
return ivyEnabled ? PASSTHROUGH : IGNORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,29 +90,17 @@ export interface JasmineMethods {
|
||||||
fit: typeof fit;
|
fit: typeof fit;
|
||||||
describe: typeof describe;
|
describe: typeof describe;
|
||||||
fdescribe: typeof fdescribe;
|
fdescribe: typeof fdescribe;
|
||||||
fixmeIvy: typeof fixmeIvy;
|
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PASSTHROUGH: JasmineMethods = {
|
const PASSTHROUGH: JasmineMethods = {
|
||||||
it: maybeAppendFindPassingTestsMarker(it),
|
it: it,
|
||||||
fit: maybeAppendFindPassingTestsMarker(fit),
|
fit: fit,
|
||||||
describe: maybeAppendFindPassingTestsMarker(describe),
|
describe: describe,
|
||||||
fdescribe: maybeAppendFindPassingTestsMarker(fdescribe),
|
fdescribe: fdescribe,
|
||||||
fixmeIvy: maybeAppendFindPassingTestsMarker(fixmeIvy),
|
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FIND_PASSING_TESTS_MARKER = '__FIND_PASSING_TESTS_MARKER__';
|
|
||||||
function maybeAppendFindPassingTestsMarker<T extends Function>(fn: T): T {
|
|
||||||
return FIND_PASSING_TESTS ? function(...args: any[]) {
|
|
||||||
if (typeof args[0] == 'string') {
|
|
||||||
args[0] += FIND_PASSING_TESTS_MARKER;
|
|
||||||
}
|
|
||||||
return fn.apply(this, args);
|
|
||||||
} : fn as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
function noop() {}
|
function noop() {}
|
||||||
|
|
||||||
const IGNORE: JasmineMethods = {
|
const IGNORE: JasmineMethods = {
|
||||||
|
@ -153,39 +108,5 @@ const IGNORE: JasmineMethods = {
|
||||||
fit: noop,
|
fit: noop,
|
||||||
describe: noop,
|
describe: noop,
|
||||||
fdescribe: noop,
|
fdescribe: noop,
|
||||||
fixmeIvy: (reason) => IGNORE,
|
|
||||||
isEnabled: false,
|
isEnabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (FIND_PASSING_TESTS) {
|
|
||||||
const env = jasmine.getEnv();
|
|
||||||
const passingTests: jasmine.CustomReporterResult[] = [];
|
|
||||||
const stillFailing: jasmine.CustomReporterResult[] = [];
|
|
||||||
let specCount = 0;
|
|
||||||
env.clearReporters();
|
|
||||||
env.addReporter({
|
|
||||||
specDone: function(result: jasmine.CustomReporterResult) {
|
|
||||||
specCount++;
|
|
||||||
if (result.fullName.indexOf(FIND_PASSING_TESTS_MARKER) != -1) {
|
|
||||||
(result.status == 'passed' ? passingTests : stillFailing).push(result);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
jasmineDone: function(details: jasmine.RunDetails) {
|
|
||||||
if (passingTests.length) {
|
|
||||||
passingTests.forEach((result) => {
|
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
console.log('ALREADY PASSING', result.fullName.replace(FIND_PASSING_TESTS_MARKER, ''));
|
|
||||||
});
|
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
console.log(
|
|
||||||
`${specCount} specs,`, //
|
|
||||||
`${passingTests.length} passing specs,`, //
|
|
||||||
`${stillFailing.length} still failing specs`);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
console.log('NO PASSING TESTS FOUND.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -24,42 +24,6 @@ window.testBlocklist = {
|
||||||
"MatSidenav should set fixed bottom and top when in fixed mode": {
|
"MatSidenav should set fixed bottom and top when in fixed mode": {
|
||||||
"error": "Error: Expected '' to be '20px'.",
|
"error": "Error: Expected '' to be '20px'.",
|
||||||
"notes": "FW-1132: Host class bindings don't work if super class has host class bindings"
|
"notes": "FW-1132: Host class bindings don't work if super class has host class bindings"
|
||||||
},
|
|
||||||
"Dialog should set the proper animation states": {
|
|
||||||
"error": "TypeError: Cannot read property 'componentInstance' of null",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatAutocomplete aria should set role of autocomplete panel to listbox": {
|
|
||||||
"error": "TypeError: Cannot read property 'nativeElement' of null",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatAutocomplete aria should set aria-owns based on the attached autocomplete": {
|
|
||||||
"error": "TypeError: Cannot read property 'nativeElement' of null",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatDialog should set the proper animation states": {
|
|
||||||
"error": "TypeError: Cannot read property 'componentInstance' of null",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatMenu animations should enable ripples on items by default": {
|
|
||||||
"error": "TypeError: Cannot read property 'query' of null",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatMenu animations should disable ripples on disabled items": {
|
|
||||||
"error": "TypeError: Cannot read property 'query' of undefined",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatMenu animations should disable ripples if disableRipple is set": {
|
|
||||||
"error": "TypeError: Cannot read property 'query' of undefined",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatMenu nested menu should close submenu when hovering over disabled sibling item": {
|
|
||||||
"error": "TypeError: Cannot read property 'nativeElement' of undefined",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
},
|
|
||||||
"MatMenu nested menu should not open submenu when hovering over disabled trigger": {
|
|
||||||
"error": "TypeError: Cannot read property 'componentInstance' of null",
|
|
||||||
"notes": "FW-1059: DebugNode.query should query nodes in the logical tree"
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
Loading…
Reference in New Issue