Reputation: 1114
I am getting
Failed: Uncaught (in promise): Error: Cannot find primary outlet to load 'UserList'
error.
This happens when unit testing my login component that redirects to admin/userlist
using this._router.navigate(['admin/userlist']);
in the component.
login.html:
<div class="row registration">
<div id="login-form-control" class="col-xs-12 form-area">
<h2 class="registration-header">Login</h2>
<form (ngSubmit)="login()" [formGroup]="form">
<input id="login-username-textbox" type="text" class="form-control" placeholder="Username" [formControl]="username" required>
<input id="login-password-textbox" type="password" class="form-control" placeholder="Password" [formControl]="password" required>
<div *ngIf="errorMessage">
<span class="help-block error">{{errorMessage}}</span>
</div>
<div *ngIf="successMessage">
<span class="help-block success">{{successMessage}}</span>
</div>
<button id="login-submit" type="submit" class="go-btn btn btn-lg btn-success btn-block">Login</button>
</form>
<div class="row">
<a [routerLink]="['../register']">Register</a>
</div>
</div>
</div>
login.ts:
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { RegistrationService } from '../shared/services/registration';
@Component({
selector: 'rg-login',
templateUrl: './login.html'
})
export class Login {
form: FormGroup;
username = new FormControl('', Validators.required);
password = new FormControl('', Validators.required);
errorMessage = '';
successMessage = '';
constructor(private _registrationService: RegistrationService, private _formBuilder: FormBuilder, private _router: Router) {
this._createForm();
}
ngOnInit() {
}
login() {
this._registrationService.loginUser(this.form.value)
.subscribe(data => {
if (data) {
this.errorMessage = '';
this.successMessage = 'Login successful';
this._router.navigate(['admin/userlist']);
} else {
this.errorMessage = 'Error';
this.successMessage = '';
}
}, error => {
this.errorMessage = error;
this.successMessage = '';
});
}
_createForm() {
this.form = this._formBuilder.group({
username: this.username,
password: this.password
});
}
}
login.spec.ts:
import { Component } from '@angular/core';
import { async, inject, TestBed } from '@angular/core/testing';
import { BaseRequestOptions, ConnectionBackend, Http } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { Observable } from 'rxjs/Rx';
import { RegistrationService } from '../shared/services/registration';
import { Login } from './login';
@Component({
template: ''
})
class Register { }
@Component({
template: ''
})
class UserList { }
class MockRegistrationService {
loginUser(user) {
return Observable.of({
username: 'TestUser1',
password: 'TestPassword1'
});
}
}
describe('Login', () => {
let mockRegistrationService = new MockRegistrationService();
beforeEach(() => TestBed.configureTestingModule({
declarations: [
Login,
Register,
UserList
],
providers: [
Login,
{ provide: RegistrationService, useValue: mockRegistrationService }
],
imports: [
ReactiveFormsModule,
RouterTestingModule.withRoutes([
{ path: 'register', component: Register },
{ path: 'admin/userlist', component: UserList }
])
]
}));
it('should successfully login', async(() => {
let fixture = TestBed.createComponent(Login);
let loginComponent = fixture.componentInstance;
fixture.detectChanges();
loginComponent.login({
username: 'TestUser1',
password: 'TestPassword1'
});
expect(loginComponent.successMessage).toEqual('Login successful');
expect(loginComponent.errorMessage).toEqual('');
}));
});
Full error:
FAILED TESTS:
Login
✖ should successfully login
PhantomJS 2.1.1 (Mac OS X 0.0.0)
Failed: Uncaught (in promise): Error: Cannot find primary outlet to load 'UserList'
resolvePromise@webpack:///~/zone.js/dist/zone.js:429:0 <- config/spec-bundle.js:53943:75
webpack:///~/zone.js/dist/zone.js:406:0 <- config/spec-bundle.js:53920:27
invoke@webpack:///~/zone.js/dist/zone.js:203:0 <- config/spec-bundle.js:53717:33
onInvoke@webpack:///~/zone.js/dist/async-test.js:42:0 <- config/spec-bundle.js:52757:45
onInvoke@webpack:///~/zone.js/dist/proxy.js:69:0 <- config/spec-bundle.js:53414:47
invoke@webpack:///~/zone.js/dist/zone.js:202:0 <- config/spec-bundle.js:53716:42
run@webpack:///~/zone.js/dist/zone.js:96:0 <- config/spec-bundle.js:53610:49
webpack:///~/zone.js/dist/zone.js:462:0 <- config/spec-bundle.js:53976:60
invokeTask@webpack:///~/zone.js/dist/zone.js:236:0 <- config/spec-bundle.js:53750:42
onInvokeTask@webpack:///~/zone.js/dist/proxy.js:96:0 <- config/spec-bundle.js:53441:49
invokeTask@webpack:///~/zone.js/dist/zone.js:235:0 <- config/spec-bundle.js:53749:54
runTask@webpack:///~/zone.js/dist/zone.js:136:0 <- config/spec-bundle.js:53650:57
drainMicroTaskQueue@webpack:///~/zone.js/dist/zone.js:368:0 <- config/spec-bundle.js:53882:42
invoke@webpack:///~/zone.js/dist/zone.js:308:0 <- config/spec-bundle.js:53822:44
Upvotes: 1
Views: 2350
Reputation: 209112
Can you explain how to correctly create the router stub
Just do
let routerStub;
beforeEach(() => {
routerStub = {
navigate: jasmine.createSpy("navigate")
};
TestBed.configureTestingModule({
providers: [
// add router provider
{ provide: Router, useValue: routerStub },
{ provide: RegistrationService, useValue: mockRegistrationService }
],
imports: [
ReactiveFormsModule,
// Remove Router Module
]
})
});
Then in your test, just check that the navigate
method is called with the correct arguments
expect(routerStub.navigate).toHaveBeenCalledWith(['someurl'])
This is more what a unit test should look like. You just want to test the behavior of the component. So you just check that it calls the navigate method on the router.
Upvotes: 2