Karma/Jasmine using the npm run test -- --no-watch --no-progress-command, throws the following error:
Chrome 92.0.4515.159 (Mac OS 10.15.7) LoginComponent should create FAILED
TypeError: this.loggerService.onDebug is not a function
at LoginComponent.ngAfterViewInit (src/app/pages/login/login.component.ts:22:24)
at callHook (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2526:1)
at callHooks (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2495:1)
at executeInitAndCheckHooks (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2446:1)
at refreshView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9516:1)
at renderComponentOrTemplate (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9559:1)
at tickRootContext (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10790:1)
at detectChangesInRootView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10815:1)
at RootViewRef.detectChanges (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:22865:1)
at ComponentFixture._tick (node_modules/@angular/core/__ivy_ngcc__/fesm2015/testing.js:141:1)
Hi there,
I am writing a Logger-Service in Angular 12.0.x. The service pipes to both the ngx-logger (e.g. to an external server) as well as into a store (ngxs).
The karma.conf.js-File aims at root of the application.
Pretty much straight forwad Logger-Service (logging-service.ts):
import { Injectable } from '@angular/core';
import { Store } from '@ngxs/store';
import { NGXLogger } from 'ngx-logger';
import { AddLog, LogEntry } from '../store/log-state';
@Injectable({
providedIn: 'root',
})
export class LoggerService {
// This service pipes Log-Messages both to the store as well as to the NGXLogger.
// Further forwarding by the NGXLogger is dependent on the properties provided in the environment.ts-file.
constructor(private logger: NGXLogger, private store: Store) {}
private createLogEntry(level: string, origin: string, msg: string) {
const dateTime = new Date();
const log: LogEntry = {
time: dateTime,
level: level,
origin: origin,
msg: msg,
noticed: false,
};
return log;
}
onTrace(origin: string, msg: string) {
// To NGXLogger
this.logger.trace(msg);
// To Store
this.store.dispatch(new AddLog(this.createLogEntry('trace', origin, msg)));
}
onDebug(origin: string, msg: string) {
// To NGXLogger
this.logger.trace(msg);
// To Store
this.store.dispatch(new AddLog(this.createLogEntry('debug', origin, msg)));
}
onInfo(origin: string, msg: string) {
// To NGXLogger
this.logger.trace(msg);
// To Store
this.store.dispatch(new AddLog(this.createLogEntry('info', origin, msg)));
}
... ... ...
}
The Logger-Service's methods are called from two different locations:
- Login-Component
- Log-Manager-Component
Using ng serve this works fine. No problems klicking through. Works as expected.
Still, running Karma/Jasmine using the npm run test -- --no-watch --no-progress-command, throws the error displayed on top.
Removing the calls in Login-Component, there is no error thrown by Karma/Jasmine (=> It's only in the Login-Component).
Consequently both Components import the Logger-Service the same way, but ONLY the LoginComponent throws the error.
The difference: The Login-Component uses the 'onInfo'-Function in an Angular Lifecycle hook. Both ngOnInit() & ngAfterViewInit() offer the same result - Works live (ng serve) but fails the Karma/Jasmine Test.
I need the Lifecycle hook as I want to track the pages visited (in the basic scenario).
For the record the two affected files:
- login.component.ts
import { Component, AfterViewInit } from '@angular/core';
import { LoggerService } from 'src/app/services/logging.service';
@Component({
selector: 'lep-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less'],
})
export class LoginComponent implements AfterViewInit {
constructor(
private loggerService: LoggerService,
) {}
ngAfterViewInit() {
this.loggerService.onDebug('Login', 'SampleMessage');
}
}
- login.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoggerService } from 'src/app/services/logging.service';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LoginComponent],
providers: [{ provide: LoggerService, useClass: class {} }],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Those files from the Log-Manager-Component look (almost) the same.
As said before, the difference is that in the Log-Manager-Component the line ..
this.loggerService.onDebug('Login', 'SampleMessage');
.. is in a function triggered by a button on the html-page.
Any suggestions how to fix this? :)