Triggering event listeners bound to document with custom events in tests #12518

Closed
wesleycho opened this Issue Oct 25, 2016 · 3 comments

Comments

Projects
None yet
6 participants
@wesleycho
Contributor

wesleycho commented Oct 25, 2016

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[x] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Currently triggering custom event listeners bound to document is awkward.
DebugElement.prototype.triggerEventHandler with 'myEvent' or 'document:myEvent' passed as the event name does not match any listener, so my listener is not fired in the test.

Expected behavior
I expect to be able to pass 'myEvent' or 'document:myEvent' to DebugElement.prototype.triggerEventHandler so that I can take advantage of passing custom objects into the event handler. Any other approaches to this that satisfies ease of testing with custom event property values would also be welcome.

Minimal reproduction of the problem with instructions
N/A - discussed situation with @robwormald about being able to trigger an event listener bound to document by a directive via @HostListener('document:myEvent', ['$event'])

What is the motivation / use case for changing the behavior?
I would like an easier way to test event listeners bound to document by directives with the ability to pass custom event objects. Currently there is DebugElement.prototype.triggerEventHandler, but passing 'myEvent' or 'document:myEvent' does not match any listener, so my listener is not fired in the test.

My use case is because I have a complex module for dragging & dropping built purely in Angular 2 that my company intends on open sourcing, so I need to test DOM positioning & logic involving the mouse cursor. I am trying to hook into a directive's listener bound to document:mousemove in order to test that it repositions my dragged element in the proper location.

I can currently hack around this by querying the directive, creating a fake mouse event object, and using the injector for the element to get the directive instance & firing the method directly, but this is awkward. This is necessary for me currently because our CI setup uses PhantomJS, and we don't have anything like headless Chrome yet, so creating a custom MouseEvent using new MouseEvent is not an option.

Please tell us about your environment: macOS Sierra

  • Angular version: 2.1.1
  • Browser: PhantomJS
  • Language: TypeScript 2.0.3
  • Node (for AoT issues): node --version = 4.5.0

@wesleycho wesleycho changed the title from Triggering event listeners bound to document with custom events to Triggering event listeners bound to document with custom events in tests Oct 25, 2016

@juliemr juliemr self-assigned this Oct 25, 2016

@vicb vicb added the comp: testing label Oct 26, 2016

@deanproxy

This comment has been minimized.

Show comment
Hide comment
@deanproxy

deanproxy Apr 21, 2017

Just to show a workaround solution to help others...

@Component({
   template: '<div myDirective>'
})
class TestComponent implements OnInit {
   // Here is how to get access directly to your directive from your test component.
   @ViewChild(MyDirective) directive: MyDirective;

   constructor() { }
   ngOnInit() { }
}

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

   beforeEach(async(() => {
      TestBed.configureTestingModule({
         declarations: [ TestComponent, MyDirective ]
       })
       .compileComponents();
   }));

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

   it('should organize the tabs', () => {
      // Now we can use that directive and call the method that was bound to document:mouseup
      component.directive.onMouseUp({ pageX: 10, pageY: 20 });

      // expect tests below...
   });
});    

Just to show a workaround solution to help others...

@Component({
   template: '<div myDirective>'
})
class TestComponent implements OnInit {
   // Here is how to get access directly to your directive from your test component.
   @ViewChild(MyDirective) directive: MyDirective;

   constructor() { }
   ngOnInit() { }
}

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

   beforeEach(async(() => {
      TestBed.configureTestingModule({
         declarations: [ TestComponent, MyDirective ]
       })
       .compileComponents();
   }));

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

   it('should organize the tabs', () => {
      // Now we can use that directive and call the method that was bound to document:mouseup
      component.directive.onMouseUp({ pageX: 10, pageY: 20 });

      // expect tests below...
   });
});    
@applecool

This comment has been minimized.

Show comment
Hide comment
@applecool

applecool Aug 21, 2017

@deanproxy Fabulous workaround! 🍻 cheers.

@deanproxy Fabulous workaround! 🍻 cheers.

@vikerman

This comment has been minimized.

Show comment
Hide comment
@vikerman

vikerman Jan 26, 2018

Contributor

Closing this as there is a workaround.

Contributor

vikerman commented Jan 26, 2018

Closing this as there is a workaround.

@vikerman vikerman closed this Jan 26, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment