I have an angular directive that attaches to an event on the element:
@Directive({
selector: '[myDirective]'
})
export class MyDirective {
@HostListener('click', ['$event']) click(event: Event): void {
debugger;
console.log(event); // <-- this is null in unit tests, MouseEvent when running the app normally
}
}
This works fine, but for some reason the event parameter is null
when unit testing the directive.
My Karma Jasmine unit test setup:
import { CommonModule } from '@angular/common';
import { Component, DebugElement, ElementRef, Injector } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
@Component({
selector: 'host-component',
template: `
<input type="text" myDirective id="findMe"/>
`
})
class HostComponent {
}
describe(`MyDirective`, () => {
let host: HostComponent;
let fixture: ComponentFixture<HostComponent>;
let debugElement: DebugElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CommonModule
],
declarations: [
HostComponent, MyDirective
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HostComponent);
host = fixture.componentInstance;
debugElement = fixture.debugElement.query(By.css('#findMe'))
fixture.autoDetectChanges();
});
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
fixture..triggerEventHandler('click', null);
fixture.detectChanges();
// expect...
});
});
});
Now, the problem: The directive is being hit in the unit test, but there is no event
sent through as a parameter.
I've followed this example: https://codecraft.tv/courses/angular/unit-testing/directives/ but unfortunately it doesnt use the event parameter.
Edit
I've also followed this example to pass through arguments to the @HostListener()
decorator:
@HostListener('mouseenter', ['$event.target.id'])
onMouseEnter(id: string) {
// Logs the id of the element
// where the event is originally invoked.
console.log(id);
}
Edit 2
It seems that the events raised from the DebugElement does not really represent the actual event listeners from the DOM element?
From what Hojou said on this angular github issue, if you trigger the event from the nativeElement it works. So the following code does send through the event to the directive, just not too sure if its the right way:
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
// fixture.triggerEventHandler('click', null);
debugElement.nativeElement.dispatchEvent(newEvent('click'));
fixture.detectChanges();
// expect...
});
});
function newEvent(eventName: string) {
const customEvent: CustomEvent<any> = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
customEvent.initCustomEvent(eventName, false, false, null);
return customEvent;
}
From the Edit 2, raising a custom event does the trick and sends through the attached element of the directive
From what Hojou said on this angular github issue (#22148, currently 'Open'), if you trigger the event from the nativeElement it works. So the following code does send through the event to the directive, just not too sure if its the right way:
describe(`should listen to the events`, () => {
it(`should listen to the click event`, () => {
// fixture.triggerEventHandler('click', null);
debugElement.nativeElement.dispatchEvent(newEvent('click'));
fixture.detectChanges();
// expect...
});
});
function newEvent(eventName: string) {
const customEvent: CustomEvent<any> = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
customEvent.initCustomEvent(eventName, false, false, null);
return customEvent;
}