Select to view content in your preferred language

How to Unit Test Angular with ArcGIS JS API

107
1
2 weeks ago
joelwüthrich
New Contributor

Hi, 
I use the credential-identity Manager in an Anular 18 OAuth Project ( I log myself on into an Enterprise portal). I can't figure out how I could log my testuser in (in the Angular Jasmin and Karma Tests). I can't log my user in because I have a UI -Login Process and it looks like this:

authService:

 

...

  public async initializeLogin(): Promise<boolean> {
    IdentityManager.registerOAuthInfos([oauthInfo]);
    try {
      this.credential = await IdentityManager.checkSignInStatus(signInUrl);
      console.log('user successfully logged in');

      this.portal = (await new Portal({ url: oauthInfo.portalUrl }).load()) as Portal;
      this.groups = await this.portal.user?.fetchGroups();

      // Update credentials after successful login
      this._credential$.next(this.credential);

      // Initialize the ArcGIS REST API with the user's credentials
      this.initArcGisRESTAuthentication();
      environment.token = this.credential.token;
      return true;
    } catch (e) {
      console.log('user not logged in', e);
      this.login();
      return false;
    }
  }

  private initArcGisRESTAuthentication() {
    const token = Promise.resolve(this.credential.token);
    const authentication = {
      portal: signInUrl + '/rest',
      getToken(): Promise<string> {
        return token;
      },
    };
    setDefaultRequestOptions({ authentication }, true);
  }

  login() {
    void IdentityManager.getCredential(signInUrl);
  }

...

 

 

AppComponent:

 

...

  async ngOnInit() {
    await this.authService.initialized;
  }

  get userName() {
    return this.authService.portal.user.username;
  }

  get displayName() {
    return this.authService.portal.user.fullName;
  }

  get initialized() {
    return this.authService.initialized;
  }

  logout() {
    return this.authService.logout();
  }

...

 

 

The Test of the AppComponent:

 

import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SbbIconTestingModule } from '@sbb-esta/angular/icon/testing';
import { SbbMenuItem } from '@sbb-esta/angular/menu';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { AuthService } from './services/oauth/auth.service';
import { ActivatedRoute } from '@angular/router';

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let authService: AuthService;

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        NoopAnimationsModule,
        SbbIconTestingModule,
        HttpClientModule,
        AppComponent // Standalone-Komponente wird hier importiert
      ],
      providers: [
        AuthService,
        {
          provide: ActivatedRoute,
          useValue: {
            snapshot: {
              paramMap: {
                get: (key: string) => (key === 'id' ? '123' : null),
              },
            },
          },
        },
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    authService = TestBed.inject(AuthService);
    fixture.detectChanges();
  });

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

  it('should render title', () => {
    expect(fixture.nativeElement.querySelector('.sbb-header-lean-titlebox > span').textContent).toContain('ESTA GIS Blueprint');
  });

  it('should render username', waitForAsync(async () => {
    await authService.initializeLogin();
    fixture.detectChanges();
    expect(fixture.nativeElement.querySelector('.sbb-usermenu-user-info-display-name').textContent).toContain(authService.portal.user.fullName);
  }));

  it('should logout', waitForAsync(async () => {
    await authService.initializeLogin();
    fixture.detectChanges();

    const logoutSpy = spyOn(authService, 'logout').and.callThrough();

    const usermenuOpenButton = fixture.debugElement.query(By.css('.sbb-menu-trigger-usermenu'));
    usermenuOpenButton.nativeElement.click();
    fixture.detectChanges();

    const logoutButton = fixture.debugElement.query(By.directive(SbbMenuItem));
    logoutButton.nativeElement.click();

    expect(logoutSpy).toHaveBeenCalled();
  }));

  it('should login successfully', waitForAsync(async () => {
    await authService.initializeLogin();
    await component.ngOnInit();
    fixture.detectChanges();

    expect(authService.authenticated).toBeTrue();
    expect(component.initialized).toBeTrue();
  }));

  it('should fail to login and not proceed with tests', waitForAsync(async () => {
    spyOn(authService, 'initializeLogin').and.throwError('Login failed');
    await component.ngOnInit();
    fixture.detectChanges();

    expect(authService.authenticated).toBeFalse();
    expect(component.initialized).toBeFalsy();
  }));
});

 

 

I can't say my authService with which user he should log in...

0 Kudos
1 Reply
mleahy_cl
Regular Contributor

I'd recommend avoiding testing the IdentityManager itself.  I've faced a similar challenge as you.  The solution that seems to have worked best is to not import IdentityManager directly in the component you want to test.  Instead, you could create a small wrapper class that provides a static method to returns the instance of IdentityManager.  In your component, you would then refer to YourWrapperClass.IdentityManager()... Then, in your tests, you can import the wrapper class, and in `beforeEach()`, spy on the static method that provides the identity manager class, and return a simple object with whatever methods/data you want to validate with your tests mocked.  This way you will not actually be executing the IdentityMangager, but you can validate that its methods are called with expected parameters, and/or validate that your code behaves as expected when its methods return specific results.

I'll be interested to see if there are other ideas offered on this.

0 Kudos