0

I'm working through my test environment and am trying to get around an issue with a single user profile through oidc-client being undefined during unit testing.

I have tried making the BeforeEach methods async that has not helped, and I have also tried restructuring my AuthService as well.

This is the error that I am getting from the test components:

ResourcesCardComponent > should create

Failed: Cannot read property 'profile' of undefined

AuthService

import { Injectable } from '@angular/core';
import { UserManager, User, WebStorageStateStore } from 'oidc-client';
import { BehaviorSubject } from 'rxjs';
import { ConfigAssetLoaderService } from '../config-asset-loader.service';
@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private _userManager: UserManager;
  public _user: User;
  public isLoggedInSubject$ = new BehaviorSubject<any>(this._user);
  isLoggedIn = this.isLoggedInSubject$.asObservable();

  constructor(private configService: ConfigAssetLoaderService) {
    const config = {};
    this.configService.loadConfiguration().subscribe(response => {
      config['authority'] = response.authority;
      config['client_id'] = response.client_id;
      config['redirect_uri'] = response.redirect_uri;
      config['scope'] = response.scope;
      config['response_type'] = response.response_type;
      config['loadUserInfo'] = response.loadUserInfo;
      config['userStore'] = new WebStorageStateStore({store: window.sessionStorage});
      config['metadata'] = {
        issuer: response.issuer,
        authorization_endpoint: response.authorization_endpoint,
        userinfo_endpoint: response.userinfo_endpoint,
        jwks_uri: response.jwks_uri,
        end_session_endpoint: response.end_session_endpoint
      };
      config['signingKeys'] = response.signingKeys;
      config['extraQueryParams'] = {
        resource: response.claimsApiResourceId
      };
      this._userManager = new UserManager(config);
      this._userManager.getUser().then(user => {
        if (user && !user.expired) {
          this._user = user;
          this.isLoggedInSubject$.next(user);
        }
      });
    });
  }
}

The AuthService is pretty standard, all the important pieces for this question are in the constructor.

The component in question using this service is the following:

import { Component, Input } from '@angular/core';
import { ActionLink } from '../../shared/models/actionlink';
import { AuthService } from '../../core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Component({
  selector: 'thrive-resources-card',
  templateUrl: './resources-card.component.html',
  styleUrls: ['./resources-card.component.scss']
})
export class ResourcesCardComponent {

  @Input() public actionLinks: ActionLink[];

  public firstName$: Observable<string>;

  constructor(private authService: AuthService) {
    this.firstName$ = this.authService.isLoggedInSubject$.pipe(
      map(response => response.profile.unique_name.replace(/\s+/, '').split(',')[1])
    );
  }
}

Here is the test component for the ResourceCardComponent as well:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ResourcesCardComponent } from './resources-card.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { ResourcesCardContainerComponent } from './resources-card-container/resources-card-container.component';

const fakeRoute = {
  snapshot: {
      data: {
        actionLinks: []
      }
  }
};

describe('ResourcesCardComponent', () => {
  let component: ResourcesCardComponent;
  let fixture: ComponentFixture<ResourcesCardComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        ResourcesCardComponent,
        ResourcesCardContainerComponent
      ],
      imports: [
        RouterModule,
        HttpClientTestingModule
      ],
      providers: [
        {
          provide: ActivatedRoute,
          useFactory: () => fakeRoute
        }
      ]
    }).compileComponents();
  }));

  beforeEach(async(() => {
    fixture = TestBed.createComponent(ResourcesCardComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  it('should create', () => {
    component.actionLinks = [];
    expect(component).toBeTruthy();
  });
});

1 Answer 1

1

You initiate your service with

public isLoggedInSubject$ = new BehaviorSubject<any>(this._user)

because this._user is undefined.

Then in your component, your want response.profile.unique_name.replace(/\s+/, '') ...

BUT this.authService.isLoggedInSubject$ return as first value undefined. This is why you have this error.

You should either mock you service to return an observable of({profile:{unique_name:'some name'}}) Or initiate your user with better data.

spyOn(authService , 'isLoggedInSubject$').and.returnValue(of({profile:{unique_name:'some name'}}))
Sign up to request clarification or add additional context in comments.

1 Comment

I am fully aware of this as the error, I'm trying to figure out how to potentially get around this error. Any ideas?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.