Component test for ContentChildren with directive not working

Multi tool use
Multi tool use












0















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?










share|improve this question

























  • You could try a testFixture.detectChanges() before you query for the element to be sure it has rendered first.

    – dmcgrandle
    Dec 31 '18 at 7:22











  • Thanks, @dmcgrandle, this was not all my problem but it helped!

    – Augusto Pimenta
    Dec 31 '18 at 18:40
















0















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?










share|improve this question

























  • You could try a testFixture.detectChanges() before you query for the element to be sure it has rendered first.

    – dmcgrandle
    Dec 31 '18 at 7:22











  • Thanks, @dmcgrandle, this was not all my problem but it helped!

    – Augusto Pimenta
    Dec 31 '18 at 18:40














0












0








0








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?










share|improve this question
















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?







angular testing






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 30 '18 at 21:00







Augusto Pimenta

















asked Dec 30 '18 at 20:54









Augusto PimentaAugusto Pimenta

134




134













  • You could try a testFixture.detectChanges() before you query for the element to be sure it has rendered first.

    – dmcgrandle
    Dec 31 '18 at 7:22











  • Thanks, @dmcgrandle, this was not all my problem but it helped!

    – Augusto Pimenta
    Dec 31 '18 at 18:40



















  • You could try a testFixture.detectChanges() before you query for the element to be sure it has rendered first.

    – dmcgrandle
    Dec 31 '18 at 7:22











  • Thanks, @dmcgrandle, this was not all my problem but it helped!

    – Augusto Pimenta
    Dec 31 '18 at 18:40

















You could try a testFixture.detectChanges() before you query for the element to be sure it has rendered first.

– dmcgrandle
Dec 31 '18 at 7:22





You could try a testFixture.detectChanges() before you query for the element to be sure it has rendered first.

– dmcgrandle
Dec 31 '18 at 7:22













Thanks, @dmcgrandle, this was not all my problem but it helped!

– Augusto Pimenta
Dec 31 '18 at 18:40





Thanks, @dmcgrandle, this was not all my problem but it helped!

– Augusto Pimenta
Dec 31 '18 at 18:40












1 Answer
1






active

oldest

votes


















0














In the end, I had two problems:




  1. I was not calling testFixture.detectChanges().

  2. I was adding spyOn to component.hide, but this.hide.bind(this) overwritten it and broke my test (adding spyOn(ModalComponent.prototype, 'hide') solves my problem)






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53981351%2fcomponent-test-for-contentchildren-with-directive-not-working%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    In the end, I had two problems:




    1. I was not calling testFixture.detectChanges().

    2. I was adding spyOn to component.hide, but this.hide.bind(this) overwritten it and broke my test (adding spyOn(ModalComponent.prototype, 'hide') solves my problem)






    share|improve this answer




























      0














      In the end, I had two problems:




      1. I was not calling testFixture.detectChanges().

      2. I was adding spyOn to component.hide, but this.hide.bind(this) overwritten it and broke my test (adding spyOn(ModalComponent.prototype, 'hide') solves my problem)






      share|improve this answer


























        0












        0








        0







        In the end, I had two problems:




        1. I was not calling testFixture.detectChanges().

        2. I was adding spyOn to component.hide, but this.hide.bind(this) overwritten it and broke my test (adding spyOn(ModalComponent.prototype, 'hide') solves my problem)






        share|improve this answer













        In the end, I had two problems:




        1. I was not calling testFixture.detectChanges().

        2. I was adding spyOn to component.hide, but this.hide.bind(this) overwritten it and broke my test (adding spyOn(ModalComponent.prototype, 'hide') solves my problem)







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Dec 31 '18 at 18:57









        Augusto PimentaAugusto Pimenta

        134




        134






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53981351%2fcomponent-test-for-contentchildren-with-directive-not-working%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            3j4,t,8IAL3hsfIYv,zDv4BmrW,R04RP Bfdv,2Kul3fljTzPiopEvYTtpR9jW5jzOUZN,uT PIvXeN6
            snDXnNyF2E BD4WCffImmMWc8KRhT,n9ZKXSrxcIy3GKq

            Popular posts from this blog

            Monofisismo

            Angular Downloading a file using contenturl with Basic Authentication

            Olmecas