import { TestBed, ComponentFixture } from "@angular/core/testing"; import { Component, Output, ViewChild, EventEmitter } from "@angular/core"; import { By } from "@angular/platform-browser"; // Load the implementations that should be tested import { SamDragDropDirective, DragState } from "./drag-drop.directive"; interface FakeDragEvent { preventDefault: () => void; stopPropagation: () => void; dataTransfer?: { dropEffect?: string; files?: unknown[] }; target?: EventTarget; } @Component({ selector: "test-cmp", template: `
dummy
`, standalone: false, }) class TestComponent { @Output() action: EventEmitter = new EventEmitter(); @ViewChild("var", { static: true }) var; @ViewChild("dummydrop", { static: true }) dummydrop; dropHandler() { this.action.emit(true); } stateChange() { this.action.emit(true); } } describe("The Sam Focus directive", () => { let directive: SamDragDropDirective; let component: TestComponent; let fixture: ComponentFixture; beforeEach(() => { TestBed.configureTestingModule({ declarations: [SamDragDropDirective, TestComponent], }); fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; fixture.detectChanges(); directive = fixture.debugElement .query(By.directive(SamDragDropDirective)) .injector.get(SamDragDropDirective); }); it("should compile", () => { expect(true).toBe(true); }); it("should emit drag+drop event", () => { component.action.subscribe((val) => { expect(val).toBe(true); }); directive.onWindowDrop({ preventDefault: function () {}, stopPropagation: function () {}, dataTransfer: { files: ["test.jpg"], }, target: component.dummydrop.nativeElement, }); }); it("should emit dragover event", () => { component.action.subscribe((val) => { expect(val).toBe(true); }); directive.onWindowDragover({ preventDefault: function () {}, stopPropagation: function () {}, dataTransfer: { files: ["test.jpg"], }, target: component.dummydrop.nativeElement, }); directive.onElementDragend({ preventDefault: function () {}, stopPropagation: function () {}, dataTransfer: { files: ["test.jpg"], }, target: component.dummydrop.nativeElement, }); }); it("should emit window drop event", () => { const preventDefault = vi.fn(); const stopPropagation = vi.fn(); directive.onWindowDrop({ preventDefault, stopPropagation, }); expect(preventDefault).toHaveBeenCalled(); expect(stopPropagation).toHaveBeenCalled(); }); it("should set drag state to NotDragging on element drag end", () => { directive.dragState = DragState.DraggingInTarget; directive.onElementDragend({ preventDefault: () => undefined, stopPropagation: () => undefined, }); expect(directive.dragState).toBe(DragState.NotDragging); }); describe("onElementDrop", () => { it("does nothing but sets dropEffect to none when disabled", () => { directive.disabled = true; const event: FakeDragEvent = { preventDefault: vi.fn(), stopPropagation: vi.fn(), dataTransfer: { dropEffect: "", files: ["test.jpg"] }, target: component.dummydrop.nativeElement, }; const dropSpy = vi.fn(); directive.dropEvent.subscribe(dropSpy); directive.onElementDrop(event); expect(event.dataTransfer.dropEffect).toBe("none"); expect(dropSpy).not.toHaveBeenCalled(); }); it("emits dropEvent with files when the drop is inside the target with files", () => { const files = ["test.jpg"]; const event: FakeDragEvent = { preventDefault: vi.fn(), stopPropagation: vi.fn(), dataTransfer: { dropEffect: "", files }, target: component.dummydrop.nativeElement, }; const dropSpy = vi.fn(); directive.dropEvent.subscribe(dropSpy); directive.onElementDrop(event); expect(dropSpy).toHaveBeenCalledWith(files); expect(directive.dragState).toBe(DragState.NotDragging); }); it("does not emit dropEvent when the drop target has no files", () => { const event: FakeDragEvent = { preventDefault: vi.fn(), stopPropagation: vi.fn(), dataTransfer: { dropEffect: "", files: [] }, target: component.dummydrop.nativeElement, }; const dropSpy = vi.fn(); directive.dropEvent.subscribe(dropSpy); directive.onElementDrop(event); expect(dropSpy).not.toHaveBeenCalled(); }); }); describe("onElementDragOver", () => { it("sets dropEffect to none and skips processing when disabled", () => { directive.disabled = true; const event: FakeDragEvent = { preventDefault: vi.fn(), stopPropagation: vi.fn(), dataTransfer: { dropEffect: "" }, target: component.dummydrop.nativeElement, }; directive.onElementDragOver(event); expect(event.dataTransfer.dropEffect).toBe("none"); }); it("sets DraggingInTarget state and copy dropEffect when dragging inside the target", () => { const event: FakeDragEvent = { preventDefault: vi.fn(), stopPropagation: vi.fn(), dataTransfer: { dropEffect: "" }, target: component.dummydrop.nativeElement, }; directive.onElementDragOver(event); expect(directive.dragState).toBe(DragState.DraggingInTarget); expect(event.dataTransfer.dropEffect).toBe("copy"); }); it("sets DraggingOutsideTarget state and none dropEffect when dragging outside the target", () => { const outsideElement = document.createElement("div"); const event: FakeDragEvent = { preventDefault: vi.fn(), stopPropagation: vi.fn(), dataTransfer: { dropEffect: "" }, target: outsideElement, }; directive.onElementDragOver(event); expect(directive.dragState).toBe(DragState.DraggingOutsideTarget); expect(event.dataTransfer.dropEffect).toBe("none"); }); }); });