angularunit-testingangular-materialcoded-ui-tests

CdkDragDrop unit testing events


So, i've been working on unit testing for my Angular app. I'm using Angular Material and i have a component which uses the drag-drop CDK cdk drag-drop API.

the html code looks like this

<mat-card class="interventionCard">
  <div  cdkDropListGroup class="container">
    <div cdkDropList
         [cdkDropListData]="interventionsTodo" (cdkDropListDropped)="drop($event)" class="dragContainer">
      <div class="dragInter" *ngFor="let i of interventionsTodo" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      <a class="resetInv" (click)="reset()"><div class="dragInter"><img src="../assets/images/simulation-intervention/noIntervention.svg" class="interventionIc" alt=""></div></a><!---->
    </div>

    <div class="dropContainer">
      <div cdkDropList [cdkDropListData]="interventionsDoneNow" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDoneNow" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      </div>
        <div class="connectorL"></div>
      <div cdkDropList [cdkDropListData]="interventionsDonePlus5" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDonePlus5" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc"></div>
      </div>
      <div class="connectorL"></div>
      <div cdkDropList  [cdkDropListData]="interventionsDonePlus10" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDonePlus10" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      </div>
      <div class="connectorL"></div>
      <div cdkDropList  [cdkDropListData]="interventionsDonePlus15" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDonePlus15" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      </div>
      <div class="connectorL"></div>
      <div cdkDropList  [cdkDropListData]="interventionsDonePlus20" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDonePlus20" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      </div>
      <div class="connectorL"></div>
      <div cdkDropList  [cdkDropListData]="interventionsDonePlus25" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDonePlus25" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      </div>
      <div class="connectorL"></div>
      <div cdkDropList [cdkDropListData]="interventionsDonePlus30" (cdkDropListDropped)="drop($event)" class="dropZone">
        <div class="dragInterDropped" *ngFor="let i of interventionsDonePlus30" cdkDrag><img src="{{i.imgSrc}}" class="interventionIc" alt=""></div>
      </div>
    </div>

All fine, now i have the cdkDropListDroppet event which calls the function drop()

    drop(event: CdkDragDrop<{ imgSrc: string }[], any>) {
     if (event.previousContainer === event.container) {
       moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
     } else {
         if(event.container.data === this.interventionsTodo){
         this.deleteSpecificItemFromTimeline(event.previousContainer.data[0]);
         }else if(event.container.data.length <1){
           copyArrayItem(event.previousContainer.data,
             event.container.data,
             event.previousIndex,
             event.currentIndex);
         }
      }
    }

this works fine, now im unit testing the angular app (jasmine/karma) and i am trying to dispatch the CdkDragDrop event so that i can verify the functionality of my drop() function.

So in the angular documentation there is a small chapter on component DOM testing, where its mentioned that you have to get the htmlElement where you want to fire the event which i do like this:angular docs

    let Dbg = fixture.debugElement;
    let htE = Dbg.nativeElement.querySelector(".dragContainer");

now i have the element on which i want to fire the event so full code looks like this

    let Dbg = fixture.debugElement;
      let htE = Dbg.nativeElement.querySelector(".dragContainer");

      htE.addEventListener('CdkDragDrop', (ev:any) => {
        component.drop(ev);
      });
      let bla = htE.dispatchEvent(new Event('cdkDropListDropped'));

So bla returns true, which (if im not wrong) means the event was fired, but nothing seems to happen fixture.detectchanges() happens automatically.. i tried to debug it to see what is happening but the listenerfunction is never fired and i looked everywhere to find an answer but cant seem to find anything on that kind of eventtesting..


Solution

  • Instead of trying to fire events from your template, you could test your drop method with some mocked events. I'm doing so by using a generic DragDropEventFactory.

    summary-table-side-bar.component.spec.ts illustrates how corresponding unit tests may look like.