diff --git a/packages/core/test/acceptance/query_spec.ts b/packages/core/test/acceptance/query_spec.ts index fb499187d8..323b5604c9 100644 --- a/packages/core/test/acceptance/query_spec.ts +++ b/packages/core/test/acceptance/query_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, forwardRef} from '@angular/core'; +import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef, forwardRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -749,11 +749,13 @@ describe('query logic', () => { class ViewContainerManipulatorDirective { constructor(private _vcRef: ViewContainerRef) {} - insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number) { - this._vcRef.createEmbeddedView(tpl, ctx, idx); + insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number): ViewRef { + return this._vcRef.createEmbeddedView(tpl, ctx, idx); } remove(index?: number) { this._vcRef.remove(index); } + + move(viewRef: ViewRef, index: number) { this._vcRef.move(viewRef, index); } } it('should report results in views inserted / removed by ngIf', () => { @@ -822,6 +824,59 @@ describe('query logic', () => { expect(queryList.last.nativeElement.id).toBe('c'); }); + /** + * ViewContainerRef API allows "moving" a view to the same (previous) index. Such operation + * has no observable effect on the rendered UI (displays stays the same) but internally we've + * got 2 implementation choices when it comes to "moving" a view: + * - systematically detach and insert a view - this would result in unnecessary processing + * when the previous and new indexes for the move operation are the same; + * - detect the situation where the indexes are the same and do no processing in such case. + * + * This tests asserts on the implementation choices done by the VE (detach and insert) so we + * can replicate the same behaviour in ivy. + */ + it('should notify on changes when a given view is removed and re-inserted at the same index', + () => { + + @Component({ + selector: 'test-comp', + template: ` +
match
+ + `, + }) + class TestComponent implements AfterViewInit { + queryListNotificationCounter = 0; + + @ViewChild(ViewContainerManipulatorDirective) + vc !: ViewContainerManipulatorDirective; + @ViewChild('tpl') tpl !: TemplateRef; + @ViewChildren('foo') query !: QueryList; + + ngAfterViewInit() { + this.query.changes.subscribe(() => this.queryListNotificationCounter++); + } + } + + TestBed.configureTestingModule( + {declarations: [ViewContainerManipulatorDirective, TestComponent]}); + const fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + + const queryList = fixture.componentInstance.query; + const {tpl, vc} = fixture.componentInstance; + + const viewRef = vc.insertTpl(tpl, {}, 0); + fixture.detectChanges(); + expect(queryList.length).toBe(1); + expect(fixture.componentInstance.queryListNotificationCounter).toBe(1); + + vc.move(viewRef, 0); + fixture.detectChanges(); + expect(queryList.length).toBe(1); + expect(fixture.componentInstance.queryListNotificationCounter).toBe(2); + }); + it('should support a mix of content queries from the declaration and embedded view', () => { @Directive({selector: '[query-for-lots-of-content]'}) class QueryForLotsOfContent {