dimanche 30 décembre 2018

Component test for ContentChildren with directive not working

I have a modal component also using the bootstrap. Among the features, I have a @ContentChildren for the CloseModalDirective directive that makes any element be responsible for closing modal when clicked. The problem is that I can not test this functionality.

The component works perfectly in the application, my problem is only with the test

This is my component:

import {
    AfterContentInit,
    Component,
    ContentChildren,
    Directive,
    ElementRef, EventEmitter,
    Input,
    OnDestroy,
    Output,
    QueryList,
    ViewChild
} from '@angular/core';

import $ from 'jquery';
import ModalSizes from './modal-size.enum';

@Directive({ selector: '[appCloseModal]' })
export class CloseModalDirective {}

@Component({
    selector: 'app-modal',
    templateUrl: './modal.component.html',
    styleUrls: ['./modal.component.scss']
})
export class ModalComponent implements AfterContentInit, OnDestroy {

    @Input() header: string;

    @Input() size = ModalSizes.Medium;

    @Input() closeable = true;

    @Output() openFinished = new EventEmitter();

    @Output() closeFinished = new EventEmitter();

    @ViewChild('modal') modal: ElementRef;

    // FINDING MY ELEMENTS        
    @ContentChildren(CloseModalDirective, { descendants: true, read: ElementRef })
    closeButtons: QueryList<ElementRef> = new QueryList();

    show() {
        $(this.modal.nativeElement).modal({
          backdrop: this.closeable ? true : 'static',
          keyboard: this.closeable,
          show: true
        });
    }

    hide() {
        $(this.modal.nativeElement).modal('hide');
    }

    ngAfterContentInit() {
        $(this.modal.nativeElement).on('shown.bs.modal', () => {
          this.openFinished.emit();
        });

        $(this.modal.nativeElement).on('hide.bs.modal', () => {
          this.closeFinished.emit();
        });

        // ADDING HIDE EVENT
        this.closeButtons.forEach(button => {
          button.nativeElement.addEventListener('click', this.hide.bind(this), false);
        });
    }

    ngOnDestroy() {
        $(this.modal.nativeElement).off('shown.bs.modal');
        $(this.modal.nativeElement).off('hide.bs.modal');

        this.closeButtons.forEach(button => {
          button.nativeElement.removeEventListener('click', this.hide.bind(this));
        });

        $(this.modal.nativeElement).modal('dispose');
    }

}

This is my tests:

import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';

import {CloseModalDirective, ModalComponent} from './modal.component';

import {Component} from '@angular/core';
import ModalSize from './modal-size.enum';
import 'bootstrap';

@Component({
    selector: 'app-test',
    template: `
        <app-modal header="Title">
          <div class="content">Content</div>
          <button appCloseModal footer>Close</button>
        </app-modal>
    `
})
class TestComponent {}

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

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

    beforeEach(() => {
        fixture = TestBed.createComponent(ModalComponent);
        component = fixture.componentInstance;
        component.header = 'Titulo';
        component.size = ModalSize.Small;
        component.closeable = true;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('should display title and size', () => {
        const title = fixture.nativeElement.querySelector('.modal-title');
        expect(title.textContent).toBe(component.header);

        const classes = fixture.nativeElement.querySelector('.modal-dialog').classList;
        expect(classes).toContain(component.size);
    });

    it('should show the modal on call show method', fakeAsync(() => {
        component.show();

        tick(1000);

        const modal = fixture.nativeElement.querySelector('.modal');
        expect(modal.classList).toContain('show');

        component.hide();
        tick(1000);
    }));

    // NOT WORKING!
    it('should hide modal on click elements with appCloseModal directive', () => {
        spyOn(component, 'hide');

        const testFixture: ComponentFixture<TestComponent> = TestBed.createComponent(TestComponent);
        const element = testFixture.nativeElement.querySelector('[appCloseModal]');
        element.click();

        expect(component.hide).toHaveBeenCalled();
    });
});

What did I do wrong?

Aucun commentaire:

Enregistrer un commentaire