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
|
||||
*/
|
||||
|
||||
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 {By} from '@angular/platform-browser';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
|
@ -601,6 +603,44 @@ describe('query logic', () => {
|
|||
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
|
||||
|
@ -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) {
|
||||
|
|
|
@ -6,19 +6,17 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter} from '../..';
|
||||
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 {query, ΔcontentQuery, ΔloadContentQuery, ΔloadViewQuery, ΔqueryRefresh, ΔviewQuery} from '../../src/render3/query';
|
||||
import {getLView} from '../../src/render3/state';
|
||||
import {getNativeByIndex} from '../../src/render3/util/view_utils';
|
||||
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';
|
||||
|
||||
|
||||
|
@ -1346,412 +1344,6 @@ describe('query', () => {
|
|||
|
||||
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', () => {
|
||||
|
||||
it('should report results in embedded views', () => {
|
||||
|
@ -2233,12 +1825,8 @@ describe('query', () => {
|
|||
|
||||
describe('content', () => {
|
||||
let withContentInstance: WithContentDirective|null;
|
||||
let shallowCompInstance: ShallowComp|null;
|
||||
|
||||
beforeEach(() => {
|
||||
withContentInstance = null;
|
||||
shallowCompInstance = null;
|
||||
});
|
||||
beforeEach(() => { withContentInstance = null; });
|
||||
|
||||
class WithContentDirective {
|
||||
// @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', () => {
|
||||
/**
|
||||
* <div with-content>
|
||||
|
@ -2381,43 +1946,6 @@ describe('query', () => {
|
|||
.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
|
||||
it('should support view and content queries matching the same element', () => {
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue