Accessing a `declare`d variable from unit tests Angular 5 & Karma/Jasmine - javascript

I'm trying to unit test a component which has a variable declared using the declare keyword and it has a couple of methods which are then called later in the component, like so:
import {
Component,
OnInit
} from '#angular/core';
import {
AlertController,
App,
Events,
Platform
} from "ionic-angular";
declare
let PushNotification: any;
#Component({
selector: 'push',
template: ``
})
export class PushComponent implements OnInit {
nav: any;
_push: any;
constructor() {}
ngOnInit() {
// ...
this._push = PushNotification.init({
android: {
senderID: '...'
},
ios: {
alert: "true",
badge: false,
sound: 'false'
},
windows: {}
});
/**
* On Registration, we want to register the device with BE
*/
this._push.on('registration', (data) => {
// ...
});
/**
* If we have a notification come through we need to work out what we are doing with it
*/
this._push.on('notification', (notification) => {
// ...
});
}
/**
* Based on the payload we need to perform different actions
* #param payload
*/
public pushAction(payload: any) {
// ...
}
/**
* The user has received a notification while using the app so we need to show an alert
* #param notification
*/
public pushAlert(notification: any) {
// ...
}
/**
* Reset the badge number to 0
*/
public resetBadge() {
// ...
}
/**
* Register deviceId and platform
* #param registrationId
*/
public registerPush(registrationId) {
// ...
}
/**
* De-Register the deviceId
* #param deviceId
*/
public deRegisterPush(deviceId) {
// ...
}
}
In my unit tests, I want to be able to assert that the init() and on() methods have been called, but I always get an error saying that PushNotification is undefined, so I was hoping somebody would be able to tell me how I can access it or inject it into the component somehow?
My test file is below:
// Core files and modules
import {
TestBed,
ComponentFixture
} from '#angular/core/testing';
import {
NO_ERRORS_SCHEMA
} from '#angular/core';
import {
App,
AlertController,
Events,
Platform,
Config
} from 'ionic-angular';
import {
ViewController,
Slides
} from 'ionic-angular';
import {
mockOpportunityProvider,
mockPushProvider,
mockAuth,
mockAppPlatform,
mockPlatform,
mockEvents,
presentableControllerMock
} from '../../mocks';
import {
OpportunityProvider
} from '../../providers/opportunity/opportunity.provider';
import {
AuthProvider
} from '../../providers/auth/auth';
import {
ProfileComponent
} from '../../pages/profile/profile.component';
import {
PushProvider
} from '../../providers/push/push.provider';
// Pages
import {
PushComponent
} from './push.component';
import {
OpportunityComponent
} from '../../pages/opportunity/opportunity.component';
let component: PushComponent;
let fixture: ComponentFixture < PushComponent > ;
describe('Component: ProfileTemplateComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [PushComponent],
providers: [{
provide: OpportunityProvider,
useClass: mockOpportunityProvider
},
{
provide: AuthProvider,
useClass: mockAuth
},
{
provide: PushProvider,
useClass: mockPushProvider
},
{
provide: App,
useClass: mockAppPlatform
},
ProfileComponent,
{
provide: Platform,
useClass: mockPlatform
},
Config,
{
provide: AlertController,
useClass: presentableControllerMock
},
{
provide: Events,
useClass: mockEvents
}
],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PushComponent);
component = fixture.componentInstance;
});
afterEach(() => {
fixture.destroy();
component = null;
});
// TODO - figure out how to access the PushNotification variable
it('expect ngOnInit() to call ', async() => {
// ...
});
it('expect pushAction() to call nav.setRoot()', () => {
// ...
});
it('expect pushAlert() to call alertCtrl.create() and present()', () => {
// ...
});
it('expect resetBadge() to call _push.setApplicationIconBadgeNumber() if on cordova platform', () => {
// ...
});
it('expect registerPush() to call pushProvider.register()', () => {
// ...
});
it('expect deRegisterPush() to call pushProvider.deRegister()', () => {
// ...
});
});
Thanks in advance!

Related

How to unit test Angular's Meta service?

My Component is using Angular's Meta service to update a meta tag during ngOnInit. I'm using my RegionService to get an app-id and set it with Meta's updateTag method via a template literal. But my unit test is having problems getting the value set by my RegionService in the template literal. The test returns the following error:
Expected spy Meta.updateTag to have been called with:
[ Object({ name: 'apple-itunes-app', content: 'app-id=0123456789' }) ]
but actual calls were:
[ Object({ name: 'apple-itunes-app', content: 'app-id=undefined' }) ].
How can I modify my test so that it knows the value app-id, set by my template literal ${this.regionService.getAppId()} ?
my.component.ts
import { Component, OnInit } from '#angular/core';
import { RegionService } from 'src/services/region.service';
import { Meta } from '#angular/platform-browser';
export class MyComponent implements OnInit {
constructor(
private regionService: RegionService,
private meta: Meta
) {}
ngOnInit() {
this.meta.updateTag({name: 'apple-itunes-app', content: `app-id=${this.regionService.getAppId()}`});
}
}
my.component.spec.ts
import { ComponentFixture, TestBed, waitForAsync } from '#angular/core/testing';
import { NO_ERRORS_SCHEMA } from '#angular/core';
import { MyComponent } from './my.component';
import { RegionService } from 'src/services/region.service';
import { Meta } from '#angular/platform-browser';
import { RouterTestingModule } from '#angular/router/testing';
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let regionServiceSpy: jasmine.SpyObj<RegionService>;
let metaServiceSpy: jasmine.SpyObj<Meta>;
beforeEach(
waitForAsync(() => {
const regionServiceSpyObj = jasmine.createSpyObj('RegionService', ['getAppId', 'retrieveABCRegions', 'retrieveDEFRegions']);
const metaServiceSpyObj = jasmine.createSpyObj('Meta', ['updateTag']);
TestBed.configureTestingModule({
declarations: [MyComponent],
imports: [RouterTestingModule],
providers: [
{ provide: RegionService, useValue: regionServiceSpyObj },
{ provide: Meta, useValue: metaServiceSpyObj },
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
regionServiceSpy = TestBed.inject(RegionService) as jasmine.SpyObj<RegionService>;
metaServiceSpy = TestBed.inject(Meta) as jasmine.SpyObj<Meta>;
}),
);
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should set app-id to 0123456789 if selectedRegion is FR', () => {
// arrange
// act
regionServiceSpy.selectedRegion = 'FR';
// assert
expect(metaServiceSpy.updateTag).toHaveBeenCalledWith({name: 'apple-itunes-app', content: 'app-id=0123456789'});
});
});
region.service.ts
import { retrieveABCRegions, retrieveDEFRegions} from 'src/regions';
#Injectable({
providedIn: 'root',
})
export class RegionService {
selectedRegion: Region;
getAppId(): string {
if (retrieveABCRegions().includes(this.selectedRegion)) {
return '111111111';
} else if (retrieveDEFRegions().includes(this.selectedRegion)) {
return '0123456789';
}
}
}
Since you've replaced the RegionService with a SpyObj mock:
{ provide: RegionService, useValue: regionServiceSpyObj }
the real service is no longer being used in your tests (which is the correct approach because you're not testing that service here).
So now you need to define what value the mock service's getAppId() method is going to return. You do that by creating a spy strategy for that method on your spy object.
There are different types of spy strategies you can use, but for your purposes here, probably the simplest is returnValue():
it('should set app-id to 0123456789 if selectedRegion is FR', () => {
// arrange
// act
// regionServiceSpy.selectedRegion = 'FR'; <--- not needed for this test since real service method not being called
regionServiceSpy.getAppId.and.returnValue('0123456789'); // <-- define what value mock service's getAppId returns
// assert
expect(metaServiceSpy.updateTag).toHaveBeenCalledWith({name: 'apple-itunes-app', content: 'app-id=0123456789'});
});
And note that setting regionServiceSpy.selectedRegion = 'FR' is not needed here since the actual service method is not being called.

Nest.js testing cannot override Provider

I have a problem with overriding provider in nest.js application for testing.
My stats.controller.spec.ts:
import { StatsService } from './services/stats.service';
import { StatsController } from './stats.controller';
describe('StatsController', () => {
let controller: StatsController;
const mockStatsService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [StatsController],
providers: [StatsService],
})
.overrideProvider(StatsService)
.useValue(mockStatsService)
.compile();
controller = module.get<StatsController>(StatsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
My stats.controller.ts:
import { Controller, Get } from '#nestjs/common';
import { StatsService } from './services/stats.service';
#Controller('stats')
export class StatsController {
constructor(private statsService: StatsService) {}
#Get('weekly')
getWeeklyStats() {
return this.statsService.getWeeklyStats();
}
#Get('monthly')
getMonthlyStats() {
return this.statsService.getMonthlyStats();
}
}
And my stats.service.ts:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { Trip } from 'src/trips/trip.entity';
import { from, map } from 'rxjs';
import { DatesService } from 'src/shared/services/dates.service';
#Injectable()
export class StatsService {
constructor(
#InjectRepository(Trip) private tripRepository: Repository<Trip>,
private datesServices: DatesService,
) {}
//some code here
}
And after running test I get following errors:
Cannot find module 'src/trips/trip.entity' from 'stats/services/stats.service.ts'
I would really appreciate some help.
The error is unrelated to if you're using a custom provider or overrideProvider. Jest by default doesn't understand absolute imports by default, and you need to use the moduleNameMapper option to tell Jest how to resolve src/ imports. Usually something like
{
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/$1"
}
}
Assuming rootDir has been set to src
You can override the provider by using the below code in nest js:
import { StatsService } from './services/stats.service';
import { StatsController } from './stats.controller';
describe('StatsController', () => {
let controller: StatsController;
const mockStatsService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [StatsController],
providers: [{
provide: StatsService,
useValue: mockStatsService
}],
})
.compile();
controller = module.get<StatsController>(StatsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

Error: Trying to get the AngularJS injector before it being set

I am testing an angular 7 component that uses angular js components inside.
I am using jasmine and karma for testing.
I am getting
Angular Hybrid - **Error: Trying to get the AngularJS injector before it being set.** when I am running the ng test my-app-name.
app-module-ts -
import { BrowserModule } from '#angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '#angular/core';
import { UpgradeModule, downgradeComponent } from '#angular/upgrade/static';
import { AppComponent } from './app.component';
import { BusyOverlayModule } from '../../../app-shared/src/lib/busy-overlay/busy-overlay.module';
import { L10nTranslationModule } from '../../../app-shared/src/lib/l10n-translation/l10n-translation.module';
import { LocalizationService } from '../../../app-shared/src/lib/l10n-translation/localization.service';
import { WatchlistModule } from './watchlist/watchlist.module';
import oprRtsmViewPickerModuleName
from '../../../../../../shared/shared-html/js/directives/oprRtsmViewPicker/oprRtsmViewPicker.module.js';
import oprTableFooterModuleName
from '../../../../../../shared/shared-html/js/directives/oprTableFooter/oprTableFooter.module.js';
import oprContextPanelModuleName
from '../../../../../../shared/shared-html/js/directives/oprContextPanel/oprContextPanel/oprContextPanel.module.js';
import { FilterFactory } from 'angular';
import oprPopupServiceModuleName from '../../../../../../shared/shared-html/js/services/oprPopup.service';
import ciContextDataServiceModuleName from '../../../../../../shared/shared-html/js/services/ciContextData.service';
import watchlistOptionsLauncherComponent from './watchlist/options-menu/watchlistOptionsLauncherNg1';
import oprLocalizationUtil from '../../../../../../shared/shared-html/js/utils/oprLocalizationUtil';
import appConstantsUtil from '../../../../../../shared/shared-html/js/utils/appConstantsUtil';
import { setAngularJSGlobal } from '#angular/upgrade/static';
import { oprContextPanelApiServiceProvider } from '../../../ng1-adapters/adapters/opr-conext-panel-api-provider';
import { ciContextDataServiceProvider } from '../../../ng1-adapters/adapters/opr-conext-data-api-provider';
import { Oprl10nPipe } from '../../../app-shared/src/lib/l10n-translation/oprl10n.pipe';
declare const angular: angular.IAngularStatic;
setAngularJSGlobal(angular);
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
UpgradeModule,
BusyOverlayModule,
L10nTranslationModule,
WatchlistModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: onAppInit,
multi: true
},
oprContextPanelApiServiceProvider,
ciContextDataServiceProvider,
Oprl10nPipe
],
entryComponents: [
AppComponent
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule, private localizationService: LocalizationService) { }
/**
* bootstrap hybrid AngularJS/Angular application with downgraded root Angular component
*/
ngDoBootstrap() {
const ng1L10nfilterFactory: angular.Injectable<FilterFactory> = function () {
return (key) => {
return oprLocalizationUtil.getLocalizedString(key);
};
};
const ng1RootModule = angular.module('ng1-root', [
oprPopupServiceModuleName,
oprRtsmViewPickerModuleName,
oprTableFooterModuleName,
oprContextPanelModuleName,
ciContextDataServiceModuleName
]).directive('oprRoot', downgradeComponent({component: AppComponent})).filter('oprL10n', ng1L10nfilterFactory)
.component('oprOptionsMenuLauncher', watchlistOptionsLauncherComponent);
this.localizationService.setOprLocalizeUtilObj(oprLocalizationUtil);
this.upgrade.bootstrap(document.body, [ng1RootModule.name], {strictDi: false});
}
}
export function onAppInit(): () => Promise<any> {
return (): Promise<any> => {
return new Promise((resolve, reject) => {
const appConstants = appConstantsUtil.appConstants;
/**
* For Testing, Replace this appConstants.locale.toLowerCase() code with en
*/
oprLocalizationUtil.init(appConstants.locale.toLowerCase(), appConstants.staticContext, [
'opr-watchlist',
'opr-common'
], function () {
resolve(true);
});
});
};
}
app.component.ts -
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'opr-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
showBusyOverlay:boolean = true;
ngOnInit() {
this.showBusyOverlay = false;
}
}
app.component.spec.ts -
import { TestBed, ComponentFixture, async } from '#angular/core/testing';
import * as angular from '#angular/upgrade/src/common/angular1';
import { UpgradeModule, setAngularJSGlobal } from '#angular/upgrade/static';
import { AppComponent } from './app.component';
import { AppSharedModule } from "../../../app-shared/src/lib/app-shared.module";
import { WatchlistComponent } from './watchlist/watchlist.component';
import { WatchlistFooterComponent } from './watchlist/watchlist-footer/watchlist-footer.component';
import { WatchlistHeaderComponent } from './watchlist/watchlist-header/watchlist-header.component';
import { WatchlistCardsComponent } from './watchlist/watchlist-cards/watchlist-cards.component';
import { WatchlistCardComponent } from "./watchlist/watchlist-cards/watchlist-card/watchlist-card.component";
import { WatchlistCardsModule } from './watchlist/watchlist-cards/watchlist-cards.module';
import { WatchlistModule } from './watchlist/watchlist.module';
import { ContextPanelApi } from 'shared/directives/oprContextPanel/oprContextPanel/oprContextPanelApi.service';
import { CiContextDataService } from '../../../../../../shared/shared-html/js/services/ciContextData.service';
import { CiContextMenuService } from '../../../app-shared/src/lib/ci-context-menu.service';
import { HttpClientModule, HttpClientXsrfModule } from '#angular/common/http';
import { Ng1AdaptersModule } from '../../../ng1-adapters/ng1-adapters.module';
import { FormsModule } from '#angular/forms';
import { BusyOverlayModule } from '../../../app-shared/src/lib/busy-overlay/busy-overlay.module';
import { L10nTranslationModule } from '../../../app-shared/src/lib/l10n-translation/l10n-translation.module';
import '../../../../../../shared/shared-html/test/config/index-mock.js';
import { Oprl10nPipe } from "../../../app-shared/src/lib/l10n-translation/oprl10n.pipe";
import { of } from 'rxjs';
export class MockBusyOverlayModule {
}
export class MockContextPanelApi {
}
export class MockCiContextDataService {
}
export class MockCiContextMenuService {
}
export class Oprl10nPipeStub {
public get(key: any): any {
return of(key);
}
public transform(key: any): any {
return of(key);
}
}
setAngularJSGlobal(angular);
describe('AppComponent', () => {
window['getAppConstants'] = function () {
return JSON.stringify({
topazWebContext: "",
mashupParams: { uim_sourceId: "0" },
staticContext: "/base",
locale: "en-us",
oprWebContext: ""
});
};
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
UpgradeModule,
BusyOverlayModule,
HttpClientModule,
AppSharedModule,
// appConstantsUtil,
// WatchlistModule,
Ng1AdaptersModule,
L10nTranslationModule,
// WatchlistModule
// WatchlistCardsModule
],
providers: [
{ provide: Oprl10nPipe, useClass: Oprl10nPipeStub },
// { provide: BusyOverlayModule, useClass: MockBusyOverlayModule }
{ provide: ContextPanelApi, useClass: MockContextPanelApi },
{ provide: CiContextDataService, useClass: MockCiContextDataService },
{ provide: CiContextMenuService, useClass: MockCiContextMenuService },
// { provide: appConstantsUtil }
],
declarations: [
AppComponent,
WatchlistComponent,
WatchlistFooterComponent,
WatchlistHeaderComponent,
WatchlistCardComponent,
WatchlistCardsComponent
]
// imports: [
// WatchlistModule
// ]
}).compileComponents();
}));
beforeEach(() => {
window['getAppConstants'] = function () {
return JSON.stringify({
topazWebContext: "",
mashupParams: { uim_sourceId: "0" },
staticContext: "/base",
locale: "en-us",
oprWebContext: ""
});
};
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create the app', () => {
expect(component).toBeTruthy();
});
});
My angular js version is 1.6 and Angular 7, as I said I am testing angular 7 components that use angular js components.
I found some links on web -
Angular Hybrid Error: Trying to get the AngularJS injector before it being set
https://github.com/angular/angular/issues/23141
but no help. I am not able to find anything in the angular doc as well.
Please guide.
I solved this by adding
const upgrade = TestBed.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document);
const serviceWhichReliesonInjector =
to my beforeEach Method.
afterwards i could simply access the service by
TestBed.get(ServiceWhichReliesonInjector)

