import commonModule from '../..';

describe('wsContentSelector directive', () => {
  let outerScope;
  let isolatedScope;
  let element;

  beforeEach(angular.mock.module(commonModule));

  function injectElement(elementHtml) {
    beforeEach(angular.mock.inject(($rootScope, $compile) => {
      outerScope = $rootScope.$new();
      outerScope.options = [
        { body: 'test1', header: 'head1', value: 'a' },
        { body: 'test2', header: 'head2', value: 'b' },
        { body: 'test3', header: 'head3', value: 'c', color: '#7cb7b6' },
        { body: 'test4', header: 'head4', value: 'd', color: '#f27c5e' },
        { body: 'test5', header: 'head5', value: 'e' },
        { body: "test6 <br/><br/><a href='http://www.wealthsimple.com' target='_blank'>Wealthsimple</a>", header: 'head6', value: 'f' },
      ];
      outerScope.selected = outerScope.options[0];
      outerScope.selectedMulti = [outerScope.options[2], outerScope.options[4]];
      element = angular.element(elementHtml);
      $compile(element)(outerScope);
      outerScope.$digest();
      isolatedScope = element.isolateScope();
    }));
  }

  describe('with invalid type', () => {
    it('should throw an exception', () => {
      expect(() => { injectElement('<ws-content-selector type="foo" />'); })
        .toThrow();
    });
  });

  describe('with options', () => {
    injectElement('<ws-content-selector options="options" />');

    it('should not modify passed-in options', () => {
      expect(outerScope.options[0].trustedBody).not.toBeDefined();
    });
  });

  describe('without type', () => {
    injectElement('<ws-content-selector options="options" />');
    it('should default to single', () => {
      expect(isolatedScope.type).toEqual('single');
    });
  });

  describe('without row size', () => {
    injectElement('<ws-content-selector options="options" />');
    it('should default to 4', () => {
      expect(isolatedScope.rowSize).toEqual(4);
    });
  });

  describe('with row size', () => {
    injectElement('<ws-content-selector options="options" row-size="2" />');
    it('should use provided row size', () => {
      expect(parseInt(isolatedScope.rowSize, 10)).toEqual(2);
    });

    it('should calculate a col size assuming 12 grid units', () => {
      expect(parseInt(isolatedScope.colSize, 10)).toEqual(6);
    });
  });

  describe('without stacked', () => {
    injectElement('<ws-content-selector options="options" />');
    it('should default to false', () => {
      expect(isolatedScope.stacked).toBeFalsy();
    });
  });

  describe('with stacked true', () => {
    injectElement('<ws-content-selector options="options" stacked="true"/>');
    it('should set stacked to true', () => {
      expect(isolatedScope.stacked).toBeTruthy();
    });
    it('should set row size to 1', () => {
      expect(isolatedScope.rowSize).toEqual(1);
    });
  });

  describe('with stacked false', () => {
    injectElement('<ws-content-selector options="options" stacked="false"/>');
    it('should set stacked to false', () => {
      expect(isolatedScope.stacked).toBeFalsy();
    });
    it('should use default row size of 4', () => {
      expect(isolatedScope.rowSize).toEqual(4);
    });
  });

  describe('without selected items', () => {
    injectElement('<ws-content-selector options="options" />');
    it('no items should be selected', () => {
      const selectedElems = element.find('.selected');
      expect(selectedElems.length).toEqual(0);
    });
  });

  describe('with 1 selected item', () => {
    injectElement('<ws-content-selector options="options" selected="selected" />');

    it('1 internal item should be selected', () => {
      expect(isolatedScope.selected).toBeDefined();
    });

    it('1 external item should be selected', () => {
      expect(outerScope.selected).toBeDefined();
    });

    it('external item and internal item should refer to the same object', () => {
      expect(outerScope.selected).toBe(isolatedScope.selected);
    });

    it('has one element with a selected CSS class', () => {
      const selectedItems = element.find('.selected');
      expect(selectedItems.length).toEqual(1);
    });
  });

  describe('with multiple selected items', () => {
    injectElement('<ws-content-selector options="options" type="multi" selected="selectedMulti" />');

    it('all provided items should be selected', () => {
      expect(isolatedScope.selected.length).toEqual(2);
    });

    it('has multiple elements with a selected CSS class', () => {
      const selectedItems = element.find('.selected');
      expect(selectedItems.length).toEqual(2);
    });
  });

  describe('#select', () => {
    injectElement('<ws-content-selector options="options" type="multi" selected="selectedMulti" />');

    it('deselects items that are currently selected', () => {
      const alreadySelectedItem = isolatedScope.selected[0];
      const alreadySelectedElement = element.find('.selected')[0];

      isolatedScope.select(alreadySelectedItem);

      expect(isolatedScope.selected).not.toContain(alreadySelectedItem);
      outerScope.$digest();
      expect(outerScope.selectedMulti).not.toContain(alreadySelectedItem);
      isolatedScope.$digest();
      expect(element.find('.selected')).not.toContain(alreadySelectedElement);
    });

    it('selects items that are currently unselected', () => {
      const notYetSelectedItem = isolatedScope.options[0];

      isolatedScope.select(notYetSelectedItem);

      expect(isolatedScope.selected).toContain(notYetSelectedItem);
      outerScope.$digest();
      expect(outerScope.selectedMulti).toContain(notYetSelectedItem);
      isolatedScope.$digest();
      expect(element.find('.selected')).toContain(element.find('.option')[0]);
    });
  });

  describe('when selected items are changed in the outer scope', () => {
    injectElement('<ws-content-selector options="options" type="multi" selected="selectedMulti" />');

    it('sets the items in the content selector appopriately', () => {
      outerScope.selectedMulti = [outerScope.options[0]];
      outerScope.$digest();
      expect(element.find('.selected').length).toBe(1);

      outerScope.selectedMulti.push(outerScope.options[1]);
      outerScope.$digest();
      expect(element.find('.selected').length).toBe(2);
    });
  });

  describe('validations - required', () => {
    function buildForm(type, model) {
      const content = `<ws-content-selector name="contentSelector" options="options" type="${type}" ng-model="${model}" required />`;
      return `<ng-form name="testForm">${content}</ng-form>`;
    }

    describe('single-select', () => {
      injectElement(buildForm('single', 'selected'));

      it('is valid when an option is chosen', () => {
        outerScope.selected = outerScope.options[2];
        outerScope.$digest();
        expect(outerScope.testForm.contentSelector.$valid).toBe(true);
      });

      it('is invalid when no option is chosen', () => {
        outerScope.selected = undefined;
        outerScope.$digest();
        expect(outerScope.testForm.contentSelector.$valid).toBe(false);
      });
    });

    describe('multi-select', () => {
      injectElement(buildForm('multi', 'selectedMulti'));

      it('is valid when one option is chosen', () => {
        outerScope.selectedMulti = [outerScope.options[2]];
        outerScope.$digest();
        expect(outerScope.testForm.contentSelector.$valid).toBe(true);
      });

      it('is valid when multiple options are chosen', () => {
        outerScope.selectedMulti = [outerScope.options[2], outerScope.options[4]];
        outerScope.$digest();
        expect(outerScope.testForm.contentSelector.$valid).toBe(true);
      });

      it('is invalid when no options are chosen', () => {
        outerScope.selectedMulti = [];
        outerScope.$digest();
        expect(outerScope.testForm.contentSelector.$valid).toBe(false);
      });
    });
  });
});
