/**
 * @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 {ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty, template, getRenderedText} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, text, textBinding, tick} from '../../src/render3/instructions';
import {ComponentDef, RenderFlags} from '../../src/render3/interfaces/definition';
import {NgIf} from './common_with_def';
import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, MockRendererFactory, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util';
describe('component', () => {
  class CounterComponent {
    count = 0;
    increment() { this.count++; }
    static ngComponentDef = defineComponent({
      type: CounterComponent,
      encapsulation: ViewEncapsulation.None,
      selectors: [['counter']],
      consts: 1,
      vars: 1,
      template: function(rf: RenderFlags, ctx: CounterComponent) {
        if (rf & RenderFlags.Create) {
          text(0);
        }
        if (rf & RenderFlags.Update) {
          textBinding(0, bind(ctx.count));
        }
      },
      factory: () => new CounterComponent,
      inputs: {count: 'count'},
    });
  }
  describe('renderComponent', () => {
    it('should render on initial call', () => {
      renderComponent(CounterComponent);
      expect(toHtml(containerEl)).toEqual('0');
    });
    it('should re-render on input change or method invocation', () => {
      const component = renderComponent(CounterComponent);
      expect(toHtml(containerEl)).toEqual('0');
      component.count = 123;
      markDirty(component);
      expect(toHtml(containerEl)).toEqual('0');
      requestAnimationFrame.flush();
      expect(toHtml(containerEl)).toEqual('123');
      component.increment();
      markDirty(component);
      expect(toHtml(containerEl)).toEqual('123');
      requestAnimationFrame.flush();
      expect(toHtml(containerEl)).toEqual('124');
    });
    class MyService {
      constructor(public value: string) {}
      static ngInjectableDef =
          defineInjectable({providedIn: 'root', factory: () => new MyService('no-injector')});
    }
    class MyComponent {
      constructor(public myService: MyService) {}
      static ngComponentDef = defineComponent({
        type: MyComponent,
        encapsulation: ViewEncapsulation.None,
        selectors: [['my-component']],
        factory: () => new MyComponent(directiveInject(MyService)),
        consts: 1,
        vars: 1,
        template: function(fs: RenderFlags, ctx: MyComponent) {
          if (fs & RenderFlags.Create) {
            text(0);
          }
          if (fs & RenderFlags.Update) {
            textBinding(0, bind(ctx.myService.value));
          }
        }
      });
    }
    class MyModule {
      static ngInjectorDef = defineInjector({
        factory: () => new MyModule(),
        providers: [{provide: MyService, useValue: new MyService('injector')}]
      });
    }
    it('should support bootstrapping without injector', () => {
      const fixture = new ComponentFixture(MyComponent);
      expect(fixture.html).toEqual('no-injector');
    });
    it('should support bootstrapping with injector', () => {
      const fixture = new ComponentFixture(MyComponent, {injector: createInjector(MyModule)});
      expect(fixture.html).toEqual('injector');
    });
  });
  it('should instantiate components at high indices', () => {
    // {{ name }}
    class Comp {
      // @Input
      name = '';
      static ngComponentDef = defineComponent({
        type: Comp,
        selectors: [['comp']],
        factory: () => new Comp(),
        consts: 1,
        vars: 1,
        template: (rf: RenderFlags, ctx: Comp) => {
          if (rf & RenderFlags.Create) {
            text(0);
          }
          if (rf & RenderFlags.Update) {
            textBinding(0, bind(ctx.name));
          }
        },
        inputs: {name: 'name'}
      });
    }
    // Artificially inflating the slot IDs of this app component to mimic an app
    // with a very large view
    const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
      if (rf & RenderFlags.Create) {
        element(4097, 'comp');
      }
      if (rf & RenderFlags.Update) {
        elementProperty(4097, 'name', bind(ctx.name));
      }
    }, 4098, 1, [Comp]);
    const fixture = new ComponentFixture(App);
    expect(fixture.html).toEqual('