Developing a Alert Componnent
Looks like this

Features
- Base on
bootstrap - Use
typeto select a displaying style, includingsuccess,info,warninganddanger - Use
dismissibleto specify whether to showclose button - When clicking close button, emit an
close eventthat will be subscribed by parent component
Codes
Component
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'my-alert',
templateUrl: './my-alert.component.html',
styleUrls: ['./my-alert.component.scss']
})
export class MyAlertComponent {
@Input() type: string;
@Input() dismissible: boolean;
constructor() {
this.dismissible = true;
}
}
Template
<div class="alert-content" [class]="'alert alert-' + type" [ngClass]="{'alert-dismissible': dismissible}">
<button *ngIf="dismissible" type="button" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<ng-content></ng-content>
</div>
And the most important is the spec
First of all, instead of testing a standalone component, test the component with a mocked component as the host of the MyAlertComponent is much more useful. It will show how the component will be used
Create an empty component with the reference of MyAlertComponent to be tested.
@Component({
selector: 'test-component',
template: ''
})
export class TestComponent {
@ViewChild(MyAlertComponent) myAlertComponent: MyAlertComponent;
closeAlert() {
}
}
@ViewChildwill get the child component with the component typeMyAlertComponent.templateis empty and we will override it with html in each spec to showhow the component is used.closeAlertwill be used to process close event later.
Init the test enviroment with the mocked host component and MyAlertCompoennt to be tested
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TestComponent, MyAlertComponent]
});
}));
- Remember that
do notcompile the component withcompileComponentsmethod, otherwise we can’t override the host component with specified template.
In a specified test case, we create a html which shows how the component is used and override the host’s template
const html = `
<my-alert>
<strong>Warning!</strong>Alert message!
</my-alert>
`;
fixture = createGenericTestComponent(html, TestComponent);
const contentElement = fixture.debugElement.query(By.css('.alert-content')).nativeElement;
expect(contentElement.textContent).toContain(`Alert message!`);
createGenericTestComponentis a common method thatoverridethe empty template in the host which will return a fixture.
export function createGenericTestComponent<T>(html: string, type: {new (...args: any[]): T}): ComponentFixture<T> {
TestBed.overrideComponent(type, {set: {template: html}});
const fixture = TestBed.createComponent(type);
fixture.detectChanges();
return fixture as ComponentFixture<T>;
}
- We get the element with
fixture.debugElement.query. - And then check whether the result element contains a correct content that we create in
html.
Then we develop the event emitter when clicking close button with TDD approach
Create a spec first to show how we will process with the event and when the event will be emitted
it('should emit close event when clicking close button', () => {
const customFixture = createGenericTestComponent(`
<my-alert type='success' (close)="closeAlert()">
Message
</my-alert>
`, TestComponent);
const spy = spyOn(customFixture.componentInstance, 'closeAlert');
const button = customFixture.debugElement.query(By.css('button.close')).nativeElement;
button.click();
expect(spy).toHaveBeenCalled();
});
(close)="closeAlert()"shows that when acloseevent is emitted by MyAlertComponent,closeAlertmethod in the host component will be called- We create a spy to check whether closeAlert is called in a suitable time
- Then get the button element displayed in MyAlertCompnent instance and click the button with
button.click(); - In the end, we check whether the event is processed with
toHaveBeenCalledthat is offered by Jasmine
We run the test and get a fealure because we haven’t implement anything
Bind clicking event on the close button to the method in component
<button *ngIf="dismissible" type="button" class="close" aria-label="Close" (click)="onClose()">
<span aria-hidden="true">×</span>
</button>
Create an onClick method in the component
onClose() {
// process with the clicking event
}
Create an output property that will be cached by the parent
@Output() close = new EventEmitter();
In the end, fill onClose to emit an event
onClose() {
this.close.emit(null);
}
And then the spec is passed

Gains
Bootstrap supports
- supports alert style with
alert alert-{type}class - supports close button with
closeclass insidealert
Component test
- A
mocked host componentis much more helpful, it is more convenient forbuilding a test sceneand helps the code consumer to knowhow the component is used - Build the host component with
a specified html templateand override the host component’s withTestBed.overrideComponentmethod which is extracted to a common file Do not compile componentbefore overriding, otherwise jasmine will throw a runtime error- TDD helps us to figure out what we are going to do before we are caught in details