Angular 如何模拟鼠标事件来测试指令

How to simulate mouse events to test Directive in Angular

我正在尝试测试一个可调整列大小的指令,它会侦听 mouseup、mousemove、mousedown 等鼠标事件。

调整大小-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");
    }
  };
}

我想为此指令创建单元测试,但我无法模拟鼠标事件。我正在使用 triggerEventHandler 来处理事件,但是在模拟 mousedown 和 mousemove 之后我无法更新 max-width 和 flex-basis 的值。

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);
    });
});

请帮助我了解如何测试此指令,并建议是否有更好的方法为此编写测试,因为我是 Jasmine 的新手。提前致谢。

尝试使用

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

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

更新:

我检查了你的代码,这里有两点:

  1. 你应该使用document.dispatchEvent(new MouseEvent('mousemove', {clientX: 50, clientY: 150, buttons: 1}))来模拟鼠标移动事件
  2. '$event' 添加到您的 @HostListener 以防止在处理程序中出现未定义
@HostListener('document: mousemove', ['$event'])
onMouseMove(event: MouseEvent) {
  const offset = 35;
  if (this.pressed && event.buttons) {
    //your code
  }
};

请记住变量 pressed 应该是 true 以使您的代码正常工作