import { moduleFor, RenderingTest } from '../../utils/test-case';
import { strip } from '../../utils/abstract-test-case';
import { Component } from '../../utils/helpers';
import { set } from '@ember/-internals/metal';

moduleFor(
  'Components test: fragment components',
  class extends RenderingTest {
    getCustomDispatcherEvents() {
      return {
        hitDem: 'folks',
      };
    }

    ['@test fragments do not render an outer tag']() {
      let instance;
      let FooBarComponent = Component.extend({
        tagName: '',
        init() {
          this._super();
          instance = this;
          this.foo = true;
          this.bar = 'bar';
        },
      });

      let template = `{{#if foo}}<div>Hey</div>{{/if}}{{yield bar}}`;

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });

      this.render(`{{#foo-bar as |bar|}}{{bar}}{{/foo-bar}}`);

      this.assertHTML(strip`<div>Hey</div>bar`);

      this.assertStableRerender();

      this.runTask(() => set(instance, 'foo', false));

      this.assertHTML(strip`<!---->bar`);

      this.runTask(() => set(instance, 'bar', 'bizz'));

      this.assertHTML(strip`<!---->bizz`);

      this.runTask(() => {
        set(instance, 'bar', 'bar');
        set(instance, 'foo', true);
      });
    }

    ['@test throws an error if an event function is defined in a tagless component']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
        click() {},
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });

      expectAssertion(() => {
        this.render(`{{#foo-bar}}{{/foo-bar}}`);
      }, /You can not define a function that handles DOM events in the .* tagless component since it doesn't have any DOM element./);
    }

    ['@test throws an error if a custom defined event function is defined in a tagless component']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
        folks() {},
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });

      expectAssertion(() => {
        this.render(`{{#foo-bar}}{{/foo-bar}}`);
      }, /You can not define a function that handles DOM events in the .* tagless component since it doesn't have any DOM element./);
    }

    ['@test throws an error if `tagName` is an empty string and `classNameBindings` are specified']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
        foo: true,
        classNameBindings: ['foo:is-foo:is-bar'],
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });

      expectAssertion(() => {
        this.render(`{{#foo-bar}}{{/foo-bar}}`);
      }, /You cannot use `classNameBindings` on a tag-less component/);
    }

    ['@test throws an error if `tagName` is an empty string and `attributeBindings` are specified']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
        attributeBindings: ['href'],
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });
      expectAssertion(() => {
        this.render(`{{#foo-bar}}{{/foo-bar}}`);
      }, /You cannot use `attributeBindings` on a tag-less component/);
    }

    ['@test throws an error if `tagName` is an empty string and `elementId` is specified via JS']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
        elementId: 'turntUp',
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });
      expectAssertion(() => {
        this.render(`{{#foo-bar}}{{/foo-bar}}`);
      }, /You cannot use `elementId` on a tag-less component/);
    }

    ['@test throws an error if `tagName` is an empty string and `elementId` is specified via template']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });
      expectAssertion(() => {
        this.render(`{{#foo-bar elementId='turntUp'}}{{/foo-bar}}`);
      }, /You cannot use `elementId` on a tag-less component/);
    }

    ['@test does not throw an error if `tagName` is an empty string and `id` is specified via JS']() {
      let template = `{{id}}`;
      let FooBarComponent = Component.extend({
        tagName: '',
        id: 'baz',
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });
      this.render(`{{#foo-bar}}{{/foo-bar}}`);
      this.assertText('baz');
    }

    ['@test does not throw an error if `tagName` is an empty string and `id` is specified via template']() {
      let template = `{{id}}`;
      let FooBarComponent = Component.extend({
        tagName: '',
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });
      this.render(`{{#foo-bar id='baz'}}{{/foo-bar}}`);
      this.assertText('baz');
    }

    ['@test does not throw an error if `tagName` is an empty string and `id` is bound property specified via template']() {
      let template = `{{id}}`;
      let FooBarComponent = Component.extend({
        tagName: '',
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });

      this.render(`{{#foo-bar id=fooBarId}}{{/foo-bar}}`, { fooBarId: 'baz' });

      this.assertText('baz');

      this.assertStableRerender();

      this.runTask(() => set(this.context, 'fooBarId', 'qux'));

      this.assertText('qux');

      this.runTask(() => set(this.context, 'fooBarId', 'baz'));

      this.assertText('baz');
    }

    ['@test does not throw an error if `tagName` is an empty string and `id` is specified via template and passed to child component']() {
      let fooBarTemplate = `{{#baz-child id=id}}{{/baz-child}}`;
      let FooBarComponent = Component.extend({
        tagName: '',
      });
      let BazChildComponent = Component.extend();
      let bazChildTemplate = `{{id}}`;

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template: fooBarTemplate,
      });
      this.registerComponent('baz-child', {
        ComponentClass: BazChildComponent,
        template: bazChildTemplate,
      });
      this.render(`{{#foo-bar id='baz'}}{{/foo-bar}}`);
      this.assertText('baz');
    }

    ['@test throws an error if when $() is accessed on component where `tagName` is an empty string']() {
      let template = `hit dem folks`;
      let FooBarComponent = Component.extend({
        tagName: '',
        init() {
          this._super();
          this.$();
        },
      });

      this.registerComponent('foo-bar', {
        ComponentClass: FooBarComponent,
        template,
      });

      expectAssertion(() => {
        this.render(`{{#foo-bar}}{{/foo-bar}}`);
      }, /You cannot access this.\$\(\) on a component with `tagName: \'\'` specified/);
    }

    ['@test renders a contained view with omitted start tag and tagless parent view context']() {
      this.registerComponent('root-component', {
        ComponentClass: Component.extend({
          tagName: 'section',
        }),
        template: '{{frag-ment}}',
      });

      this.registerComponent('frag-ment', {
        ComponentClass: Component.extend({
          tagName: '',
        }),
        template: '{{my-span}}',
      });

      this.registerComponent('my-span', {
        ComponentClass: Component.extend({
          tagName: 'span',
        }),
        template: 'dab',
      });

      this.render(`{{root-component}}`);

      this.assertElement(this.firstChild, { tagName: 'section' });
      this.assertElement(this.firstChild.firstElementChild, {
        tagName: 'span',
      });

      this.runTask(() => this.rerender());

      this.assertElement(this.firstChild, { tagName: 'section' });
      this.assertElement(this.firstChild.firstElementChild, {
        tagName: 'span',
      });
    }
  }
);