How to mock value of a config object used in a constructor, with Jasmine

I recently started using Jasmine for testing a Angular application and, while most of the tests are working fine, I'm having trouble with a specific one.

So this is the test for the AppComponent, which looks like this:

app.component.ts

import { Component, OnDestroy } from '@angular/core';
import { Idle, DEFAULT_INTERRUPTSOURCES } from 'ng2-idle-core';
import { Store } from '@ngxs/store';

import { OAuthService, JwksValidationHandler } from 'angular-oauth2-oidc';

import {
    authConfig,
    LoginSuccess,
    CriticalErrorHandler,
    CriticalLogoutRequest } from './core';

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent {
    idleState = 'Not started.';
    timedOut = false;
    lastPing?: Date = null;

    // Important to keep the "CriticalErrorHandler" here, because it is an injectable "service" and must be
    // instancied at the beginning of the app
    constructor(
        private idle: Idle,
        private store: Store,
        private criticalErrorHandler: CriticalErrorHandler,
        private oauthService: OAuthService) {

        // Idle time
        this.idle.setIdle(3600);

        this.idle.setTimeout(30);

        this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);

        this.idle.onIdleStart.subscribe(() => {
          this.store.dispatch(new CriticalLogoutRequest());
        });

        this.login();
    }

    private login() {
        this.oauthService.configure(authConfig);
        this.oauthService.tokenValidationHandler = new JwksValidationHandler();

        this.oauthService.loadDiscoveryDocumentAndLogin({
            onTokenReceived : () => {
                this.oauthService.setupAutomaticSilentRefresh();
                this.store.dispatch(new LoginSuccess());
            }
        });
    }
}

As can be seen, the constructor calls the login() function, which in turn uses a config object called authConfig and that looks like this:

auth.config.ts

import { AuthConfig } from "angular-oauth2-oidc";
import { environment } from "environments/environment";

export const authConfig: AuthConfig = {

  silentRefreshRedirectUri : window.location.origin + '/assets/silent-refresh.html',

  issuer: environment.endpoints.identity,

  redirectUri: window.location.origin + '/index.html',

  clientId: 'ID',

  scope: 'openid user permissions My.WebApp',

  responseType: "id_token token",

  requireHttps: environment.requireHttps,

  sessionChecksEnabled : true

};

This calls another config object called environment, which has requireHttps set to true. So when I do a simple create test like this:

app.component.spec.ts

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, TestBed, ComponentFixture } from '@angular/core/testing';
import { RouterModule } from '@angular/router';
import { SharedModule } from 'primeng/primeng';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';

fdescribe('AppComponent', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<AppComponent>;

    beforeEach(async(() => {

        TestBed.configureTestingModule({
            imports: [
                CoreModule,
                SharedModule,
                RouterModule.forRoot([])
            ],
            declarations: [AppComponent],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        }).compileComponents();
    }));

    it('should create the app', async(() => {

        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
        expect(component).toBeTruthy();
    }));

});

I get a "issuer must use https, or config value for property requireHttps must allow http". So what I want to do is, for the scope of the test, change this requireHttps property in the config object to false. But I'm not really sure how to do this... any help is appreciated!

Upvotes: 0

Views: 2273

Answers (1)

terahertz
terahertz

Reputation: 3511

What if you were to pass the AuthConfig as an argument to your component's login() instead? This will allow you to mock your AuthConfig again in your unit test.

app.component.ts

import { authConfig } from './core';
...
this.login(authConfig);
...

private login(_authConfig:AuthConfig) {
    this.oauthService.configure(_authConfig);
    this.oauthService.tokenValidationHandler = new JwksValidationHandler();

    this.oauthService.loadDiscoveryDocumentAndLogin({
        onTokenReceived : () => {             
            this.oauthService.setupAutomaticSilentRefresh();
            this.store.dispatch(new LoginSuccess());
        }
    });
}

And in your test suite, you will be mocking (creating a new instance of it for the sake of this test) the AuthConfig object

app.component.spec.ts


beforeEach(() => async(() => {
   ...
   let mockAuthConfig: AuthConfig;
   ...
}))

beforeEach(() => {
   ...
   mockAuthConfig = {
      silentRefreshRedirectUri : window.location.origin + '/assets/silent-refresh.html',
      issuer: environment.endpoints.identity,
      redirectUri: window.location.origin + '/index.html',
      clientId: 'ID',
      scope: 'openid user permissions My.WebApp',
      responseType: "id_token token",
      requireHttps: false, // <== CHANGED
      sessionChecksEnabled : true
   }
   ...

   fixture.detectChanges();
})

...

it(`testing login()`, fakeAsync(() => {
   component.login(mockAuthConfig);

   //your tests here
   //expect(...).toBe(...);
}))

Essentially, the invocation of login() in app.component.ts will use the authConfig that you have predefined and exported out. However, in your unit test case, you will mock(recreate) a new AuthConfig object that has different properties which you then pass into login() via component.login(mockAuthConfig).

Hope this works for you!

Upvotes: 1

Related Questions