Component test for ContentChildren with directive not working

Multi tool use
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
add a comment |
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
You could try atestFixture.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
add a comment |
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
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
angular testing
edited Dec 30 '18 at 21:00
Augusto Pimenta
asked Dec 30 '18 at 20:54
Augusto PimentaAugusto Pimenta
134
134
You could try atestFixture.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
add a comment |
You could try atestFixture.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
add a comment |
1 Answer
1
active
oldest
votes
In the end, I had two problems:
- I was not calling
testFixture.detectChanges()
. - I was adding
spyOn
tocomponent.hide
, butthis.hide.bind(this)
overwritten it and broke my test (addingspyOn(ModalComponent.prototype, 'hide')
solves my problem)
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
In the end, I had two problems:
- I was not calling
testFixture.detectChanges()
. - I was adding
spyOn
tocomponent.hide
, butthis.hide.bind(this)
overwritten it and broke my test (addingspyOn(ModalComponent.prototype, 'hide')
solves my problem)
add a comment |
In the end, I had two problems:
- I was not calling
testFixture.detectChanges()
. - I was adding
spyOn
tocomponent.hide
, butthis.hide.bind(this)
overwritten it and broke my test (addingspyOn(ModalComponent.prototype, 'hide')
solves my problem)
add a comment |
In the end, I had two problems:
- I was not calling
testFixture.detectChanges()
. - I was adding
spyOn
tocomponent.hide
, butthis.hide.bind(this)
overwritten it and broke my test (addingspyOn(ModalComponent.prototype, 'hide')
solves my problem)
In the end, I had two problems:
- I was not calling
testFixture.detectChanges()
. - I was adding
spyOn
tocomponent.hide
, butthis.hide.bind(this)
overwritten it and broke my test (addingspyOn(ModalComponent.prototype, 'hide')
solves my problem)
answered Dec 31 '18 at 18:57
Augusto PimentaAugusto Pimenta
134
134
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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