Developing a Alert Componnent
Looks like this
Features
- Base on
bootstrap
- Use
type
to select a displaying style, includingsuccess
,info
,warning
anddanger
- Use
dismissible
to specify whether to showclose button
- When clicking close button, emit an
close event
that 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() {
}
}
@ViewChild
will get the child component with the component typeMyAlertComponent
.template
is empty and we will override it with html in each spec to showhow the component is used
.closeAlert
will 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 not
compile the component withcompileComponents
method, 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!`);
createGenericTestComponent
is a common method thatoverride
the 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 aclose
event is emitted by MyAlertComponent,closeAlert
method 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
toHaveBeenCalled
that 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
close
class insidealert
Component test
- A
mocked host component
is much more helpful, it is more convenient forbuilding a test scene
and helps the code consumer to knowhow the component is used
- Build the host component with
a specified html template
and override the host component’s withTestBed.overrideComponent
method which is extracted to a common file Do not compile component
before 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