Asif Zaidi
Asif Zaidi

Reputation: 93

How to simulate mouse events to test Directive in Angular

I am trying to test a column resizable directive which listens to mouse events like mouseup, mousemove, mousedown.

resize-column.directive.ts

import { Directive, OnInit, Renderer2, Input, ElementRef, HostListener } from "@angular/core";

@Directive({
  selector: "[resizeColumn]"
})
export class ResizeColumnDirective implements OnInit {

  @Input() index: number;    
  private startX: number;    
  private startWidth: number;    
  private column: HTMLElement;    
  private table: HTMLElement;    
  private pressed: boolean;

  constructor(private renderer: Renderer2, private el: ElementRef) {
    this.column = this.el.nativeElement;
  }

  ngOnInit() {
    if (this.resizable) {
      const row = this.renderer.parentNode(this.column);
      this.table = this.renderer.parentNode(row);

      const resizer = this.renderer.createElement("span");
      this.renderer.addClass(resizer, "resize-holder");
      this.renderer.appendChild(this.column, resizer);
      this.renderer.listen(resizer, "mousedown", this.onMouseDown);
    }
  }

  onMouseDown = (event: MouseEvent) => {
    this.pressed = true;
    this.startX = event.pageX;
    this.startWidth = this.column.offsetWidth;
  };

  @HostListener('document: mousemove')
  onMouseMove(event: MouseEvent) {
    const offset = 35;
    if (this.pressed && event.buttons) {
      this.renderer.addClass(this.table, "resizing");

      // Calculate width of column
      let width =
        this.startWidth + (event.pageX - this.startX - offset);

      const tableCells = Array.from(this.table.querySelectorAll(".custom-row")).map(
        (row: any) => row.querySelectorAll(".custom-cell").item(this.index)
      );

      // Set table header width
      this.renderer.setStyle(this.column, "max-width", `${width}px`);
      this.renderer.setStyle(this.column, "flex-basis", `${width}px`);

      // Set table cells width
      for (const cell of tableCells) {
        this.renderer.setStyle(cell, "max-width", `${width}px`);
        this.renderer.setStyle(cell, "flex-basis", `${width}px`);
      }
    }
  };

  @HostListener('document: mouseup')
  onMouseUp(){
    if (this.pressed) {
      this.pressed = false;
      this.renderer.removeClass(this.table, "resizing");
    }
  };
}

I want to create unit test for this directive but I am not able to simulate mouse events. I am using triggerEventHandler to handle events but I am not able to update the values of max-width and flex-basis after simulating mousedown and mousemove.

resize-column.directive.spec.ts

import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ResizeColumnDirective } from './resize-column.directive';

@Component({
    template: `
    <custom-table>
        <custom-row>
            <custom-header *ngFor="let column of displayedColumns; let i = index"
                 resizeColumn [index]="i">
                {{column.label}}
            </custom-header>
        </custom-row>
        <custom-row *ngFor="let row of rowDatas">
            <custom-cell *ngFor="let column of displayedColumns">{{row[column.field]}}
            </custom-cell>
        </custom-row>
    </custom-table>`
})
class TestComponent {
    displayedColumns = [
        { field: 'name', label: 'Name' },
        { field: 'sex', label: 'Sex' },
        { field: 'age', label: 'Age' }
    ];

    rowDatas = [{
        name: 'Albert',
        sex: 'M',
        age: '20'
    }.....];
}

describe('RsizableDirective', () => {
    let component: TestComponent;
    let fixture: ComponentFixture<TestComponent>;

    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [ResizeColumnDirective, TestComponent, table component components....]
        });

        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create component', () => {
        expect(component).toBeDefined();
    });

    it('should update the flex and max-width value on mouse move', () => {
        const headerEl = fixture.debugElement.query(By.css('custom-header'));
        console.log(headerEl.nativeElement.style.flex);
        headerEl.triggerEventHandler('mousedown', { pageX: 50 });
        headerEl.triggerEventHandler('mousemove', { pageX: 150 });
        fixture.detectChanges();
        console.log(headerEl.nativeElement.style.flex);
    });
});

Please help me on how to test this directive and suggest is there any better way to write test for this as I am new to Jasmine. Thanks in advance.

Upvotes: 1

Views: 7432

Answers (1)

Yury Polubinsky
Yury Polubinsky

Reputation: 373

Try to use

headerEl.dispatchEvent(new MouseEvent('mousemove', {clientX: 50, clientY: 150}));

or

headerEl.dispatchEvent(new Event('mousemove', {pageX: 50, pageY: 150}));

Update:

I've checked your code, there're two points here:

  1. You should use document.dispatchEvent(new MouseEvent('mousemove', {clientX: 50, clientY: 150, buttons: 1})) to simulate mouse move event
  2. Add '$event' to your @HostListener to prevent undefined in a handler
@HostListener('document: mousemove', ['$event'])
onMouseMove(event: MouseEvent) {
  const offset = 35;
  if (this.pressed && event.buttons) {
    //your code
  }
};

Keep in mind that the variable pressed should be true to make your code work

Upvotes: 3

Related Questions