import { InitService } from './init.service'; import { APP_CONFIG } from 'src/config/app-config.interface'; import { APP_INITIALIZER, Injectable } from '@angular/core'; import { inject, TestBed, waitForAsync } from '@angular/core/testing'; import { GoogleAnalyticsService } from './statistics/google-analytics.service'; import { MetadataService } from './core/metadata/metadata.service'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { CommonModule } from '@angular/common'; import { Store, StoreModule } from '@ngrx/store'; import { authReducer } from './core/auth/auth.reducer'; import { storeModuleConfig } from './app.reducer'; import { AngularticsProviderMock } from './shared/mocks/angulartics-provider.service.mock'; import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; import { AuthService } from './core/auth/auth.service'; import { AuthServiceMock } from './shared/mocks/auth.service.mock'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterMock } from './shared/mocks/router.mock'; import { MockActivatedRoute } from './shared/mocks/active-router.mock'; import { MenuService } from './shared/menu/menu.service'; import { LocaleService } from './core/locale/locale.service'; import { environment } from '../environments/environment'; import { provideMockStore } from '@ngrx/store/testing'; import { AppComponent } from './app.component'; import { RouteService } from './core/services/route.service'; import { getMockLocaleService } from './app.component.spec'; import { MenuServiceStub } from './shared/testing/menu-service.stub'; import { CorrelationIdService } from './correlation-id/correlation-id.service'; import { DSpaceTransferState } from '../modules/transfer-state/dspace-transfer-state.service'; import { KlaroService } from './shared/cookies/klaro.service'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from './shared/mocks/translate-loader.mock'; import { getTestScheduler } from 'jasmine-marbles'; import objectContaining = jasmine.objectContaining; import createSpyObj = jasmine.createSpyObj; import SpyObj = jasmine.SpyObj; import { ThemeService } from './shared/theme-support/theme.service'; import { getMockThemeService } from './shared/mocks/theme-service.mock'; let spy: SpyObj; @Injectable() export class ConcreteInitServiceMock extends InitService { protected static resolveAppConfig() { spy.resolveAppConfig(); } protected init(): () => Promise { spy.init(); return async () => true; } } const initialState = { core: { auth: { loading: false, blocking: true, } } }; describe('InitService', () => { describe('providers', () => { beforeEach(() => { spy = createSpyObj('ConcreteInitServiceMock', { resolveAppConfig: null, init: null, }); }); it('should throw error when called on abstract InitService', () => { expect(() => InitService.providers()).toThrow(); }); it('should correctly set up provider dependencies', () => { const providers = ConcreteInitServiceMock.providers(); expect(providers).toContain(objectContaining({ provide: InitService, useClass: ConcreteInitServiceMock })); expect(providers).toContain(objectContaining({ provide: APP_CONFIG, })); expect(providers).toContain(objectContaining({ provide: APP_INITIALIZER, deps: [ InitService ], multi: true, })); }); it('should call resolveAppConfig() in APP_CONFIG factory', () => { const factory = ( ConcreteInitServiceMock.providers() .find((p: any) => p.provide === APP_CONFIG) as any ).useFactory; // this factory is called _before_ InitService is instantiated factory(); expect(spy.resolveAppConfig).toHaveBeenCalled(); expect(spy.init).not.toHaveBeenCalled(); }); it('should defer to init() in APP_INITIALIZER factory', () => { const factory = ( ConcreteInitServiceMock.providers() .find((p: any) => p.provide === APP_INITIALIZER) as any ).useFactory; // we don't care about the dependencies here // @ts-ignore const instance = new ConcreteInitServiceMock(null, null, null); // provider ensures that the right concrete instance is passed to the factory factory(instance); expect(spy.resolveAppConfig).not.toHaveBeenCalled(); expect(spy.init).toHaveBeenCalled(); }); }); describe('common initialization steps', () => { let correlationIdServiceSpy; let dspaceTransferStateSpy; let transferStateSpy; let metadataServiceSpy; let breadcrumbsServiceSpy; beforeEach(waitForAsync(() => { correlationIdServiceSpy = jasmine.createSpyObj('correlationIdServiceSpy', [ 'initCorrelationId', ]); dspaceTransferStateSpy = jasmine.createSpyObj('dspaceTransferStateSpy', [ 'transfer', ]); transferStateSpy = jasmine.createSpyObj('dspaceTransferStateSpy', [ 'get', 'hasKey' ]); breadcrumbsServiceSpy = jasmine.createSpyObj('breadcrumbsServiceSpy', [ 'listenForRouteChanges', ]); metadataServiceSpy = jasmine.createSpyObj('metadataService', [ 'listenForRouteChange', ]); TestBed.resetTestingModule(); TestBed.configureTestingModule({ imports: [ CommonModule, StoreModule.forRoot(authReducer, storeModuleConfig), TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateLoaderMock } }), ], providers: [ { provide: InitService, useClass: ConcreteInitServiceMock }, { provide: CorrelationIdService, useValue: correlationIdServiceSpy }, { provide: DSpaceTransferState, useValue: dspaceTransferStateSpy }, { provide: APP_CONFIG, useValue: environment }, { provide: LocaleService, useValue: getMockLocaleService() }, { provide: Angulartics2DSpace, useValue: new AngularticsProviderMock() }, { provide: MetadataService, useValue: metadataServiceSpy }, { provide: BreadcrumbsService, useValue: breadcrumbsServiceSpy }, { provide: AuthService, useValue: new AuthServiceMock() }, { provide: Router, useValue: new RouterMock() }, { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, { provide: MenuService, useValue: new MenuServiceStub() }, { provide: KlaroService, useValue: undefined }, { provide: GoogleAnalyticsService, useValue: undefined }, { provide: ThemeService, useValue: getMockThemeService() }, provideMockStore({ initialState }), AppComponent, RouteService, ] }); })); describe('initÀnalytics', () => { describe('when GoogleAnalyticsService is provided', () => { let googleAnalyticsSpy; beforeEach(() => { googleAnalyticsSpy = jasmine.createSpyObj('googleAnalyticsService', [ 'addTrackingIdToPage', ]); TestBed.overrideProvider(GoogleAnalyticsService, { useValue: googleAnalyticsSpy }); }); it('should call googleAnalyticsService.addTrackingIdToPage()', inject([InitService], (service) => { // @ts-ignore service.initAnalytics(); expect(googleAnalyticsSpy.addTrackingIdToPage).toHaveBeenCalledTimes(1); })); }); }); describe('initRouteListeners', () => { it('should call listenForRouteChanges', inject([InitService], (service) => { // @ts-ignore service.initRouteListeners(); expect(metadataServiceSpy.listenForRouteChange).toHaveBeenCalledTimes(1); expect(breadcrumbsServiceSpy.listenForRouteChanges).toHaveBeenCalledTimes(1); })); }); describe('initKlaro', () => { const BLOCKING = { t: { core: { auth: { blocking: true } } }, f: { core: { auth: { blocking: false } } }, }; it('should not error out if KlaroService is not provided', inject([InitService], (service) => { // @ts-ignore service.initKlaro(); })); describe('when KlaroService is provided', () => { let klaroServiceSpy; beforeEach(() => { klaroServiceSpy = jasmine.createSpyObj('klaroServiceSpy', [ 'initialize', ]); TestBed.overrideProvider(KlaroService, { useValue: klaroServiceSpy }); }); it('should not initialize Klaro while auth is blocking', () => { getTestScheduler().run(({ cold, flush}) => { TestBed.overrideProvider(Store, { useValue: cold('t--t--t--', BLOCKING) }); const service = TestBed.inject(InitService); // @ts-ignore service.initKlaro(); flush(); expect(klaroServiceSpy.initialize).not.toHaveBeenCalled(); }); }); it('should only initialize Klaro the first time auth is unblocked', () => { getTestScheduler().run(({ cold, flush}) => { TestBed.overrideProvider(Store, { useValue: cold('t--t--f--t--f--', BLOCKING) }); const service = TestBed.inject(InitService); // @ts-ignore service.initKlaro(); flush(); expect(klaroServiceSpy.initialize).toHaveBeenCalledTimes(1); }); }); }); describe('when KlaroService is not provided', () => { it('should not error out when auth is unblocked', () => { getTestScheduler().run(({ cold, flush}) => { TestBed.overrideProvider(Store, { useValue: cold('t--t--f--t--f--', BLOCKING) }); const service = TestBed.inject(InitService); // @ts-ignore service.initKlaro(); flush(); }); }); }); }); }); });