import { Component, DebugElement } from '@angular/core'; import { ComponentFixture, ComponentFixtureAutoDetect, inject, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { DraggableItem, DraggableItemService, SortableComponent, SortableItem, SortableModule } from '../index'; import { SpyObject } from "@ngneat/spectator"; const HEROES: string[] = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado']; // eslint-disable-next-line @typescript-eslint/no-explicit-any const HEROES_OBJ: any[] = [ { id: 1, name: 'Windstorm' }, { id: 2, name: 'Bombasto' }, { id: 3, name: 'Magneta' } ]; @Component({ template: ` ` }) class TestSortableComponent { selectedState?: string; heroes: string[] = [...HEROES]; // eslint-disable-next-line @typescript-eslint/no-explicit-any heroesObj: any[] = [...HEROES_OBJ]; } xdescribe('Component: Sortable', () => { let fixture: ComponentFixture; let sort1: SortableComponent; let sort2: SortableComponent; beforeEach( () => { fixture = TestBed.configureTestingModule({ declarations: [TestSortableComponent], imports: [SortableModule.forRoot(), FormsModule], providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }] }).createComponent(TestSortableComponent); fixture.detectChanges(); const sortableComponents = fixture.debugElement .queryAll(By.directive(SortableComponent)) .map( (de: DebugElement) => de.injector.get(SortableComponent) ); [sort1, sort2] = sortableComponents; } ); it('should be defined on the testing component', () => { expect(sort1).not.toBeNull(); expect(sort2).not.toBeNull(); }); it('different zones should have different ids', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any expect((sort1 as any).currentZoneIndex).not.toBe( // eslint-disable-next-line @typescript-eslint/no-explicit-any (sort2 as any).currentZoneIndex ); }); describe('onChange', () => { it('should render list of strings', () => { // arrange // act const renderedItems = getItemsByContainerId(); // assert expect(renderedItems).toEqual(HEROES); } ); it('should render list of complex models', () => { // arrange // act const renderedItems = getItemsByContainerId('sort2'); // assert // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(renderedItems).toEqual(HEROES_OBJ.map((h: any) => h.name)); }); }); it('should apply active item style over item style', () => { // arrange const activeItemStyle = Object.assign( {}, sort1.itemStyle, sort1.itemActiveStyle ); // act const style = sort1.getItemStyle(true); // assert expect(style).toEqual(activeItemStyle); }); it('should return normal item style', () => { // arrange const normalItemStyle = Object.assign({}, sort1.itemStyle); // act const style = sort1.getItemStyle(false); // assert expect(style).toEqual(normalItemStyle); }); describe('process drag & drop', () => { let transfer: DraggableItemService; let item: SortableItem; let event: DragEvent; let draggableItem: DraggableItem; let spyOnChanged: SpyObject; let spyGetItem: SpyObject; let spyCaptureItem: SpyObject; let sort1ZoneNumber: number; let spyPreventDefault: SpyObject; let spyOnDrop: SpyObject; beforeEach( inject([DraggableItemService], (service: DraggableItemService) => { transfer = service; item = getItemToDrag(); event = { preventDefault: Function.prototype, dataTransfer: { setData: Function.prototype } } as DragEvent; // eslint-disable-next-line @typescript-eslint/no-explicit-any sort1ZoneNumber = (sort1 as any).currentZoneIndex; draggableItem = getDraggableItem(item, event, sort1ZoneNumber); spyOnChanged = jest.spyOn(sort1, 'onChanged'); spyGetItem = jest.spyOn(transfer, 'getItem').mockReturnValue(draggableItem); spyCaptureItem = jest.spyOn(transfer, 'captureItem').mockReturnValue( draggableItem ); spyPreventDefault = jest.spyOn(event, 'preventDefault'); spyOnDrop = jest.spyOn(sort1, 'onDrop').mockImplementation(); }) ); it('should pass dragged item to transfer', () => { // arrange const spy = jest.spyOn(transfer, 'dragStart'); // act sort1.onItemDragstart(event, item, 0); // assert expect(spy).toHaveBeenCalledWith( getDraggableItem(item, event, sort1ZoneNumber) ); }); it('sould prevent event default when dragover item', () => { // arrange // act sort1.onItemDragover(event, 1); // assert expect(spyPreventDefault).toHaveBeenCalled(); }); it('souldn NOT prevent event default when no item is dragged over items', () => { // arrange spyGetItem.and.returnValue(undefined); // act sort1.onItemDragover(event, 1); // assert expect(spyPreventDefault).not.toHaveBeenCalled(); }); it('sould prevent event default when dragover zone', () => { // arrange // act sort1.cancelEvent(event); // assert expect(spyPreventDefault).toHaveBeenCalled(); }); it('souldn NOT prevent event default when no item is dragged over zone', () => { // arrange spyGetItem.and.returnValue(undefined); // act sort1.cancelEvent(event); // assert expect(spyPreventDefault).not.toHaveBeenCalled(); }); it('should remove item if it was captured or dropped in another continer', () => { // arrange draggableItem.overZoneIndex = -1; // act sort1.onDrop(draggableItem); // assert expect(spyOnChanged).toHaveBeenCalledWith([ HEROES[1], HEROES[2], HEROES[3] ]); }); it('shouldn NOT remove item if it was dropped in the same continer', () => { // arrange // act sort1.onDrop(draggableItem); // assert expect(spyOnChanged).not.toHaveBeenCalled(); }); it('should fire onChanged when drag over item', () => { // arrange // act sort1.onItemDragover(event, 1); // assert expect(spyOnChanged).toHaveBeenCalled(); }); it('should swap first and second item', () => { // arrange // act sort1.onItemDragover(event, 1); // assert expect(spyOnChanged).toHaveBeenCalledWith([ HEROES[1], HEROES[0], HEROES[2], HEROES[3] ]); }); it('should return unchanged array', () => { // arrange // act sort1.onItemDragover(event, 0); // assert expect(spyOnChanged).toHaveBeenCalledWith(HEROES); }); it('should move first item to the end', () => { // arrange // act sort1.onItemDragover(event, 3); // assert expect(spyOnChanged).toHaveBeenCalledWith([ HEROES[1], HEROES[2], HEROES[3], HEROES[0] ]); }); it('should move last item to the begining', () => { // arrange item.id = 3; item.initData = item.value = HEROES[3]; draggableItem.i = 3; // act sort1.onItemDragover(event, 0); // assert expect(spyOnChanged).toHaveBeenCalledWith([ HEROES[3], HEROES[0], HEROES[1], HEROES[2] ]); }); it('should insert a new item if was empty', () => { // arrange sort1.writeValue([]); // act sort1.onItemDragover(event, 0); // assert expect(spyOnChanged).toHaveBeenCalledWith([HEROES[0]]); }); it('should insert a new item', () => { // arrange item.value = item.initData = 'new'; draggableItem.i = 4; // act sort1.onItemDragover(event, 0); // assert expect(spyOnChanged).toHaveBeenCalledWith(['new', ...HEROES]); }); it('should call onDrop when item is over an another container', done => { // arrange spyGetItem.and.callThrough(); spyCaptureItem.and.callThrough(); sort1.onItemDragstart(event, item, 0); // act const capturedItem = transfer.captureItem(-1, 0); // assert transfer .onCaptureItem() .subscribe(() => { expect(spyOnDrop).toHaveBeenCalledWith(capturedItem); done(); }); }); it('should remove item when it is over an another container', done => { // arrange spyGetItem.and.callThrough(); spyCaptureItem.and.callThrough(); sort1.onItemDragstart(event, item, 0); // act transfer.captureItem(-1, 0); // assert transfer .onCaptureItem() .subscribe(() => { expect(spyOnChanged).toHaveBeenCalledWith([ HEROES[1], HEROES[2], HEROES[3] ]); done(); } ); } ); it('shouldn NOT remove item when it is dropped into the same container', done => { // arrange spyGetItem.and.callThrough(); spyCaptureItem.and.callThrough(); sort1.onItemDragstart(event, item, 0); // act transfer.captureItem(draggableItem.overZoneIndex, 4); // assert transfer .onCaptureItem() .subscribe(() => { expect(spyOnChanged).toHaveBeenCalledWith([...HEROES]); done(); }); } ); it('should reset active item after drop', done => { // arrange spyGetItem.and.callThrough(); spyCaptureItem.and.callThrough(); sort1.onItemDragstart(event, item, 0); // act transfer.captureItem(draggableItem.overZoneIndex, 4); // assert transfer .onCaptureItem() .subscribe(() => { // eslint-disable-next-line @typescript-eslint/no-explicit-any expect((sort1 as any).activeItem).toBe(-1); done(); }); }); function getItemToDrag(): SortableItem { return { id: 0, value: HEROES[0], initData: HEROES[0] }; } function getDraggableItem(sortableItem: SortableItem, dragEvent: DragEvent, zone: number): DraggableItem { return { event: dragEvent, item: sortableItem, i: 0, initialIndex: 0, lastZoneIndex: zone, overZoneIndex: zone }; } }); function getItemsByContainerId(id = 'sort1'): string[] { return fixture.debugElement .queryAll(By.css(`#${id} div[draggable]`)) // eslint-disable-next-line @typescript-eslint/no-explicit-any .map((item: any) => item.nativeElement.innerText); } });