test(ivy): move render3 query tests to acceptance (#30382)
Moves all manual render3 query tests that use the `elementProperty` instructions to TestBed acceptance tests. PR Close #30382
This commit is contained in:
parent
1a0e500eea
commit
45e11915dc
@ -6,8 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren} from '@angular/core';
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {By} from '@angular/platform-browser';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
@ -601,6 +603,44 @@ describe('query logic', () => {
|
|||||||
expect(fixture.componentInstance.subComp.foo.length).toBe(2);
|
expect(fixture.componentInstance.subComp.foo.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should match shallow content queries in views inserted / removed by ngIf', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'test-comp',
|
||||||
|
template: `
|
||||||
|
<shallow-comp>
|
||||||
|
<div *ngIf="showing" #foo></div>
|
||||||
|
</shallow-comp>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
showing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'shallow-comp',
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
|
class ShallowComp {
|
||||||
|
@ContentChildren('foo', {descendants: false}) foos !: QueryList<ElementRef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [TestComponent, ShallowComp], imports: [CommonModule]});
|
||||||
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const shallowComp = fixture.debugElement.query(By.directive(ShallowComp)).componentInstance;
|
||||||
|
const queryList = shallowComp !.foos;
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
|
||||||
|
fixture.componentInstance.showing = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(1);
|
||||||
|
|
||||||
|
fixture.componentInstance.showing = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Some root components may have ContentChildren queries if they are also
|
// Some root components may have ContentChildren queries if they are also
|
||||||
@ -665,6 +705,253 @@ describe('query logic', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('view boundaries', () => {
|
||||||
|
|
||||||
|
describe('ViewContainerRef', () => {
|
||||||
|
|
||||||
|
@Directive({selector: '[vc]', exportAs: 'vc'})
|
||||||
|
class ViewContainerManipulatorDirective {
|
||||||
|
constructor(private _vcRef: ViewContainerRef) {}
|
||||||
|
|
||||||
|
insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number) {
|
||||||
|
this._vcRef.createEmbeddedView(tpl, ctx, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(index?: number) { this._vcRef.remove(index); }
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should report results in views inserted / removed by ngIf', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'test-comp',
|
||||||
|
template: `
|
||||||
|
<ng-template [ngIf]="value">
|
||||||
|
<div #foo></div>
|
||||||
|
</ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
value: boolean = false;
|
||||||
|
@ViewChildren('foo') query !: QueryList<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TestComponent]});
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const queryList = fixture.componentInstance.query;
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
|
||||||
|
fixture.componentInstance.value = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(1);
|
||||||
|
|
||||||
|
fixture.componentInstance.value = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should report results in views inserted / removed by ngFor', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'test-comp',
|
||||||
|
template: `
|
||||||
|
<ng-template ngFor let-item [ngForOf]="value">
|
||||||
|
<div #foo [id]="item"></div>
|
||||||
|
</ng-template>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
value: string[]|undefined;
|
||||||
|
@ViewChildren('foo') query !: QueryList<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TestComponent]});
|
||||||
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const queryList = fixture.componentInstance.query;
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
|
||||||
|
fixture.componentInstance.value = ['a', 'b', 'c'];
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(3);
|
||||||
|
|
||||||
|
// Remove the "b" element from the value.
|
||||||
|
fixture.componentInstance.value.splice(1, 1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(2);
|
||||||
|
|
||||||
|
// make sure that the "b" element has been removed from query results
|
||||||
|
expect(queryList.first.nativeElement.id).toBe('a');
|
||||||
|
expect(queryList.last.nativeElement.id).toBe('c');
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://stackblitz.com/edit/angular-rrmmuf?file=src/app/app.component.ts
|
||||||
|
it('should report results when different instances of TemplateRef are inserted into one ViewContainerRefs',
|
||||||
|
() => {
|
||||||
|
@Component({
|
||||||
|
selector: 'test-comp',
|
||||||
|
template: `
|
||||||
|
<ng-template #tpl1 let-idx="idx">
|
||||||
|
<div #foo [id]="'foo1_' + idx"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div #foo id="middle"></div>
|
||||||
|
|
||||||
|
<ng-template #tpl2 let-idx="idx">
|
||||||
|
<div #foo [id]="'foo2_' + idx"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template vc></ng-template>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
@ViewChild(ViewContainerManipulatorDirective) vc !: ViewContainerManipulatorDirective;
|
||||||
|
@ViewChild('tpl1') tpl1 !: TemplateRef<any>;
|
||||||
|
@ViewChild('tpl2') tpl2 !: TemplateRef<any>;
|
||||||
|
@ViewChildren('foo') query !: QueryList<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [ViewContainerManipulatorDirective, TestComponent]});
|
||||||
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const queryList = fixture.componentInstance.query;
|
||||||
|
const {tpl1, tpl2, vc} = fixture.componentInstance;
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(1);
|
||||||
|
expect(queryList.first.nativeElement.getAttribute('id')).toBe('middle');
|
||||||
|
|
||||||
|
vc.insertTpl(tpl1 !, {idx: 0}, 0);
|
||||||
|
vc.insertTpl(tpl2 !, {idx: 1}, 1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(3);
|
||||||
|
let qListArr = queryList.toArray();
|
||||||
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||||
|
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
||||||
|
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||||
|
|
||||||
|
vc.insertTpl(tpl1 !, {idx: 1}, 1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(4);
|
||||||
|
qListArr = queryList.toArray();
|
||||||
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||||
|
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo1_1');
|
||||||
|
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('middle');
|
||||||
|
expect(qListArr[3].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||||
|
|
||||||
|
vc.remove(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(3);
|
||||||
|
qListArr = queryList.toArray();
|
||||||
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||||
|
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
||||||
|
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||||
|
|
||||||
|
vc.remove(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(2);
|
||||||
|
qListArr = queryList.toArray();
|
||||||
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||||
|
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts
|
||||||
|
// https://stackblitz.com/edit/angular-xzwp6n
|
||||||
|
onlyInIvy('FW-1318: QueryList entries are ordered differently in Ivy.')
|
||||||
|
.it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
|
||||||
|
() => {
|
||||||
|
@Component({
|
||||||
|
selector: 'test-comp',
|
||||||
|
template: `
|
||||||
|
<ng-template #tpl let-idx="idx" let-container_idx="container_idx">
|
||||||
|
<div #foo [id]="'foo_' + container_idx + '_' + idx"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template vc #vi0="vc"></ng-template>
|
||||||
|
<ng-template vc #vi1="vc"></ng-template>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
@ViewChild('tpl') tpl !: TemplateRef<any>;
|
||||||
|
@ViewChild('vi0') vi0 !: ViewContainerManipulatorDirective;
|
||||||
|
@ViewChild('vi1') vi1 !: ViewContainerManipulatorDirective;
|
||||||
|
@ViewChildren('foo') query !: QueryList<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [ViewContainerManipulatorDirective, TestComponent]});
|
||||||
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const queryList = fixture.componentInstance.query;
|
||||||
|
const {tpl, vi0, vi1} = fixture.componentInstance;
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
|
||||||
|
vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
|
||||||
|
vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(2);
|
||||||
|
let qListArr = queryList.toArray();
|
||||||
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
||||||
|
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_0_0');
|
||||||
|
|
||||||
|
vi0.remove();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(queryList.length).toBe(1);
|
||||||
|
qListArr = queryList.toArray();
|
||||||
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
||||||
|
|
||||||
|
vi1.remove();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://stackblitz.com/edit/angular-wpd6gv?file=src%2Fapp%2Fapp.component.ts
|
||||||
|
it('should report results from views inserted in a lifecycle hook', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: `
|
||||||
|
<ng-template #tpl>
|
||||||
|
<span #foo id="from_tpl"></span>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngTemplateOutlet]="show ? tpl : null"></ng-template>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class MyApp {
|
||||||
|
show = false;
|
||||||
|
@ViewChildren('foo') query !: QueryList<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]});
|
||||||
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const queryList = fixture.componentInstance.query;
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
|
||||||
|
fixture.componentInstance.show = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(1);
|
||||||
|
expect(queryList.first.nativeElement.id).toBe('from_tpl');
|
||||||
|
|
||||||
|
fixture.componentInstance.show = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(queryList.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function initWithTemplate(compType: Type<any>, template: string) {
|
function initWithTemplate(compType: Type<any>, template: string) {
|
||||||
|
@ -6,19 +6,17 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NgForOfContext} from '@angular/common';
|
|
||||||
import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {EventEmitter} from '../..';
|
import {EventEmitter} from '../..';
|
||||||
import {AttributeMarker, detectChanges, ΔProvidersFeature, ΔdefineComponent, ΔdefineDirective} from '../../src/render3/index';
|
import {AttributeMarker, detectChanges, ΔProvidersFeature, ΔdefineComponent, ΔdefineDirective} from '../../src/render3/index';
|
||||||
import {Δbind, Δcontainer, ΔcontainerRefreshEnd, ΔcontainerRefreshStart, ΔdirectiveInject, Δelement, ΔelementContainerEnd, ΔelementContainerStart, ΔelementEnd, ΔelementProperty, ΔelementStart, ΔembeddedViewEnd, ΔembeddedViewStart, Δload, Δreference, Δtemplate, Δtext} from '../../src/render3/instructions/all';
|
import {Δcontainer, ΔcontainerRefreshEnd, ΔcontainerRefreshStart, ΔdirectiveInject, Δelement, ΔelementContainerEnd, ΔelementContainerStart, ΔelementEnd, ΔelementStart, ΔembeddedViewEnd, ΔembeddedViewStart, Δload, Δtemplate, Δtext} from '../../src/render3/instructions/all';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {query, ΔcontentQuery, ΔloadContentQuery, ΔloadViewQuery, ΔqueryRefresh, ΔviewQuery} from '../../src/render3/query';
|
import {query, ΔcontentQuery, ΔloadContentQuery, ΔloadViewQuery, ΔqueryRefresh, ΔviewQuery} from '../../src/render3/query';
|
||||||
import {getLView} from '../../src/render3/state';
|
import {getLView} from '../../src/render3/state';
|
||||||
import {getNativeByIndex} from '../../src/render3/util/view_utils';
|
import {getNativeByIndex} from '../../src/render3/util/view_utils';
|
||||||
import {ΔtemplateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
import {ΔtemplateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
||||||
|
|
||||||
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
|
|
||||||
import {ComponentFixture, TemplateFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent} from './render_util';
|
import {ComponentFixture, TemplateFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent} from './render_util';
|
||||||
|
|
||||||
|
|
||||||
@ -1346,412 +1344,6 @@ describe('query', () => {
|
|||||||
|
|
||||||
describe('view boundaries', () => {
|
describe('view boundaries', () => {
|
||||||
|
|
||||||
describe('ViewContainerRef', () => {
|
|
||||||
|
|
||||||
let directiveInstances: ViewContainerManipulatorDirective[] = [];
|
|
||||||
|
|
||||||
class ViewContainerManipulatorDirective {
|
|
||||||
static ngDirectiveDef = ΔdefineDirective({
|
|
||||||
type: ViewContainerManipulatorDirective,
|
|
||||||
selectors: [['', 'vc', '']],
|
|
||||||
factory: () => {
|
|
||||||
const directiveInstance =
|
|
||||||
new ViewContainerManipulatorDirective(ΔdirectiveInject(ViewContainerRef as any));
|
|
||||||
directiveInstances.push(directiveInstance);
|
|
||||||
return directiveInstance;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor(private _vcRef: ViewContainerRef) {}
|
|
||||||
|
|
||||||
insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number) {
|
|
||||||
this._vcRef.createEmbeddedView(tpl, ctx, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(index?: number) { this._vcRef.remove(index); }
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => { directiveInstances = []; });
|
|
||||||
|
|
||||||
it('should report results in views inserted / removed by ngIf', () => {
|
|
||||||
|
|
||||||
function Cmpt_Template_1(rf: RenderFlags, ctx1: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <ng-template [ngIf]="value">
|
|
||||||
* <div #foo></div>
|
|
||||||
* </ng-template>
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') query;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(
|
|
||||||
0, Cmpt_Template_1, 2, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'ngIf', Δbind(ctx.value));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
2, 1, [NgIf], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Cmpt);
|
|
||||||
const qList = fixture.component.query;
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
fixture.component.value = true;
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
|
|
||||||
fixture.component.value = false;
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report results in views inserted / removed by ngFor', () => {
|
|
||||||
|
|
||||||
function Cmpt_Template_1(rf1: RenderFlags, row: NgForOfContext<string>) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'id', Δbind(row.$implicit));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <ng-template ngFor let-item [ngForOf]="value">
|
|
||||||
* <div #foo [id]="item"></div>
|
|
||||||
* </ng-template>
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') query;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
class Cmpt {
|
|
||||||
// TODO(issue/24571): remove '!'.
|
|
||||||
value !: string[];
|
|
||||||
query: any;
|
|
||||||
static ngComponentDef = ΔdefineComponent({
|
|
||||||
type: Cmpt,
|
|
||||||
factory: () => new Cmpt(),
|
|
||||||
selectors: [['my-app']],
|
|
||||||
consts: 2,
|
|
||||||
vars: 1,
|
|
||||||
template: function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(0, Cmpt_Template_1, 2, 1, 'ng-template', ['ngForOf', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'ngForOf', Δbind(ctx.value));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
viewQuery: function(rf: RenderFlags, ctx: Cmpt) {
|
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: () => [NgForOf]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Cmpt);
|
|
||||||
const qList = fixture.component.query;
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
fixture.component.value = ['a', 'b', 'c'];
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(3);
|
|
||||||
|
|
||||||
fixture.component.value.splice(1, 1); // remove "b"
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
|
|
||||||
// make sure that a proper element was removed from query results
|
|
||||||
expect(qList.first.nativeElement.id).toBe('a');
|
|
||||||
expect(qList.last.nativeElement.id).toBe('c');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-rrmmuf?file=src/app/app.component.ts
|
|
||||||
it('should report results when different instances of TemplateRef are inserted into one ViewContainerRefs',
|
|
||||||
() => {
|
|
||||||
let tpl1: TemplateRef<{}>;
|
|
||||||
let tpl2: TemplateRef<{}>;
|
|
||||||
|
|
||||||
function Cmpt_Template_1(rf: RenderFlags, ctx: {idx: number}) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'id', Δbind('foo1_' + ctx.idx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Cmpt_Template_5(rf: RenderFlags, ctx: {idx: number}) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'id', Δbind('foo2_' + ctx.idx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <ng-template #tpl1 let-idx="idx">
|
|
||||||
* <div #foo [id]="'foo1_'+idx"></div>
|
|
||||||
* </ng-template>
|
|
||||||
*
|
|
||||||
* <div #foo id="middle"></div>
|
|
||||||
*
|
|
||||||
* <ng-template #tpl2 let-idx="idx">
|
|
||||||
* <div #foo [id]="'foo2_'+idx"></div>
|
|
||||||
* </ng-template>
|
|
||||||
*
|
|
||||||
* <ng-template viewInserter #vi="vi"></ng-template>
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(
|
|
||||||
0, Cmpt_Template_1, 2, 1, 'ng-template', null, ['tpl1', ''],
|
|
||||||
ΔtemplateRefExtractor);
|
|
||||||
Δelement(2, 'div', ['id', 'middle'], ['foo', '']);
|
|
||||||
Δtemplate(
|
|
||||||
4, Cmpt_Template_5, 2, 1, 'ng-template', null, ['tpl2', ''],
|
|
||||||
ΔtemplateRefExtractor);
|
|
||||||
Δtemplate(6, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'vc']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
tpl1 = Δreference(1);
|
|
||||||
tpl2 = Δreference(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
8, 0, [ViewContainerManipulatorDirective], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Cmpt);
|
|
||||||
const qList = fixture.component.query;
|
|
||||||
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
expect(qList.first.nativeElement.getAttribute('id')).toBe('middle');
|
|
||||||
|
|
||||||
directiveInstances[0].insertTpl(tpl1 !, {idx: 0}, 0);
|
|
||||||
directiveInstances[0].insertTpl(tpl2 !, {idx: 1}, 1);
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(3);
|
|
||||||
let qListArr = qList.toArray();
|
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
|
||||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
|
||||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
|
||||||
|
|
||||||
directiveInstances[0].insertTpl(tpl1 !, {idx: 1}, 1);
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(4);
|
|
||||||
qListArr = qList.toArray();
|
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
|
||||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo1_1');
|
|
||||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('middle');
|
|
||||||
expect(qListArr[3].nativeElement.getAttribute('id')).toBe('foo2_1');
|
|
||||||
|
|
||||||
directiveInstances[0].remove(1);
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(3);
|
|
||||||
qListArr = qList.toArray();
|
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
|
||||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
|
||||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
|
||||||
|
|
||||||
directiveInstances[0].remove(1);
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
qListArr = qList.toArray();
|
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
|
||||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts
|
|
||||||
// https://stackblitz.com/edit/angular-xzwp6n
|
|
||||||
it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
|
|
||||||
() => {
|
|
||||||
let tpl: TemplateRef<{}>;
|
|
||||||
|
|
||||||
function Cmpt_Template_1(rf: RenderFlags, ctx: {idx: number, container_idx: number}) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(0, 'id', Δbind('foo_' + ctx.container_idx + '_' + ctx.idx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <ng-template #tpl let-idx="idx" let-container_idx="container_idx">
|
|
||||||
* <div #foo [id]="'foo_'+container_idx+'_'+idx"></div>
|
|
||||||
* </ng-template>
|
|
||||||
*
|
|
||||||
* <ng-template viewInserter #vi1="vi"></ng-template>
|
|
||||||
* <ng-template viewInserter #vi2="vi"></ng-template>
|
|
||||||
*/
|
|
||||||
class Cmpt {
|
|
||||||
query: any;
|
|
||||||
static ngComponentDef = ΔdefineComponent({
|
|
||||||
type: Cmpt,
|
|
||||||
factory: () => new Cmpt(),
|
|
||||||
selectors: [['my-app']],
|
|
||||||
consts: 4,
|
|
||||||
vars: 0,
|
|
||||||
template: function(rf: RenderFlags, ctx: any) {
|
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(
|
|
||||||
0, Cmpt_Template_1, 2, 1, 'ng-template', [], ['tpl', ''],
|
|
||||||
ΔtemplateRefExtractor);
|
|
||||||
Δtemplate(2, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'vc']);
|
|
||||||
Δtemplate(3, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'vc']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
tpl = Δreference(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
viewQuery: (rf: RenderFlags, cmpt: Cmpt) => {
|
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(cmpt.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: () => [ViewContainerManipulatorDirective],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const fixture = new ComponentFixture(Cmpt);
|
|
||||||
const qList = fixture.component.query;
|
|
||||||
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
directiveInstances[0].insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
|
|
||||||
directiveInstances[1].insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
let qListArr = qList.toArray();
|
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
|
||||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_0_0');
|
|
||||||
|
|
||||||
directiveInstances[0].remove();
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
qListArr = qList.toArray();
|
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
|
||||||
|
|
||||||
directiveInstances[1].remove();
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-wpd6gv?file=src%2Fapp%2Fapp.component.ts
|
|
||||||
it('should report results from views inserted in a lifecycle hook', () => {
|
|
||||||
|
|
||||||
function MyApp_Template_1(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'span', ['id', 'from_tpl'], ['foo', '']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyApp {
|
|
||||||
show = false;
|
|
||||||
query: any;
|
|
||||||
static ngComponentDef = ΔdefineComponent({
|
|
||||||
type: MyApp,
|
|
||||||
factory: () => new MyApp(),
|
|
||||||
selectors: [['my-app']],
|
|
||||||
consts: 4,
|
|
||||||
vars: 1,
|
|
||||||
/**
|
|
||||||
* <ng-template #tpl><span #foo id="from_tpl"></span></ng-template>
|
|
||||||
* <ng-template [ngTemplateOutlet]="show ? tpl : null"></ng-template>
|
|
||||||
*/
|
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δtemplate(
|
|
||||||
0, MyApp_Template_1, 2, 0, 'ng-template', undefined, ['tpl', ''],
|
|
||||||
ΔtemplateRefExtractor);
|
|
||||||
Δtemplate(
|
|
||||||
2, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'ngTemplateOutlet']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
const tplRef = Δreference(1);
|
|
||||||
ΔelementProperty(2, 'ngTemplateOutlet', Δbind(myApp.show ? tplRef : null));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: () => [NgTemplateOutlet],
|
|
||||||
viewQuery: (rf: RenderFlags, myApp: MyApp) => {
|
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(myApp.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(MyApp);
|
|
||||||
const qList = fixture.component.query;
|
|
||||||
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
fixture.component.show = true;
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
expect(qList.first.nativeElement.id).toBe('from_tpl');
|
|
||||||
|
|
||||||
fixture.component.show = false;
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('JS blocks', () => {
|
describe('JS blocks', () => {
|
||||||
|
|
||||||
it('should report results in embedded views', () => {
|
it('should report results in embedded views', () => {
|
||||||
@ -2233,12 +1825,8 @@ describe('query', () => {
|
|||||||
|
|
||||||
describe('content', () => {
|
describe('content', () => {
|
||||||
let withContentInstance: WithContentDirective|null;
|
let withContentInstance: WithContentDirective|null;
|
||||||
let shallowCompInstance: ShallowComp|null;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => { withContentInstance = null; });
|
||||||
withContentInstance = null;
|
|
||||||
shallowCompInstance = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
class WithContentDirective {
|
class WithContentDirective {
|
||||||
// @ContentChildren('foo')
|
// @ContentChildren('foo')
|
||||||
@ -2268,29 +1856,6 @@ describe('query', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShallowComp {
|
|
||||||
// @ContentChildren('foo', {descendants: false})
|
|
||||||
foos !: QueryList<ElementRef>;
|
|
||||||
|
|
||||||
static ngComponentDef = ΔdefineComponent({
|
|
||||||
type: ShallowComp,
|
|
||||||
selectors: [['shallow-comp']],
|
|
||||||
factory: () => shallowCompInstance = new ShallowComp(),
|
|
||||||
template: function(rf: RenderFlags, ctx: any) {},
|
|
||||||
consts: 0,
|
|
||||||
vars: 0,
|
|
||||||
contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔcontentQuery(dirIndex, ['foo'], false, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ΔqueryRefresh(tmp = ΔloadContentQuery<ElementRef>()) && (ctx.foos = tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should support content queries for directives', () => {
|
it('should support content queries for directives', () => {
|
||||||
/**
|
/**
|
||||||
* <div with-content>
|
* <div with-content>
|
||||||
@ -2381,43 +1946,6 @@ describe('query', () => {
|
|||||||
.toBe(0, `Expected content query not to match <div with-content #foo>.`);
|
.toBe(0, `Expected content query not to match <div with-content #foo>.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match shallow content queries in views inserted / removed by ngIf', () => {
|
|
||||||
function IfTemplate(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
Δelement(0, 'div', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <shallow-comp>
|
|
||||||
* <div *ngIf="showing" #foo></div>
|
|
||||||
* </shallow-comp>
|
|
||||||
*/
|
|
||||||
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ΔelementStart(0, 'shallow-comp');
|
|
||||||
{ Δtemplate(1, IfTemplate, 2, 0, 'div', [AttributeMarker.Template, 'ngIf', '']); }
|
|
||||||
ΔelementEnd();
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ΔelementProperty(1, 'ngIf', Δbind(ctx.showing));
|
|
||||||
}
|
|
||||||
}, 2, 1, [ShallowComp, NgIf]);
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(AppComponent);
|
|
||||||
const qList = shallowCompInstance !.foos;
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
fixture.component.showing = true;
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
|
|
||||||
fixture.component.showing = false;
|
|
||||||
fixture.update();
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
|
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
|
||||||
it('should support view and content queries matching the same element', () => {
|
it('should support view and content queries matching the same element', () => {
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user