Угловой модульный тест с @ContentChild завершается неудачно - PullRequest
0 голосов
/ 14 мая 2019

У меня есть пользовательское меню, закрывающееся на компоненте меню Angular Material, и я хочу проверить это, но оно продолжает давать сбой и выдает "TypeError: Невозможно прочитать свойство 'closeMenu' of undefined"

Файл спецификации (тест внизу):

@Component({
  selector: 'app-test',
  template: `
    <app-header-menu>
      <button [matMenuTriggerFor]="test">Test</button>
      <mat-menu #test="matMenu">menu content</mat-menu>
    </app-header-menu>
  `
})
class TestComponent {}

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

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

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

  it ('should close on click', () => {
    document.dispatchEvent(new MouseEvent('click'));
    expect(fixture.debugElement.query(By.css('app-header-menu')).nativeElement.trigger.closeMenu()).toHaveBeenCalled();
  });
});

Файл компонента:

export class HeaderMenuComponent {
  @ContentChild(MatMenuTrigger) trigger: MatMenuTrigger;

  @HostListener('document:click', ['$event'])
  onClick = event => {
    if (this.trigger && this.trigger.menuOpen && !this.elRef.nativeElement.contains(event.target)) {
      this.trigger.closeMenu();
    }
  }
}

Кто-нибудь понял, что я делаю не так?

1 Ответ

1 голос
/ 24 мая 2019

Использование компонента-обертки - верный путь, если вы хотите проверить @ContentChild(ren).

Однако дело в том, что для проверки триггера на закрытие необходимо сначала открыть его, прежде чем вызвать событие щелчка на документе.

PFB моя тестовая настройка и сам тест. То, что я не выяснил, это то, почему mat-menu не открывается после нажатия кнопки. Я вызвал openMenu напрямую, используя дочерний контент.

В некотором смысле, имхо, нет необходимости проверять функциональность углового материала, поэтому прямой запуск меню в любом случае должен подойти для вашего теста.

То, что я сделал по-другому в вашей тестовой настройке, я использовал componentInstance из компонента, имеющего MatMenuTrigger, так как у нативного HtmlElement нет ни триггера, ни метода closeMenu для шпионажа.

Вот стек стека :


component.spec.ts

@Component({
    selector: 'test-cmp',
    template: `<app-component>
        <button [matMenuTriggerFor]="test">Test</button>
        <mat-menu #test="matMenu">menu content</mat-menu>
    </app-component>`,
  })
  class TestWrapperComponent { }


describe('AppComponent', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<TestWrapperComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [AppComponent, TestWrapperComponent],
            providers: [],
            imports: [MatMenuModule, BrowserAnimationsModule],
            schemas: []
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(TestWrapperComponent);
        component = fixture.debugElement.children[0].componentInstance;
    });

    it('should be created', () => {
      fixture.detectChanges();
      expect(component).toBeTruthy();
    });

    it ('should close on click', fakeAsync(() => {
    fixture.detectChanges();
    const closeMenueSpy: jasmine.Spy = spyOn(component.trigger, 'closeMenu');

    // Not too sure why this menu doesn't open on click
    // const debugElem: DebugElement = fixture.debugElement.query(By.css('button'));
    // debugElem.triggerEventHandler('click', null);
    component.trigger.openMenu();
    tick();

    expect(component.trigger.menuOpen).toBeTruthy();

    document.dispatchEvent(new MouseEvent('click'));
    tick();

    expect(closeMenueSpy).toHaveBeenCalled();
  }));
});
...