fix(ivy): DebugNode.query should query nodes in the logical tree (#29480)
PR Close #29480
This commit is contained in:
@ -14,7 +14,7 @@ import {CommonModule} from '@angular/common';
|
||||
import {Component, HostBinding, ViewChild} from '@angular/core';
|
||||
import {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||
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';
|
||||
|
||||
|
@ -11,7 +11,6 @@ import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/brow
|
||||
import {Component, HostBinding} from '@angular/core';
|
||||
import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {ActivatedRoute, Router, RouterOutlet} from '@angular/router';
|
||||
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 {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
@ -154,6 +154,15 @@ class BankAccount {
|
||||
normalizedBankName !: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div class="content" #content>Some content</div>
|
||||
`
|
||||
})
|
||||
class SimpleContentComp {
|
||||
@ViewChild('content') content !: ElementRef;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template: `
|
||||
@ -202,6 +211,7 @@ class HostClassBindingCmp {
|
||||
BankAccount,
|
||||
TestCmpt,
|
||||
HostClassBindingCmp,
|
||||
SimpleContentComp,
|
||||
],
|
||||
providers: [Logger],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
@ -369,6 +379,122 @@ class HostClassBindingCmp {
|
||||
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', () => {
|
||||
fixture = TestBed.createComponent(ParentComp);
|
||||
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 {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
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 {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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {fixmeIvy, modifiedInIvy} from '@angular/private/testing';
|
||||
import {modifiedInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('projection', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({declarations: [MainComp, OtherComp, Simple]}));
|
||||
@ -511,25 +511,23 @@ describe('projection', () => {
|
||||
});
|
||||
}
|
||||
|
||||
fixmeIvy('FW-869: debugElement.queryAllNodes returns nodes in the wrong order')
|
||||
.it('should support nested conditionals that contain ng-contents', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalTextComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(
|
||||
MainComp, {set: {template: `<conditional-text>a</conditional-text>`}});
|
||||
const main = TestBed.createComponent(MainComp);
|
||||
it('should support nested conditionals that contain ng-contents', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalTextComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(
|
||||
MainComp, {set: {template: `<conditional-text>a</conditional-text>`}});
|
||||
const main = TestBed.createComponent(MainComp);
|
||||
|
||||
expect(main.nativeElement).toHaveText('MAIN()');
|
||||
expect(main.nativeElement).toHaveText('MAIN()');
|
||||
|
||||
let viewportElement =
|
||||
main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0];
|
||||
viewportElement.injector.get(ManualViewportDirective).show();
|
||||
expect(main.nativeElement).toHaveText('MAIN(FIRST())');
|
||||
let viewportElement = main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[0];
|
||||
viewportElement.injector.get(ManualViewportDirective).show();
|
||||
expect(main.nativeElement).toHaveText('MAIN(FIRST())');
|
||||
|
||||
viewportElement = main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[1];
|
||||
viewportElement.injector.get(ManualViewportDirective).show();
|
||||
expect(main.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))');
|
||||
});
|
||||
viewportElement = main.debugElement.queryAllNodes(By.directive(ManualViewportDirective))[1];
|
||||
viewportElement.injector.get(ManualViewportDirective).show();
|
||||
expect(main.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))');
|
||||
});
|
||||
|
||||
it('should allow to switch the order of nested components via ng-content', () => {
|
||||
TestBed.configureTestingModule({declarations: [CmpA, CmpB, CmpD, CmpC]});
|
||||
@ -639,49 +637,48 @@ describe('projection', () => {
|
||||
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', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalContentComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<ng-template manual class="left">B</ng-template>' +
|
||||
'<div class="left">C</div>' +
|
||||
'<div>D</div>' +
|
||||
'</conditional-content>'
|
||||
}
|
||||
});
|
||||
const main = TestBed.createComponent(MainComp);
|
||||
it('should project filled view containers into a view container', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalContentComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<ng-template manual class="left">B</ng-template>' +
|
||||
'<div class="left">C</div>' +
|
||||
'<div>D</div>' +
|
||||
'</conditional-content>'
|
||||
}
|
||||
});
|
||||
const main = TestBed.createComponent(MainComp);
|
||||
|
||||
const conditionalComp = main.debugElement.query(By.directive(ConditionalContentComponent));
|
||||
const conditionalComp = main.debugElement.query(By.directive(ConditionalContentComponent));
|
||||
|
||||
const viewViewportDir =
|
||||
conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get(
|
||||
ManualViewportDirective);
|
||||
const viewViewportDir =
|
||||
conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[0].injector.get(
|
||||
ManualViewportDirective);
|
||||
|
||||
expect(main.nativeElement).toHaveText('(, D)');
|
||||
expect(main.nativeElement).toHaveText('(, D)');
|
||||
expect(main.nativeElement).toHaveText('(, D)');
|
||||
expect(main.nativeElement).toHaveText('(, D)');
|
||||
|
||||
viewViewportDir.show();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('(AC, D)');
|
||||
viewViewportDir.show();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('(AC, D)');
|
||||
|
||||
const contentViewportDir =
|
||||
conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[1].injector.get(
|
||||
ManualViewportDirective);
|
||||
const contentViewportDir =
|
||||
conditionalComp.queryAllNodes(By.directive(ManualViewportDirective))[1].injector.get(
|
||||
ManualViewportDirective);
|
||||
|
||||
contentViewportDir.show();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('(ABC, D)');
|
||||
contentViewportDir.show();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('(ABC, D)');
|
||||
|
||||
// hide view viewport, and test that it also hides
|
||||
// the content viewport's views
|
||||
viewViewportDir.hide();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('(, D)');
|
||||
});
|
||||
// hide view viewport, and test that it also hides
|
||||
// the content viewport's views
|
||||
viewViewportDir.hide();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('(, D)');
|
||||
});
|
||||
|
||||
describe('projectable nodes', () => {
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit,
|
||||
import {ElementRef} from '@angular/core/src/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
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 {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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
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) {
|
||||
|
@ -17,7 +17,7 @@ import {CompilerFacade, ExportedCompilerFacade} from '@angular/core/src/compiler
|
||||
import {getErrorLogger} from '@angular/core/src/errors';
|
||||
import {resolveComponentResources} from '@angular/core/src/metadata/resource_loading';
|
||||
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', () => {
|
||||
let resourceLoader: MockResourceLoader;
|
||||
|
@ -14,7 +14,6 @@ import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
||||
import {NgIf} from './common_with_def';
|
||||
import {ComponentFixture, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame} from './render_util';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
|
||||
describe('lifecycles', () => {
|
||||
|
||||
|
Reference in New Issue
Block a user