/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import {CommonModule} from '@angular/common';
import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, forwardRef, InjectionToken, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef} 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';
describe('query logic', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComp,
        QueryComp,
        SimpleCompA,
        SimpleCompB,
        StaticViewQueryComp,
        TextDirective,
        SubclassStaticViewQueryComp,
        StaticContentQueryComp,
        SubclassStaticContentQueryComp,
        QueryCompWithChanges,
        StaticContentQueryDir,
        SuperDirectiveQueryTarget,
        SuperDirective,
        SubComponent,
        TestComponentWithToken,
        TestInjectionTokenContentQueries,
        TestInjectionTokenQueries,
      ]
    });
  });
  describe('view queries', () => {
    it('should return Component instances when Components are labeled and retrieved', () => {
      const template = `
           
           
         `;
      const fixture = initWithTemplate(QueryComp, template);
      const comp = fixture.componentInstance;
      expect(comp.viewChild).toBeAnInstanceOf(SimpleCompA);
      expect(comp.viewChildren.first).toBeAnInstanceOf(SimpleCompA);
      expect(comp.viewChildren.last).toBeAnInstanceOf(SimpleCompB);
    });
    it('should return ElementRef when HTML element is labeled and retrieved', () => {
      const template = `
      
    `;
      const fixture = initWithTemplate(QueryComp, template);
      const comp = fixture.componentInstance;
      expect(comp.viewChild).toBeAnInstanceOf(ElementRef);
      expect(comp.viewChildren.first).toBeAnInstanceOf(ElementRef);
    });
    onlyInIvy('multiple local refs are supported in Ivy')
        .it('should return ElementRefs when HTML elements are labeled and retrieved', () => {
          const template = `
              A
              B
            `;
          const fixture = initWithTemplate(QueryComp, template);
          const comp = fixture.componentInstance;
          expect(comp.viewChild).toBeAnInstanceOf(ElementRef);
          expect(comp.viewChild.nativeElement).toBe(fixture.debugElement.children[0].nativeElement);
          expect(comp.viewChildren.first).toBeAnInstanceOf(ElementRef);
          expect(comp.viewChildren.last).toBeAnInstanceOf(ElementRef);
          expect(comp.viewChildren.length).toBe(2);
        });
    it('should return TemplateRef when template is labeled and retrieved', () => {
      const template = `
      
    `;
      const fixture = initWithTemplate(QueryComp, template);
      const comp = fixture.componentInstance;
      expect(comp.viewChildren.first).toBeAnInstanceOf(TemplateRef);
    });
    it('should support selecting InjectionToken', () => {
      const fixture = TestBed.createComponent(TestInjectionTokenQueries);
      const instance = fixture.componentInstance;
      fixture.detectChanges();
      expect(instance.viewFirstOption).toBeDefined();
      expect(instance.viewFirstOption instanceof TestComponentWithToken).toBe(true);
      expect(instance.viewOptions).toBeDefined();
      expect(instance.viewOptions.length).toBe(2);
      expect(instance.contentFirstOption).toBeUndefined();
      expect(instance.contentOptions).toBeDefined();
      expect(instance.contentOptions.length).toBe(0);
    });
    onlyInIvy('multiple local refs are supported in Ivy')
        .it('should return TemplateRefs when templates are labeled and retrieved', () => {
          const template = `
              
              
            `;
          const fixture = initWithTemplate(QueryComp, template);
          const comp = fixture.componentInstance;
          expect(comp.viewChild).toBeAnInstanceOf(TemplateRef);
          expect(comp.viewChild.elementRef.nativeElement)
              .toBe(fixture.debugElement.childNodes[0].nativeNode);
          expect(comp.viewChildren.first).toBeAnInstanceOf(TemplateRef);
          expect(comp.viewChildren.last).toBeAnInstanceOf(TemplateRef);
          expect(comp.viewChildren.length).toBe(2);
        });
    it('should set static view child queries in creation mode (and just in creation mode)', () => {
      const fixture = TestBed.createComponent(StaticViewQueryComp);
      const component = fixture.componentInstance;
      // static ViewChild query should be set in creation mode, before CD runs
      expect(component.textDir).toBeAnInstanceOf(TextDirective);
      expect(component.textDir.text).toEqual('');
      expect(component.setEvents).toEqual(['textDir set']);
      // dynamic ViewChild query should not have been resolved yet
      expect(component.foo).not.toBeDefined();
      const span = fixture.nativeElement.querySelector('span');
      fixture.detectChanges();
      expect(component.textDir.text).toEqual('some text');
      expect(component.foo.nativeElement).toBe(span);
      expect(component.setEvents).toEqual(['textDir set', 'foo set']);
    });
    it('should support static view child queries inherited from superclasses', () => {
      const fixture = TestBed.createComponent(SubclassStaticViewQueryComp);
      const component = fixture.componentInstance;
      const divs = fixture.nativeElement.querySelectorAll('div');
      const spans = fixture.nativeElement.querySelectorAll('span');
      // static ViewChild queries should be set in creation mode, before CD runs
      expect(component.textDir).toBeAnInstanceOf(TextDirective);
      expect(component.textDir.text).toEqual('');
      expect(component.bar.nativeElement).toEqual(divs[1]);
      // dynamic ViewChild queries should not have been resolved yet
      expect(component.foo).not.toBeDefined();
      expect(component.baz).not.toBeDefined();
      fixture.detectChanges();
      expect(component.textDir.text).toEqual('some text');
      expect(component.foo.nativeElement).toBe(spans[0]);
      expect(component.baz.nativeElement).toBe(spans[1]);
    });
    it('should support multiple static view queries (multiple template passes)', () => {
      const template = `
           
           
         `;
      TestBed.overrideComponent(AppComp, {set: new Component({template})});
      const fixture = TestBed.createComponent(AppComp);
      const firstComponent = fixture.debugElement.children[0].injector.get(StaticViewQueryComp);
      const secondComponent = fixture.debugElement.children[1].injector.get(StaticViewQueryComp);
      // static ViewChild query should be set in creation mode, before CD runs
      expect(firstComponent.textDir).toBeAnInstanceOf(TextDirective);
      expect(secondComponent.textDir).toBeAnInstanceOf(TextDirective);
      expect(firstComponent.textDir.text).toEqual('');
      expect(secondComponent.textDir.text).toEqual('');
      expect(firstComponent.setEvents).toEqual(['textDir set']);
      expect(secondComponent.setEvents).toEqual(['textDir set']);
      // dynamic ViewChild query should not have been resolved yet
      expect(firstComponent.foo).not.toBeDefined();
      expect(secondComponent.foo).not.toBeDefined();
      const spans = fixture.nativeElement.querySelectorAll('span');
      fixture.detectChanges();
      expect(firstComponent.textDir.text).toEqual('some text');
      expect(secondComponent.textDir.text).toEqual('some text');
      expect(firstComponent.foo.nativeElement).toBe(spans[0]);
      expect(secondComponent.foo.nativeElement).toBe(spans[1]);
      expect(firstComponent.setEvents).toEqual(['textDir set', 'foo set']);
      expect(secondComponent.setEvents).toEqual(['textDir set', 'foo set']);
    });
    it('should allow for view queries to be inherited from a directive', () => {
      const fixture = TestBed.createComponent(SubComponent);
      const comp = fixture.componentInstance;
      fixture.detectChanges();
      expect(comp.headers).toBeTruthy();
      expect(comp.headers.length).toBe(2);
      expect(comp.headers.toArray().every(result => result instanceof SuperDirectiveQueryTarget))
          .toBe(true);
    });
    it('should support ViewChild query inherited from undecorated superclasses', () => {
      class MyComp {
        @ViewChild('foo') foo: any;
      }
      @Component({selector: 'sub-comp', template: ''})
      class SubComp extends MyComp {
      }
      TestBed.configureTestingModule({declarations: [SubComp]});
      const fixture = TestBed.createComponent(SubComp);
      fixture.detectChanges();
      expect(fixture.componentInstance.foo).toBeAnInstanceOf(ElementRef);
    });
    it('should support ViewChild query inherited from undecorated grand superclasses', () => {
      class MySuperComp {
        @ViewChild('foo') foo: any;
      }
      class MyComp extends MySuperComp {}
      @Component({selector: 'sub-comp', template: ''})
      class SubComp extends MyComp {
      }
      TestBed.configureTestingModule({declarations: [SubComp]});
      const fixture = TestBed.createComponent(SubComp);
      fixture.detectChanges();
      expect(fixture.componentInstance.foo).toBeAnInstanceOf(ElementRef);
    });
    it('should support ViewChildren query inherited from undecorated superclasses', () => {
      @Directive({selector: '[some-dir]'})
      class SomeDir {
      }
      class MyComp {
        @ViewChildren(SomeDir) foo!: QueryList;
      }
      @Component({
        selector: 'sub-comp',
        template: `
          
          
        `
      })
      class SubComp extends MyComp {
      }
      TestBed.configureTestingModule({declarations: [SubComp, SomeDir]});
      const fixture = TestBed.createComponent(SubComp);
      fixture.detectChanges();
      expect(fixture.componentInstance.foo).toBeAnInstanceOf(QueryList);
      expect(fixture.componentInstance.foo.length).toBe(2);
    });
    it('should support ViewChildren query inherited from undecorated grand superclasses', () => {
      @Directive({selector: '[some-dir]'})
      class SomeDir {
      }
      class MySuperComp {
        @ViewChildren(SomeDir) foo!: QueryList;
      }
      class MyComp extends MySuperComp {}
      @Component({
        selector: 'sub-comp',
        template: `
          
          
        `
      })
      class SubComp extends MyComp {
      }
      TestBed.configureTestingModule({declarations: [SubComp, SomeDir]});
      const fixture = TestBed.createComponent(SubComp);
      fixture.detectChanges();
      expect(fixture.componentInstance.foo).toBeAnInstanceOf(QueryList);
      expect(fixture.componentInstance.foo.length).toBe(2);
    });
    it('should support ViewChild query where template is inserted in child component', () => {
      @Component({selector: 'required', template: ''})
      class Required {
      }
      @Component({
        selector: 'insertion',
        template: ``
      })
      class Insertion {
        @Input() content!: TemplateRef<{}>;
      }
      @Component({
        template: `
          
            
          
          
          `
      })
      class App {
        @ViewChild(Required) requiredEl!: Required;
        viewChildAvailableInAfterViewInit?: boolean;
        ngAfterViewInit() {
          this.viewChildAvailableInAfterViewInit = this.requiredEl !== undefined;
        }
      }
      const fixture = TestBed.configureTestingModule({declarations: [App, Insertion, Required]})
                          .createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.viewChildAvailableInAfterViewInit).toBe(true);
    });
    it('should destroy QueryList when the containing view is destroyed', () => {
      let queryInstance: QueryList;
      @Component({
        selector: 'comp-with-view-query',
        template: 'Content
',
      })
      class ComponentWithViewQuery {
        @ViewChildren('foo')
        set foo(value: any) {
          queryInstance = value;
        }
        get foo() {
          return queryInstance;
        }
      }
      @Component({
        selector: 'root',
        template: `
          
            
          
        `
      })
      class Root {
        condition = true;
      }
      TestBed.configureTestingModule({
        declarations: [Root, ComponentWithViewQuery],
        imports: [CommonModule],
      });
      const fixture = TestBed.createComponent(Root);
      fixture.detectChanges();
      expect((queryInstance!.changes as EventEmitter).closed).toBeFalsy();
      fixture.componentInstance.condition = false;
      fixture.detectChanges();
      expect((queryInstance!.changes as EventEmitter).closed).toBeTruthy();
    });
  });
  describe('content queries', () => {
    it('should return Component instance when Component is labeled and retrieved', () => {
      const template = `
           
             
           
         `;
      const fixture = initWithTemplate(AppComp, template);
      const comp = fixture.debugElement.children[0].references['q'];
      expect(comp.contentChild).toBeAnInstanceOf(SimpleCompA);
      expect(comp.contentChildren.first).toBeAnInstanceOf(SimpleCompA);
    });
    it('should support selecting InjectionToken', () => {
      const fixture = TestBed.createComponent(TestInjectionTokenContentQueries);
      const instance =
          fixture.debugElement.query(By.directive(TestInjectionTokenQueries)).componentInstance;
      fixture.detectChanges();
      expect(instance.contentFirstOption).toBeDefined();
      expect(instance.contentFirstOption instanceof TestComponentWithToken).toBe(true);
      expect(instance.contentOptions).toBeDefined();
      expect(instance.contentOptions.length).toBe(2);
    });
    onlyInIvy('multiple local refs are supported in Ivy')
        .it('should return Component instances when Components are labeled and retrieved', () => {
          const template = `
                
                  
                  
                
              `;
          const fixture = initWithTemplate(AppComp, template);
          const comp = fixture.debugElement.children[0].references['q'];
          expect(comp.contentChild).toBeAnInstanceOf(SimpleCompA);
          expect(comp.contentChildren.first).toBeAnInstanceOf(SimpleCompA);
          expect(comp.contentChildren.last).toBeAnInstanceOf(SimpleCompB);
          expect(comp.contentChildren.length).toBe(2);
        });
    it('should return ElementRef when HTML element is labeled and retrieved', () => {
      const template = `
         
           
         
       `;
      const fixture = initWithTemplate(AppComp, template);
      const comp = fixture.debugElement.children[0].references['q'];
      expect(comp.contentChildren.first).toBeAnInstanceOf(ElementRef);
    });
    onlyInIvy('multiple local refs are supported in Ivy')
        .it('should return ElementRefs when HTML elements are labeled and retrieved', () => {
          const template = `
              
                
                
              
            `;
          const fixture = initWithTemplate(AppComp, template);
          const firstChild = fixture.debugElement.children[0];
          const comp = firstChild.references['q'];
          expect(comp.contentChild).toBeAnInstanceOf(ElementRef);
          expect(comp.contentChild.nativeElement).toBe(firstChild.children[0].nativeElement);
          expect(comp.contentChildren.first).toBeAnInstanceOf(ElementRef);
          expect(comp.contentChildren.last).toBeAnInstanceOf(ElementRef);
          expect(comp.contentChildren.length).toBe(2);
        });
    it('should return TemplateRef when template is labeled and retrieved', () => {
      const template = `
       
         
       
     `;
      const fixture = initWithTemplate(AppComp, template);
      const comp = fixture.debugElement.children[0].references['q'];
      expect(comp.contentChildren.first).toBeAnInstanceOf(TemplateRef);
    });
    onlyInIvy('multiple local refs are supported in Ivy')
        .it('should return TemplateRefs when templates are labeled and retrieved', () => {
          const template = `
              
                
                
              
            `;
          const fixture = initWithTemplate(AppComp, template);
          const firstChild = fixture.debugElement.children[0];
          const comp = firstChild.references['q'];
          expect(comp.contentChild).toBeAnInstanceOf(TemplateRef);
          expect(comp.contentChild.elementRef.nativeElement)
              .toBe(firstChild.childNodes[0].nativeNode);
          expect(comp.contentChildren.first).toBeAnInstanceOf(TemplateRef);
          expect(comp.contentChildren.last).toBeAnInstanceOf(TemplateRef);
          expect(comp.contentChildren.length).toBe(2);
        });
    it('should set static content child queries in creation mode (and just in creation mode)',
       () => {
         const template = `
              
                  
                  
              
            `;
         TestBed.overrideComponent(AppComp, {set: new Component({template})});
         const fixture = TestBed.createComponent(AppComp);
         const component = fixture.debugElement.children[0].injector.get(StaticContentQueryComp);
         // static ContentChild query should be set in creation mode, before CD runs
         expect(component.textDir).toBeAnInstanceOf(TextDirective);
         expect(component.textDir.text).toEqual('');
         expect(component.setEvents).toEqual(['textDir set']);
         // dynamic ContentChild query should not have been resolved yet
         expect(component.foo).not.toBeDefined();
         const span = fixture.nativeElement.querySelector('span');
         (fixture.componentInstance as any).text = 'some text';
         fixture.detectChanges();
         expect(component.textDir.text).toEqual('some text');
         expect(component.foo.nativeElement).toBe(span);
         expect(component.setEvents).toEqual(['textDir set', 'foo set']);
       });
    it('should support static content child queries inherited from superclasses', () => {
      const template = `
              
                  
                  
                  
                  
              
            `;
      TestBed.overrideComponent(AppComp, {set: new Component({template})});
      const fixture = TestBed.createComponent(AppComp);
      const component =
          fixture.debugElement.children[0].injector.get(SubclassStaticContentQueryComp);
      const divs = fixture.nativeElement.querySelectorAll('div');
      const spans = fixture.nativeElement.querySelectorAll('span');
      // static ContentChild queries should be set in creation mode, before CD runs
      expect(component.textDir).toBeAnInstanceOf(TextDirective);
      expect(component.textDir.text).toEqual('');
      expect(component.bar.nativeElement).toEqual(divs[1]);
      // dynamic ContentChild queries should not have been resolved yet
      expect(component.foo).not.toBeDefined();
      expect(component.baz).not.toBeDefined();
      (fixture.componentInstance as any).text = 'some text';
      fixture.detectChanges();
      expect(component.textDir.text).toEqual('some text');
      expect(component.foo.nativeElement).toBe(spans[0]);
      expect(component.baz.nativeElement).toBe(spans[1]);
    });
    it('should set static content child queries on directives', () => {
      const template = `
              
            `;
      TestBed.overrideComponent(AppComp, {set: new Component({template})});
      const fixture = TestBed.createComponent(AppComp);
      const component = fixture.debugElement.children[0].injector.get(StaticContentQueryDir);
      // static ContentChild query should be set in creation mode, before CD runs
      expect(component.textDir).toBeAnInstanceOf(TextDirective);
      expect(component.textDir.text).toEqual('');
      expect(component.setEvents).toEqual(['textDir set']);
      // dynamic ContentChild query should not have been resolved yet
      expect(component.foo).not.toBeDefined();
      const span = fixture.nativeElement.querySelector('span');
      (fixture.componentInstance as any).text = 'some text';
      fixture.detectChanges();
      expect(component.textDir.text).toEqual('some text');
      expect(component.foo.nativeElement).toBe(span);
      expect(component.setEvents).toEqual(['textDir set', 'foo set']);
    });
    it('should support multiple content query components (multiple template passes)', () => {
      const template = `
              
                  
                  
              
              
                  
                  
              
            `;
      TestBed.overrideComponent(AppComp, {set: new Component({template})});
      const fixture = TestBed.createComponent(AppComp);
      const firstComponent = fixture.debugElement.children[0].injector.get(StaticContentQueryComp);
      const secondComponent = fixture.debugElement.children[1].injector.get(StaticContentQueryComp);
      // static ContentChild query should be set in creation mode, before CD runs
      expect(firstComponent.textDir).toBeAnInstanceOf(TextDirective);
      expect(secondComponent.textDir).toBeAnInstanceOf(TextDirective);
      expect(firstComponent.textDir.text).toEqual('');
      expect(secondComponent.textDir.text).toEqual('');
      expect(firstComponent.setEvents).toEqual(['textDir set']);
      expect(secondComponent.setEvents).toEqual(['textDir set']);
      // dynamic ContentChild query should not have been resolved yet
      expect(firstComponent.foo).not.toBeDefined();
      expect(secondComponent.foo).not.toBeDefined();
      const spans = fixture.nativeElement.querySelectorAll('span');
      (fixture.componentInstance as any).text = 'some text';
      fixture.detectChanges();
      expect(firstComponent.textDir.text).toEqual('some text');
      expect(secondComponent.textDir.text).toEqual('some text');
      expect(firstComponent.foo.nativeElement).toBe(spans[0]);
      expect(secondComponent.foo.nativeElement).toBe(spans[1]);
      expect(firstComponent.setEvents).toEqual(['textDir set', 'foo set']);
      expect(secondComponent.setEvents).toEqual(['textDir set', 'foo set']);
    });
    it('should support ContentChild query inherited from undecorated superclasses', () => {
      class MyComp {
        @ContentChild('foo') foo: any;
      }
      @Component({selector: 'sub-comp', template: ''})
      class SubComp extends MyComp {
      }
      @Component({template: ''})
      class App {
        @ViewChild(SubComp) subComp!: SubComp;
      }
      TestBed.configureTestingModule({declarations: [App, SubComp]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.subComp.foo).toBeAnInstanceOf(ElementRef);
    });
    it('should support ContentChild query inherited from undecorated grand superclasses', () => {
      class MySuperComp {
        @ContentChild('foo') foo: any;
      }
      class MyComp extends MySuperComp {}
      @Component({selector: 'sub-comp', template: ''})
      class SubComp extends MyComp {
      }
      @Component({template: ''})
      class App {
        @ViewChild(SubComp) subComp!: SubComp;
      }
      TestBed.configureTestingModule({declarations: [App, SubComp]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.subComp.foo).toBeAnInstanceOf(ElementRef);
    });
    it('should support ContentChildren query inherited from undecorated superclasses', () => {
      @Directive({selector: '[some-dir]'})
      class SomeDir {
      }
      class MyComp {
        @ContentChildren(SomeDir) foo!: QueryList;
      }
      @Component({selector: 'sub-comp', template: ''})
      class SubComp extends MyComp {
      }
      @Component({
        template: `
        
          
          
        
      `
      })
      class App {
        @ViewChild(SubComp) subComp!: SubComp;
      }
      TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.subComp.foo).toBeAnInstanceOf(QueryList);
      expect(fixture.componentInstance.subComp.foo.length).toBe(2);
    });
    it('should support ContentChildren query inherited from undecorated grand superclasses', () => {
      @Directive({selector: '[some-dir]'})
      class SomeDir {
      }
      class MySuperComp {
        @ContentChildren(SomeDir) foo!: QueryList;
      }
      class MyComp extends MySuperComp {}
      @Component({selector: 'sub-comp', template: ''})
      class SubComp extends MyComp {
      }
      @Component({
        template: `
        
          
          
        
      `
      })
      class App {
        @ViewChild(SubComp) subComp!: SubComp;
      }
      TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.subComp.foo).toBeAnInstanceOf(QueryList);
      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: `
          
            
          
        `
      })
      class TestComponent {
        showing = false;
      }
      @Component({
        selector: 'shallow-comp',
        template: '',
      })
      class ShallowComp {
        @ContentChildren('foo', {descendants: false}) foos!: QueryList;
      }
      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);
    });
    it('should support content queries for directives within repeated embedded views', () => {
      const withContentInstances: DirWithContentQuery[] = [];
      @Directive({
        selector: '[with-content]',
      })
      class DirWithContentQuery {
        constructor() {
          withContentInstances.push(this);
        }
        @ContentChildren('foo', {descendants: false}) foos!: QueryList;
        contentInitQuerySnapshot = 0;
        contentCheckedQuerySnapshot = 0;
        ngAfterContentInit() {
          this.contentInitQuerySnapshot = this.foos ? this.foos.length : 0;
        }
        ngAfterContentChecked() {
          this.contentCheckedQuerySnapshot = this.foos ? this.foos.length : 0;
        }
      }
      @Component({
        selector: 'comp',
        template: `
          
            
              
            
          
        `,
      })
      class Root {
        items = [1, 2, 3];
      }
      TestBed.configureTestingModule({
        declarations: [Root, DirWithContentQuery],
        imports: [CommonModule],
      });
      const fixture = TestBed.createComponent(Root);
      fixture.detectChanges();
      for (let i = 0; i < 3; i++) {
        expect(withContentInstances[i].foos.length)
            .toBe(1, `Expected content query to match .`);
        expect(withContentInstances[i].contentInitQuerySnapshot)
            .toBe(
                1,
                `Expected content query results to be available when ngAfterContentInit was called.`);
        expect(withContentInstances[i].contentCheckedQuerySnapshot)
            .toBe(
                1,
                `Expected content query results to be available when ngAfterContentChecked was called.`);
      }
    });
  });
  // Some root components may have ContentChildren queries if they are also
  // usable as a child component. We should still generate an empty QueryList
  // for these queries when they are at root for backwards compatibility with
  // ViewEngine.
  it('should generate an empty QueryList for root components', () => {
    const fixture = TestBed.createComponent(QueryComp);
    fixture.detectChanges();
    expect(fixture.componentInstance.contentChildren).toBeAnInstanceOf(QueryList);
    expect(fixture.componentInstance.contentChildren.length).toBe(0);
  });
  describe('descendants: false (default)', () => {
    /**
     * A helper function to check if a given object looks like ElementRef. It is used in place of
     * the `instanceof ElementRef` check since ivy returns a type that looks like ElementRef (have
     * the same properties but doesn't pass the instanceof ElementRef test)
     */
    function isElementRefLike(result: any): boolean {
      return result.nativeElement != null;
    }
    it('should match directives on elements that used to be wrapped by a required parent in HTML parser',
       () => {
         @Directive({selector: '[myDef]'})
         class MyDef {
         }
         @Component({selector: 'my-container', template: ``})
         class MyContainer {
           @ContentChildren(MyDef) myDefs!: QueryList;
         }
         @Component(
             {selector: 'test-cmpt', template: `|
`})
         class TestCmpt {
         }
         TestBed.configureTestingModule({declarations: [TestCmpt, MyContainer, MyDef]});
         const fixture = TestBed.createComponent(TestCmpt);
         const cmptWithQuery = fixture.debugElement.children[0].injector.get(MyContainer);
         fixture.detectChanges();
         expect(cmptWithQuery.myDefs.length).toBe(1);
       });
    it('should match elements with local refs inside ', () => {
      @Component({selector: 'needs-target', template: ``})
      class NeedsTarget {
        @ContentChildren('target') targets!: QueryList;
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
            
              |
            
          
        `,
      })
      class TestCmpt {
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget]});
      const fixture = TestBed.createComponent(TestCmpt);
      const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
      fixture.detectChanges();
      expect(cmptWithQuery.targets.length).toBe(1);
      expect(isElementRefLike(cmptWithQuery.targets.first)).toBeTruthy();
    });
    it('should match elements with local refs inside nested ', () => {
      @Component({selector: 'needs-target', template: ``})
      class NeedsTarget {
        @ContentChildren('target') targets!: QueryList;
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
            
              
                
                  |
                
              
            
          
        `,
      })
      class TestCmpt {
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget]});
      const fixture = TestBed.createComponent(TestCmpt);
      const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
      fixture.detectChanges();
      expect(cmptWithQuery.targets.length).toBe(1);
      expect(isElementRefLike(cmptWithQuery.targets.first)).toBeTruthy();
    });
    it('should match directives inside ', () => {
      @Directive({selector: '[targetDir]'})
      class TargetDir {
      }
      @Component({selector: 'needs-target', template: ``})
      class NeedsTarget {
        @ContentChildren(TargetDir) targets!: QueryList;
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
            
              |
            
          
        `,
      })
      class TestCmpt {
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget, TargetDir]});
      const fixture = TestBed.createComponent(TestCmpt);
      const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
      fixture.detectChanges();
      expect(cmptWithQuery.targets.length).toBe(1);
      expect(cmptWithQuery.targets.first).toBeAnInstanceOf(TargetDir);
    });
    it('should match directives inside nested ', () => {
      @Directive({selector: '[targetDir]'})
      class TargetDir {
      }
      @Component({selector: 'needs-target', template: ``})
      class NeedsTarget {
        @ContentChildren(TargetDir) targets!: QueryList;
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
            
              
                
                  |
                
              
            
          
        `,
      })
      class TestCmpt {
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget, TargetDir]});
      const fixture = TestBed.createComponent(TestCmpt);
      const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
      fixture.detectChanges();
      expect(cmptWithQuery.targets.length).toBe(1);
      expect(cmptWithQuery.targets.first).toBeAnInstanceOf(TargetDir);
    });
    it('should cross child ng-container when query is declared on ng-container', () => {
      @Directive({selector: '[targetDir]'})
      class TargetDir {
      }
      @Directive({selector: '[needs-target]'})
      class NeedsTarget {
        @ContentChildren(TargetDir) targets!: QueryList;
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
            
              
                |
              
            
          
        `,
      })
      class TestCmpt {
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget, TargetDir]});
      const fixture = TestBed.createComponent(TestCmpt);
      const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
      fixture.detectChanges();
      expect(cmptWithQuery.targets.length).toBe(1);
      expect(cmptWithQuery.targets.first).toBeAnInstanceOf(TargetDir);
    });
    it('should match nodes when using structural directives (*syntax) on ', () => {
      @Directive({selector: '[targetDir]'})
      class TargetDir {
      }
      @Component({selector: 'needs-target', template: ``})
      class NeedsTarget {
        @ContentChildren(TargetDir) dirTargets!: QueryList;
        @ContentChildren('target') localRefsTargets!: QueryList;
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
            
              
              
            
          
        `,
      })
      class TestCmpt {
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget, TargetDir]});
      const fixture = TestBed.createComponent(TestCmpt);
      const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
      fixture.detectChanges();
      expect(cmptWithQuery.dirTargets.length).toBe(1);
      expect(cmptWithQuery.dirTargets.first).toBeAnInstanceOf(TargetDir);
      expect(cmptWithQuery.localRefsTargets.length).toBe(1);
      expect(isElementRefLike(cmptWithQuery.localRefsTargets.first)).toBeTruthy();
    });
    onlyInIvy(
        'VE uses injectors hierarchy to determine if node matches, ivy uses elements as written in a template')
        .it('should match directives on  when crossing nested ', () => {
          @Directive({selector: '[targetDir]'})
          class TargetDir {
          }
          @Component({selector: 'needs-target', template: ``})
          class NeedsTarget {
            @ContentChildren(TargetDir) targets!: QueryList;
          }
          @Component({
            selector: 'test-cmpt',
            template: `
          
            
              
                
                  |
                
              
            
          
        `,
          })
          class TestCmpt {
          }
          TestBed.configureTestingModule({declarations: [TestCmpt, NeedsTarget, TargetDir]});
          const fixture = TestBed.createComponent(TestCmpt);
          const cmptWithQuery = fixture.debugElement.children[0].injector.get(NeedsTarget);
          fixture.detectChanges();
          expect(cmptWithQuery.targets.length).toBe(3);
        });
  });
  describe('observable interface', () => {
    it('should allow observing changes to query list', () => {
      const fixture = TestBed.createComponent(QueryCompWithChanges);
      let changes = 0;
      fixture.detectChanges();
      fixture.componentInstance.foos.changes.subscribe((value: any) => {
        changes += 1;
        expect(value).toBe(fixture.componentInstance.foos);
      });
      // refresh without setting dirty - no emit
      fixture.detectChanges();
      expect(changes).toBe(0);
      // refresh with setting dirty - emit
      fixture.componentInstance.showing = true;
      fixture.detectChanges();
      expect(changes).toBe(1);
    });
  });
  describe('view boundaries', () => {
    describe('ViewContainerRef', () => {
      @Directive({selector: '[vc]', exportAs: 'vc'})
      class ViewContainerManipulatorDirective {
        constructor(private _vcRef: ViewContainerRef) {}
        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', () => {
        @Component({
          selector: 'test-comp',
          template: `
            
              
            
          `
        })
        class TestComponent {
          value: boolean = false;
          @ViewChildren('foo') query!: QueryList;
        }
        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: `
            
              
            
          `,
        })
        class TestComponent {
          value: string[]|undefined;
          @ViewChildren('foo') query!: QueryList;
        }
        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');
      });
      /**
       * 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 {
          @ContentChildren('foo', {descendants: true}) foos1!: QueryList;
          @ContentChildren('foo', {descendants: true}) foos2!: QueryList;
        }
        @Directive({selector: '[query-for-content]'})
        class QueryForContent {
          @ContentChildren('foo') foos!: QueryList;
        }
        @Component({
          selector: 'test-comp',
          template: `
            
          `
        })
        class TestComponent {
          items = [1, 2];
        }
        TestBed.configureTestingModule(
            {declarations: [TestComponent, QueryForContent, QueryForLotsOfContent]});
        const fixture = TestBed.createComponent(TestComponent);
        fixture.detectChanges();
        const lotsOfContentEl = fixture.debugElement.query(By.directive(QueryForLotsOfContent));
        const lotsOfContentInstance = lotsOfContentEl.injector.get(QueryForLotsOfContent);
        const contentEl = fixture.debugElement.query(By.directive(QueryForContent));
        const contentInstance = contentEl.injector.get(QueryForContent);
        expect(lotsOfContentInstance.foos1.length).toBe(2);
        expect(lotsOfContentInstance.foos2.length).toBe(2);
        expect(contentInstance.foos.length).toBe(1);
        fixture.componentInstance.items = [];
        fixture.detectChanges();
        expect(lotsOfContentInstance.foos1.length).toBe(0);
        expect(lotsOfContentInstance.foos2.length).toBe(0);
      });
      // 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: `
               
                 
               
               
               
                 
               
               
             `,
           })
           class TestComponent {
             @ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective;
             @ViewChild('tpl1') tpl1!: TemplateRef;
             @ViewChild('tpl2') tpl2!: TemplateRef;
             @ViewChildren('foo') query!: QueryList;
           }
           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
      it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
         () => {
           @Component({
             selector: 'test-comp',
             template: `
               
                 
               
               
               
             `,
           })
           class TestComponent {
             @ViewChild('tpl') tpl!: TemplateRef;
             @ViewChild('vi0') vi0!: ViewContainerManipulatorDirective;
             @ViewChild('vi1') vi1!: ViewContainerManipulatorDirective;
             @ViewChildren('foo') query!: QueryList;
           }
           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_0_0');
           expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_1_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: `
            
              
            
            
          `,
        })
        class MyApp {
          show = false;
          @ViewChildren('foo') query!: QueryList;
        }
        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);
      });
    });
  });
  describe('non-regression', () => {
    it('should query by provider super-type in an embedded view', () => {
      @Directive({selector: '[child]'})
      class Child {
      }
      @Directive({selector: '[parent]', providers: [{provide: Child, useExisting: Parent}]})
      class Parent extends Child {
      }
      @Component({
        selector: 'test-cmpt',
        template:
            ``
      })
      class TestCmpt {
        @ViewChildren(Child) instances!: QueryList;
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, Parent, Child]});
      const fixture = TestBed.createComponent(TestCmpt);
      fixture.detectChanges();
      expect(fixture.componentInstance.instances.length).toBe(1);
    });
    it('should flatten multi-provider results', () => {
      class MyClass {}
      @Component({
        selector: 'with-multi-provider',
        template: '',
        providers:
            [{provide: MyClass, useExisting: forwardRef(() => WithMultiProvider), multi: true}]
      })
      class WithMultiProvider {
      }
      @Component({selector: 'test-cmpt', template: ``})
      class TestCmpt {
        @ViewChildren(MyClass) queryResults!: QueryList;
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
      const fixture = TestBed.createComponent(TestCmpt);
      fixture.detectChanges();
      expect(fixture.componentInstance.queryResults.length).toBe(1);
      expect(fixture.componentInstance.queryResults.first).toBeAnInstanceOf(WithMultiProvider);
    });
    it('should flatten multi-provider results when crossing ng-template', () => {
      class MyClass {}
      @Component({
        selector: 'with-multi-provider',
        template: '',
        providers:
            [{provide: MyClass, useExisting: forwardRef(() => WithMultiProvider), multi: true}]
      })
      class WithMultiProvider {
      }
      @Component({
        selector: 'test-cmpt',
        template: `
          
          
        `
      })
      class TestCmpt {
        @ViewChildren(MyClass) queryResults!: QueryList;
      }
      TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
      const fixture = TestBed.createComponent(TestCmpt);
      fixture.detectChanges();
      expect(fixture.componentInstance.queryResults.length).toBe(2);
      expect(fixture.componentInstance.queryResults.first).toBeAnInstanceOf(WithMultiProvider);
      expect(fixture.componentInstance.queryResults.last).toBeAnInstanceOf(WithMultiProvider);
    });
    it('should allow undefined provider value in a [View/Content]Child queries', () => {
      @Directive({selector: '[group]'})
      class GroupDir {
      }
      @Directive(
          {selector: '[undefinedGroup]', providers: [{provide: GroupDir, useValue: undefined}]})
      class UndefinedGroup {
      }
      @Component({
        template: `
          
          
            
          
        `
      })
      class App {
        @ViewChild(GroupDir) group!: GroupDir;
      }
      TestBed.configureTestingModule(
          {declarations: [App, GroupDir, UndefinedGroup], imports: [CommonModule]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.group).toBeAnInstanceOf(GroupDir);
    });
    it('should allow null / undefined provider value in a [View/Content]Children queries', () => {
      @Directive({selector: '[group]'})
      class GroupDir {
      }
      @Directive({selector: '[nullGroup]', providers: [{provide: GroupDir, useValue: null}]})
      class NullGroup {
      }
      @Directive(
          {selector: '[undefinedGroup]', providers: [{provide: GroupDir, useValue: undefined}]})
      class UndefinedGroup {
      }
      @Component({
        template: `
          
            
          
          
          
            
          
        `
      })
      class App {
        @ViewChildren(GroupDir) groups!: QueryList;
      }
      TestBed.configureTestingModule(
          {declarations: [App, GroupDir, NullGroup, UndefinedGroup], imports: [CommonModule]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      const queryList = fixture.componentInstance.groups;
      expect(queryList.length).toBe(3);
      const groups = queryList.toArray();
      expect(groups[0]).toBeNull();
      expect(groups[1]).toBeAnInstanceOf(GroupDir);
      expect(groups[2]).toBeUndefined();
    });
  });
  describe('querying for string token providers', () => {
    @Directive({
      selector: '[text-token]',
      providers: [{provide: 'Token', useExisting: TextTokenDirective}],
    })
    class TextTokenDirective {
    }
    it('should match string injection token in a ViewChild query', () => {
      @Component({template: ''})
      class App {
        @ViewChild('Token') token: any;
      }
      TestBed.configureTestingModule({declarations: [App, TextTokenDirective]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.token).toBeAnInstanceOf(TextTokenDirective);
    });
    it('should give precedence to local reference if both a reference and a string injection token provider match a ViewChild query',
       () => {
         @Component({template: ''})
         class App {
           @ViewChild('Token') token: any;
         }
         TestBed.configureTestingModule({declarations: [App, TextTokenDirective]});
         const fixture = TestBed.createComponent(App);
         fixture.detectChanges();
         expect(fixture.componentInstance.token).toBeAnInstanceOf(ElementRef);
       });
    it('should match string injection token in a ViewChildren query', () => {
      @Component({template: ''})
      class App {
        @ViewChildren('Token') tokens!: QueryList;
      }
      TestBed.configureTestingModule({declarations: [App, TextTokenDirective]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      const tokens = fixture.componentInstance.tokens;
      expect(tokens.length).toBe(1);
      expect(tokens.first).toBeAnInstanceOf(TextTokenDirective);
    });
    it('should match both string injection token and local reference inside a ViewChildren query',
       () => {
         @Component({template: ''})
         class App {
           @ViewChildren('Token') tokens!: QueryList;
         }
         TestBed.configureTestingModule({declarations: [App, TextTokenDirective]});
         const fixture = TestBed.createComponent(App);
         fixture.detectChanges();
         expect(fixture.componentInstance.tokens.toArray()).toEqual([
           jasmine.any(ElementRef), jasmine.any(TextTokenDirective)
         ]);
       });
    it('should match string injection token in a ContentChild query', () => {
      @Component({selector: 'has-query', template: ''})
      class HasQuery {
        @ContentChild('Token') token: any;
      }
      @Component({template: ''})
      class App {
        @ViewChild(HasQuery) queryComp!: HasQuery;
      }
      TestBed.configureTestingModule({declarations: [App, HasQuery, TextTokenDirective]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.queryComp.token).toBeAnInstanceOf(TextTokenDirective);
    });
    it('should give precedence to local reference if both a reference and a string injection token provider match a ContentChild query',
       () => {
         @Component({selector: 'has-query', template: ''})
         class HasQuery {
           @ContentChild('Token') token: any;
         }
         @Component({template: ''})
         class App {
           @ViewChild(HasQuery) queryComp!: HasQuery;
         }
         TestBed.configureTestingModule({declarations: [App, HasQuery, TextTokenDirective]});
         const fixture = TestBed.createComponent(App);
         fixture.detectChanges();
         expect(fixture.componentInstance.queryComp.token).toBeAnInstanceOf(ElementRef);
       });
    it('should match string injection token in a ContentChildren query', () => {
      @Component({selector: 'has-query', template: ''})
      class HasQuery {
        @ContentChildren('Token') tokens!: QueryList;
      }
      @Component({template: ''})
      class App {
        @ViewChild(HasQuery) queryComp!: HasQuery;
      }
      TestBed.configureTestingModule({declarations: [App, HasQuery, TextTokenDirective]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      const tokens = fixture.componentInstance.queryComp.tokens;
      expect(tokens.length).toBe(1);
      expect(tokens.first).toBeAnInstanceOf(TextTokenDirective);
    });
    it('should match both string injection token and local reference inside a ContentChildren query',
       () => {
         @Component({selector: 'has-query', template: ''})
         class HasQuery {
           @ContentChildren('Token') tokens!: QueryList;
         }
         @Component({template: ''})
         class App {
           @ViewChild(HasQuery) queryComp!: HasQuery;
         }
         TestBed.configureTestingModule({declarations: [App, HasQuery, TextTokenDirective]});
         const fixture = TestBed.createComponent(App);
         fixture.detectChanges();
         expect(fixture.componentInstance.queryComp.tokens.toArray()).toEqual([
           jasmine.any(ElementRef), jasmine.any(TextTokenDirective)
         ]);
       });
    it('should match string token specified through the `read` option of a view query', () => {
      @Component({template: ''})
      class App {
        @ViewChild('Token', {read: 'Token'}) token: any;
      }
      TestBed.configureTestingModule({declarations: [App, TextTokenDirective]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.token).toBeAnInstanceOf(TextTokenDirective);
    });
    it('should match string token specified through the `read` option of a content query', () => {
      @Component({selector: 'has-query', template: ''})
      class HasQuery {
        @ContentChild('Token', {read: 'Token'}) token: any;
      }
      @Component({template: ''})
      class App {
        @ViewChild(HasQuery) queryComp!: HasQuery;
      }
      TestBed.configureTestingModule({declarations: [App, HasQuery, TextTokenDirective]});
      const fixture = TestBed.createComponent(App);
      fixture.detectChanges();
      expect(fixture.componentInstance.queryComp.token).toBeAnInstanceOf(TextTokenDirective);
    });
  });
});
function initWithTemplate(compType: Type, template: string) {
  TestBed.overrideComponent(compType, {set: new Component({template})});
  const fixture = TestBed.createComponent(compType);
  fixture.detectChanges();
  return fixture;
}
@Component({selector: 'local-ref-query-component', template: ''})
class QueryComp {
  @ViewChild('viewQuery') viewChild!: any;
  @ContentChild('contentQuery') contentChild!: any;
  @ViewChildren('viewQuery') viewChildren!: QueryList;
  @ContentChildren('contentQuery') contentChildren!: QueryList;
}
@Component({selector: 'app-comp', template: ``})
class AppComp {
}
@Component({selector: 'simple-comp-a', template: ''})
class SimpleCompA {
}
@Component({selector: 'simple-comp-b', template: ''})
class SimpleCompB {
}
@Directive({selector: '[text]'})
class TextDirective {
  @Input() text = '';
}
@Component({
  selector: 'static-view-query-comp',
  template: `
    
    
  `
})
class StaticViewQueryComp {
  private _textDir!: TextDirective;
  private _foo!: ElementRef;
  setEvents: string[] = [];
  @ViewChild(TextDirective, {static: true})
  get textDir(): TextDirective {
    return this._textDir;
  }
  set textDir(value: TextDirective) {
    this.setEvents.push('textDir set');
    this._textDir = value;
  }
  @ViewChild('foo')
  get foo(): ElementRef {
    return this._foo;
  }
  set foo(value: ElementRef) {
    this.setEvents.push('foo set');
    this._foo = value;
  }
  text = 'some text';
}
@Component({
  selector: 'subclass-static-view-query-comp',
  template: `
    
    
    
    
  `
})
class SubclassStaticViewQueryComp extends StaticViewQueryComp {
  @ViewChild('bar', {static: true}) bar!: ElementRef;
  @ViewChild('baz') baz!: ElementRef;
}
@Component({selector: 'static-content-query-comp', template: ``})
class StaticContentQueryComp {
  private _textDir!: TextDirective;
  private _foo!: ElementRef;
  setEvents: string[] = [];
  @ContentChild(TextDirective, {static: true})
  get textDir(): TextDirective {
    return this._textDir;
  }
  set textDir(value: TextDirective) {
    this.setEvents.push('textDir set');
    this._textDir = value;
  }
  @ContentChild('foo')
  get foo(): ElementRef {
    return this._foo;
  }
  set foo(value: ElementRef) {
    this.setEvents.push('foo set');
    this._foo = value;
  }
}
@Directive({selector: '[staticContentQueryDir]'})
class StaticContentQueryDir {
  private _textDir!: TextDirective;
  private _foo!: ElementRef;
  setEvents: string[] = [];
  @ContentChild(TextDirective, {static: true})
  get textDir(): TextDirective {
    return this._textDir;
  }
  set textDir(value: TextDirective) {
    this.setEvents.push('textDir set');
    this._textDir = value;
  }
  @ContentChild('foo')
  get foo(): ElementRef {
    return this._foo;
  }
  set foo(value: ElementRef) {
    this.setEvents.push('foo set');
    this._foo = value;
  }
}
@Component({selector: 'subclass-static-content-query-comp', template: ``})
class SubclassStaticContentQueryComp extends StaticContentQueryComp {
  @ContentChild('bar', {static: true}) bar!: ElementRef;
  @ContentChild('baz') baz!: ElementRef;
}
@Component({
  selector: 'query-with-changes',
  template: `
    
  `
})
export class QueryCompWithChanges {
  @ViewChildren('foo') foos!: QueryList;
  showing = false;
}
@Component({selector: 'query-target', template: ''})
class SuperDirectiveQueryTarget {
}
@Directive({selector: '[super-directive]'})
class SuperDirective {
  @ViewChildren(SuperDirectiveQueryTarget) headers!: QueryList;
}
@Component({
  template: `
    One
    Two
  `
})
class SubComponent extends SuperDirective {
}
const MY_OPTION_TOKEN = new InjectionToken('ComponentWithToken');
@Component({
  selector: 'my-option',
  template: 'Option',
  providers: [{provide: MY_OPTION_TOKEN, useExisting: TestComponentWithToken}],
})
class TestComponentWithToken {
}
@Component({
  selector: 'test-injection-token',
  template: `
    
    
    
  `
})
class TestInjectionTokenQueries {
  @ViewChild(MY_OPTION_TOKEN) viewFirstOption!: TestComponentWithToken;
  @ViewChildren(MY_OPTION_TOKEN) viewOptions!: QueryList;
  @ContentChild(MY_OPTION_TOKEN) contentFirstOption!: TestComponentWithToken;
  @ContentChildren(MY_OPTION_TOKEN) contentOptions!: QueryList;
}
@Component({
  template: `
    
      
      
    
  `
})
class TestInjectionTokenContentQueries {
}