/**
 * @license
 * Copyright Google Inc. 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 {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF} from '../../src/render3/di';
import {QueryList, detectChanges} from '../../src/render3/index';
import {container, containerRefreshEnd, containerRefreshStart, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, memory} from '../../src/render3/instructions';
import {query, queryRefresh} from '../../src/render3/query';
import {createComponent, createDirective, renderComponent} from './render_util';
/**
 * Helper function to check if a given candidate object resembles ElementRef
 * @param candidate
 * @returns true if `ElementRef`.
 */
function isElementRef(candidate: any): boolean {
  return candidate.nativeElement != null;
}
/**
 * Helper function to check if a given candidate object resembles TemplateRef
 * @param candidate
 * @returns true if `TemplateRef`.
 */
function isTemplateRef(candidate: any): boolean {
  return candidate.createEmbeddedView != null && candidate.createComponent == null;
}
/**
 * Helper function to check if a given candidate object resembles ViewContainerRef
 * @param candidate
 * @returns true if `ViewContainerRef`.
 */
function isViewContainerRef(candidate: any): boolean {
  return candidate.createEmbeddedView != null && candidate.createComponent != null;
}
describe('query', () => {
  it('should project query children', () => {
    const Child = createComponent('child', function(ctx: any, cm: boolean) {});
    let child1 = null;
    let child2 = null;
    const Cmp = createComponent('cmp', function(ctx: any, cm: boolean) {
      /**
       * 
       *   
       *   
       * 
       * class Cmp {
       *   @ViewChildren(Child) query0;
       *   @ViewChildren(Child, {descend: true}) query1;
       * }
       */
      let tmp: any;
      if (cm) {
        query(0, Child, false);
        query(1, Child, true);
        elementStart(2, Child);
        {
          child1 = memory(3);
          elementStart(4, Child);
          { child2 = memory(5); }
          elementEnd();
        }
        elementEnd();
      }
      queryRefresh(tmp = memory>(0)) && (ctx.query0 = tmp as QueryList);
      queryRefresh(tmp = memory>(1)) && (ctx.query1 = tmp as QueryList);
    });
    const parent = renderComponent(Cmp);
    expect((parent.query0 as QueryList).toArray()).toEqual([child1]);
    expect((parent.query1 as QueryList).toArray()).toEqual([child1, child2]);
  });
  describe('types predicate', () => {
    it('should query using type predicate and read a specified token', () => {
      const Child = createDirective();
      let elToQuery;
      /**
       * 
       * class Cmpt {
       *  @ViewChildren(Child, {read: ElementRef}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, Child, false, QUERY_READ_ELEMENT_REF);
          elToQuery = elementStart(1, 'div', null, [Child]);
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(isElementRef(qList.first)).toBeTruthy();
      expect(qList.first.nativeElement).toEqual(elToQuery);
    });
    it('should query using type predicate and read another directive type', () => {
      const Child = createDirective();
      const OtherChild = createDirective();
      let otherChildInstance;
      /**
       * 
       * class Cmpt {
       *  @ViewChildren(Child, {read: OtherChild}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, Child, false, OtherChild);
          elementStart(1, 'div', null, [Child, OtherChild]);
          { otherChildInstance = memory(3); }
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(qList.first).toBe(otherChildInstance);
    });
    it('should not add results to query if a requested token cant be read', () => {
      const Child = createDirective();
      const OtherChild = createDirective();
      /**
       * 
       * class Cmpt {
       *  @ViewChildren(Child, {read: OtherChild}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, Child, false, OtherChild);
          elementStart(1, 'div', null, [Child]);
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(0);
    });
  });
  describe('local names predicate', () => {
    it('should query for a single element and read ElementRef by default', () => {
      let elToQuery;
      /**
       * 
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], false, QUERY_READ_FROM_NODE);
          elToQuery = elementStart(1, 'div', null, null, ['foo', '']);
          elementEnd();
          elementStart(2, 'div');
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(qList.first.nativeElement).toEqual(elToQuery);
    });
    it('should query for multiple elements and read ElementRef by default', () => {
      let el1ToQuery;
      let el2ToQuery;
      /**
       * 
       * 
       * 
       * class Cmpt {
       *  @ViewChildren('foo,bar') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE);
          el1ToQuery = elementStart(1, 'div', null, null, ['foo', '']);
          elementEnd();
          elementStart(2, 'div');
          elementEnd();
          el2ToQuery = elementStart(3, 'div', null, null, ['bar', '']);
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(2);
      expect(qList.first.nativeElement).toEqual(el1ToQuery);
      expect(qList.last.nativeElement).toEqual(el2ToQuery);
    });
    it('should read ElementRef from an element when explicitly asked for', () => {
      let elToQuery;
      /**
       * 
       * 
       * class Cmpt {
       *  @ViewChildren('foo', {read: ElementRef}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], false, QUERY_READ_ELEMENT_REF);
          elToQuery = elementStart(1, 'div', null, null, ['foo', '']);
          elementEnd();
          elementStart(2, 'div');
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(isElementRef(qList.first)).toBeTruthy();
      expect(qList.first.nativeElement).toEqual(elToQuery);
    });
    it('should read ViewContainerRef from element nodes when explicitly asked for', () => {
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo', {read: ViewContainerRef}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], false, QUERY_READ_CONTAINER_REF);
          elementStart(1, 'div', null, null, ['foo', '']);
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(isViewContainerRef(qList.first)).toBeTruthy();
    });
    it('should read ViewContainerRef from container nodes when explicitly asked for', () => {
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo', {read: ViewContainerRef}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], false, QUERY_READ_CONTAINER_REF);
          container(1, undefined, undefined, undefined, undefined, ['foo', '']);
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(isViewContainerRef(qList.first)).toBeTruthy();
    });
    it('should no longer read ElementRef with a native element pointing to comment DOM node from containers',
       () => {
         /**
          * 
          * class Cmpt {
          *  @ViewChildren('foo', {read: ElementRef}) query;
          * }
          */
         const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
           let tmp: any;
           if (cm) {
             query(0, ['foo'], false, QUERY_READ_ELEMENT_REF);
             container(1, undefined, undefined, undefined, undefined, ['foo', '']);
           }
           queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
         });
         const cmptInstance = renderComponent(Cmpt);
         const qList = (cmptInstance.query as QueryList);
         expect(qList.length).toBe(1);
         expect(qList.first.nativeElement).toBe(null);
       });
    it('should read TemplateRef from container nodes by default', () => {
      // http://plnkr.co/edit/BVpORly8wped9I3xUYsX?p=preview
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], undefined, QUERY_READ_FROM_NODE);
          container(1, undefined, undefined, undefined, undefined, ['foo', '']);
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(isTemplateRef(qList.first)).toBeTruthy();
    });
    it('should read TemplateRef from container nodes when explicitly asked for', () => {
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo', {read: TemplateRef}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], false, QUERY_READ_TEMPLATE_REF);
          container(1, undefined, undefined, undefined, undefined, ['foo', '']);
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(isTemplateRef(qList.first)).toBeTruthy();
    });
    it('should read component instance if element queried for is a component host', () => {
      const Child = createComponent('child', function(ctx: any, cm: boolean) {});
      let childInstance;
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], true, QUERY_READ_FROM_NODE);
          elementStart(1, Child, null, null, ['foo', '']);
          { childInstance = memory(2); }
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(qList.first).toBe(childInstance);
    });
    it('should read directive instance if element queried for has an exported directive with a matching name',
       () => {
         const Child = createDirective({exportAs: 'child'});
         let childInstance;
         /**
          * 
          * class Cmpt {
          *  @ViewChildren('foo') query;
          * }
          */
         const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
           let tmp: any;
           if (cm) {
             query(0, ['foo'], true, QUERY_READ_FROM_NODE);
             elementStart(1, 'div', null, [Child], ['foo', 'child']);
             childInstance = memory(2);
             elementEnd();
           }
           queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
         });
         const cmptInstance = renderComponent(Cmpt);
         const qList = (cmptInstance.query as QueryList);
         expect(qList.length).toBe(1);
         expect(qList.first).toBe(childInstance);
       });
    it('should read all matching directive instances from a given element', () => {
      const Child1 = createDirective({exportAs: 'child1'});
      const Child2 = createDirective({exportAs: 'child2'});
      let child1Instance, child2Instance;
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo, bar') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE);
          elementStart(1, 'div', null, [Child1, Child2], ['foo', 'child1', 'bar', 'child2']);
          {
            child1Instance = memory(2);
            child2Instance = memory(3);
          }
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(2);
      expect(qList.first).toBe(child1Instance);
      expect(qList.last).toBe(child2Instance);
    });
    it('should match match on exported directive name and read a requested token', () => {
      const Child = createDirective({exportAs: 'child'});
      let div;
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo', {read: ElementRef}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], undefined, QUERY_READ_ELEMENT_REF);
          div = elementStart(1, 'div', null, [Child], ['foo', 'child']);
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(1);
      expect(qList.first.nativeElement).toBe(div);
    });
    it('should support reading a mix of ElementRef and directive instances', () => {
      const Child = createDirective({exportAs: 'child'});
      let childInstance, div;
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo, bar') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo', 'bar'], undefined, QUERY_READ_FROM_NODE);
          div = elementStart(1, 'div', null, [Child], ['foo', '', 'bar', 'child']);
          { childInstance = memory(2); }
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(2);
      expect(qList.first.nativeElement).toBe(div);
      expect(qList.last).toBe(childInstance);
    });
    it('should not add results to query if a requested token cant be read', () => {
      const Child = createDirective();
      /**
       * 
       * class Cmpt {
       *  @ViewChildren('foo', {read: Child}) query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], false, Child);
          elementStart(1, 'div', null, null, ['foo', '']);
          elementEnd();
        }
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as QueryList);
      expect(qList.length).toBe(0);
    });
  });
  describe('view boundaries', () => {
    it('should report results in embedded views', () => {
      let firstEl;
      /**
       * 
       *    
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], true, QUERY_READ_FROM_NODE);
          container(1);
        }
        containerRefreshStart(1);
        {
          if (ctx.exp) {
            let cm1 = embeddedViewStart(1);
            {
              if (cm1) {
                firstEl = elementStart(0, 'div', null, null, ['foo', '']);
                elementEnd();
              }
            }
            embeddedViewEnd();
          }
        }
        containerRefreshEnd();
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as any);
      expect(qList.length).toBe(0);
      cmptInstance.exp = true;
      detectChanges(cmptInstance);
      expect(qList.length).toBe(1);
      expect(qList.first.nativeElement).toBe(firstEl);
      cmptInstance.exp = false;
      detectChanges(cmptInstance);
      expect(qList.length).toBe(0);
    });
    it('should add results from embedded views in the correct order - views and elements mix',
       () => {
         let firstEl, lastEl, viewEl;
         /**
          * 
          * 
          *    
          * 
          * 
          * class Cmpt {
          *  @ViewChildren('foo') query;
          * }
          */
         const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
           let tmp: any;
           if (cm) {
             query(0, ['foo'], true, QUERY_READ_FROM_NODE);
             firstEl = elementStart(1, 'b', null, null, ['foo', '']);
             elementEnd();
             container(2);
             lastEl = elementStart(3, 'i', null, null, ['foo', '']);
             elementEnd();
           }
           containerRefreshStart(2);
           {
             if (ctx.exp) {
               let cm1 = embeddedViewStart(1);
               {
                 if (cm1) {
                   viewEl = elementStart(0, 'div', null, null, ['foo', '']);
                   elementEnd();
                 }
               }
               embeddedViewEnd();
             }
           }
           containerRefreshEnd();
           queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
         });
         const cmptInstance = renderComponent(Cmpt);
         const qList = (cmptInstance.query as any);
         expect(qList.length).toBe(2);
         expect(qList.first.nativeElement).toBe(firstEl);
         expect(qList.last.nativeElement).toBe(lastEl);
         cmptInstance.exp = true;
         detectChanges(cmptInstance);
         expect(qList.length).toBe(3);
         expect(qList.toArray()[0].nativeElement).toBe(firstEl);
         expect(qList.toArray()[1].nativeElement).toBe(viewEl);
         expect(qList.toArray()[2].nativeElement).toBe(lastEl);
         cmptInstance.exp = false;
         detectChanges(cmptInstance);
         expect(qList.length).toBe(2);
         expect(qList.first.nativeElement).toBe(firstEl);
         expect(qList.last.nativeElement).toBe(lastEl);
       });
    it('should add results from embedded views in the correct order - views side by side', () => {
      let firstEl, lastEl;
      /**
       * 
       *    
       * 
       * 
       *    
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], true, QUERY_READ_FROM_NODE);
          container(1);
        }
        containerRefreshStart(1);
        {
          if (ctx.exp1) {
            let cm1 = embeddedViewStart(0);
            {
              if (cm1) {
                firstEl = elementStart(0, 'div', null, null, ['foo', '']);
                elementEnd();
              }
            }
            embeddedViewEnd();
          }
          if (ctx.exp2) {
            let cm1 = embeddedViewStart(1);
            {
              if (cm1) {
                lastEl = elementStart(0, 'span', null, null, ['foo', '']);
                elementEnd();
              }
            }
            embeddedViewEnd();
          }
        }
        containerRefreshEnd();
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as any);
      expect(qList.length).toBe(0);
      cmptInstance.exp2 = true;
      detectChanges(cmptInstance);
      expect(qList.length).toBe(1);
      expect(qList.last.nativeElement).toBe(lastEl);
      cmptInstance.exp1 = true;
      detectChanges(cmptInstance);
      expect(qList.length).toBe(2);
      expect(qList.first.nativeElement).toBe(firstEl);
      expect(qList.last.nativeElement).toBe(lastEl);
    });
    it('should add results from embedded views in the correct order - nested views', () => {
      let firstEl, lastEl;
      /**
       * 
       *    
       *    
       *      
       *    
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], true, QUERY_READ_FROM_NODE);
          container(1);
        }
        containerRefreshStart(1);
        {
          if (ctx.exp1) {
            let cm1 = embeddedViewStart(0);
            {
              if (cm1) {
                firstEl = elementStart(0, 'div', null, null, ['foo', '']);
                elementEnd();
                container(1);
              }
              containerRefreshStart(1);
              {
                if (ctx.exp2) {
                  let cm2 = embeddedViewStart(0);
                  {
                    if (cm2) {
                      lastEl = elementStart(0, 'span', null, null, ['foo', '']);
                      elementEnd();
                    }
                  }
                  embeddedViewEnd();
                }
              }
              containerRefreshEnd();
            }
            embeddedViewEnd();
          }
        }
        containerRefreshEnd();
        queryRefresh(tmp = memory>(0)) && (ctx.query = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const qList = (cmptInstance.query as any);
      expect(qList.length).toBe(0);
      cmptInstance.exp1 = true;
      detectChanges(cmptInstance);
      expect(qList.length).toBe(1);
      expect(qList.first.nativeElement).toBe(firstEl);
      cmptInstance.exp2 = true;
      detectChanges(cmptInstance);
      expect(qList.length).toBe(2);
      expect(qList.first.nativeElement).toBe(firstEl);
      expect(qList.last.nativeElement).toBe(lastEl);
    });
    it('should support combination of deep and shallow queries', () => {
      /**
       * 
       *    
       * 
       * 
       * class Cmpt {
       *  @ViewChildren('foo') query;
       * }
       */
      const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
        let tmp: any;
        if (cm) {
          query(0, ['foo'], true, QUERY_READ_FROM_NODE);
          query(1, ['foo'], false, QUERY_READ_FROM_NODE);
          container(2);
          elementStart(3, 'span', null, null, ['foo', '']);
          elementEnd();
        }
        containerRefreshStart(2);
        {
          if (ctx.exp) {
            let cm1 = embeddedViewStart(0);
            {
              if (cm1) {
                elementStart(0, 'div', null, null, ['foo', '']);
                elementEnd();
              }
            }
            embeddedViewEnd();
          }
        }
        containerRefreshEnd();
        queryRefresh(tmp = memory>(0)) && (ctx.deep = tmp as QueryList);
        queryRefresh(tmp = memory>(1)) && (ctx.shallow = tmp as QueryList);
      });
      const cmptInstance = renderComponent(Cmpt);
      const deep = (cmptInstance.deep as any);
      const shallow = (cmptInstance.shallow as any);
      expect(deep.length).toBe(1);
      expect(shallow.length).toBe(1);
      cmptInstance.exp = true;
      detectChanges(cmptInstance);
      expect(deep.length).toBe(2);
      expect(shallow.length).toBe(1);
      cmptInstance.exp = false;
      detectChanges(cmptInstance);
      expect(deep.length).toBe(1);
      expect(shallow.length).toBe(1);
    });
  });
  describe('observable interface', () => {
    it('should allow observing changes to query list', () => {
      const queryList = new QueryList();
      let changes = 0;
      queryList.changes.subscribe({
        next: (arg) => {
          changes += 1;
          expect(arg).toBe(queryList);
        }
      });
      // initial refresh, the query should be dirty
      queryRefresh(queryList);
      expect(changes).toBe(1);
      // refresh without setting dirty - no emit
      queryRefresh(queryList);
      expect(changes).toBe(1);
      // refresh with setting dirty - emit
      queryList.setDirty();
      queryRefresh(queryList);
      expect(changes).toBe(2);
    });
  });
});