Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RouterTestingModule: DebugElement.triggerEventHandler errors for an anchor tag #11804

Closed
wardbell opened this issue Sep 21, 2016 · 6 comments
Closed

Comments

@wardbell
Copy link
Contributor

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

[x] bug report
[ ] feature request
[ ] support request

Current behavior
Given a RouterLink on an anchor tag like this:

<a routerLink="/simple">click me</a>

The test expression debugElement.triggerEventHandler('click', null); throws error Cannot read property 'button' of null

Traced the problem to a line in the generated component.ngfactory.js:

_View_RootComponent0.prototype._handle_click_1_0 = function($event) {
  var self = this;
  self.markPathToRootAsCheckOnce();
  self.debug(1,1,2);
  var pd_0 = (self._RouterLinkWithHref_1_3.onClick($event.button,$event.ctrlKey,$event.metaKey) !== false);
  return (true && pd_0);
};

Notice the reference to $event.button. In practice, the $event is null and wouldn't be a button anyway.

See repro

Expected behavior
Should be able to trigger button.

Workaround is to click the native element:

 // element.triggerEventHandler('click', null); // bug
    element.nativeElement.click(); // works in Chrome, fails in phantom

Angular version: 2.0.0-final

Reproduction of the problem

Paste this as a spec in any test harness and watch it fail:

import { Location } from '@angular/common';
import { Component } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, inject, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';

@Component({selector: 'root-cmp', template: `
  <a routerLink="/simple">click me</a>
  <router-outlet></router-outlet>
`})
class RootComponent {}

@Component({selector: 'blank-cmp', template: `<p>blank component</p>`})
class DefaultComponent {}


@Component({selector: 'simple-cmp', template: `simple`})
class FirstPageComponent {}

fdescribe('Integration', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([
          {path: '', component: DefaultComponent},
          {path: 'simple', component: FirstPageComponent}
        ]),
      ],
      declarations: [
        RootComponent,
        DefaultComponent,
        FirstPageComponent
      ]
    });
  });

  // Fails when call triggerEventHandler
  it('should navigate when the link is clicked',
      fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
    const fixture = createRoot(router, RootComponent);
    let element = fixture.debugElement.query(By.css('a'));

    // element.triggerEventHandler('click', null); // bug!
    // element.nativeElement.dispatchEvent(newEvent('click')); // also fails by doing nothing
    element.nativeElement.click(); // works in Chrome, fails in phantom

    advance(fixture);
    expect(location.path()).toEqual('/simple');
  })));

});

function advance(fixture: ComponentFixture<any>): void {
  tick();
  fixture.detectChanges();
}

function createRoot(router: Router, type: any): ComponentFixture<any> {
  const f = TestBed.createComponent(type);
  advance(f);
  router.initialNavigation();
  advance(f);
  return f;
}

function newEvent(eventName: string, bubbles = false, cancelable = false) {
  let evt = document.createEvent('CustomEvent');  // MUST be 'CustomEvent'
  evt.initCustomEvent(eventName, bubbles, cancelable, null);
  return evt;
}
@wardbell
Copy link
Contributor Author

wardbell commented Sep 21, 2016

Per @vsavkin this is not a bug. It works as intended. We're supposed to pass a mouse eventObj as the second parameter to triggerEventHandler.

This works:

const leftclickevent = {button: 0}; // left mouse button click event
element.triggerEventHandler('click', leftclickevent );

Personally, I'm adding a utility method to my library of helpers (see the docs Testing Chapter)


// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
export const ButtonClickEvents = {
   left:  { button: 0 },
   right: { button: 2 }
};

/** Simulate element click. Defaults to left mouse button click. */
export function click(el: DebugElement | HTMLElement, eventObj: any = ButtonClickEvents.left): void {
  if (el instanceof HTMLElement) {
    el.click();
  } else {
    el.triggerEventHandler('click', eventObj);
  }
}

so now, after importing click, I write:

  it('should navigate when the link is clicked',
      fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
    const fixture = createRoot(router, RootComponent);
    const element = fixture.debugElement.query(By.css('a'));
    click(element);
    advance(fixture);
    expect(location.path()).toEqual('/simple');
  })));

@vsavkin
Copy link
Contributor

vsavkin commented Sep 22, 2016

Talk to Ward about it.

This isn't an issue with the router. null is not a valid substitution for a mouse event. So it's OK for the router to throw.

Closing this issue.

@ishitatsuyuki
Copy link

I don't want to bump, but the docs mentions this usage without describing details of DebugElement. We should fix that for less misperception.

@ilkercat
Copy link

(Meanwhile) it is mentioned in the docs:
https://angular.io/guide/testing#triggereventhandler

Other handlers are less forgiving. For example, the RouterLink directive expects an object with a button property that identifies which mouse button was pressed. This directive throws an error if the event object doesn't do this correctly.

@jesus-crysist
Copy link

I know that this ticket is closed, but I stumbled upon this issue while trying to test click on anchor with no routerLink directive and it won't trigger click event at all. It doesn't display any errors but it won't work either.

Is there any indications that this issue will be solved or documentation updated to display the real use case?

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants