Home Reference Source

src/behaviors/sortable-store.spec.js

/*eslint-env node, jasmine*//*global module, inject*/
/*eslint-disable max-statements, max-params*/
import angular from 'angular';
import 'angular-mocks';
import 'luxyflux/ng-luxyflux';

import {
  Store,
  SortableStore,
  SortableStoreBehavior,
  TransformableCollection,
  Transformer,
  Annotations
} from 'anglue/anglue';

describe('SortableStore', () => {
  // Clear the AnnotationCache for unit tests to ensure we create new annotations for each class.
  beforeEach(() => {
    Annotations.clear();
  });

  describe('SortableStoreBehavior', () => {
    let mockInstance, behavior, refreshSpy, addSpy, removeSpy;
    beforeEach(() => {
      refreshSpy = jasmine.createSpy('refresh');
      addSpy = jasmine.createSpy('addSpy');
      removeSpy = jasmine.createSpy('removeSpy');

      module(angular.module('test', []).name);
      inject(_$filter_ => {
        mockInstance = {
          $filter: _$filter_,
          transformables: {items: {
            refresh: refreshSpy,
            addTransformer: addSpy,
            removeTransformer: removeSpy,
            transformers: []
          }}
        };

        behavior = new SortableStoreBehavior(mockInstance);
      });
    });

    it('should set default values for its config', () => {
      expect(behavior.collection).toEqual('items');
    });

    it('should read the passed config', () => {
      behavior = new SortableStoreBehavior(mockInstance, {
        collection: 'foo'
      });
      expect(behavior.collection).toEqual('foo');
    });

    describe('#transformer', () => {
      it('should define a Transformer instance as the transformers property', () => {
        expect(behavior.transformer).toEqual(jasmine.any(Transformer));
      });

      it('should name the transformer sortableStore', () => {
        expect(behavior.transformer.name).toEqual('sortableStore');
      });

      it('should have a noop fn on the transformer', () => {
        const items = ['foo'];
        expect(behavior.transformer.exec(items)).toEqual(items);
      });
    });

    describe('onSortChange()', () => {
      it('should create working sorter fns on the transformer', () => {
        const transformer = behavior.transformer;
        behavior.onSortChange(false);
        expect(transformer.exec(['bar', 'foo', 'bar'])).toEqual(['bar', 'bar', 'foo']);

        behavior.onSortChange(true);
        expect(transformer.exec(['bar', 'foo', 'bar'])).toEqual(['foo', 'bar', 'bar']);

        behavior.onSortChange('name');
        expect(transformer.exec([{name: 'foo'}, {name: 'bar'}]))
          .toEqual([{name: 'bar'}, {name: 'foo'}]);

        behavior.onSortChange('-name');
        expect(transformer.exec([{name: 'bar'}, {name: 'foo'}]))
          .toEqual([{name: 'foo'}, {name: 'bar'}]);

        behavior.onSortChange(['name', 'age']);
        expect(transformer.exec([
          {name: 'foo', age: 10},
          {name: 'bar', age: 30},
          {name: 'bar', age: 20}
        ])).toEqual([
          {name: 'bar', age: 20},
          {name: 'bar', age: 30},
          {name: 'foo', age: 10}
        ]);
      });

      it('should add the sort transformer if it was not active yet', () => {
        behavior.onSortChange('name');
        expect(addSpy).toHaveBeenCalled();
        expect(refreshSpy).not.toHaveBeenCalled();
      });

      it('should just refresh if there is an active sort transformer', () => {
        behavior.transformableCollection.transformers.push(behavior.transformer);
        behavior.onSortChange('name');
        expect(addSpy).not.toHaveBeenCalled();
        expect(refreshSpy).toHaveBeenCalled();
      });
    });

    describe('onSortClear()', () => {
      it('should remove the sortable transformer from the transformableColleciton', () => {
        behavior.onSortClear();
        expect(removeSpy).toHaveBeenCalledWith(behavior.transformer);
      });
    });
  });

  describe('@SortableStore() decorator', () => {
    @Store() @SortableStore() class TestStore {}
    @Store() @SortableStore({entity: 'custom'}) class CustomSortableStore {}
    @Store() @SortableStore('custom') class CustomSortableStringStore {}
    @Store() @SortableStore({collection: 'foo'}) class CollectionStore {}

    let store, customSortableStore, customSortableStringStore, collectionStore, $filter;
    beforeEach(() => {
      angular.module('test', [
        'luxyflux',
        TestStore.annotation.module.name,
        CustomSortableStore.annotation.module.name,
        CustomSortableStringStore.annotation.module.name,
        CollectionStore.annotation.module.name
      ]).service('ApplicationDispatcher', () => {
        return {
          register() {},
          dispatch() {}
        };
      });

      module('test');
      inject((
        _TestStore_,
        _CustomSortableStore_,
        _CustomSortableStringStore_,
        _CollectionStore_,
        _$filter_
      ) => {
        store = _TestStore_;
        customSortableStore = _CustomSortableStore_;
        customSortableStringStore = _CustomSortableStringStore_;
        collectionStore = _CollectionStore_;
        $filter = _$filter_;
      });
    });

    it('should define the EntityStore API methods on the store', () => {
      [
        'sortableStore',
        'onTestSortChange',
        'onTestSortClear'
      ].forEach(api => expect(store[api]).toBeDefined());
    });

    it('should inject $filter on the store', () => {
      expect(store.$filter).toBe($filter);
    });

    it('should make the collection transformable', () => {
      expect(store.transformables.items).toEqual(jasmine.any(TransformableCollection));
    });

    it('should have an instance of SortableStoreBehavior as the behavior property', () => {
      expect(store.sortableStore).toEqual(jasmine.any(SortableStoreBehavior));
    });

    it('should use the class name to determine the crud entity by default', () => {
      expect(store.sortableStore.config.entity).toEqual('Test');
    });

    it('should be possible to configure the entity to manage', () => {
      expect(customSortableStore.sortableStore.config.entity).toEqual('Custom');
    });

    it('should be possible to pass the entity property as a string', () => {
      expect(customSortableStringStore.sortableStore.config.entity).toEqual('Custom');
    });

    it('should create properly named handlers when configuring the entity', () => {
      expect(customSortableStore.onCustomSortChange).toBeDefined();
      expect(customSortableStore.onCustomSortClear).toBeDefined();
    });

    it('should use the item property as the collection by default', () => {
      expect(store.sortableStore.collection).toEqual('items');
    });

    it('should be possible to configure the collection property the entities are stored in', () => {
      expect(collectionStore.sortableStore.collection).toEqual('foo');
    });

    it('should add handlers for actions', () => {
      expect(TestStore.handlers)
        .toEqual(jasmine.objectContaining({
          TEST_SORT_CHANGE: 'onTestSortChange',
          TEST_SORT_CLEAR: 'onTestSortClear'
        }));
    });
  });
});