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

    beforeEach(ngModule('vnCore'));

    beforeEach(inject(($compile, $rootScope) => {
        $element = $compile(`<vn-treeview></vn-treeview>`)($rootScope);
        controller = $element.controller('vnTreeview');

        const promise = new Promise(() => {
            return {name: 'My item'};
        });

        controller.fetchFunc = () => {
            return promise;
        };

        controller.createFunc = () => {
            return promise;
        };

        controller.removeFunc = () => {
            return promise;
        };

        controller.sortFunc = () => {
            return promise;
        };
    }));

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

    describe('dragOver()', () => {
        it(`should set the dragClientY property`, () => {
            const event = new Event('dragover');
            event.clientY = 100;

            controller.dragOver(event);

            expect(controller.dragClientY).toEqual(100);
        });
    });

    describe('data() setter', () => {
        it(`should set the items property nested into a root element`, () => {
            const items = [{name: 'Item1'}, {name: 'Item2'}];
            controller.data = items;

            const rootItem = controller.items[0];

            expect(rootItem.childs).toEqual(items);
        });
    });

    describe('fetch()', () => {
        it(`should call the fetchFunc() method`, () => {
            jest.spyOn(controller, 'fetchFunc');
            controller.fetch().then(() => {
                expect(controller.data).toBeDefined();
            });

            expect(controller.fetchFunc).toHaveBeenCalledWith();
        });
    });

    describe('setParent()', () => {
        it(`should set the parent property recursively to each element of an item list`, () => {
            jest.spyOn(controller, 'setParent');
            const items = [{name: 'Item1'}, {name: 'Item2', childs: [
                {name: 'Item3'}
            ]}];
            const rootItem = {name: 'Nested tree', sons: items};
            controller.setParent(rootItem, items);

            expect(items[0].parent).toEqual(rootItem);
            expect(items[1].parent).toEqual(rootItem);
            expect(controller.setParent).toHaveBeenCalledWith(rootItem, items[1].childs);
        });
    });

    describe('onToggle()', () => {
        it(`should call the fold() or unfold() methods`, () => {
            jest.spyOn(controller, 'fold');
            jest.spyOn(controller, 'unfold');

            let event = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
            });
            const item = {name: 'My item', sons: 1};

            controller.onToggle(event, item);
            item.active = true;
            controller.onToggle(event, item);

            expect(controller.unfold).toHaveBeenCalledWith(item);
            expect(controller.fold).toHaveBeenCalledWith(item);
        });
    });

    describe('fold()', () => {
        it(`should remove the childs and set the active property to false`, () => {
            const item = {name: 'My item', childs: [{name: 'Item 1'}], active: true};

            controller.fold(item);

            expect(item.childs).toBeUndefined();
            expect(item.active).toBeFalsy();
        });
    });

    describe('unfold()', () => {
        it(`should unfold a parent item`, () => {
            const expectedResponse = [{name: 'Item 1'}, {name: 'Item 2'}];
            jest.spyOn(controller, 'fetchFunc');
            jest.spyOn(controller, 'setParent');
            jest.spyOn(controller, 'sortFunc');
            const parent = {name: 'My item', sons: 1};
            const child = {name: 'Item 1'};
            child.parent = parent;
            parent.childs = [child];

            controller.unfold(parent).then(() => {
                expect(controller.fetchFunc).toHaveBeenCalledWith({$item: parent});
                expect(controller.setParent).toHaveBeenCalledWith(parent, expectedResponse);
                expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object));
                expect(parent.active).toBeTruthy();
            });
        });
    });

    describe('onRemove()', () => {
        it(`should call the removeFunc() method`, () => {
            let event = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
            });

            jest.spyOn(controller, 'removeFunc');
            const item = {name: 'My item'};
            controller.onRemove(event, item);

            expect(controller.removeFunc).toHaveBeenCalledWith({$item: item});
        });
    });

    describe('remove()', () => {
        it(`should remove a child element`, () => {
            const parent = {name: 'My item', sons: 1};
            const child = {name: 'Item 1'};
            child.parent = parent;
            parent.childs = [child];

            controller.remove(child);

            expect(parent.childs).toEqual([]);
            expect(parent.sons).toEqual(0);
        });
    });

    describe('onCreate()', () => {
        it(`should call the createFunc() method`, () => {
            let event = new MouseEvent('click', {
                bubbles: true,
                cancelable: true,
                view: window
            });

            jest.spyOn(controller, 'createFunc');
            const parent = {name: 'My item'};
            controller.onCreate(event, parent);

            expect(controller.createFunc).toHaveBeenCalledWith({$parent: parent});
        });
    });

    describe('create()', () => {
        it(`should unfold an inactive parent and then create a child`, () => {
            jest.spyOn(controller, 'unfold');
            jest.spyOn(controller, 'sortFunc');
            const parent = {name: 'My item', sons: 2, childs: [
                {name: 'Item 1'},
                {name: 'Item 2'}
            ]};
            const child = {name: 'Item 3'};
            child.parent = parent;
            parent.childs.push(child);

            controller.create(child);

            expect(parent.sons).toEqual(3);
            expect(controller.unfold).toHaveBeenCalledWith(parent);
            expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object));
            expect(controller.sortFunc).toHaveBeenCalledTimes(2);
        });

        it(`should create a child on an active parent`, () => {
            jest.spyOn(controller, 'unfold');
            jest.spyOn(controller, 'sortFunc');
            const parent = {name: 'My item', sons: 2, childs: [
                {name: 'Item 1'},
                {name: 'Item 2'}
            ], active: true};
            const child = {name: 'Item 3'};
            child.parent = parent;

            controller.create(child);

            expect(parent.sons).toEqual(3);
            expect(controller.unfold).not.toHaveBeenCalledWith(parent);
            expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object));
            expect(controller.sortFunc).toHaveBeenCalledTimes(2);
        });
    });

    describe('move()', () => {
        it(`should move an item to anocher parent and then unfold the parent`, () => {
            jest.spyOn(controller, 'unfold');
            const newParent = {name: 'My item 2', sons: 0};
            const parent = {name: 'My item', sons: 3, childs: [
                {name: 'Item 1'},
                {name: 'Item 2'}
            ]};
            const child = {name: 'Item 3'};
            child.parent = parent;
            parent.childs.push(child);

            controller.move(child, newParent);

            expect(parent.sons).toEqual(2);
            expect(controller.unfold).toHaveBeenCalledWith(newParent);
        });

        it(`should move an item to anocher parent`, () => {
            jest.spyOn(controller, 'unfold');
            jest.spyOn(controller, 'create');
            const newParent = {name: 'My item 2', sons: 0, active: true};
            const parent = {name: 'My item', sons: 3, childs: [
                {name: 'Item 1'},
                {name: 'Item 2'}
            ]};
            const child = {name: 'Item 3'};
            child.parent = parent;
            parent.childs.push(child);

            controller.move(child, newParent);

            expect(parent.sons).toEqual(2);
            expect(controller.unfold).not.toHaveBeenCalledWith(newParent);
        });
    });
});