describe('Component smartTable', () => {
    let $element;
    let controller;
    let $httpBackend;

    beforeEach(ngModule('vnCore'));

    beforeEach(inject(($compile, $rootScope, _$httpBackend_) => {
        $httpBackend = _$httpBackend_;
        $element = $compile(`<smart-table></smart-table>`)($rootScope);
        controller = $element.controller('smartTable');
        controller.model = {
            refresh: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
            addFilter: jest.fn(),
            userParams: {}
        };
    }));

    afterEach(() => {
        $element.remove();
    });

    describe('options setter()', () => {
        it(`should throw an error if the table doesn't have an identifier`, () => {
            const options = {activeButtons: {shownColumns: []}};

            expect(() => controller.options = options).toThrowError(/View identifier not defined/);
        });

        it('should not throw an error if the table does have an identifier', () => {
            const options = {activeButtons: {shownColumns: []}};
            controller.viewConfigId = 'test';

            expect(() => controller.options = options).not.toThrow();
        });
    });

    describe('getDefaultViewConfig()', () => {
        it('should perform a query and return the default view columns', done => {
            const expectedResponse = [{
                columns: {}
            }];

            $httpBackend.expectGET('DefaultViewConfigs').respond(expectedResponse);
            controller.getDefaultViewConfig().then(columns => {
                expect(columns).toEqual(expectedResponse[0].columns);
                done();
            }).catch(done.fail);
            $httpBackend.flush();
        });
    });

    describe('viewConfig setter', () => {
        it('should just call applyViewConfig() if a viewConfig was provided', () => {
            spyOn(controller, 'applyViewConfig');
            controller.viewConfig = [{}];

            expect(controller.applyViewConfig).toHaveBeenCalled();
        });

        it('should not get a defaultConfig then insert a new one', () => {
            spyOn(controller, 'applyViewConfig');

            controller.$.userViewModel = {
                insert: jest.fn()
            };

            const emptyResponse = [{
                columns: {}
            }];

            controller.columns = [
                {field: 'test1'},
                {field: 'test2'}
            ];

            $httpBackend.expectGET('DefaultViewConfigs').respond(emptyResponse);
            controller.viewConfig = [];
            $httpBackend.flush();

            const expectedObject = {configuration: {'test1': true, 'test2': true}};

            expect(controller.$.userViewModel.insert).toHaveBeenCalledWith(expect.objectContaining(expectedObject));
            expect(controller.applyViewConfig).toHaveBeenCalled();
        });
    });

    describe('defaultOrder', () => {
        it('should insert a new object to the controller sortCriteria with a sortType value of "ASC"', () => {
            const element = document.createElement('div');
            controller.model.order = 'id';
            controller.columns = [
                {field: 'id', element: element},
                {field: 'test1', element: element},
                {field: 'test2', element: element}
            ];

            controller.defaultOrder();

            const firstSortCriteria = controller.sortCriteria[0];

            expect(firstSortCriteria.field).toEqual('id');
            expect(firstSortCriteria.sortType).toEqual('ASC');
            expect(firstSortCriteria.priority).toEqual(1);
        });

        it('should add new entries to the controller sortCriteria with a sortType values of "ASC" and "DESC"', () => {
            const element = document.createElement('div');
            controller.model.order = 'test1, id DESC';

            controller.columns = [
                {field: 'id', element: element},
                {field: 'test1', element: element},
                {field: 'test2', element: element}
            ];

            controller.defaultOrder();

            const firstSortCriteria = controller.sortCriteria[0];
            const secondSortCriteria = controller.sortCriteria[1];

            expect(firstSortCriteria.field).toEqual('test1');
            expect(firstSortCriteria.sortType).toEqual('ASC');
            expect(firstSortCriteria.priority).toEqual(1);

            expect(secondSortCriteria.field).toEqual('id');
            expect(secondSortCriteria.sortType).toEqual('DESC');
            expect(secondSortCriteria.priority).toEqual(2);
        });
    });

    describe('addFilter()', () => {
        it('should call the model addFilter() with a basic where filter if exprBuilder() was not received', () => {
            controller.addFilter('myField', 'myValue');

            const expectedFilter = {
                where: {
                    myField: 'myValue'
                }
            };

            expect(controller.model.addFilter).toHaveBeenCalledWith(expectedFilter);
        });

        it('should call the model addFilter() with a built where filter resultant of exprBuilder()', () => {
            controller.exprBuilder = jest.fn().mockReturnValue({builtField: 'builtValue'});

            controller.addFilter('myField', 'myValue');

            const expectedFilter = {
                where: {
                    builtField: 'builtValue'
                }
            };

            expect(controller.model.addFilter).toHaveBeenCalledWith(expectedFilter);
        });
    });

    describe('applySort()', () => {
        it('should call the $state go and model refresh without making changes on the model order', () => {
            controller.$state = {
                go: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
                current: {
                    name: 'section'
                }
            };
            jest.spyOn(controller, 'refresh');

            controller.applySort();

            expect(controller.model.order).toBeUndefined();
            expect(controller.$state.go).toHaveBeenCalled();
        });

        it('should call the $state go and model refresh after setting model order according to the controller sortCriteria', () => {
            const orderBy = {field: 'myField', sortType: 'ASC'};
            controller.$state = {
                go: jest.fn().mockReturnValue(new Promise(resolve => resolve())),
                current: {
                    name: 'section'
                }
            };
            jest.spyOn(controller, 'refresh');

            controller.sortCriteria = [orderBy];

            controller.applySort();

            expect(controller.model.order).toEqual(`${orderBy.field} ${orderBy.sortType}`);
            expect(controller.$state.go).toHaveBeenCalled();
        });
    });

    describe('filterSanitizer()', () => {
        it('should remove the where filter after leaving no fields in it', () => {
            controller.model.userFilter = {
                where: {fieldToRemove: 'valueToRemove'}
            };
            controller.model.userParams = {};

            const result = controller.filterSanitizer('fieldToRemove');

            const exectedObject = {userFilter: {}, userParams: {}};

            expect(result).toEqual(exectedObject);
        });

        it('should remove the where filter after leaving no fields and "empty ands/ors" in it', () => {
            controller.model.userFilter = {
                where: {
                    and: [
                        {aFieldToRemove: 'aValueToRemove'},
                        {aFieldToRemove: 'aValueToRemove'},
                        {
                            or: [
                                {aFieldToRemove: 'aValueToRemove'},
                                {aFieldToRemove: 'aValueToRemove'},
                            ]
                        }
                    ]
                }
            },
            controller.model.userParams = {};

            const result = controller.filterSanitizer('aFieldToRemove');

            const exectedObject = {userFilter: {}, userParams: {}};

            expect(result).toEqual(exectedObject);
        });

        it('should not remove the where filter after leaving no empty "ands/ors" in it', () => {
            controller.model.userFilter = {
                where: {
                    and: [
                        {aFieldToRemove: 'aValueToRemove'},
                        {aFieldToRemove: 'aValueToRemove'},
                        {
                            or: [
                                {aFieldToRemove: 'aValueToRemove'},
                                {aFieldToRemove: 'aValueToRemove'},
                            ]
                        }
                    ],
                    or: [{dontKillMe: 'thanks'}]
                }
            };
            controller.model.userParams = {};

            const result = controller.filterSanitizer('aFieldToRemove');

            const exectedObject = {userFilter: {where: {or: [{dontKillMe: 'thanks'}]}}, userParams: {}};

            expect(result).toEqual(exectedObject);
        });
    });

    describe('saveAll()', () => {
        it('should throw an error if there are no changes to save in the model', () => {
            jest.spyOn(controller.vnApp, 'showError');
            controller.model.isChanged = false;
            controller.saveAll();

            expect(controller.vnApp.showError).toHaveBeenCalledWith('No changes to save');
        });

        it('should call the showSuccess() if there are changes to save in the model', done => {
            jest.spyOn(controller.vnApp, 'showSuccess');

            controller.model.save = jest.fn().mockReturnValue(Promise.resolve());
            controller.model.isChanged = true;

            controller.saveAll().then(() => {
                expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
                done();
            }).catch(done.fail);
        });
    });

    describe('defaultFilter()', () => {
        it('should call model refresh and model addFilter with filter', () => {
            controller.exprBuilder = jest.fn().mockReturnValue({builtField: 'builtValue'});

            controller.$params = {
                q: '{"tableQ": {"fieldName":"value"}}'
            };
            controller.columns = [
                {field: 'fieldName'}
            ];
            controller.$inputsScope = {
                searchProps: {}
            };

            controller.defaultFilter();

            expect(controller.model.addFilter).toHaveBeenCalled();
        });
    });

    describe('searchPropsSanitizer()', () => {
        it('should searchProps sanitize', () => {
            controller.$inputsScope = {
                searchProps: {
                    filterOne: '1',
                    filterTwo: ''
                }
            };
            const searchPropsExpected = {
                filterOne: '1'
            };
            const newSearchProps = controller.searchPropsSanitizer();

            expect(newSearchProps).toEqual(searchPropsExpected);
        });
    });
});