swimlane/ngx-ui

View on GitHub
projects/swimlane/ngx-ui/src/lib/components/select/select.component.spec.ts

Summary

Maintainability
D
1 day
Test Coverage
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { KeyboardKeys } from '../../enums/keyboard-keys.enum';
import { SelectComponentFixture } from './select.component.fixture';
import { SelectComponent } from './select.component';
import { SelectOptionDirective } from './select-option.directive';
import { SelectInputComponent } from './select-input.component';
import { SelectDropdownComponent } from './select-dropdown.component';
import { selectDropdownOptionMock } from './select-dropdown-option.mock';

describe('SelectComponent', () => {
  let component: SelectComponentFixture;
  let fixture: ComponentFixture<SelectComponentFixture>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      schemas: [NO_ERRORS_SCHEMA],
      imports: [FormsModule],
      declarations: [
        SelectComponent,
        SelectOptionDirective,
        SelectInputComponent,
        SelectDropdownComponent,
        SelectComponentFixture
      ]
    });
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(SelectComponentFixture);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('can load instance', () => {
    expect(component).toBeTruthy();
    expect(component.select).toBeTruthy();
    expect(component.select.filterable).toBeFalsy();
    expect(component.select.filterCaseSensitive).toBeFalsy();
  });

  describe('value', () => {
    beforeEach(() => {
      component.selected = [component.select.options[0].value];
      fixture.detectChanges();
    });

    it('should update value', () => {
      component.select.value = [component.select.options[1].value];
      fixture.detectChanges();
      expect(component.selected.length).toBe(1);
    });

    it('should not update value if hasnt changed', () => {
      // eslint-disable-next-line no-self-assign
      component.select.value = component.select.value;
      fixture.detectChanges();
      expect(component.selected.length).toBe(1);
    });
  });

  describe('dropdownVisible', () => {
    it('should be false when disableDropdown', () => {
      component.disableDropdown$.next(true);
      fixture.detectChanges();
      expect(component.select.dropdownVisible).toBeFalsy();
    });

    it('should be false when tagging and !options', () => {
      component.tagging$.next(true);
      component.options$.next([]);
      fixture.detectChanges();
      component.select.options = [];
      expect(component.select.dropdownVisible).toBeFalsy();
    });
  });

  describe('onDropdownSelection', () => {
    beforeEach(() => {
      component.select.value = [];
    });

    it('should do nothing if option disabled', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.onDropdownSelection(selectDropdownOptionMock({ disabled: true }));
      expect(spy).not.toHaveBeenCalled();
    });

    it('should do nothing if max selection reached', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.value = component.options$.value.map(o => o.value);
      component.select.onDropdownSelection(selectDropdownOptionMock());
      expect(spy).not.toHaveBeenCalled();
    });

    describe('closeOnSelect', () => {
      beforeEach(() => {
        component.select.closeOnSelect = true;
        component.select.multiple = false;
        component.select.value = [];
        fixture.detectChanges();
      });

      it('should close dropdown', () => {
        const spy = spyOn(component.select, 'toggleDropdown');
        component.select.onDropdownSelection(selectDropdownOptionMock());
        expect(spy).toHaveBeenCalled();
      });
    });

    describe('tagging', () => {
      beforeEach(() => {
        fixture = TestBed.createComponent(SelectComponentFixture);
        component = fixture.componentInstance;
        component.tagging$.next(true);
        fixture.detectChanges();
      });

      beforeEach(() => {
        component.select.value = [];
        fixture.detectChanges();
      });

      it('should reset input if tagging', () => {
        component.select.onDropdownSelection(selectDropdownOptionMock());
        expect(component.select.inputComponent.inputElement.nativeElement.value).toBe('');
      });
    });

    describe('value', () => {
      it('should set value if option found', () => {
        component.select.onDropdownSelection(component.select.options[0]);
        fixture.detectChanges();
        expect(component.selected.length).toBe(1);
      });

      it('should multi select if option found', () => {
        component.multiple$.next(true);
        component.select.value = [component.options$.value[0].value];
        fixture.detectChanges();
        component.select.onDropdownSelection(component.options$.value[1]);
        expect(component.selected.length).toBe(2);
      });

      it('should set value if option found with identifier', () => {
        component.select.identifier = 'value';
        component.select.value = [{ value: 'test1' }];
        component.select.options = [
          selectDropdownOptionMock({ value: { value: 'test' } }),
          selectDropdownOptionMock({ value: { value: 'test1' } }),
          selectDropdownOptionMock({ value: { value: 'test2' } })
        ];
        fixture.detectChanges();

        component.select.onDropdownSelection(component.select.options[0]);
        expect(component.selected.length).toBe(1);
      });

      it('should not set value if already selected', () => {
        component.select.value = ['test'];
        component.select.onDropdownSelection({ name: 'test', value: 'test' });
        fixture.detectChanges();
        expect(component.selected.length).toBe(1);
      });
    });
  });

  describe('onInputSelection', () => {
    beforeEach(() => {
      component.selected = [];
      fixture.detectChanges();
    });

    it('should set value', () => {
      component.select.onInputSelection([component.select.options[0].value]);
      fixture.detectChanges();
      expect(component.selected.length).toBe(1);
    });
  });

  describe('onFocus', () => {
    it('should do nothing if disabled', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.disabled = true;
      component.select.onFocus();
      expect(spy).not.toHaveBeenCalled();
    });

    it('should toggle dropdown', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.disabled = false;
      component.select.onFocus();
      expect(spy).toHaveBeenCalled();
    });
  });

  describe('onClear', () => {
    beforeEach(() => {
      component.selected = [component.select.options[0].value];
      fixture.detectChanges();
    });

    it('should clear selection', () => {
      component.select.onClear();
      fixture.detectChanges();
      expect(component.selected.length).toBe(0);
    });
  });

  describe('onBodyClick', () => {
    const event: any = {
      stopPropagation: () => undefined,
      target: document.createElement('div')
    };

    beforeEach(() => {
      component.select.dropdownActive = true;
      fixture.detectChanges();
    });

    it('should do nothing if not active', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.dropdownActive = false;
      component.select.onBodyClick(event);
      expect(spy).not.toHaveBeenCalled();
    });

    it('should toggle dropdown', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.onBodyClick(event);
      expect(spy).toHaveBeenCalled();
    });
  });

  describe('onClose', () => {
    it('should toggle dropdown', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.onClose();
      expect(spy).toHaveBeenCalledWith(false);
    });
  });

  describe('onToggle', () => {
    it('should do nothing if disabled', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.disabled = true;
      component.select.onToggle();
      expect(spy).not.toHaveBeenCalled();
    });

    it('should toggle dropdown', () => {
      const spy = spyOn(component.select, 'toggleDropdown');
      component.select.onToggle();
      expect(spy).toHaveBeenCalled();
    });
  });

  describe('toggleDropdown', () => {
    beforeEach(() => {
      fixture = TestBed.createComponent(SelectComponentFixture);
      component = fixture.componentInstance;
      component.closeOnBodyClick$.next(true);
      fixture.detectChanges();
    });

    it('should call toggle listener', () => {
      component.select.toggleDropdown(!component.select.dropdownActive);
      const spy = spyOn(component.select, 'toggleListener');
      component.select.toggleDropdown(!component.select.dropdownActive);
      expect(spy).toHaveBeenCalled();
    });
  });

  describe('onKeyUp', () => {
    let event: any;

    beforeEach(() => {
      event = {
        stopPropagation: () => undefined,
        key: ''
      };

      component.select.focusIndex = 0;
      fixture.detectChanges();
    });

    it('should focus next element on arrow down', () => {
      const idx = component.select.focusIndex;
      event.key = KeyboardKeys.ARROW_DOWN;
      component.select.onKeyUp({ event, value: '' });
      expect(component.select.focusIndex).toEqual(idx + 1);
    });

    it('should set filter when not arrow down', () => {
      component.select.onKeyUp({ event, value: 'test' });
      expect(component.select.filterQuery).toEqual('test');
    });
  });

  describe('invalid', () => {
    it('should have valid class when invalid but not touched', () => {
      component.select.required = true;
      component.select.value = undefined;
      expect(component.select.touched).toBeFalsy();
      expect(fixture.elementRef.nativeElement.querySelector('ngx-select').classList.contains('ng-invalid')).toBeFalsy();
      expect(fixture.elementRef.nativeElement.querySelector('ngx-select').classList.contains('ng-valid')).toBeTruthy();
    });

    it('should be true when required and value invalid', () => {
      component.select.required = true;
      component.select.value = undefined;
      expect(component.select.invalid).toBeTruthy();
      component.select.value = [];
      expect(component.select.invalid).toBeTruthy();
    });

    it('should be false when required and value valid', () => {
      component.select.required = true;
      component.select.value = ['test'];
      expect(component.select.invalid).toBeFalsy();
    });

    it('should be true when value.length > maxSelections', () => {
      component.select.maxSelections = 1;
      component.select.value = ['test', 'test1'];
      expect(component.select.invalid).toBeTruthy();
    });
  });

  describe('requiredIndicatorView', () => {
    it('should get indicator if required', () => {
      component.select.required = true;
      component.select.requiredIndicator = '*';
      expect(component.select.requiredIndicatorView).toEqual('*');
    });
  });

  describe('optionTemplates', () => {
    it('should get templates', () => {
      expect(component.select.optionTemplates.length).toBeGreaterThan(0);
    });

    it('should not set options if !value', () => {
      component.select.optionTemplates = { toArray: () => ['test'] } as any;
      component.select.optionTemplates = undefined;
      expect(component.select.optionTemplates).toBeUndefined();
      expect(component.select.options.length).toBe(1);
    });
  });

  describe('autosize', () => {
    it('setting autosize adds class to component', () => {
      component.autosize$.next(true);
      fixture.detectChanges();
      const select = fixture.nativeElement.querySelector('.ngx-select');
      expect(select).toHaveClass('autosize');
    });
  });

  describe('setDisabledState', () => {
    it('should disable select element accordingly', async () => {
      component.select.setDisabledState(true);

      fixture.detectChanges();
      await fixture.whenStable();

      expect(component.select.disabled).toBeTrue();
    });

    it('should enable select element accordingly', async () => {
      // first disables the component
      component.select.setDisabledState(true);

      fixture.detectChanges();
      await fixture.whenStable();

      expect(component.select.disabled).toBeTrue();

      component.select.setDisabledState(false);

      fixture.detectChanges();
      await fixture.whenStable();

      expect(component.select.disabled).toBeFalse();
    });
  });
});