Jasmine error while testing redux dispatch

I'm throwing a coin in the ocean after spending a couple hours trying to fix an ngRedux testing error.
Here is the component itself, as you can see, it's calling the ModuleActions:
#Component
class TestedClass {
constructor(private actions: ModuleActions) {}
restoreDefault() {
this.action.reloadPageModule(true);
}
}
Here is the module action class, I'm using an if to dispatch the action or not, because I also call it from an epic:
#Injectable()
export class ModuleActions {
constructor(private ngRedux: NgRedux<PageModules>) {}
...
reloadPageModule(dispatch?: boolean) {
if(dispatch) {
this.ngRedux.dispatch({
type: ModuleActions.RELOAD_PAGE_MODULES,
meta: { status: 'success' }
});
} else {
return {
type: ModuleActions.RELOAD_PAGE_MODULES,
meta: { status: 'success' }
};
}
}
}
And this is my testedClass component test:
it('should fire the reload page module action', () => {
const executeActionSpy = spyOn(component['action'], 'loadSucceeded');
component.restoreDefault();
expect(executeActionSpy).toHaveBeenCalled()
})
...
const compileAndCreate = () => {
TestBed.configureTestingModule({
declarations: [
...
],
providers: [
ModuleActions,
NgRedux
]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(EditorComponent);
component = fixture.componentInstance;
})
...
This is giving me a this.ngRedux.dispatch() is not a function error when called from the
In order to fix it, I modified my test to use an import of NgReduxTestingModule instead of a providers of NgRedux:
import { NgReduxTestingModule } from '#angular-redux/store/testing';
TestBed.configureTestingModule({
imports: [NgReduxTestingModule, ...],
providers: [
...

Can't run service in Angular2 test (code copy pasted from the docs)

I have the following error (unit testing Angular2):
Cannot configure the test module when the test module has already been
instantiated. Make sure you are not using inject before
TestBed.configureTestingModule
Here is my code (it's basically a copy paste from the angular docs) which throws the above error:
import { TestBed, async } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
let fixture = TestBed.createComponent(AppComponent);
let comp = fixture.componentInstance;
// UserService from the root injector
let userService = TestBed.get(MyServiceService);
// get the "welcome" element by CSS selector (e.g., by class name)
let de = fixture.debugElement.query(By.css('.nesto'));
let el = de.nativeElement;
it('should welcome "Bubba"', () => {
userService.user.name = 'something'; // welcome message hasn't been shown yet
fixture.detectChanges();
expect(el.textContent).toContain('some');
});
});
I want to run a service but it seems that I just can't do that.
The most likely problem is that you're attempting to run testing within your beforeEach(). You need to make sure all it() methods are outside/after the beforeEach():
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
let fixture = TestBed.createComponent(AppComponent);
let comp = fixture.componentInstance;
// get the "welcome" element by CSS selector (e.g., by class name)
let de = fixture.debugElement.query(By.css('.nesto'));
let el = de.nativeElement;
});
it('should welcome "Bubba"', inject([MyServiceService], (userService) => {
userService.user.name = 'something'; // welcome message hasn't been shown yet
fixture.detectChanges();
expect(el.textContent).toContain('some');
}));
This works, removed the instance in the beforeEach() and injected into the it().
All credits go to Z. Bagley
import { TestBed, async, inject } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component'
import { MyServiceService } from './my-service.service'
import { Inject } from '#angular/core';
beforeEach(() => {
// stub UserService for test purposes
let userServiceStub = {
isLoggedIn: true,
user: { name: 'Test User'}
};
TestBed.configureTestingModule({
declarations: [ AppComponent],
providers: [ {provide: MyServiceService, useValue: userServiceStub } ]
});
});
it('should welcome "Bubba"', inject([MyServiceService], (userService) => {
let fixture=TestBed.createComponent(AppComponent);
fixture.detectChanges();
expect(userService.user.name).toContain('se');
}));

Categories

Resources