Search code examples
angularangular-directiveangular-test

Testing a clickable Angular Directive with an Event Emitter - @Output() isn't raised / fired


I have a little directive that detects if the user has clicked outside the HTML element that the directive has been attached to, it looks like this, nothing special:

@Directive({
  selector: '[vcrClickOutside]'
})
export class ClickOutsideDirective {

  constructor(private elementRef: ElementRef) { }

  @Output()
  clickOutside = new EventEmitter();

  @HostListener('document:click', ['$event.target'])
  onClick(targetElement): void {
    const clickedInside = this.elementRef.nativeElement.contains(targetElement);
    if (!clickedInside) {
      this.clickOutside.emit(targetElement);
    }
  }
}

Now it is time to test my directive so I created a test file, introduce a Mock Component, attach the directive and within my test I click the Mock Component's Template and try to catch the emitter, here's the test:

// Mock Component
@Component({
  template: '<div (clickOutside)="toggleClick($event)"><div vcrClickOutside>Oh I just love writing tests</div></div>'
})
class MockComponent {
  constructor() { }
  hasBeenClicked = false;
  toggleClick(ev): void {
    this.hasBeenClicked = !this.hasBeenClicked;
  }
}
describe('ClickOutsideDirective', () => {

  let component: MockComponent;
  let fixture: ComponentFixture<MockComponent>;
  let element: HTMLElement;
  let outSideDiv: HTMLElement;
  let insideDiv: HTMLElement;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        MockComponent,
        ClickOutsideDirective
      ]
    });

    fixture = TestBed.createComponent(MockComponent);
    component = fixture.componentInstance;
    element = fixture.debugElement.nativeElement;
    outSideDiv = element.querySelectorAll('div')[0];
    insideDiv = element.querySelectorAll('div')[1];
    fixture.detectChanges();
  });

  it('click the outer div thus emitting the clickOutside and toggle the component value', () => {
    outSideDiv.click();
    fixture.detectChanges();
    expect(component.hasBeenClicked).toBeTruthy();
  });

Everything seems fine but when I click the outside div, even though I know that the directive is emitting the @Output on the outer div isn't being raised, so looking at the code (clickOutside) isn't invoked and thus the method toggleClick($event) isn't ran, what am I doing wrong?

*** UPDATE ****

I had the output on the wrong div! I am an idiot!


Solution

  • As answered in my comments - I put the emitter in the wrong location!

    @Component({
      template: '<div><div vcrClickOutside (clickOutside)="toggleClick($event)">Oh I just love writing tests</div></div>'
    })