import { createComponentFactory, Spectator } from '@ngneat/spectator';
import { UiIconModule } from '@ui/icon';
import { UiSelectBulletComponent } from './select-bullet.component';

describe('SelectBulletComponent', () => {
  let spectator: Spectator<UiSelectBulletComponent>;

  const createComponent = createComponentFactory({
    component: UiSelectBulletComponent,
    imports: [UiIconModule],
  });

  beforeEach(() => {
    spectator = createComponent();
  });

  it('should create', () => {
    expect(spectator.component).toBeTruthy();
  });

  describe('div .ui-bullet-wrapper', () => {
    it('should render', () => {
      expect(spectator.query('div.ui-bullet-wrapper')).toBeVisible();
    });

    it('should have class checked when checked is true', () => {
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(true);
      spectator.detectComponentChanges();
      expect(spectator.query('div.ui-bullet-wrapper')).toHaveClass('checked');
    });

    it('should not have class checked when checked is false', () => {
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(false);
      spectator.detectComponentChanges();
      expect(spectator.query('div.ui-bullet-wrapper')).not.toHaveClass('checked');
    });

    it('should call select() on click', () => {
      spyOn(spectator.component, 'select');
      spectator.click('.ui-bullet-wrapper');
      expect(spectator.component.select).toHaveBeenCalled();
    });
  });

  describe('ui-icon', () => {
    it('should render when checked is true', async () => {
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(true);
      await spectator.detectComponentChanges();
      expect(spectator.query('ui-icon')).toBeVisible();
    });

    it('should not render when checked is false', () => {
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(false);
      spectator.detectComponentChanges();
      expect(spectator.query('ui-icon')).not.toBeVisible();
    });
  });

  describe('checked', () => {
    it('should return true when selected and value are equal', () => {
      const value = 'test';
      spectator.setInput({ value });
      spectator.component['selected'] = value;
      expect(spectator.component.checked).toBe(true);
    });

    it('should return false when selected and value are not equal', () => {
      spectator.setInput({ value: 'test' });
      spectator.component['selected'] = 'test2';
      expect(spectator.component.checked).toBe(false);
    });
  });

  describe('writeValue()', () => {
    it('should call selectValue with passed obj and emitEvent false', () => {
      spyOn(spectator.component, 'selectValue');

      const obj = 'test';
      spectator.component.writeValue(obj);
      expect(spectator.component.selectValue).toHaveBeenCalledWith(obj, false);
    });
  });

  describe('registerOnTouched()', () => {
    it('should set onTouched to the passed function', () => {
      const touched = () => {};
      spectator.component.registerOnTouched(touched);
      expect(spectator.component['onTouched']).toEqual(touched);
    });
  });

  describe('registerOnChange()', () => {
    it('should set onChange to the passed function', () => {
      const change = () => {};
      spectator.component.registerOnChange(change);
      expect(spectator.component['onChange']).toEqual(change);
    });
  });

  describe('setDisabledState()', () => {
    it('should set disabled to false when false was passed', () => {
      spectator.component.setDisabledState(false);
      expect(spectator.component.disabled).toBe(false);
    });

    it('should set disabled to true when true was passed', () => {
      spectator.component.setDisabledState(true);
      expect(spectator.component.disabled).toBe(true);
    });
  });

  describe('selectValue()', () => {
    it('should set selected to the passed value', () => {
      const value = 'testValue';
      spectator.component.selectValue(value);
      expect(spectator.component['selected']).toBe(value);
    });

    it('should call onChange() with the control value when emitEvent is true', () => {
      spectator.component['onChange'] = jasmine.createSpy();

      const value = 'value';
      spectator.setInput({ value });
      spectator.component.selectValue('testValue', true);
      expect(spectator.component['onChange']).toHaveBeenCalledWith(value);
    });

    it('should call onChange() when emitEvent is false', () => {
      spectator.component['onChange'] = jasmine.createSpy();
      spectator.component.selectValue('testValue', false);
      expect(spectator.component['onChange']).not.toHaveBeenCalled();
    });

    it('should call onTouched when emitEvent is true', () => {
      spectator.component['onTouched'] = jasmine.createSpy();
      spectator.component.selectValue('testValue', true);
      expect(spectator.component['onTouched']).toHaveBeenCalled();
    });

    it('should not call onTouched when emitEvent is false', () => {
      spectator.component['onTouched'] = jasmine.createSpy();
      spectator.component.selectValue('testValue', false);
      expect(spectator.component['onTouched']).not.toHaveBeenCalled();
    });
  });

  describe('select()', () => {
    it('should call stopPropagation() on the passed event', () => {
      const event = new MouseEvent('test');
      event.stopPropagation = jasmine.createSpy();
      spectator.component.select(event);
      expect(event.stopPropagation).toHaveBeenCalled();
    });

    it('should set selected to undefined when checked is true', () => {
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(true);

      spectator.component['selected'] = 'test';
      spectator.component.select(new MouseEvent('test'));
      expect(spectator.component['selected']).toBeUndefined();
    });

    it('should set selected to value when checked is false', () => {
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(false);

      spectator.component.value = 'test';
      spectator.component.select(new MouseEvent('test'));
      expect(spectator.component['selected']).toBe(spectator.component.value);
    });

    it('should call onChange() with value when checked is true', () => {
      spectator.component['onChange'] = jasmine.createSpy();
      spectator.component.value = 'test';
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(true);
      spectator.component.select(new MouseEvent('test'));

      expect(spectator.component['onChange']).toHaveBeenCalledWith(spectator.component.value);
    });

    it('should call onChange() with undefined when checked is false', () => {
      spectator.component['onChange'] = jasmine.createSpy();
      spectator.component.value = 'test';
      spyOnProperty(spectator.component, 'checked', 'get').and.returnValue(false);
      spectator.component.select(new MouseEvent('test'));

      expect(spectator.component['onChange']).toHaveBeenCalledWith(undefined);
    });

    it('should call onTouched()', () => {
      spectator.component['onTouched'] = jasmine.createSpy();
      spectator.component.select(new MouseEvent('test'));
      expect(spectator.component['onTouched']).toHaveBeenCalled();
    });
  });
});
