I've recently made an ng2 (using 2.0.1) app with multiple components and services. I'm in the middle of testing (Karma Jasmine) my HeaderComponent which contains my UserService (Which uses an extended Http class).
I've replicated simple tests from the Angular.io Docs to spy on a service and wait for after component initialization to check if the service function has been fired and its content. Every time I run the last test using fakeAsync (and async), which checks the content of the currentUser variable in header.component, I receive the following error...
Error: Uncaught (in promise): Error: Template parse errors:
'header-section' is not a known element:
1. If 'header-section' is an Angular component, then verify that it is part of this module.
2. If 'header-section' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("
[ERROR ->]<header-section></header-section>
<router-outlet></router-outlet>
<footer-section></footer-sectio"): AppComponent@1:2
'router-outlet' is not a known element:
1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
2. If 'router-outlet' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("
<header-section></header-section>
[ERROR ->]<router-outlet></router-outlet>
<footer-section></footer-section>"): AppComponent@2:2
'footer-section' is not a known element:
1. If 'footer-section' is an Angular component, then verify that it is part of this module.
2. If 'footer-section' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("
<header-section></header-section>
<router-outlet></router-outlet>
[ERROR ->]<footer-section></footer-section>"): AppComponent@3:2
These selectors are from my AppComponent...
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'my-app',
moduleId: module.id,
template: `
<header-section></header-section>
<router-outlet></router-outlet>
<footer-section></footer-section>`
})
export class AppComponent {
constructor() {}
test(): string {
return 'this is a test';
}
}
My header.component.spec...
import { Http, Request, RequestOptionsArgs, Response, XHRBackend, RequestOptions, ConnectionBackend, Headers } from '@angular/http';
import { HttpIntercept } from '../../services/auth/auth.service';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule, JsonpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { RouterTestingModule } from "@angular/router/testing";
import { appRoutes } from '../../routes';
import { Cookie } from 'ng2-cookies/ng2-cookies';
import { AppComponent } from '../app/app.component';
import { HeaderComponent } from './header.component';
import { FooterComponent } from '../footer/footer.component';
import { HomeComponent } from '../home/home.component';
import { Four0FourComponent } from '../404/four0four.component';
import { UserProfileComponent } from '../user-profile/user-profile.component';
import { UserService } from '../../services/user/user.service';
import { ClockService } from '../../services/clock/clock.service';
import { Observable } from 'rxjs/Observable';
import { TestBed, async, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { User } from '../../models/user/user.model';
class MockRouter { public navigate() { }; }
describe('HeaderComponent Test', () => {
let fixture;
let comp;
let userService;
let spy;
let user = new User({
_id: 123456,
userName: 'testName',
firstName: 'testFirst',
lastName: 'testLast',
email: 'test@email.com',
create: 'now',
role: 'user'
});
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
BrowserModule,
HttpModule,
FormsModule,
JsonpModule,
RouterTestingModule.withRoutes(appRoutes)
],
declarations: [
HomeComponent,
UserProfileComponent,
Four0FourComponent,
FooterComponent,
HeaderComponent,
AppComponent
],
providers: [
{
provide: Http,
useFactory: (
backend: XHRBackend,
defaultOptions: RequestOptions) =>
new HttpIntercept(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
},
Cookie
]
});
fixture = TestBed.createComponent(HeaderComponent);
comp = fixture.componentInstance;
userService = fixture.debugElement.injector.get(UserService);
spy = spyOn(userService, 'getMe')
.and.returnValue(Observable.of(user));
});
it('should instantiate component', () => {
expect(fixture.componentInstance instanceof HeaderComponent).toBe(true);
});
it('should not show currentUser before OnInit', () => {
expect(spy.calls.any()).toBe(false, 'getMe not yet called');
});
it('should still not show currentUser after component initialized', () => {
// Set cookie token, for the getMe to call
Cookie.set('token', 'test_token_alpha');
fixture.detectChanges();
expect(spy.calls.any()).toBe(true, 'getMe called');
});
//The problem test is bellow
it('should show currentUser after getMe promise', fakeAsync(() => {
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(comp.currentUser).toEqual(user);
}));
});
Here's my header.component...
import { Component } from '@angular/core';
import { Cookie } from 'ng2-cookies/ng2-cookies';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import { UserService } from '../../services/user/user.service';
import { ClockService } from '../../services/clock/clock.service';
import { User } from '../../models/user/user.model';
@Component({
selector: 'header-section',
providers: [
UserService,
ClockService
],
moduleId: module.id,
template: `
<style>
header{
background: rgb(55, 129, 215);
position: relative;
}
.user-sign{
position: absolute;
top:0;
right:0;
margin: 23px 5%;
}
.app-title{
font-family: cursive;
padding: 15px;
text-align: center;
font-size: 36px;
color: white;
}
.user-sign button:hover{
cursor: pointer;
}
.active{
color: orange;
}
</style>
<header>
<a routerLink='/' routerLinkActive='active'>Home</a>
<a routerLink='/profile' routerLinkActive='active'>Profile</a>
<a routerLink='/yoloswaq69420blazeitfgt' routerLinkActive='active'>404</a>
<div class='user-sign'>
<h3 *ngIf='currentUser'>Welcome, {{currentUser.userName}}</h3>
<button *ngIf='!currentUser' type='button' (click)='testRegisterUser()'>Sign up</button>
<button *ngIf='!currentUser' type='button' (click)='testUser()'>Sign in</button>
<button type='button' (click)='logout()'>Sign out</button>
</div>
<h1 class='app-title'>MEA2N Fullstack</h1>
</header>`
})
export class HeaderComponent {
errorMessage: string;
public currentUser: User;
clock = this.clockService.currentTime;
constructor(private userService: UserService, private clockService: ClockService) { }
ngOnInit() {
let token = Cookie.get('token');
if (token)
this.userService.getMe().subscribe(user => this.currentUser = user);
}
login(email: string, password: string) {
this.userService.login(email, password)
.subscribe(() => {
return this.userService.getMe()
.subscribe(user => {
this.currentUser = user;
})
});
}
logout() {
this.userService.logout();
this.currentUser = null;
}
registerUser(username: string, email: string, password: string) {
this.userService.signup(username, email, password)
.subscribe(() => {
return this.userService.getMe()
.subscribe(user => {
this.currentUser = user;
})
});
}
testUser() {
this.login('jc.thomas4214@gmail.com', 'flight1855');
}
testRegisterUser() {
this.registerUser('Jason', 'jc.thomas4214@gmail.com', 'flight1855');
}
}
I've suspected that this error is occurring because of how I'm initializing my TestBed.configureTestingModule().
I've tried...
Since you are unit testing the Header component in this case, there is no need to include other modules and components
TestBed can look like this:
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AppModule
],
providers: [
{provide: UserService, useClass: MockUserService},
{provide: ClockService, useClass: MockClockService}
]
});
fixture = TestBed.createComponent(HeaderComponent);
});
In this example the Services have been mocked but they can be spied too as you have correctly done for the UserService