Reputation: 1800
I am currently working on an Angular4 web application which uses Auth0 for authentication.
While the authentication works as expected the integration of Auth0 has broken (lets fail) the default tests (Karma unit tests) of my application.
My code looks as follows:
// app.component.ts
/*
* Angular 2 decorators and services
*/
import {
Component,
ViewEncapsulation
} from '@angular/core';
import { Auth } from './auth.service';
/*
* App Component
* Top Level Component
*/
@Component({
selector: 'app',
providers: [ Auth ],
encapsulation: ViewEncapsulation.None,
styleUrls: [
'./app.component.scss'
],
template: `
<div class="container-fluid">
<router-outlet></router-outlet>
</div>
`
})
export class AppComponent {
public angularclassLogo = 'assets/img/ExampleApp_smallLogo.png';
public name = 'ExampleApp';
public url = 'https://www.example.com/';
constructor(private auth: Auth) {
this.auth.handleAuth();
}
}
// auth.service.ts
import { Injectable } from '@angular/core';
import { tokenNotExpired } from 'angular2-jwt';
import { Router } from '@angular/router';
import { Http, Headers, RequestOptions, RequestMethod, Response } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/filter';
import Auth0Lock from 'auth0-lock';
import Auth0 from 'auth0-js';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { myConfig, postConfig, necessaryRoles } from './auth.config';
// Avoid name not found warnings
// declare var auth0: any;
@Injectable()
export class Auth {
public lock = new Auth0Lock(myConfig.clientID, myConfig.domain, myConfig.lock);
public userProfile: any;
public idToken: string;
public signUpIncomplete: boolean;
// Configure Auth0
private auth0 = new Auth0.WebAuth({
domain: myConfig.domain,
clientID: myConfig.clientID,
redirectUri: myConfig.redirectUri,
responseType: myConfig.responseType
});
// Create a stream of logged in status to communicate throughout app
private loggedIn: boolean;
private loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);
constructor(private router: Router, private http: Http) {
// Set userProfile attribute of already saved profile
this.userProfile = JSON.parse(localStorage.getItem('profile'));
}
...
}
// app.component.spec
import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
async,
TestBed,
ComponentFixture
} from '@angular/core/testing';
import {
BaseRequestOptions,
HttpModule,
Http,
XHRBackend,
} from '@angular/http';
import { RouterTestingModule } from '@angular/router/testing';
import { MockBackend } from '@angular/http/testing';
// Load the implementations that should be tested
import { AppComponent } from './app.component';
import { AppState } from './app.service';
import { Auth } from './auth.service';
describe(`App`, () => {
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
// async beforeEach
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent ],
imports: [RouterTestingModule, HttpModule],
schemas: [NO_ERRORS_SCHEMA],
providers: [
AppState,
Auth,
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory:
(backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
]
})
.compileComponents(); // compile template and css
}));
// synchronous beforeEach
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding
});
it(`should be readly initialized`, () => {
expect(fixture).toBeDefined();
expect(comp).toBeDefined();
});
it(`should be ExampleApp`, () => {
expect(comp.url).toEqual('https://www.example.com/');
expect(comp.angularclassLogo).toEqual('assets/img/ExampleApp_smallLogo.png');
expect(comp.name).toEqual('ExampleApp');
});
});
The problem is that both App: should be readly initialized and App: should be MyApp fail with Cannot read property 'WebAuth' of undefined
although WebAuth is defined in auth.service.ts which is then imported in app.component.spec.
Am I missing any import or declaration?
Upvotes: 0
Views: 1390
Reputation: 1800
I finally solved the questions by myself.
I had to create a mock for the Auth service.
Further, I had to override the App component so that it uses that mock object instead of the real Auth service.
Therefore the solution looks as follows:
import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
async,
TestBed,
ComponentFixture
} from '@angular/core/testing';
import {
BaseRequestOptions,
HttpModule,
Http,
XHRBackend,
} from '@angular/http';
import { RouterTestingModule } from '@angular/router/testing';
import { MockBackend } from '@angular/http/testing';
// Load the implementations that should be tested
import { AppComponent } from './app.component';
import { Auth } from './auth.service';
// Mock our Auth service
export class MockAuthService {
public handleAuth(): void {
return;
}
}
describe(`App`, () => {
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
// async beforeEach
beforeEach(async(() => {
TestBed
.configureTestingModule({
declarations: [ AppComponent ],
imports: [RouterTestingModule, HttpModule],
schemas: [NO_ERRORS_SCHEMA],
providers: [
MockBackend,
BaseRequestOptions,
{
provide: Http,
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: XHRBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
]
})
.overrideComponent(AppComponent, {
set: {
providers: [{ provide: Auth, useValue: new MockAuthService() }]
}
})
.compileComponents();
}));
// synchronous beforeEach
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
fixture.detectChanges(); // trigger initial data binding
});
it(`should be readly initialized`, () => {
expect(fixture).toBeDefined();
expect(comp).toBeDefined();
});
it(`should be ExampleApp`, () => {
expect(comp.url).toEqual('https://www.example.com/');
expect(comp.angularclassLogo).toEqual('assets/img/ExampleApp_smallLogo.png');
expect(comp.name).toEqual('ExampleApp');
});
});
Upvotes: 1