fix: fix some tests
This commit is contained in:
		
							parent
							
								
									28da493462
								
							
						
					
					
						commit
						6a03a2bd62
					
				@ -135,7 +135,7 @@ export class AppComponent implements OnInit {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  login() {
 | 
			
		||||
    this.oidcSecurityService.authorize();
 | 
			
		||||
    this.oidcSecurityService.authorize().subscribe();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  logout() {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							@ -26,15 +26,14 @@
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "build": "rslib build",
 | 
			
		||||
    "dev": "rslib build --watch",
 | 
			
		||||
    "test": "vitest --browser.headless",
 | 
			
		||||
    "test-ci": "vitest --watch=false --browser.headless --coverage",
 | 
			
		||||
    "test": "vitest --coverage",
 | 
			
		||||
    "test-ci": "vitest --watch=false --coverage",
 | 
			
		||||
    "pack": "npm run build && npm pack ./dist",
 | 
			
		||||
    "publish": "npm run build && npm publish ./dist",
 | 
			
		||||
    "coverage": "vitest run --coverage",
 | 
			
		||||
    "lint": "ultracite lint",
 | 
			
		||||
    "format": "ultracite format",
 | 
			
		||||
    "cli": "tsx scripts/cli.ts",
 | 
			
		||||
    "test:browser": "vitest --workspace=vitest.workspace.ts"
 | 
			
		||||
    "cli": "tsx scripts/cli.ts"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@ngify/http": "^2.0.4",
 | 
			
		||||
@ -51,11 +50,14 @@
 | 
			
		||||
    "@evilmartians/lefthook": "^1.0.3",
 | 
			
		||||
    "@playwright/test": "^1.49.1",
 | 
			
		||||
    "@rslib/core": "^0.3.1",
 | 
			
		||||
    "@swc/core": "^1.10.12",
 | 
			
		||||
    "@types/jsdom": "^21.1.7",
 | 
			
		||||
    "@types/lodash-es": "^4.17.12",
 | 
			
		||||
    "@types/node": "^22.12.0",
 | 
			
		||||
    "@vitest/browser": "^3.0.4",
 | 
			
		||||
    "@vitest/coverage-v8": "^3.0.4",
 | 
			
		||||
    "commander": "^13.1.0",
 | 
			
		||||
    "jsdom": "^26.0.0",
 | 
			
		||||
    "lodash-es": "^4.17.21",
 | 
			
		||||
    "oxc-parser": "^0.48.1",
 | 
			
		||||
    "oxc-walker": "^0.2.2",
 | 
			
		||||
@ -65,9 +67,9 @@
 | 
			
		||||
    "tsx": "^4.19.2",
 | 
			
		||||
    "typescript": "^5.7.3",
 | 
			
		||||
    "ultracite": "^4.1.15",
 | 
			
		||||
    "unplugin-swc": "^1.5.1",
 | 
			
		||||
    "vite-tsconfig-paths": "^5.1.4",
 | 
			
		||||
    "vitest": "^3.0.4",
 | 
			
		||||
    "webdriverio": "^9.7.2"
 | 
			
		||||
    "vitest": "^3.0.4"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "rxjs",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										805
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										805
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,7 +1,7 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
 | 
			
		||||
import { Command } from 'commander';
 | 
			
		||||
import { rewriteAllObservableSubscribeToLastValueFrom } from './code-transform';
 | 
			
		||||
import { rewriteAllObservableSubscribeTofirstValueFrom } from './code-transform';
 | 
			
		||||
 | 
			
		||||
const program = new Command();
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ program
 | 
			
		||||
  .command('rewrite <pattern>')
 | 
			
		||||
  .description('Rewrite files matching the given glob pattern')
 | 
			
		||||
  .action(async (pattern: string) => {
 | 
			
		||||
    await rewriteAllObservableSubscribeToLastValueFrom(pattern);
 | 
			
		||||
    await rewriteAllObservableSubscribeTofirstValueFrom(pattern);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
program.parse(process.argv);
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
import assert from 'node:assert/strict';
 | 
			
		||||
import { describe, it } from 'node:test';
 | 
			
		||||
import { Biome, Distribution } from '@biomejs/js-api';
 | 
			
		||||
import { rewriteObservableSubscribeToLastValueFrom } from './code-transform';
 | 
			
		||||
import { rewriteObservableSubscribeTofirstValueFrom } from './code-transform';
 | 
			
		||||
 | 
			
		||||
describe('rewriteSpecObservableSubscribeToLastValueFrom', () => {
 | 
			
		||||
describe('rewriteSpecObservableSubscribeTofirstValueFrom', () => {
 | 
			
		||||
  it('should transform simple example valid string', async () => {
 | 
			
		||||
    const actual = await rewriteObservableSubscribeToLastValueFrom(
 | 
			
		||||
    const actual = await rewriteObservableSubscribeTofirstValueFrom(
 | 
			
		||||
      'index.ts',
 | 
			
		||||
      `refreshSessionIframeService
 | 
			
		||||
              .refreshSessionWithIframe(allConfigs[0]!, allConfigs)
 | 
			
		||||
@ -20,7 +20,7 @@ describe('rewriteSpecObservableSubscribeToLastValueFrom', () => {
 | 
			
		||||
              });`
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const expect = `const result = await lastValueFrom(refreshSessionIframeService.refreshSessionWithIframe(allConfigs[0]!, allConfigs));
 | 
			
		||||
    const expect = `const result = await firstValueFrom(refreshSessionIframeService.refreshSessionWithIframe(allConfigs[0]!, allConfigs));
 | 
			
		||||
      expect(result).toHaveBeenCalledExactlyOnceWith('a-url',allConfigs[0]!,allConfigs);`;
 | 
			
		||||
 | 
			
		||||
    const biome = await Biome.create({
 | 
			
		||||
@ -34,7 +34,7 @@ describe('rewriteSpecObservableSubscribeToLastValueFrom', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should rewrite complex exmaple to valid string', async () => {
 | 
			
		||||
    const actual = await rewriteObservableSubscribeToLastValueFrom(
 | 
			
		||||
    const actual = await rewriteObservableSubscribeTofirstValueFrom(
 | 
			
		||||
      'index.ts',
 | 
			
		||||
      `codeFlowCallbackService
 | 
			
		||||
          .authenticatedCallbackWithCode('some-url4', config, [config])
 | 
			
		||||
@ -56,7 +56,7 @@ describe('rewriteSpecObservableSubscribeToLastValueFrom', () => {
 | 
			
		||||
 | 
			
		||||
    const expect = `
 | 
			
		||||
  try {
 | 
			
		||||
    const abc = await lastValueFrom(codeFlowCallbackService.authenticatedCallbackWithCode('some-url4', config, [config]));
 | 
			
		||||
    const abc = await firstValueFrom(codeFlowCallbackService.authenticatedCallbackWithCode('some-url4', config, [config]));
 | 
			
		||||
    expect(abc).toBeTruthy();
 | 
			
		||||
  } catch (err: any) {
 | 
			
		||||
    if (err instanceof EmptyError) {
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ function sourceTextFromNode(
 | 
			
		||||
  return magicString.getSourceText(start, end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function rewriteObservableSubscribeToLastValueFrom(
 | 
			
		||||
export async function rewriteObservableSubscribeTofirstValueFrom(
 | 
			
		||||
  filename: string,
 | 
			
		||||
  content?: string
 | 
			
		||||
) {
 | 
			
		||||
@ -83,7 +83,7 @@ export async function rewriteObservableSubscribeToLastValueFrom(
 | 
			
		||||
              error = args[1];
 | 
			
		||||
              complete = args[2];
 | 
			
		||||
            }
 | 
			
		||||
            let newContent = `await lastValueFrom(${sourceTextFromNode(context, child.expression.callee.object)});`;
 | 
			
		||||
            let newContent = `await firstValueFrom(${sourceTextFromNode(context, child.expression.callee.object)});`;
 | 
			
		||||
 | 
			
		||||
            if (next) {
 | 
			
		||||
              const nextParam =
 | 
			
		||||
@ -161,12 +161,12 @@ export async function rewriteObservableSubscribeToLastValueFrom(
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function rewriteAllObservableSubscribeToLastValueFrom(
 | 
			
		||||
export async function rewriteAllObservableSubscribeTofirstValueFrom(
 | 
			
		||||
  pattern: string | string[]
 | 
			
		||||
) {
 | 
			
		||||
  const files = fsp.glob(pattern);
 | 
			
		||||
  for await (const file of files) {
 | 
			
		||||
    const result = await rewriteObservableSubscribeToLastValueFrom(file);
 | 
			
		||||
    const result = await rewriteObservableSubscribeTofirstValueFrom(file);
 | 
			
		||||
 | 
			
		||||
    await fsp.writeFile(file, result, 'utf-8');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import { provideHttpClientTesting } from '@/testing/http';
 | 
			
		||||
import { HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { HttpTestingController } from '@ngify/http/testing';
 | 
			
		||||
import { provideHttpClient, withInterceptorsFromDi } from 'oidc-client-rx';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { DataService } from './data.service';
 | 
			
		||||
import { HttpBaseService } from './http-base.service';
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ describe('Data Service', () => {
 | 
			
		||||
    it('get call sets the accept header', async () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
 | 
			
		||||
      const data = await lastValueFrom(
 | 
			
		||||
      const data = await firstValueFrom(
 | 
			
		||||
        dataService.get(url, { configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(data).toBe('bodyData');
 | 
			
		||||
@ -51,7 +51,7 @@ describe('Data Service', () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
      const token = 'token';
 | 
			
		||||
 | 
			
		||||
      const data = await lastValueFrom(
 | 
			
		||||
      const data = await firstValueFrom(
 | 
			
		||||
        dataService.get(url, { configId: 'configId1' }, token)
 | 
			
		||||
      );
 | 
			
		||||
      expect(data).toBe('bodyData');
 | 
			
		||||
@ -69,7 +69,7 @@ describe('Data Service', () => {
 | 
			
		||||
    it('call without ngsw-bypass param by default', async () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
 | 
			
		||||
      const data = await lastValueFrom(
 | 
			
		||||
      const data = await firstValueFrom(
 | 
			
		||||
        dataService.get(url, { configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(data).toBe('bodyData');
 | 
			
		||||
@ -87,7 +87,7 @@ describe('Data Service', () => {
 | 
			
		||||
    it('call with ngsw-bypass param', async () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
 | 
			
		||||
      const data = await lastValueFrom(
 | 
			
		||||
      const data = await firstValueFrom(
 | 
			
		||||
        dataService.get(url, { configId: 'configId1', ngswBypass: true })
 | 
			
		||||
      );
 | 
			
		||||
      expect(data).toBe('bodyData');
 | 
			
		||||
@ -107,8 +107,9 @@ describe('Data Service', () => {
 | 
			
		||||
    it('call sets the accept header when no other params given', async () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(dataService
 | 
			
		||||
        .post(url, { some: 'thing' }, { configId: 'configId1' }));
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        dataService.post(url, { some: 'thing' }, { configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      const req = httpMock.expectOne(url);
 | 
			
		||||
 | 
			
		||||
      expect(req.request.method).toBe('POST');
 | 
			
		||||
@ -125,7 +126,7 @@ describe('Data Service', () => {
 | 
			
		||||
 | 
			
		||||
      headers = headers.set('X-MyHeader', 'Genesis');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        dataService.post(
 | 
			
		||||
          url,
 | 
			
		||||
          { some: 'thing' },
 | 
			
		||||
@ -147,7 +148,7 @@ describe('Data Service', () => {
 | 
			
		||||
    it('call without ngsw-bypass param by default', async () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        dataService.post(url, { some: 'thing' }, { configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      const req = httpMock.expectOne(url);
 | 
			
		||||
@ -164,7 +165,7 @@ describe('Data Service', () => {
 | 
			
		||||
    it('call with ngsw-bypass param', async () => {
 | 
			
		||||
      const url = 'testurl';
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        dataService.post(
 | 
			
		||||
          url,
 | 
			
		||||
          { some: 'thing' },
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
import { HttpHeaders, HttpParams } from '@ngify/http';
 | 
			
		||||
import { HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import type { Observable } from 'rxjs';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
import { HttpParams } from '../http';
 | 
			
		||||
import { HttpBaseService } from './http-base.service';
 | 
			
		||||
 | 
			
		||||
const NGSW_CUSTOM_PARAM = 'ngsw-bypass';
 | 
			
		||||
 | 
			
		||||
@ -257,7 +257,7 @@ describe('Auth State Service', () => {
 | 
			
		||||
        [{ configId: 'configId1' }]
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(2);
 | 
			
		||||
      expect(spy).toHaveBeenCalledWith([
 | 
			
		||||
      expect(spy.mock.calls).toEqual([
 | 
			
		||||
        ['authzData', 'accesstoken', { configId: 'configId1' }],
 | 
			
		||||
        [
 | 
			
		||||
          'access_token_expires_at',
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import {
 | 
			
		||||
  mockRouterProvider,
 | 
			
		||||
  spyOnProperty,
 | 
			
		||||
} from '@/testing';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AutoLoginService } from '../auto-login/auto-login.service';
 | 
			
		||||
import { CallbackService } from '../callback/callback.service';
 | 
			
		||||
@ -49,6 +49,8 @@ describe('CheckAuthService', () => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      imports: [],
 | 
			
		||||
      providers: [
 | 
			
		||||
        CheckAuthService,
 | 
			
		||||
        StoragePersistenceService,
 | 
			
		||||
        mockRouterProvider(),
 | 
			
		||||
        mockProvider(CheckSessionService),
 | 
			
		||||
        mockProvider(SilentRenewService),
 | 
			
		||||
@ -109,7 +111,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(checkAuthService as any, 'checkAuthWithConfig');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -136,7 +138,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      const spy = vi.spyOn(checkAuthService as any, 'checkAuthWithConfig');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -155,7 +157,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      ];
 | 
			
		||||
      const spy = vi.spyOn(checkAuthService as any, 'checkAuthWithConfig');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -184,7 +186,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      vi.spyOn(popUpService, 'isCurrentlyInPopup').mockReturnValue(true);
 | 
			
		||||
      const popupSpy = vi.spyOn(popUpService, 'sendMessageToMainWindow');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -216,7 +218,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        'http://localhost:4200'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -249,7 +251,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        .spyOn(callBackService, 'handleCallbackAndFireEvents')
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -282,7 +284,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      vi.spyOn(authStateService, 'getAccessToken').mockReturnValue('at');
 | 
			
		||||
      vi.spyOn(authStateService, 'getIdToken').mockReturnValue('idt');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -322,7 +324,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const userServiceSpy = vi.spyOn(userService, 'publishUserDataIfExists');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -362,7 +364,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const userServiceSpy = vi.spyOn(userService, 'publishUserDataIfExists');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -393,7 +395,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -420,7 +422,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(authStateService, 'setAuthenticatedAndFireEvent');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
@ -443,7 +445,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(userService, 'publishUserDataIfExists');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
@ -470,7 +472,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        'startTokenValidationPeriodically'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
@ -495,7 +497,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(checkSessionService, 'start');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
@ -520,7 +522,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(silentRenewService, 'getOrCreateIframe');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
@ -545,7 +547,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        'checkSavedRedirectRouteAndNavigate'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(1);
 | 
			
		||||
@ -568,7 +570,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        'checkSavedRedirectRouteAndNavigate'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(0);
 | 
			
		||||
@ -588,10 +590,10 @@ describe('CheckAuthService', () => {
 | 
			
		||||
 | 
			
		||||
      const fireEventSpy = vi.spyOn(publicEventsService, 'fireEvent');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(fireEventSpy).toHaveBeenCalledWith([
 | 
			
		||||
      expect(fireEventSpy.mock.calls).toEqual([
 | 
			
		||||
        [EventTypes.CheckingAuth],
 | 
			
		||||
        [EventTypes.CheckingAuthFinished],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -611,10 +613,10 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        'http://localhost:4200'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(fireEventSpy).toHaveBeenCalledWith([
 | 
			
		||||
      expect(fireEventSpy.mock.calls).toEqual([
 | 
			
		||||
        [EventTypes.CheckingAuth],
 | 
			
		||||
        [EventTypes.CheckingAuthFinishedWithError, 'ERROR'],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -634,10 +636,10 @@ describe('CheckAuthService', () => {
 | 
			
		||||
 | 
			
		||||
      const fireEventSpy = vi.spyOn(publicEventsService, 'fireEvent');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuth(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(fireEventSpy).toBeCalledWith([
 | 
			
		||||
      expect(fireEventSpy.mock.calls).toEqual([
 | 
			
		||||
        [EventTypes.CheckingAuth],
 | 
			
		||||
        [EventTypes.CheckingAuthFinished],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -665,7 +667,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(silentRenewService, 'getOrCreateIframe');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthIncludingServer(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
@ -694,7 +696,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthIncludingServer(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBeTruthy();
 | 
			
		||||
@ -742,7 +744,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthIncludingServer(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(checkSessionServiceStartSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -796,7 +798,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthIncludingServer(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(checkSessionServiceStartSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -825,7 +827,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(checkAuthService as any, 'checkAuthWithConfig');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthMultiple(allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(Array.isArray(result)).toBe(true);
 | 
			
		||||
@ -855,11 +857,11 @@ describe('CheckAuthService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(checkAuthService as any, 'checkAuthWithConfig');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthMultiple(allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(Array.isArray(result)).toBe(true);
 | 
			
		||||
      expect(spy).toBeCalledWith([
 | 
			
		||||
      expect(spy.mock.calls).toEqual([
 | 
			
		||||
        [
 | 
			
		||||
          { configId: 'configId1', authority: 'some-authority1' },
 | 
			
		||||
          allConfigs,
 | 
			
		||||
@ -886,7 +888,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(checkAuthService as any, 'checkAuthWithConfig');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkAuthService.checkAuthMultiple(allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(Array.isArray(result)).toBe(true);
 | 
			
		||||
@ -912,7 +914,7 @@ describe('CheckAuthService', () => {
 | 
			
		||||
      const allConfigs: OpenIdConfiguration[] = [];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(checkAuthService.checkAuthMultiple(allConfigs));
 | 
			
		||||
        await firstValueFrom(checkAuthService.checkAuthMultiple(allConfigs));
 | 
			
		||||
      } catch (error: any) {
 | 
			
		||||
        expect(error.message).toEqual(
 | 
			
		||||
          'could not find matching config for state the-state-param'
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import {
 | 
			
		||||
  type ActivatedRouteSnapshot,
 | 
			
		||||
  type RouterStateSnapshot,
 | 
			
		||||
} from 'oidc-client-rx';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { CheckAuthService } from '../auth-state/check-auth.service';
 | 
			
		||||
@ -24,6 +24,7 @@ describe('AutoLoginPartialRoutesGuard', () => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      imports: [],
 | 
			
		||||
      providers: [
 | 
			
		||||
        AutoLoginPartialRoutesGuard,
 | 
			
		||||
        mockRouterProvider(),
 | 
			
		||||
        AutoLoginService,
 | 
			
		||||
        mockProvider(AuthStateService),
 | 
			
		||||
@ -83,21 +84,20 @@ describe('AutoLoginPartialRoutesGuard', () => {
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard
 | 
			
		||||
          .canActivate(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          guard.canActivate(
 | 
			
		||||
            {} as ActivatedRouteSnapshot,
 | 
			
		||||
            { url: 'some-url1' } as RouterStateSnapshot
 | 
			
		||||
          ));
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          'some-url1'
 | 
			
		||||
            );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
            });;
 | 
			
		||||
expect(
 | 
			
		||||
              checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
            ).not.toHaveBeenCalled();
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it('should save current route and call `login` if not authenticated already and add custom params', async () => {
 | 
			
		||||
@ -114,22 +114,21 @@ expect(
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard
 | 
			
		||||
          .canActivate(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          guard.canActivate(
 | 
			
		||||
            { data: { custom: 'param' } } as unknown as ActivatedRouteSnapshot,
 | 
			
		||||
            { url: 'some-url1' } as RouterStateSnapshot
 | 
			
		||||
          ));
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          'some-url1'
 | 
			
		||||
            );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          { customParams: { custom: 'param' } }
 | 
			
		||||
            );;
 | 
			
		||||
expect(
 | 
			
		||||
              checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
            ).not.toHaveBeenCalled();
 | 
			
		||||
        );
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it('should call `checkSavedRedirectRouteAndNavigate` if authenticated already', async () => {
 | 
			
		||||
@ -146,13 +145,14 @@ expect(
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard
 | 
			
		||||
          .canActivate(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          guard.canActivate(
 | 
			
		||||
            {} as ActivatedRouteSnapshot,
 | 
			
		||||
            { url: 'some-url1' } as RouterStateSnapshot
 | 
			
		||||
          ));
 | 
			
		||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();;
 | 
			
		||||
expect(loginSpy).not.toHaveBeenCalled();;
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(loginSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(
 | 
			
		||||
          checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
        ).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
 | 
			
		||||
@ -174,21 +174,20 @@ expect(
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard
 | 
			
		||||
          .canActivateChild(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          guard.canActivateChild(
 | 
			
		||||
            {} as ActivatedRouteSnapshot,
 | 
			
		||||
            { url: 'some-url1' } as RouterStateSnapshot
 | 
			
		||||
          ));
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          'some-url1'
 | 
			
		||||
            );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
            });;
 | 
			
		||||
expect(
 | 
			
		||||
              checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
            ).not.toHaveBeenCalled();
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it('should save current route and call `login` if not authenticated already with custom params', async () => {
 | 
			
		||||
@ -205,22 +204,21 @@ expect(
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard
 | 
			
		||||
          .canActivateChild(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          guard.canActivateChild(
 | 
			
		||||
            { data: { custom: 'param' } } as unknown as ActivatedRouteSnapshot,
 | 
			
		||||
            { url: 'some-url1' } as RouterStateSnapshot
 | 
			
		||||
          ));
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          'some-url1'
 | 
			
		||||
            );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          { customParams: { custom: 'param' } }
 | 
			
		||||
            );;
 | 
			
		||||
expect(
 | 
			
		||||
              checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
            ).not.toHaveBeenCalled();
 | 
			
		||||
        );
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it('should call `checkSavedRedirectRouteAndNavigate` if authenticated already', async () => {
 | 
			
		||||
@ -237,13 +235,14 @@ expect(
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard
 | 
			
		||||
          .canActivateChild(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          guard.canActivateChild(
 | 
			
		||||
            {} as ActivatedRouteSnapshot,
 | 
			
		||||
            { url: 'some-url1' } as RouterStateSnapshot
 | 
			
		||||
          ));
 | 
			
		||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();;
 | 
			
		||||
expect(loginSpy).not.toHaveBeenCalled();;
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(loginSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(
 | 
			
		||||
          checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
        ).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
 | 
			
		||||
@ -265,14 +264,14 @@ expect(
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard.canLoad());
 | 
			
		||||
        await firstValueFrom(guard.canLoad());
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          ''
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          });;
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -301,14 +300,14 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
          trigger: 'imperative',
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard.canLoad());
 | 
			
		||||
        await firstValueFrom(guard.canLoad());
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          'some-url12/with/some-param?queryParam=true'
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          });;
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -326,9 +325,9 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
        );
 | 
			
		||||
        const loginSpy = vi.spyOn(loginService, 'login');
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard.canLoad());
 | 
			
		||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();;
 | 
			
		||||
expect(loginSpy).not.toHaveBeenCalled();;
 | 
			
		||||
        await firstValueFrom(guard.canLoad());
 | 
			
		||||
        expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(loginSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(
 | 
			
		||||
          checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
        ).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
 | 
			
		||||
@ -383,14 +382,14 @@ expect(
 | 
			
		||||
          autoLoginPartialRoutesGuard
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard$);
 | 
			
		||||
        await firstValueFrom(guard$);
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          ''
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          });;
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -423,14 +422,14 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
          autoLoginPartialRoutesGuard
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard$);
 | 
			
		||||
        await firstValueFrom(guard$);
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          'some-url12/with/some-param?queryParam=true'
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          });;
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -454,15 +453,15 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
          } as unknown as ActivatedRouteSnapshot)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard$);
 | 
			
		||||
        await firstValueFrom(guard$);
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          ''
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          { customParams: { custom: 'param' } }
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -484,9 +483,9 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
          autoLoginPartialRoutesGuard
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard$);
 | 
			
		||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();;
 | 
			
		||||
expect(loginSpy).not.toHaveBeenCalled();;
 | 
			
		||||
        await firstValueFrom(guard$);
 | 
			
		||||
        expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(loginSpy).not.toHaveBeenCalled();
 | 
			
		||||
        expect(
 | 
			
		||||
          checkSavedRedirectRouteAndNavigateSpy
 | 
			
		||||
        ).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
 | 
			
		||||
@ -537,14 +536,14 @@ expect(
 | 
			
		||||
          autoLoginPartialRoutesGuardWithConfig('configId1')
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        await lastValueFrom(guard$);
 | 
			
		||||
        await firstValueFrom(guard$);
 | 
			
		||||
        expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          ''
 | 
			
		||||
          );;
 | 
			
		||||
        );
 | 
			
		||||
        expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          });;
 | 
			
		||||
        });
 | 
			
		||||
        expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { Observable, lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { Observable, firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import type { CallbackContext } from '../flows/callback-context';
 | 
			
		||||
import { mockProvider } from '../testing/mock';
 | 
			
		||||
@ -59,7 +59,7 @@ describe('CallbackService ', () => {
 | 
			
		||||
        .spyOn(codeFlowCallbackService, 'authenticatedCallbackWithCode')
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        callbackService.handleCallbackAndFireEvents(
 | 
			
		||||
          'anyUrl',
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
@ -83,17 +83,16 @@ describe('CallbackService ', () => {
 | 
			
		||||
        .spyOn(implicitFlowCallbackService, 'authenticatedImplicitFlowCallback')
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        callbackService.handleCallbackAndFireEvents(
 | 
			
		||||
          'anyUrl',
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          [{ configId: 'configId1' }]
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      expect(authorizedCallbackWithCodeSpy).toHaveBeenCalledWith(
 | 
			
		||||
        { configId: 'configId1' },
 | 
			
		||||
        [{ configId: 'configId1' }]
 | 
			
		||||
      );
 | 
			
		||||
      expect(authorizedCallbackWithCodeSpy.mock.calls).toEqual([
 | 
			
		||||
        [{ configId: 'configId1' }, [{ configId: 'configId1' }]],
 | 
			
		||||
      ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('calls authorizedImplicitFlowCallback with hash if current flow is implicit flow and callbackurl does include a hash', async () => {
 | 
			
		||||
@ -105,7 +104,7 @@ describe('CallbackService ', () => {
 | 
			
		||||
        .spyOn(implicitFlowCallbackService, 'authenticatedImplicitFlowCallback')
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        callbackService.handleCallbackAndFireEvents(
 | 
			
		||||
          'anyUrlWithAHash#some-string',
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
@ -113,11 +112,9 @@ describe('CallbackService ', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(authorizedCallbackWithCodeSpy).toHaveBeenCalledWith(
 | 
			
		||||
        { configId: 'configId1' },
 | 
			
		||||
        [{ configId: 'configId1' }],
 | 
			
		||||
        'some-string'
 | 
			
		||||
      );
 | 
			
		||||
      expect(authorizedCallbackWithCodeSpy.mock.calls).toEqual([
 | 
			
		||||
        [{ configId: 'configId1' }, [{ configId: 'configId1' }], 'some-string'],
 | 
			
		||||
      ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('emits callbackinternal no matter which flow it is', async () => {
 | 
			
		||||
@ -131,7 +128,7 @@ describe('CallbackService ', () => {
 | 
			
		||||
        .spyOn(codeFlowCallbackService, 'authenticatedCallbackWithCode')
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        callbackService.handleCallbackAndFireEvents(
 | 
			
		||||
          'anyUrl',
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockRouterProvider } from '@/testing';
 | 
			
		||||
import { AbstractRouter } from 'oidc-client-rx';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import type { CallbackContext } from '../flows/callback-context';
 | 
			
		||||
import { FlowsDataService } from '../flows/flows-data.service';
 | 
			
		||||
@ -85,7 +85,7 @@ describe('CodeFlowCallbackService ', () => {
 | 
			
		||||
        triggerAuthorizationResultEvent: true,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        codeFlowCallbackService.authenticatedCallbackWithCode(
 | 
			
		||||
          'some-url2',
 | 
			
		||||
          config,
 | 
			
		||||
@ -125,7 +125,7 @@ describe('CodeFlowCallbackService ', () => {
 | 
			
		||||
        postLoginRoute: 'postLoginRoute',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        codeFlowCallbackService.authenticatedCallbackWithCode(
 | 
			
		||||
          'some-url3',
 | 
			
		||||
          config,
 | 
			
		||||
@ -163,7 +163,7 @@ describe('CodeFlowCallbackService ', () => {
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          codeFlowCallbackService.authenticatedCallbackWithCode(
 | 
			
		||||
            'some-url4',
 | 
			
		||||
            config,
 | 
			
		||||
@ -201,7 +201,7 @@ describe('CodeFlowCallbackService ', () => {
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          codeFlowCallbackService.authenticatedCallbackWithCode(
 | 
			
		||||
            'some-url5',
 | 
			
		||||
            config,
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockRouterProvider } from '@/testing';
 | 
			
		||||
import { AbstractRouter } from 'oidc-client-rx';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import type { CallbackContext } from '../flows/callback-context';
 | 
			
		||||
import { FlowsDataService } from '../flows/flows-data.service';
 | 
			
		||||
@ -20,6 +20,7 @@ describe('ImplicitFlowCallbackService ', () => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      imports: [],
 | 
			
		||||
      providers: [
 | 
			
		||||
        ImplicitFlowCallbackService,
 | 
			
		||||
        mockRouterProvider(),
 | 
			
		||||
        mockProvider(FlowsService),
 | 
			
		||||
        mockProvider(FlowsDataService),
 | 
			
		||||
@ -81,7 +82,7 @@ describe('ImplicitFlowCallbackService ', () => {
 | 
			
		||||
        triggerAuthorizationResultEvent: true,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        implicitFlowCallbackService.authenticatedImplicitFlowCallback(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -118,7 +119,7 @@ describe('ImplicitFlowCallbackService ', () => {
 | 
			
		||||
        postLoginRoute: 'postLoginRoute',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        implicitFlowCallbackService.authenticatedImplicitFlowCallback(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -152,7 +153,7 @@ describe('ImplicitFlowCallbackService ', () => {
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          implicitFlowCallbackService.authenticatedImplicitFlowCallback(
 | 
			
		||||
            config,
 | 
			
		||||
            [config],
 | 
			
		||||
@ -188,7 +189,7 @@ describe('ImplicitFlowCallbackService ', () => {
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          implicitFlowCallbackService.authenticatedImplicitFlowCallback(
 | 
			
		||||
            config,
 | 
			
		||||
            [config],
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,10 @@ describe('IntervalService', () => {
 | 
			
		||||
  let intervalService: IntervalService;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    vi.useFakeTimers();
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        IntervalService,
 | 
			
		||||
        {
 | 
			
		||||
          provide: Document,
 | 
			
		||||
          useValue: {
 | 
			
		||||
@ -22,6 +24,11 @@ describe('IntervalService', () => {
 | 
			
		||||
    intervalService = TestBed.inject(IntervalService);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    vi.useRealTimers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(intervalService).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { ReplaySubject, firstValueFrom, of, share, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { ConfigurationService } from '../config/config.service';
 | 
			
		||||
@ -33,9 +33,11 @@ describe('PeriodicallyTokenCheckService', () => {
 | 
			
		||||
  let publicEventsService: PublicEventsService;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    vi.useFakeTimers();
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      imports: [],
 | 
			
		||||
      providers: [
 | 
			
		||||
        PeriodicallyTokenCheckService,
 | 
			
		||||
        mockProvider(ResetAuthDataService),
 | 
			
		||||
        FlowHelper,
 | 
			
		||||
        mockProvider(FlowsDataService),
 | 
			
		||||
@ -73,10 +75,11 @@ describe('PeriodicallyTokenCheckService', () => {
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    if (intervalService.runTokenValidationRunning?.unsubscribe) {
 | 
			
		||||
    if (intervalService?.runTokenValidationRunning?.unsubscribe) {
 | 
			
		||||
      intervalService.runTokenValidationRunning.unsubscribe();
 | 
			
		||||
      intervalService.runTokenValidationRunning = null;
 | 
			
		||||
    }
 | 
			
		||||
    vi.useRealTimers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
@ -84,13 +87,22 @@ describe('PeriodicallyTokenCheckService', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('startTokenValidationPeriodically', () => {
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
      vi.useFakeTimers();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
      vi.useRealTimers();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('returns if no config has silentrenew enabled', async () => {
 | 
			
		||||
      const configs = [
 | 
			
		||||
        { silentRenew: false, configId: 'configId1' },
 | 
			
		||||
        { silentRenew: false, configId: 'configId2' },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        periodicallyTokenCheckService.startTokenValidationPeriodically(
 | 
			
		||||
          configs,
 | 
			
		||||
          configs[0]!
 | 
			
		||||
@ -107,7 +119,7 @@ describe('PeriodicallyTokenCheckService', () => {
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        periodicallyTokenCheckService.startTokenValidationPeriodically(
 | 
			
		||||
          configs,
 | 
			
		||||
          configs[0]!
 | 
			
		||||
@ -181,19 +193,29 @@ describe('PeriodicallyTokenCheckService', () => {
 | 
			
		||||
        of(configs[0]!)
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      periodicallyTokenCheckService.startTokenValidationPeriodically(
 | 
			
		||||
        configs,
 | 
			
		||||
        configs[0]!
 | 
			
		||||
      try {
 | 
			
		||||
        const test$ = periodicallyTokenCheckService
 | 
			
		||||
          .startTokenValidationPeriodically(configs, configs[0]!)
 | 
			
		||||
          .pipe(
 | 
			
		||||
            share({
 | 
			
		||||
              connector: () => new ReplaySubject(1),
 | 
			
		||||
              resetOnError: false,
 | 
			
		||||
              resetOnComplete: false,
 | 
			
		||||
              resetOnRefCountZero: true,
 | 
			
		||||
            })
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
        test$.subscribe();
 | 
			
		||||
 | 
			
		||||
        await vi.advanceTimersByTimeAsync(1000);
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        periodicallyTokenCheckService.startTokenValidationPeriodically
 | 
			
		||||
      ).toThrowError();
 | 
			
		||||
        await firstValueFrom(test$);
 | 
			
		||||
        expect.fail('should throw errror');
 | 
			
		||||
      } catch {
 | 
			
		||||
        expect(resetSilentRenewRunning).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          configs[0]
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('interval throws silent renew failed event with data in case of an error', async () => {
 | 
			
		||||
@ -220,20 +242,29 @@ describe('PeriodicallyTokenCheckService', () => {
 | 
			
		||||
        of(configs[0]!)
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      periodicallyTokenCheckService.startTokenValidationPeriodically(
 | 
			
		||||
        configs,
 | 
			
		||||
        configs[0]!
 | 
			
		||||
      try {
 | 
			
		||||
        const test$ = periodicallyTokenCheckService
 | 
			
		||||
          .startTokenValidationPeriodically(configs, configs[0]!)
 | 
			
		||||
          .pipe(
 | 
			
		||||
            share({
 | 
			
		||||
              connector: () => new ReplaySubject(1),
 | 
			
		||||
              resetOnComplete: false,
 | 
			
		||||
              resetOnError: false,
 | 
			
		||||
              resetOnRefCountZero: false,
 | 
			
		||||
            })
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
        test$.subscribe();
 | 
			
		||||
 | 
			
		||||
        await vi.advanceTimersByTimeAsync(1000);
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        periodicallyTokenCheckService.startTokenValidationPeriodically
 | 
			
		||||
      ).toThrowError();
 | 
			
		||||
      expect(publicEventsServiceSpy).toBeCalledWith([
 | 
			
		||||
        await firstValueFrom(test$);
 | 
			
		||||
      } catch {
 | 
			
		||||
        expect(publicEventsServiceSpy.mock.calls).toEqual([
 | 
			
		||||
          [EventTypes.SilentRenewStarted],
 | 
			
		||||
          [EventTypes.SilentRenewFailed, new Error('error')],
 | 
			
		||||
        ]);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('calls resetAuthorizationData and returns if no silent renew is configured', async () => {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, forkJoin, of, throwError } from 'rxjs';
 | 
			
		||||
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
 | 
			
		||||
import { type Observable, ReplaySubject, forkJoin, of, throwError } from 'rxjs';
 | 
			
		||||
import { catchError, map, share, switchMap } from 'rxjs/operators';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { ConfigurationService } from '../config/config.service';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
@ -52,16 +52,16 @@ export class PeriodicallyTokenCheckService {
 | 
			
		||||
  startTokenValidationPeriodically(
 | 
			
		||||
    allConfigs: OpenIdConfiguration[],
 | 
			
		||||
    currentConfig: OpenIdConfiguration
 | 
			
		||||
  ): Observable<void> {
 | 
			
		||||
  ): Observable<undefined> {
 | 
			
		||||
    const configsWithSilentRenewEnabled =
 | 
			
		||||
      this.getConfigsWithSilentRenewEnabled(allConfigs);
 | 
			
		||||
 | 
			
		||||
    if (configsWithSilentRenewEnabled.length <= 0) {
 | 
			
		||||
      return;
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.intervalService.isTokenValidationRunning()) {
 | 
			
		||||
      return;
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const refreshTimeInSeconds = this.getSmallestRefreshTimeFromConfigs(
 | 
			
		||||
@ -87,7 +87,14 @@ export class PeriodicallyTokenCheckService {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    const o$ = periodicallyCheck$.pipe(
 | 
			
		||||
      catchError((error) => throwError(() => new Error(error))),
 | 
			
		||||
      catchError((error) => {
 | 
			
		||||
        this.loggerService.logError(
 | 
			
		||||
          currentConfig,
 | 
			
		||||
          'silent renew failed!',
 | 
			
		||||
          error
 | 
			
		||||
        );
 | 
			
		||||
        return throwError(() => error);
 | 
			
		||||
      }),
 | 
			
		||||
      map((objectWithConfigIds) => {
 | 
			
		||||
        for (const [configId, _] of Object.entries(objectWithConfigIds)) {
 | 
			
		||||
          this.configurationService
 | 
			
		||||
@ -104,20 +111,18 @@ export class PeriodicallyTokenCheckService {
 | 
			
		||||
                this.flowsDataService.resetSilentRenewRunning(config);
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          return undefined;
 | 
			
		||||
        }
 | 
			
		||||
      }),
 | 
			
		||||
      catchError((error) => {
 | 
			
		||||
        this.loggerService.logError(
 | 
			
		||||
          currentConfig,
 | 
			
		||||
          'silent renew failed!',
 | 
			
		||||
          error
 | 
			
		||||
        );
 | 
			
		||||
        return throwError(() => error);
 | 
			
		||||
      }),
 | 
			
		||||
      shareReplay(1)
 | 
			
		||||
      share({
 | 
			
		||||
        connector: () => new ReplaySubject(1),
 | 
			
		||||
        resetOnError: false,
 | 
			
		||||
        resetOnComplete: false,
 | 
			
		||||
        resetOnRefCountZero: false,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    this.intervalService.runTokenValidationRunning = o$.subscribe();
 | 
			
		||||
    this.intervalService.runTokenValidationRunning = o$.subscribe({});
 | 
			
		||||
 | 
			
		||||
    return o$;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import type { CallbackContext } from '../flows/callback-context';
 | 
			
		||||
import { FlowsService } from '../flows/flows.service';
 | 
			
		||||
@ -16,6 +16,7 @@ describe('RefreshSessionRefreshTokenService', () => {
 | 
			
		||||
  let flowsService: FlowsService;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    vi.useFakeTimers();
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      imports: [],
 | 
			
		||||
      providers: [
 | 
			
		||||
@ -34,6 +35,11 @@ describe('RefreshSessionRefreshTokenService', () => {
 | 
			
		||||
    resetAuthDataService = TestBed.inject(ResetAuthDataService);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    vi.useRealTimers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(refreshSessionRefreshTokenService).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
@ -44,7 +50,7 @@ describe('RefreshSessionRefreshTokenService', () => {
 | 
			
		||||
        .spyOn(flowsService, 'processRefreshToken')
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        refreshSessionRefreshTokenService.refreshSessionWithRefreshTokens(
 | 
			
		||||
          { configId: 'configId1' },
 | 
			
		||||
          [{ configId: 'configId1' }]
 | 
			
		||||
@ -63,7 +69,7 @@ describe('RefreshSessionRefreshTokenService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          refreshSessionRefreshTokenService.refreshSessionWithRefreshTokens(
 | 
			
		||||
            { configId: 'configId1' },
 | 
			
		||||
            [{ configId: 'configId1' }]
 | 
			
		||||
@ -85,7 +91,7 @@ describe('RefreshSessionRefreshTokenService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          refreshSessionRefreshTokenService.refreshSessionWithRefreshTokens(
 | 
			
		||||
            { configId: 'configId1' },
 | 
			
		||||
            [{ configId: 'configId1' }]
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,12 @@
 | 
			
		||||
import { TestBed, spyOnProperty } from '@/testing';
 | 
			
		||||
import { EmptyError, lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { delay } from 'rxjs/operators';
 | 
			
		||||
import {
 | 
			
		||||
  EmptyError,
 | 
			
		||||
  ReplaySubject,
 | 
			
		||||
  firstValueFrom,
 | 
			
		||||
  of,
 | 
			
		||||
  throwError,
 | 
			
		||||
} from 'rxjs';
 | 
			
		||||
import { delay, share } from 'rxjs/operators';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { AuthWellKnownService } from '../config/auth-well-known/auth-well-known.service';
 | 
			
		||||
@ -22,6 +28,7 @@ import {
 | 
			
		||||
} from './refresh-session.service';
 | 
			
		||||
 | 
			
		||||
describe('RefreshSessionService ', () => {
 | 
			
		||||
  vi.useFakeTimers();
 | 
			
		||||
  let refreshSessionService: RefreshSessionService;
 | 
			
		||||
  let flowHelper: FlowHelper;
 | 
			
		||||
  let authStateService: AuthStateService;
 | 
			
		||||
@ -63,6 +70,11 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
    storagePersistenceService = TestBed.inject(StoragePersistenceService);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    vi.useRealTimers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(refreshSessionService).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
@ -91,7 +103,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
 | 
			
		||||
      const extraCustomParams = { extra: 'custom' };
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        refreshSessionService.userForceRefreshSession(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs,
 | 
			
		||||
@ -128,7 +140,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
 | 
			
		||||
      const extraCustomParams = { extra: 'custom' };
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        refreshSessionService.userForceRefreshSession(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs,
 | 
			
		||||
@ -163,7 +175,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
      ];
 | 
			
		||||
      const writeSpy = vi.spyOn(storagePersistenceService, 'write');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        refreshSessionService.userForceRefreshSession(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
@ -186,7 +198,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const result = await lastValueFrom(
 | 
			
		||||
        const result = await firstValueFrom(
 | 
			
		||||
          refreshSessionService.userForceRefreshSession(
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
            allConfigs
 | 
			
		||||
@ -217,7 +229,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          refreshSessionService.userForceRefreshSession(
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
            allConfigs
 | 
			
		||||
@ -259,7 +271,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result.idToken).toEqual('id-token');
 | 
			
		||||
@ -285,7 +297,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -328,7 +340,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result.idToken).toBeDefined();
 | 
			
		||||
@ -358,7 +370,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
@ -372,6 +384,8 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('occurs timeout error and retry mechanism exhausted max retry count throws error', async () => {
 | 
			
		||||
      vi.useRealTimers();
 | 
			
		||||
      vi.useFakeTimers();
 | 
			
		||||
      vi.spyOn(
 | 
			
		||||
        flowHelper,
 | 
			
		||||
        'isCurrentFlowCodeFlowWithRefreshTokens'
 | 
			
		||||
@ -402,10 +416,25 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
      const expectedInvokeCount = MAX_RETRY_ATTEMPTS;
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const result = await lastValueFrom(
 | 
			
		||||
          refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
        const o$ = refreshSessionService
 | 
			
		||||
          .forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
          .pipe(
 | 
			
		||||
            share({
 | 
			
		||||
              connector: () => new ReplaySubject(1),
 | 
			
		||||
              resetOnError: false,
 | 
			
		||||
              resetOnComplete: false,
 | 
			
		||||
              resetOnRefCountZero: true,
 | 
			
		||||
            })
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
        o$.subscribe();
 | 
			
		||||
 | 
			
		||||
        await vi.advanceTimersByTimeAsync(
 | 
			
		||||
          allConfigs[0]!.silentRenewTimeoutInSeconds * 10000
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const result = await firstValueFrom(o$);
 | 
			
		||||
 | 
			
		||||
        if (result) {
 | 
			
		||||
          expect.fail('It should not return any result.');
 | 
			
		||||
        }
 | 
			
		||||
@ -415,10 +444,6 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
          expectedInvokeCount
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(
 | 
			
		||||
        allConfigs[0]!.silentRenewTimeoutInSeconds * 10000
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('occurs unknown error throws it to subscriber', async () => {
 | 
			
		||||
@ -453,7 +478,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
        );
 | 
			
		||||
        expect.fail('It should not return any result.');
 | 
			
		||||
@ -489,7 +514,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
          'refreshSessionWithIFrameCompleted$'
 | 
			
		||||
        ).mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(
 | 
			
		||||
        const result = await firstValueFrom(
 | 
			
		||||
          refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
        );
 | 
			
		||||
        expect(result).toEqual({
 | 
			
		||||
@ -533,7 +558,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
          .spyOn(authStateService, 'areAuthStorageTokensValid')
 | 
			
		||||
          .mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(
 | 
			
		||||
        const result = await firstValueFrom(
 | 
			
		||||
          refreshSessionService.forceRefreshSession(allConfigs[0]!, allConfigs)
 | 
			
		||||
        );
 | 
			
		||||
        expect(result).toEqual({
 | 
			
		||||
@ -552,7 +577,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
    it('returns null if no auth well known endpoint defined', async () => {
 | 
			
		||||
      vi.spyOn(flowsDataService, 'isSilentRenewRunning').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        (refreshSessionService as any).startRefreshSession()
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBe(null);
 | 
			
		||||
@ -561,7 +586,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
    it('returns null if silent renew Is running', async () => {
 | 
			
		||||
      vi.spyOn(flowsDataService, 'isSilentRenewRunning').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        (refreshSessionService as any).startRefreshSession()
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBe(null);
 | 
			
		||||
@ -594,7 +619,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        'refreshSessionWithRefreshTokens'
 | 
			
		||||
      ).mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (refreshSessionService as any).startRefreshSession(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
@ -629,7 +654,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        )
 | 
			
		||||
        .mockReturnValue(of({} as CallbackContext));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (refreshSessionService as any).startRefreshSession(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
@ -668,7 +693,7 @@ describe('RefreshSessionService ', () => {
 | 
			
		||||
        .spyOn(refreshSessionIframeService, 'refreshSessionWithIframe')
 | 
			
		||||
        .mockReturnValue(of(false));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (refreshSessionService as any).startRefreshSession(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../../api/data.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -56,7 +56,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
      const urlWithoutSuffix = 'myUrl';
 | 
			
		||||
      const urlWithSuffix = `${urlWithoutSuffix}/.well-known/openid-configuration`;
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (service as any).getWellKnownDocument(urlWithoutSuffix, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -72,7 +72,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
      const urlWithSuffix = 'myUrl/.well-known/openid-configuration';
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (service as any).getWellKnownDocument(urlWithSuffix, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -89,7 +89,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
      const urlWithSuffix =
 | 
			
		||||
        'myUrl/.well-known/openid-configuration/and/some/more/stuff';
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (service as any).getWellKnownDocument(urlWithSuffix, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -106,7 +106,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
      const urlWithoutSuffix = 'myUrl';
 | 
			
		||||
      const urlWithSuffix = `${urlWithoutSuffix}/.well-known/test-openid-configuration`;
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        (service as any).getWellKnownDocument(urlWithoutSuffix, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          authWellknownUrlSuffix: '/.well-known/test-openid-configuration',
 | 
			
		||||
@ -126,7 +126,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res: unknown = await lastValueFrom(
 | 
			
		||||
      const res: unknown = await firstValueFrom(
 | 
			
		||||
        (service as any).getWellKnownDocument('anyurl', {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -144,7 +144,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res: any = await lastValueFrom(
 | 
			
		||||
      const res: any = await firstValueFrom(
 | 
			
		||||
        (service as any).getWellKnownDocument('anyurl', {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -164,7 +164,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          (service as any).getWellKnownDocument('anyurl', 'configId')
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: unknown) {
 | 
			
		||||
@ -181,7 +181,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(service as any, 'getWellKnownDocument');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.getWellKnownEndPointsForConfig({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          authWellknownEndpointUrl: 'any-url',
 | 
			
		||||
@ -200,7 +200,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.getWellKnownEndPointsForConfig(config));
 | 
			
		||||
        await firstValueFrom(service.getWellKnownEndPointsForConfig(config));
 | 
			
		||||
      } catch (error: any) {
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
          config,
 | 
			
		||||
@ -221,7 +221,7 @@ describe('AuthWellKnownDataService', () => {
 | 
			
		||||
        jwksUri: DUMMY_WELL_KNOWN_DOCUMENT.jwks_uri,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.getWellKnownEndPointsForConfig({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
          authWellknownEndpointUrl: 'any-url',
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { EventTypes } from '../../public-events/event-types';
 | 
			
		||||
import { PublicEventsService } from '../../public-events/public-events.service';
 | 
			
		||||
@ -36,7 +36,7 @@ describe('AuthWellKnownService', () => {
 | 
			
		||||
  describe('getAuthWellKnownEndPoints', () => {
 | 
			
		||||
    it('getAuthWellKnownEndPoints throws an error if not config provided', async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.queryAndStoreAuthWellKnownEndPoints(null));
 | 
			
		||||
        await firstValueFrom(service.queryAndStoreAuthWellKnownEndPoints(null));
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        expect(error).toEqual(
 | 
			
		||||
          new Error(
 | 
			
		||||
@ -57,7 +57,7 @@ describe('AuthWellKnownService', () => {
 | 
			
		||||
        () => ({ issuer: 'anything' })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.queryAndStoreAuthWellKnownEndPoints({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(storagePersistenceService.read).not.toHaveBeenCalled();
 | 
			
		||||
@ -77,7 +77,7 @@ describe('AuthWellKnownService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const storeSpy = vi.spyOn(service, 'storeWellKnownEndpoints');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.queryAndStoreAuthWellKnownEndPoints({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(dataServiceSpy).toHaveBeenCalled();
 | 
			
		||||
@ -92,7 +92,7 @@ describe('AuthWellKnownService', () => {
 | 
			
		||||
      const publicEventsServiceSpy = vi.spyOn(publicEventsService, 'fireEvent');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.queryAndStoreAuthWellKnownEndPoints({ configId: 'configId1' })
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
import { EventTypes } from '../public-events/event-types';
 | 
			
		||||
@ -93,7 +93,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
      };
 | 
			
		||||
      const spy = vi.spyOn(configService as any, 'loadConfigs');
 | 
			
		||||
 | 
			
		||||
      const config = await lastValueFrom(
 | 
			
		||||
      const config = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfiguration('configId1')
 | 
			
		||||
      );
 | 
			
		||||
      expect(config).toBeTruthy();
 | 
			
		||||
@ -108,7 +108,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
 | 
			
		||||
      vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      const config = await lastValueFrom(
 | 
			
		||||
      const config = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfiguration('configId1')
 | 
			
		||||
      );
 | 
			
		||||
      expect(config).toBeTruthy();
 | 
			
		||||
@ -126,7 +126,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const consoleSpy = vi.spyOn(console, 'warn');
 | 
			
		||||
 | 
			
		||||
      const config = await lastValueFrom(
 | 
			
		||||
      const config = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfiguration('configId1')
 | 
			
		||||
      );
 | 
			
		||||
      expect(config).toBeNull();
 | 
			
		||||
@ -141,7 +141,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
        configId2: { configId: 'configId2' },
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const config = await lastValueFrom(
 | 
			
		||||
      const config = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfiguration('notExisting')
 | 
			
		||||
      );
 | 
			
		||||
      expect(config).toBeNull();
 | 
			
		||||
@ -160,7 +160,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
        issuer: 'auth-well-known',
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const config = await lastValueFrom(
 | 
			
		||||
      const config = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfiguration('configId1')
 | 
			
		||||
      );
 | 
			
		||||
      expect(config?.authWellknownEndpoints).toEqual({
 | 
			
		||||
@ -182,7 +182,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(publicEventsService, 'fireEvent');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(configService.getOpenIDConfiguration('configId1'));
 | 
			
		||||
      await firstValueFrom(configService.getOpenIDConfiguration('configId1'));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        EventTypes.ConfigLoaded,
 | 
			
		||||
        expect.anything()
 | 
			
		||||
@ -209,7 +209,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
        'storeWellKnownEndpoints'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const config = await lastValueFrom(
 | 
			
		||||
      const config = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfiguration('configId1')
 | 
			
		||||
      );
 | 
			
		||||
      expect(config).toBeTruthy();
 | 
			
		||||
@ -237,7 +237,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
 | 
			
		||||
      vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfigurations('configId1')
 | 
			
		||||
      );
 | 
			
		||||
      expect(result.allConfigs.length).toEqual(2);
 | 
			
		||||
@ -254,7 +254,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
 | 
			
		||||
      vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfigurations()
 | 
			
		||||
      );
 | 
			
		||||
      expect(result.allConfigs.length).toEqual(2);
 | 
			
		||||
@ -276,7 +276,7 @@ describe('Configuration Service', () => {
 | 
			
		||||
        false
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const { allConfigs, currentConfig } = await lastValueFrom(
 | 
			
		||||
      const { allConfigs, currentConfig } = await firstValueFrom(
 | 
			
		||||
        configService.getOpenIDConfigurations()
 | 
			
		||||
      );
 | 
			
		||||
      expect(allConfigs).toEqual([]);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import type { OpenIdConfiguration } from '../openid-configuration';
 | 
			
		||||
import { StsConfigHttpLoader, StsConfigStaticLoader } from './config-loader';
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ describe('ConfigLoader', () => {
 | 
			
		||||
 | 
			
		||||
        const result$ = loader.loadConfigs();
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(result$);
 | 
			
		||||
        const result = await firstValueFrom(result$);
 | 
			
		||||
        expect(Array.isArray(result)).toBeTruthy();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ describe('ConfigLoader', () => {
 | 
			
		||||
 | 
			
		||||
        const result$ = loader.loadConfigs();
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(result$);
 | 
			
		||||
        const result = await firstValueFrom(result$);
 | 
			
		||||
        expect(Array.isArray(result)).toBeTruthy();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
@ -43,7 +43,7 @@ describe('ConfigLoader', () => {
 | 
			
		||||
 | 
			
		||||
        const result$ = loader.loadConfigs();
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(result$);
 | 
			
		||||
        const result = await firstValueFrom(result$);
 | 
			
		||||
        expect(Array.isArray(result)).toBeTruthy();
 | 
			
		||||
        expect(result[0]!.configId).toBe('configId1');
 | 
			
		||||
        expect(result[1]!.configId).toBe('configId2');
 | 
			
		||||
@ -58,7 +58,7 @@ describe('ConfigLoader', () => {
 | 
			
		||||
 | 
			
		||||
        const result$ = loader.loadConfigs();
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(result$);
 | 
			
		||||
        const result = await firstValueFrom(result$);
 | 
			
		||||
        expect(Array.isArray(result)).toBeTruthy();
 | 
			
		||||
        expect(result[0]!.configId).toBe('configId1');
 | 
			
		||||
        expect(result[1]!.configId).toBe('configId2');
 | 
			
		||||
@ -71,7 +71,7 @@ describe('ConfigLoader', () => {
 | 
			
		||||
 | 
			
		||||
        const result$ = loader.loadConfigs();
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(result$);
 | 
			
		||||
        const result = await firstValueFrom(result$);
 | 
			
		||||
        expect(Array.isArray(result)).toBeTruthy();
 | 
			
		||||
        expect(result[0]!.configId).toBe('configId1');
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ export abstract class StsConfigLoader {
 | 
			
		||||
 | 
			
		||||
export class StsConfigStaticLoader implements StsConfigLoader {
 | 
			
		||||
  constructor(
 | 
			
		||||
    // biome-ignore lint/style/noParameterProperties: <explanation>
 | 
			
		||||
    private readonly passedConfigs: OpenIdConfiguration | OpenIdConfiguration[]
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { mockImplementationWhenArgs, spyOnWithOrigin } from '@/testing/spy';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { LogLevel } from '../../logging/log-level';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -51,12 +52,12 @@ describe('Config Validation Service', () => {
 | 
			
		||||
 | 
			
		||||
  it('calls `logWarning` if one rule has warning level', () => {
 | 
			
		||||
    const loggerWarningSpy = vi.spyOn(loggerService, 'logWarning');
 | 
			
		||||
    const messageTypeSpy = vi.spyOn(
 | 
			
		||||
      configValidationService as any,
 | 
			
		||||
      'getAllMessagesOfType'
 | 
			
		||||
    const messageTypeSpy = spyOnWithOrigin(
 | 
			
		||||
      configValidationService,
 | 
			
		||||
      'getAllMessagesOfType' as any
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    mockImplementationWhenArgsEqual(
 | 
			
		||||
    mockImplementationWhenArgs(
 | 
			
		||||
      messageTypeSpy,
 | 
			
		||||
      (arg1: any, arg2: any) => arg1 === 'warning' && Array.isArray(arg2),
 | 
			
		||||
      () => ['A warning message']
 | 
			
		||||
 | 
			
		||||
@ -85,7 +85,7 @@ export class ConfigValidationService {
 | 
			
		||||
    return allErrorMessages.length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getAllMessagesOfType(
 | 
			
		||||
  protected getAllMessagesOfType(
 | 
			
		||||
    type: Level,
 | 
			
		||||
    results: RuleValidationResult[]
 | 
			
		||||
  ): string[] {
 | 
			
		||||
 | 
			
		||||
@ -102,21 +102,30 @@ describe('JwkExtractor', () => {
 | 
			
		||||
 | 
			
		||||
  describe('extractJwk', () => {
 | 
			
		||||
    it('throws error if no keys are present in array', () => {
 | 
			
		||||
      expect(() => {
 | 
			
		||||
      try {
 | 
			
		||||
        service.extractJwk([]);
 | 
			
		||||
      }).toThrow(JwkExtractorInvalidArgumentError);
 | 
			
		||||
        expect.fail('should error');
 | 
			
		||||
      } catch (error: any) {
 | 
			
		||||
        expect(error).toBe(JwkExtractorInvalidArgumentError);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('throws error if spec.kid is present, but no key was matching', () => {
 | 
			
		||||
      expect(() => {
 | 
			
		||||
      try {
 | 
			
		||||
        service.extractJwk(keys, { kid: 'doot' });
 | 
			
		||||
      }).toThrow(JwkExtractorNoMatchingKeysError);
 | 
			
		||||
        expect.fail('should error');
 | 
			
		||||
      } catch (error: any) {
 | 
			
		||||
        expect(error).toBe(JwkExtractorNoMatchingKeysError);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('throws error if spec.use is present, but no key was matching', () => {
 | 
			
		||||
      expect(() => {
 | 
			
		||||
      try {
 | 
			
		||||
        service.extractJwk(keys, { use: 'blorp' });
 | 
			
		||||
      }).toThrow(JwkExtractorNoMatchingKeysError);
 | 
			
		||||
        expect.fail('should error');
 | 
			
		||||
      } catch (error: any) {
 | 
			
		||||
        expect(error).toBe(JwkExtractorNoMatchingKeysError);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('does not throw error if no key is matching when throwOnEmpty is false', () => {
 | 
			
		||||
@ -126,9 +135,12 @@ describe('JwkExtractor', () => {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('throws error if multiple keys are present, and spec is not present', () => {
 | 
			
		||||
      expect(() => {
 | 
			
		||||
      try {
 | 
			
		||||
        service.extractJwk(keys);
 | 
			
		||||
      }).toThrow(JwkExtractorSeveralMatchingKeysError);
 | 
			
		||||
        expect.fail('should error');
 | 
			
		||||
      } catch (error: any) {
 | 
			
		||||
        expect(error).toBe(JwkExtractorSeveralMatchingKeysError);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('returns array of keys matching spec.kid', () => {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { HttpErrorResponse, HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../../api/data.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -56,7 +56,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.codeFlowCallback('test-url', { configId: 'configId1' })
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -76,7 +76,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.codeFlowCallback('test-url', { configId: 'configId1' })
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -99,7 +99,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
        existingIdToken: null,
 | 
			
		||||
      } as CallbackContext;
 | 
			
		||||
 | 
			
		||||
      const callbackContext = await lastValueFrom(
 | 
			
		||||
      const callbackContext = await firstValueFrom(
 | 
			
		||||
        service.codeFlowCallback('test-url', { configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(callbackContext).toEqual(expectedCallbackContext);
 | 
			
		||||
@ -122,7 +122,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      ).mockReturnValue(false);
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.codeFlowCodeRequest({} as CallbackContext, {
 | 
			
		||||
            configId: 'configId1',
 | 
			
		||||
          })
 | 
			
		||||
@ -144,7 +144,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.codeFlowCodeRequest({} as CallbackContext, {
 | 
			
		||||
            configId: 'configId1',
 | 
			
		||||
          })
 | 
			
		||||
@ -166,7 +166,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.codeFlowCodeRequest({} as CallbackContext, {
 | 
			
		||||
            configId: 'configId1',
 | 
			
		||||
          })
 | 
			
		||||
@ -190,7 +190,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
        'validateStateFromHashCallback'
 | 
			
		||||
      ).mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.codeFlowCodeRequest({} as CallbackContext, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -226,7 +226,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
 | 
			
		||||
      const postSpy = vi.spyOn(dataService, 'post').mockReturnValue(of({}));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.codeFlowCodeRequest({ code: 'foo' } as CallbackContext, config)
 | 
			
		||||
      );
 | 
			
		||||
      expect(urlServiceSpy).toHaveBeenCalledExactlyOnceWith('foo', config, {
 | 
			
		||||
@ -253,7 +253,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
        'validateStateFromHashCallback'
 | 
			
		||||
      ).mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.codeFlowCodeRequest({} as CallbackContext, config)
 | 
			
		||||
      );
 | 
			
		||||
      const httpHeaders = postSpy.mock.calls.at(-1)?.[3] as HttpHeaders;
 | 
			
		||||
@ -280,7 +280,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.codeFlowCodeRequest({} as CallbackContext, config)
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -313,7 +313,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      ).mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const res = await lastValueFrom(
 | 
			
		||||
        const res = await firstValueFrom(
 | 
			
		||||
          service.codeFlowCodeRequest({} as CallbackContext, config)
 | 
			
		||||
        );
 | 
			
		||||
        expect(res).toBeTruthy();
 | 
			
		||||
@ -348,7 +348,7 @@ describe('CodeFlowCallbackHandlerService', () => {
 | 
			
		||||
      ).mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const res = await lastValueFrom(
 | 
			
		||||
        const res = await firstValueFrom(
 | 
			
		||||
          service.codeFlowCodeRequest({} as CallbackContext, config)
 | 
			
		||||
        );
 | 
			
		||||
        expect(res).toBeFalsy();
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { inject, Injectable } from 'injection-js';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, of, throwError, timer } from 'rxjs';
 | 
			
		||||
import { catchError, mergeMap, retryWhen, switchMap } from 'rxjs/operators';
 | 
			
		||||
import { DataService } from '../../api/data.service';
 | 
			
		||||
@ -116,7 +116,7 @@ export class CodeFlowCallbackHandlerService {
 | 
			
		||||
        switchMap((response) => {
 | 
			
		||||
          if (response) {
 | 
			
		||||
            const authResult: AuthResult = {
 | 
			
		||||
              ...response,
 | 
			
		||||
              ...(response as any),
 | 
			
		||||
              state: callbackContext.state,
 | 
			
		||||
              session_state: callbackContext.sessionState,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../../auth-state/auth-state.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -83,14 +83,14 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
 | 
			
		||||
        of({ keys: [] } as JwtKeys)
 | 
			
		||||
      );
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
          callbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      expect(storagePersistenceServiceSpy).toBeCalledWith([
 | 
			
		||||
      expect(storagePersistenceServiceSpy.mock.calls).toEqual([
 | 
			
		||||
        ['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
 | 
			
		||||
        ['jwtKeys', { keys: [] }, allConfigs[0]],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -121,14 +121,14 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
        of({ keys: [] } as JwtKeys)
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
          callbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      expect(storagePersistenceServiceSpy).toBeCalledWith([
 | 
			
		||||
      expect(storagePersistenceServiceSpy.mock.calls).toEqual([
 | 
			
		||||
        ['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
 | 
			
		||||
        ['jwtKeys', { keys: [] }, allConfigs[0]],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -159,14 +159,14 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
 | 
			
		||||
        of({ keys: [] } as JwtKeys)
 | 
			
		||||
      );
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
          callbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      expect(storagePersistenceServiceSpy).toBeCalledWith([
 | 
			
		||||
      expect(storagePersistenceServiceSpy.mock.calls).toEqual([
 | 
			
		||||
        ['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
 | 
			
		||||
        ['reusable_refresh_token', 'dummy_refresh_token', allConfigs[0]],
 | 
			
		||||
        ['jwtKeys', { keys: [] }, allConfigs[0]],
 | 
			
		||||
@ -194,7 +194,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
 | 
			
		||||
        of({ keys: [] } as JwtKeys)
 | 
			
		||||
      );
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
          callbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
@ -223,7 +223,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
 | 
			
		||||
        of({ keys: [{ kty: 'henlo' } as JwtKey] } as JwtKeys)
 | 
			
		||||
      );
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
          callbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
@ -257,7 +257,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
        of({} as JwtKeys)
 | 
			
		||||
      );
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            callbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -290,7 +290,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
        throwError(() => new Error('error'))
 | 
			
		||||
      );
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            callbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -316,7 +316,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            callbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -353,7 +353,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            callbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -394,7 +394,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            callbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -436,7 +436,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const callbackContext: CallbackContext = await lastValueFrom(
 | 
			
		||||
        const callbackContext: CallbackContext = await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            initialCallbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -444,7 +444,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
        expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
        expect(storagePersistenceServiceSpy).toBeCalledWith([
 | 
			
		||||
        expect(storagePersistenceServiceSpy.mock.calls).toEqual([
 | 
			
		||||
          ['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
 | 
			
		||||
          ['jwtKeys', DUMMY_JWT_KEYS, allConfigs[0]],
 | 
			
		||||
        ]);
 | 
			
		||||
@ -479,7 +479,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const callbackContext: CallbackContext = await lastValueFrom(
 | 
			
		||||
        const callbackContext: CallbackContext = await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            initialCallbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -523,7 +523,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const callbackContext: CallbackContext = await lastValueFrom(
 | 
			
		||||
        const callbackContext: CallbackContext = await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            initialCallbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -560,7 +560,7 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const callbackContext: CallbackContext = await lastValueFrom(
 | 
			
		||||
        const callbackContext: CallbackContext = await firstValueFrom(
 | 
			
		||||
          service.callbackHistoryAndResetJwtKeys(
 | 
			
		||||
            initialCallbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DOCUMENT } from '../../dom';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -58,7 +58,7 @@ describe('ImplicitFlowCallbackHandlerService', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.implicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash')
 | 
			
		||||
      );
 | 
			
		||||
      expect(resetAuthorizationDataSpy).toHaveBeenCalled();
 | 
			
		||||
@ -76,7 +76,7 @@ describe('ImplicitFlowCallbackHandlerService', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.implicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash')
 | 
			
		||||
      );
 | 
			
		||||
      expect(resetAuthorizationDataSpy).not.toHaveBeenCalled();
 | 
			
		||||
@ -102,7 +102,7 @@ describe('ImplicitFlowCallbackHandlerService', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const callbackContext = await lastValueFrom(
 | 
			
		||||
      const callbackContext = await firstValueFrom(
 | 
			
		||||
        service.implicitFlowCallback(allConfigs[0]!, allConfigs, 'anyHash')
 | 
			
		||||
      );
 | 
			
		||||
      expect(callbackContext).toEqual(expectedCallbackContext);
 | 
			
		||||
@ -128,7 +128,7 @@ describe('ImplicitFlowCallbackHandlerService', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const callbackContext = await lastValueFrom(
 | 
			
		||||
      const callbackContext = await firstValueFrom(
 | 
			
		||||
        service.implicitFlowCallback(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(callbackContext).toEqual(expectedCallbackContext);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../../auth-state/auth-state.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -54,7 +54,7 @@ describe('RefreshSessionCallbackHandlerService', () => {
 | 
			
		||||
        existingIdToken: 'henlo-legger',
 | 
			
		||||
      } as CallbackContext;
 | 
			
		||||
 | 
			
		||||
      const callbackContext = await lastValueFrom(
 | 
			
		||||
      const callbackContext = await firstValueFrom(
 | 
			
		||||
        service.refreshSessionWithRefreshTokens({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(callbackContext).toEqual(expectedCallbackContext);
 | 
			
		||||
@ -69,7 +69,7 @@ describe('RefreshSessionCallbackHandlerService', () => {
 | 
			
		||||
      vi.spyOn(authStateService, 'getIdToken').mockReturnValue('henlo-legger');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.refreshSessionWithRefreshTokens({ configId: 'configId1' })
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { HttpErrorResponse, HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../../api/data.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -46,7 +46,7 @@ describe('RefreshTokenCallbackHandlerService', () => {
 | 
			
		||||
 | 
			
		||||
    it('throws error if no tokenEndpoint is given', async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          (service as any).refreshTokensRequestTokens({} as CallbackContext)
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: unknown) {
 | 
			
		||||
@ -63,7 +63,7 @@ describe('RefreshTokenCallbackHandlerService', () => {
 | 
			
		||||
        () => ({ tokenEndpoint: 'tokenEndpoint' })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.refreshTokensRequestTokens({} as CallbackContext, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -90,7 +90,7 @@ describe('RefreshTokenCallbackHandlerService', () => {
 | 
			
		||||
        () => ({ tokenEndpoint: 'tokenEndpoint' })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.refreshTokensRequestTokens({} as CallbackContext, {
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -115,7 +115,7 @@ describe('RefreshTokenCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.refreshTokensRequestTokens({} as CallbackContext, config)
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -139,7 +139,7 @@ describe('RefreshTokenCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const res = await lastValueFrom(
 | 
			
		||||
        const res = await firstValueFrom(
 | 
			
		||||
          service.refreshTokensRequestTokens({} as CallbackContext, config)
 | 
			
		||||
        );
 | 
			
		||||
        expect(res).toBeTruthy();
 | 
			
		||||
@ -165,7 +165,7 @@ describe('RefreshTokenCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const res = await lastValueFrom(
 | 
			
		||||
        const res = await firstValueFrom(
 | 
			
		||||
          service.refreshTokensRequestTokens({} as CallbackContext, config)
 | 
			
		||||
        );
 | 
			
		||||
        expect(res).toBeFalsy();
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../../auth-state/auth-state.service';
 | 
			
		||||
import { DOCUMENT } from '../../dom';
 | 
			
		||||
@ -66,7 +66,7 @@ describe('StateValidationCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
 | 
			
		||||
      const newCallbackContext = await lastValueFrom(
 | 
			
		||||
      const newCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackStateValidation(
 | 
			
		||||
          {} as CallbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
@ -95,7 +95,7 @@ describe('StateValidationCallbackHandlerService', () => {
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackStateValidation(
 | 
			
		||||
            {} as CallbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
@ -132,7 +132,7 @@ describe('StateValidationCallbackHandlerService', () => {
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackStateValidation(
 | 
			
		||||
            { isRenewProcess: true } as CallbackContext,
 | 
			
		||||
            allConfigs[0]!,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../../auth-state/auth-state.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -70,7 +70,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(flowsDataService, 'setSessionState');
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('mystate', allConfigs[0]);
 | 
			
		||||
@ -103,7 +103,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
      ];
 | 
			
		||||
      const spy = vi.spyOn(flowsDataService, 'setSessionState');
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).not.toHaveBeenCalled();
 | 
			
		||||
@ -136,7 +136,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
      ];
 | 
			
		||||
      const spy = vi.spyOn(flowsDataService, 'setSessionState');
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).not.toHaveBeenCalled();
 | 
			
		||||
@ -165,7 +165,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(flowsDataService, 'setSessionState');
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).not.toHaveBeenCalled();
 | 
			
		||||
@ -203,7 +203,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
        'updateAndPublishAuthState'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
@ -244,7 +244,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
        .spyOn(userService, 'getAndPersistUserDataInStore')
 | 
			
		||||
        .mockReturnValue(of({ user: 'some_data' }));
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(getAndPersistUserDataInStoreSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -292,7 +292,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
        'updateAndPublishAuthState'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
 | 
			
		||||
@ -335,7 +335,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const setSessionStateSpy = vi.spyOn(flowsDataService, 'setSessionState');
 | 
			
		||||
 | 
			
		||||
      const resultCallbackContext = await lastValueFrom(
 | 
			
		||||
      const resultCallbackContext = await firstValueFrom(
 | 
			
		||||
        service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(setSessionStateSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -381,7 +381,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -432,7 +432,7 @@ describe('UserCallbackHandlerService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
 | 
			
		||||
@ -247,15 +247,15 @@ describe('Flows Data Service', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('isSilentRenewRunning', () => {
 | 
			
		||||
    it('silent renew process timeout exceeded reset state object and returns false result', () => {
 | 
			
		||||
    it('silent renew process timeout exceeded reset state object and returns false result', async () => {
 | 
			
		||||
      const config = {
 | 
			
		||||
        silentRenewTimeoutInSeconds: 10,
 | 
			
		||||
        configId: 'configId1',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      vi.useRealTimers();
 | 
			
		||||
      vi.useFakeTimers();
 | 
			
		||||
      const baseTime = new Date();
 | 
			
		||||
      vi.useFakeTimers();
 | 
			
		||||
 | 
			
		||||
      vi.setSystemTime(baseTime);
 | 
			
		||||
 | 
			
		||||
@ -271,7 +271,7 @@ describe('Flows Data Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spyWrite = vi.spyOn(storagePersistenceService, 'write');
 | 
			
		||||
 | 
			
		||||
      vi.advanceTimersByTimeAsync(
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(
 | 
			
		||||
        (config.silentRenewTimeoutInSeconds + 1) * 1000
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { mockProvider } from '../testing/mock';
 | 
			
		||||
import type { CallbackContext } from './callback-context';
 | 
			
		||||
@ -87,7 +87,7 @@ describe('Flows Service', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const value = await lastValueFrom(
 | 
			
		||||
      const value = await firstValueFrom(
 | 
			
		||||
        service.processCodeFlowCallback(
 | 
			
		||||
          'some-url1234',
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
@ -129,7 +129,7 @@ describe('Flows Service', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const value = await lastValueFrom(
 | 
			
		||||
      const value = await firstValueFrom(
 | 
			
		||||
        service.processSilentRenewCodeFlowCallback(
 | 
			
		||||
          {} as CallbackContext,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
@ -167,7 +167,7 @@ describe('Flows Service', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const value = await lastValueFrom(
 | 
			
		||||
      const value = await firstValueFrom(
 | 
			
		||||
        service.processImplicitFlowCallback(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs,
 | 
			
		||||
@ -211,7 +211,7 @@ describe('Flows Service', () => {
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      const value = await lastValueFrom(
 | 
			
		||||
      const value = await firstValueFrom(
 | 
			
		||||
        service.processRefreshToken(allConfigs[0]!, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(value).toEqual({} as CallbackContext);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { HttpResponse } from '@ngify/http';
 | 
			
		||||
import { EmptyError, isObservable, lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { EmptyError, firstValueFrom, isObservable, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../api/data.service';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
@ -60,7 +60,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      const result = service.getSigningKeys({ configId: 'configId1' });
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(result);
 | 
			
		||||
        await firstValueFrom(result);
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
      }
 | 
			
		||||
@ -75,7 +75,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      const result = service.getSigningKeys({ configId: 'configId1' });
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(result);
 | 
			
		||||
        await firstValueFrom(result);
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
      }
 | 
			
		||||
@ -92,7 +92,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      const result = service.getSigningKeys({ configId: 'configId1' });
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(result);
 | 
			
		||||
        await firstValueFrom(result);
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        if (err instanceof EmptyError) {
 | 
			
		||||
          expect(spy).toHaveBeenCalledExactlyOnceWith('someUrl', {
 | 
			
		||||
@ -115,7 +115,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(
 | 
			
		||||
      const res = await firstValueFrom(
 | 
			
		||||
        service.getSigningKeys({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
@ -136,7 +136,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(
 | 
			
		||||
      const res = await firstValueFrom(
 | 
			
		||||
        service.getSigningKeys({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
@ -159,7 +159,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.getSigningKeys({ configId: 'configId1' }));
 | 
			
		||||
        await firstValueFrom(service.getSigningKeys({ configId: 'configId1' }));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
      }
 | 
			
		||||
@ -180,7 +180,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      const logSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          (service as any).handleErrorGetSigningKeys(
 | 
			
		||||
            new HttpResponse({ status: 400, statusText: 'nono' }),
 | 
			
		||||
            { configId: 'configId1' }
 | 
			
		||||
@ -198,7 +198,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      const logSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          (service as any).handleErrorGetSigningKeys('Just some Error', {
 | 
			
		||||
            configId: 'configId1',
 | 
			
		||||
          })
 | 
			
		||||
@ -215,7 +215,7 @@ describe('Signin Key Data Service', () => {
 | 
			
		||||
      const logSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          (service as any).handleErrorGetSigningKeys(
 | 
			
		||||
            { message: 'Just some Error' },
 | 
			
		||||
            { configId: 'configId1' }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import type { HttpFeature, HttpInterceptor } from '@ngify/http';
 | 
			
		||||
import { InjectionToken } from 'injection-js';
 | 
			
		||||
export { HttpParams, HttpParamsOptions } from './params';
 | 
			
		||||
 | 
			
		||||
export const HTTP_INTERCEPTORS = new InjectionToken<readonly HttpInterceptor[]>(
 | 
			
		||||
  'HTTP_INTERCEPTORS'
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										355
									
								
								src/http/params.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								src/http/params.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,355 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @license
 | 
			
		||||
 * Copyright Google LLC All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Use of this source code is governed by an MIT-style license that can be
 | 
			
		||||
 * found in the LICENSE file at https://angular.dev/license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import type { HttpParameterCodec } from '@ngify/http';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provides encoding and decoding of URL parameter and query-string values.
 | 
			
		||||
 *
 | 
			
		||||
 * Serializes and parses URL parameter keys and values to encode and decode them.
 | 
			
		||||
 * If you pass URL query parameters without encoding,
 | 
			
		||||
 * the query parameters can be misinterpreted at the receiving end.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
export class HttpUrlEncodingCodec implements HttpParameterCodec {
 | 
			
		||||
  /**
 | 
			
		||||
   * Encodes a key name for a URL parameter or query-string.
 | 
			
		||||
   * @param key The key name.
 | 
			
		||||
   * @returns The encoded key name.
 | 
			
		||||
   */
 | 
			
		||||
  encodeKey(key: string): string {
 | 
			
		||||
    return standardEncoding(key);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Encodes the value of a URL parameter or query-string.
 | 
			
		||||
   * @param value The value.
 | 
			
		||||
   * @returns The encoded value.
 | 
			
		||||
   */
 | 
			
		||||
  encodeValue(value: string): string {
 | 
			
		||||
    return standardEncoding(value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Decodes an encoded URL parameter or query-string key.
 | 
			
		||||
   * @param key The encoded key name.
 | 
			
		||||
   * @returns The decoded key name.
 | 
			
		||||
   */
 | 
			
		||||
  decodeKey(key: string): string {
 | 
			
		||||
    return decodeURIComponent(key);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Decodes an encoded URL parameter or query-string value.
 | 
			
		||||
   * @param value The encoded value.
 | 
			
		||||
   * @returns The decoded value.
 | 
			
		||||
   */
 | 
			
		||||
  decodeValue(value: string) {
 | 
			
		||||
    return decodeURIComponent(value);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encode input string with standard encodeURIComponent and then un-encode specific characters.
 | 
			
		||||
 */
 | 
			
		||||
const STANDARD_ENCODING_REGEX = /%(\d[a-f0-9])/gi;
 | 
			
		||||
const STANDARD_ENCODING_REPLACEMENTS: { [x: string]: string } = {
 | 
			
		||||
  '40': '@',
 | 
			
		||||
  '3A': ':',
 | 
			
		||||
  '24': '$',
 | 
			
		||||
  '2C': ',',
 | 
			
		||||
  '3B': ';',
 | 
			
		||||
  '3D': '=',
 | 
			
		||||
  '3F': '?',
 | 
			
		||||
  '2F': '/',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function standardEncoding(v: string): string {
 | 
			
		||||
  return encodeURIComponent(v).replace(
 | 
			
		||||
    STANDARD_ENCODING_REGEX,
 | 
			
		||||
    (s, t) => STANDARD_ENCODING_REPLACEMENTS[t] ?? s
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function paramParser(
 | 
			
		||||
  rawParams: string,
 | 
			
		||||
  codec: HttpParameterCodec
 | 
			
		||||
): Map<string, string[]> {
 | 
			
		||||
  const map = new Map<string, string[]>();
 | 
			
		||||
  if (rawParams.length > 0) {
 | 
			
		||||
    // The `window.location.search` can be used while creating an instance of the `HttpParams` class
 | 
			
		||||
    // (e.g. `new HttpParams({ fromString: window.location.search })`). The `window.location.search`
 | 
			
		||||
    // may start with the `?` char, so we strip it if it's present.
 | 
			
		||||
    const params: string[] = rawParams.replace(/^\?/, '').split('&');
 | 
			
		||||
    params.forEach((param: string) => {
 | 
			
		||||
      const eqIdx = param.indexOf('=');
 | 
			
		||||
      const [key, val]: string[] =
 | 
			
		||||
        eqIdx === -1
 | 
			
		||||
          ? [codec.decodeKey(param), '']
 | 
			
		||||
          : [
 | 
			
		||||
              codec.decodeKey(param.slice(0, eqIdx)),
 | 
			
		||||
              codec.decodeValue(param.slice(eqIdx + 1)),
 | 
			
		||||
            ];
 | 
			
		||||
      const list = map.get(key) || [];
 | 
			
		||||
      list.push(val);
 | 
			
		||||
      map.set(key, list);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Update {
 | 
			
		||||
  param: string;
 | 
			
		||||
  value?: string | number | boolean;
 | 
			
		||||
  op: 'a' | 'd' | 's';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Options used to construct an `HttpParams` instance.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
export interface HttpParamsOptions {
 | 
			
		||||
  /**
 | 
			
		||||
   * String representation of the HTTP parameters in URL-query-string format.
 | 
			
		||||
   * Mutually exclusive with `fromObject`.
 | 
			
		||||
   */
 | 
			
		||||
  fromString?: string;
 | 
			
		||||
 | 
			
		||||
  /** Object map of the HTTP parameters. Mutually exclusive with `fromString`. */
 | 
			
		||||
  fromObject?: {
 | 
			
		||||
    [param: string]:
 | 
			
		||||
      | string
 | 
			
		||||
      | number
 | 
			
		||||
      | boolean
 | 
			
		||||
      | ReadonlyArray<string | number | boolean>;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** Encoding codec used to parse and serialize the parameters. */
 | 
			
		||||
  encoder?: HttpParameterCodec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @ngify/http has slighty different implementation than Angular's HttpParams.
 | 
			
		||||
 * So this file to keep implement to angular
 | 
			
		||||
 * An HTTP request/response body that represents serialized parameters,
 | 
			
		||||
 * per the MIME type `application/x-www-form-urlencoded`.
 | 
			
		||||
 *
 | 
			
		||||
 * This class is immutable; all mutation operations return a new instance.
 | 
			
		||||
 */
 | 
			
		||||
export class HttpParams {
 | 
			
		||||
  private map: Map<string, string[]> | null;
 | 
			
		||||
  private encoder: HttpParameterCodec;
 | 
			
		||||
  private updates: Update[] | null = null;
 | 
			
		||||
  private cloneFrom: HttpParams | null = null;
 | 
			
		||||
 | 
			
		||||
  constructor(options: HttpParamsOptions = {} as HttpParamsOptions) {
 | 
			
		||||
    this.encoder = options.encoder || new HttpUrlEncodingCodec();
 | 
			
		||||
    if (options.fromString) {
 | 
			
		||||
      if (options.fromObject) {
 | 
			
		||||
        throw new Error('Cannot specify both fromString and fromObject.');
 | 
			
		||||
      }
 | 
			
		||||
      this.map = paramParser(options.fromString, this.encoder);
 | 
			
		||||
    } else if (options.fromObject) {
 | 
			
		||||
      this.map = new Map<string, string[]>();
 | 
			
		||||
      Object.keys(options.fromObject).forEach((key) => {
 | 
			
		||||
        const value = (options.fromObject as any)[key];
 | 
			
		||||
        // convert the values to strings
 | 
			
		||||
        const values = Array.isArray(value)
 | 
			
		||||
          ? value.map((value) => `${value}`)
 | 
			
		||||
          : [`${value}`];
 | 
			
		||||
        this.map!.set(key, values);
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.map = null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reports whether the body includes one or more values for a given parameter.
 | 
			
		||||
   * @param param The parameter name.
 | 
			
		||||
   * @returns True if the parameter has one or more values,
 | 
			
		||||
   * false if it has no value or is not present.
 | 
			
		||||
   */
 | 
			
		||||
  has(param: string): boolean {
 | 
			
		||||
    this.init();
 | 
			
		||||
    return this.map!.has(param);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Retrieves the first value for a parameter.
 | 
			
		||||
   * @param param The parameter name.
 | 
			
		||||
   * @returns The first value of the given parameter,
 | 
			
		||||
   * or `null` if the parameter is not present.
 | 
			
		||||
   */
 | 
			
		||||
  get(param: string): string | null {
 | 
			
		||||
    this.init();
 | 
			
		||||
    const res = this.map!.get(param);
 | 
			
		||||
    return res ? res[0] : null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Retrieves all values for a  parameter.
 | 
			
		||||
   * @param param The parameter name.
 | 
			
		||||
   * @returns All values in a string array,
 | 
			
		||||
   * or `null` if the parameter not present.
 | 
			
		||||
   */
 | 
			
		||||
  getAll(param: string): string[] | null {
 | 
			
		||||
    this.init();
 | 
			
		||||
    return this.map!.get(param) || null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Retrieves all the parameters for this body.
 | 
			
		||||
   * @returns The parameter names in a string array.
 | 
			
		||||
   */
 | 
			
		||||
  keys(): string[] {
 | 
			
		||||
    this.init();
 | 
			
		||||
    return Array.from(this.map!.keys());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Appends a new value to existing values for a parameter.
 | 
			
		||||
   * @param param The parameter name.
 | 
			
		||||
   * @param value The new value to add.
 | 
			
		||||
   * @return A new body with the appended value.
 | 
			
		||||
   */
 | 
			
		||||
  append(param: string, value: string | number | boolean): HttpParams {
 | 
			
		||||
    return this.clone({ param, value, op: 'a' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Constructs a new body with appended values for the given parameter name.
 | 
			
		||||
   * @param params parameters and values
 | 
			
		||||
   * @return A new body with the new value.
 | 
			
		||||
   */
 | 
			
		||||
  appendAll(params: {
 | 
			
		||||
    [param: string]:
 | 
			
		||||
      | string
 | 
			
		||||
      | number
 | 
			
		||||
      | boolean
 | 
			
		||||
      | ReadonlyArray<string | number | boolean>;
 | 
			
		||||
  }): HttpParams {
 | 
			
		||||
    const updates: Update[] = [];
 | 
			
		||||
    Object.keys(params).forEach((param) => {
 | 
			
		||||
      const value = params[param];
 | 
			
		||||
      if (Array.isArray(value)) {
 | 
			
		||||
        value.forEach((_value) => {
 | 
			
		||||
          updates.push({ param, value: _value, op: 'a' });
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        updates.push({
 | 
			
		||||
          param,
 | 
			
		||||
          value: value as string | number | boolean,
 | 
			
		||||
          op: 'a',
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return this.clone(updates);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Replaces the value for a parameter.
 | 
			
		||||
   * @param param The parameter name.
 | 
			
		||||
   * @param value The new value.
 | 
			
		||||
   * @return A new body with the new value.
 | 
			
		||||
   */
 | 
			
		||||
  set(param: string, value: string | number | boolean): HttpParams {
 | 
			
		||||
    return this.clone({ param, value, op: 's' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Removes a given value or all values from a parameter.
 | 
			
		||||
   * @param param The parameter name.
 | 
			
		||||
   * @param value The value to remove, if provided.
 | 
			
		||||
   * @return A new body with the given value removed, or with all values
 | 
			
		||||
   * removed if no value is specified.
 | 
			
		||||
   */
 | 
			
		||||
  delete(param: string, value?: string | number | boolean): HttpParams {
 | 
			
		||||
    return this.clone({ param, value, op: 'd' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Serializes the body to an encoded string, where key-value pairs (separated by `=`) are
 | 
			
		||||
   * separated by `&`s.
 | 
			
		||||
   */
 | 
			
		||||
  toString(): string {
 | 
			
		||||
    this.init();
 | 
			
		||||
    return (
 | 
			
		||||
      this.keys()
 | 
			
		||||
        .map((key) => {
 | 
			
		||||
          const eKey = this.encoder.encodeKey(key);
 | 
			
		||||
          // `a: ['1']` produces `'a=1'`
 | 
			
		||||
          // `b: []` produces `''`
 | 
			
		||||
          // `c: ['1', '2']` produces `'c=1&c=2'`
 | 
			
		||||
          return this.map!.get(key)!
 | 
			
		||||
            .map((value) => `${eKey}=${this.encoder.encodeValue(value)}`)
 | 
			
		||||
            .join('&');
 | 
			
		||||
        })
 | 
			
		||||
        // filter out empty values because `b: []` produces `''`
 | 
			
		||||
        // which results in `a=1&&c=1&c=2` instead of `a=1&c=1&c=2` if we don't
 | 
			
		||||
        .filter((param) => param !== '')
 | 
			
		||||
        .join('&')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private clone(update: Update | Update[]): HttpParams {
 | 
			
		||||
    const clone = new HttpParams({
 | 
			
		||||
      encoder: this.encoder,
 | 
			
		||||
    } as HttpParamsOptions);
 | 
			
		||||
    clone.cloneFrom = this.cloneFrom || this;
 | 
			
		||||
    clone.updates = (this.updates || []).concat(update);
 | 
			
		||||
    return clone;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private init() {
 | 
			
		||||
    if (this.map === null) {
 | 
			
		||||
      this.map = new Map<string, string[]>();
 | 
			
		||||
    }
 | 
			
		||||
    if (this.cloneFrom !== null) {
 | 
			
		||||
      this.cloneFrom.init();
 | 
			
		||||
      this.cloneFrom
 | 
			
		||||
        .keys()
 | 
			
		||||
        .forEach((key) => this.map!.set(key, this.cloneFrom!.map!.get(key)!));
 | 
			
		||||
      // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
 | 
			
		||||
      this.updates!.forEach((update) => {
 | 
			
		||||
        switch (update.op) {
 | 
			
		||||
          case 'a':
 | 
			
		||||
          case 's': {
 | 
			
		||||
            const base =
 | 
			
		||||
              (update.op === 'a' ? this.map!.get(update.param) : undefined) ||
 | 
			
		||||
              [];
 | 
			
		||||
            base.push(`${update.value!}`);
 | 
			
		||||
            this.map!.set(update.param, base);
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
          case 'd': {
 | 
			
		||||
            if (update.value !== undefined) {
 | 
			
		||||
              const base = this.map!.get(update.param) || [];
 | 
			
		||||
              const idx = base.indexOf(`${update.value}`);
 | 
			
		||||
              if (idx !== -1) {
 | 
			
		||||
                base.splice(idx, 1);
 | 
			
		||||
              }
 | 
			
		||||
              if (base.length > 0) {
 | 
			
		||||
                this.map!.set(update.param, base);
 | 
			
		||||
              } else {
 | 
			
		||||
                this.map!.delete(update.param);
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              this.map!.delete(update.param);
 | 
			
		||||
              break;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
          default:
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      this.cloneFrom = this.updates = null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { skip } from 'rxjs/operators';
 | 
			
		||||
import { ReplaySubject, firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { share, skip } from 'rxjs/operators';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
import { OidcSecurityService } from '../oidc.security.service';
 | 
			
		||||
@ -333,7 +333,7 @@ describe('CheckSessionService', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('init', () => {
 | 
			
		||||
    it('returns falsy observable when lastIframerefresh and iframeRefreshInterval are bigger than now', async () => {
 | 
			
		||||
    it('angular oidc client', async () => {
 | 
			
		||||
      const serviceAsAny = checkSessionService as any;
 | 
			
		||||
      const dateNow = new Date();
 | 
			
		||||
      const lastRefresh = dateNow.setMinutes(dateNow.getMinutes() + 30);
 | 
			
		||||
@ -341,7 +341,7 @@ describe('CheckSessionService', () => {
 | 
			
		||||
      serviceAsAny.lastIFrameRefresh = lastRefresh;
 | 
			
		||||
      serviceAsAny.iframeRefreshInterval = lastRefresh;
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(serviceAsAny.init());
 | 
			
		||||
      const result = await firstValueFrom(serviceAsAny.init());
 | 
			
		||||
      expect(result).toBeUndefined();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -366,18 +366,27 @@ describe('CheckSessionService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('checkSessionChanged$', () => {
 | 
			
		||||
    it('emits when internal event is thrown', async () => {
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
        checkSessionService.checkSessionChanged$.pipe(skip(1))
 | 
			
		||||
      const test$ = checkSessionService.checkSessionChanged$.pipe(
 | 
			
		||||
        skip(1),
 | 
			
		||||
        share({
 | 
			
		||||
          connector: () => new ReplaySubject(1),
 | 
			
		||||
          resetOnError: false,
 | 
			
		||||
          resetOnComplete: false,
 | 
			
		||||
          resetOnRefCountZero: true,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBe(true);
 | 
			
		||||
 | 
			
		||||
      test$.subscribe();
 | 
			
		||||
      const serviceAsAny = checkSessionService as any;
 | 
			
		||||
 | 
			
		||||
      serviceAsAny.checkSessionChangedInternal$.next(true);
 | 
			
		||||
 | 
			
		||||
      const result = await firstValueFrom(test$);
 | 
			
		||||
      expect(result).toBe(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('emits false initially', async () => {
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkSessionService.checkSessionChanged$
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBe(false);
 | 
			
		||||
@ -387,7 +396,7 @@ describe('CheckSessionService', () => {
 | 
			
		||||
      const expectedResultsInOrder = [false, true];
 | 
			
		||||
      let counter = 0;
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        checkSessionService.checkSessionChanged$
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBe(expectedResultsInOrder[counter]);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { Injectable, NgZone, type OnDestroy, inject } from 'injection-js';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { BehaviorSubject, Observable, of } from 'rxjs';
 | 
			
		||||
import { take } from 'rxjs/operators';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
@ -14,7 +14,7 @@ const IFRAME_FOR_CHECK_SESSION_IDENTIFIER = 'myiFrameForCheckSession';
 | 
			
		||||
// http://openid.net/specs/openid-connect-session-1_0-ID4.html
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class CheckSessionService implements OnDestroy {
 | 
			
		||||
export class CheckSessionService {
 | 
			
		||||
  private readonly loggerService = inject(LoggerService);
 | 
			
		||||
 | 
			
		||||
  private readonly storagePersistenceService = inject(
 | 
			
		||||
@ -25,8 +25,6 @@ export class CheckSessionService implements OnDestroy {
 | 
			
		||||
 | 
			
		||||
  private readonly eventService = inject(PublicEventsService);
 | 
			
		||||
 | 
			
		||||
  private readonly zone = inject(NgZone);
 | 
			
		||||
 | 
			
		||||
  private readonly document = inject(DOCUMENT);
 | 
			
		||||
 | 
			
		||||
  private checkSessionReceived = false;
 | 
			
		||||
@ -54,7 +52,7 @@ export class CheckSessionService implements OnDestroy {
 | 
			
		||||
    return this.checkSessionChangedInternal$.asObservable();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy(): void {
 | 
			
		||||
  [Symbol.dispose]() {
 | 
			
		||||
    this.stop();
 | 
			
		||||
    const windowAsDefaultView = this.document.defaultView;
 | 
			
		||||
 | 
			
		||||
@ -104,9 +102,9 @@ export class CheckSessionService implements OnDestroy {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private init(configuration: OpenIdConfiguration): Observable<void> {
 | 
			
		||||
  private init(configuration: OpenIdConfiguration): Observable<undefined> {
 | 
			
		||||
    if (this.lastIFrameRefresh + this.iframeRefreshInterval > Date.now()) {
 | 
			
		||||
      return of();
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const authWellKnownEndPoints = this.storagePersistenceService.read(
 | 
			
		||||
@ -120,7 +118,7 @@ export class CheckSessionService implements OnDestroy {
 | 
			
		||||
        'CheckSession - init check session: authWellKnownEndpoints is undefined. Returning.'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return of();
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const existingIframe = this.getOrCreateIframe(configuration);
 | 
			
		||||
@ -138,7 +136,7 @@ export class CheckSessionService implements OnDestroy {
 | 
			
		||||
        'CheckSession - init check session: checkSessionIframe is not configured to run'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return of();
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (contentWindow) {
 | 
			
		||||
@ -228,14 +226,12 @@ export class CheckSessionService implements OnDestroy {
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          this.zone.runOutsideAngular(() => {
 | 
			
		||||
          this.scheduledHeartBeatRunning =
 | 
			
		||||
            this.document?.defaultView?.setTimeout(
 | 
			
		||||
                () => this.zone.run(pollServerSessionRecur),
 | 
			
		||||
              pollServerSessionRecur,
 | 
			
		||||
              this.heartBeatInterval
 | 
			
		||||
            ) ?? null;
 | 
			
		||||
        });
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pollServerSessionRecur();
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
import { mockProvider } from '../testing/mock';
 | 
			
		||||
@ -41,26 +41,27 @@ describe('RefreshSessionIframeService ', () => {
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(refreshSessionIframeService
 | 
			
		||||
        .refreshSessionWithIframe(allConfigs[0]!, allConfigs));
 | 
			
		||||
expect(
 | 
			
		||||
            sendAuthorizeRequestUsingSilentRenewSpy
 | 
			
		||||
          ).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
            'a-url',
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        refreshSessionIframeService.refreshSessionWithIframe(
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      expect(
 | 
			
		||||
        sendAuthorizeRequestUsingSilentRenewSpy
 | 
			
		||||
      ).toHaveBeenCalledExactlyOnceWith('a-url', allConfigs[0]!, allConfigs);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('initSilentRenewRequest', () => {
 | 
			
		||||
    it('dispatches customevent to window object', async () => {
 | 
			
		||||
      const dispatchEventSpy = vi.spyOn(window, 'dispatchEvent');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
        (refreshSessionIframeService as any).initSilentRenewRequest()
 | 
			
		||||
    it('dispatches customevent to window object', () => {
 | 
			
		||||
      const dispatchEventSpy = vi.spyOn(
 | 
			
		||||
        document.defaultView?.window!,
 | 
			
		||||
        'dispatchEvent'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      (refreshSessionIframeService as any).initSilentRenewRequest();
 | 
			
		||||
 | 
			
		||||
      expect(dispatchEventSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        new CustomEvent('oidc-silent-renew-init', {
 | 
			
		||||
          detail: expect.any(Number),
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,11 @@
 | 
			
		||||
import { Injectable, RendererFactory2, inject } from 'injection-js';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { switchMap } from 'rxjs/operators';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import {
 | 
			
		||||
  Observable,
 | 
			
		||||
  ReplaySubject,
 | 
			
		||||
  type Subscription,
 | 
			
		||||
  fromEventPattern,
 | 
			
		||||
} from 'rxjs';
 | 
			
		||||
import { filter, share, switchMap, takeUntil } from 'rxjs/operators';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
import { DOCUMENT } from '../dom';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
@ -9,11 +14,6 @@ import { SilentRenewService } from './silent-renew.service';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class RefreshSessionIframeService {
 | 
			
		||||
  private readonly renderer = inject(RendererFactory2).createRenderer(
 | 
			
		||||
    null,
 | 
			
		||||
    null
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  private readonly loggerService = inject(LoggerService);
 | 
			
		||||
 | 
			
		||||
  private readonly urlService = inject(UrlService);
 | 
			
		||||
@ -22,6 +22,8 @@ export class RefreshSessionIframeService {
 | 
			
		||||
 | 
			
		||||
  private readonly document = inject(DOCUMENT);
 | 
			
		||||
 | 
			
		||||
  private silentRenewEventHandlerSubscription?: Subscription;
 | 
			
		||||
 | 
			
		||||
  refreshSessionWithIframe(
 | 
			
		||||
    config: OpenIdConfiguration,
 | 
			
		||||
    allConfigs: OpenIdConfiguration[],
 | 
			
		||||
@ -80,24 +82,53 @@ export class RefreshSessionIframeService {
 | 
			
		||||
  ): void {
 | 
			
		||||
    const instanceId = Math.random();
 | 
			
		||||
 | 
			
		||||
    const initDestroyHandler = this.renderer.listen(
 | 
			
		||||
      'window',
 | 
			
		||||
    const oidcSilentRenewInit$ = fromEventPattern(
 | 
			
		||||
      (handler) =>
 | 
			
		||||
        this.document.defaultView.window.addEventListener(
 | 
			
		||||
          'oidc-silent-renew-init',
 | 
			
		||||
      (e: CustomEvent) => {
 | 
			
		||||
        if (e.detail !== instanceId) {
 | 
			
		||||
          initDestroyHandler();
 | 
			
		||||
          renewDestroyHandler();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
    const renewDestroyHandler = this.renderer.listen(
 | 
			
		||||
      'window',
 | 
			
		||||
      'oidc-silent-renew-message',
 | 
			
		||||
      (e) =>
 | 
			
		||||
        this.silentRenewService.silentRenewEventHandler(e, config, allConfigs)
 | 
			
		||||
          handler
 | 
			
		||||
        ),
 | 
			
		||||
      (handler) =>
 | 
			
		||||
        this.document.defaultView.window.removeEventListener(
 | 
			
		||||
          'oidc-silent-renew-init',
 | 
			
		||||
          handler
 | 
			
		||||
        )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    this.document.defaultView?.dispatchEvent(
 | 
			
		||||
    const oidcSilentRenewInitNotSelf$ = oidcSilentRenewInit$.pipe(
 | 
			
		||||
      filter((e: CustomEvent) => e.detail !== instanceId)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (this.silentRenewEventHandlerSubscription) {
 | 
			
		||||
      this.silentRenewEventHandlerSubscription.unsubscribe();
 | 
			
		||||
    }
 | 
			
		||||
    this.silentRenewEventHandlerSubscription = fromEventPattern<CustomEvent>(
 | 
			
		||||
      (handler) =>
 | 
			
		||||
        this.document.defaultView.window.addEventListener(
 | 
			
		||||
          'oidc-silent-renew-message',
 | 
			
		||||
          handler
 | 
			
		||||
        ),
 | 
			
		||||
      (handler) =>
 | 
			
		||||
        this.document.defaultView.window.removeEventListener(
 | 
			
		||||
          'oidc-silent-renew-message',
 | 
			
		||||
          handler
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
      .pipe(
 | 
			
		||||
        takeUntil(oidcSilentRenewInitNotSelf$),
 | 
			
		||||
        switchMap((e) =>
 | 
			
		||||
          this.silentRenewService.silentRenewEventHandler(e, config, allConfigs)
 | 
			
		||||
        ),
 | 
			
		||||
        share({
 | 
			
		||||
          connector: () => new ReplaySubject(1),
 | 
			
		||||
          resetOnError: false,
 | 
			
		||||
          resetOnComplete: false,
 | 
			
		||||
          resetOnRefCountZero: true,
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
      .subscribe();
 | 
			
		||||
 | 
			
		||||
    this.document.defaultView?.window.dispatchEvent(
 | 
			
		||||
      new CustomEvent('oidc-silent-renew-init', {
 | 
			
		||||
        detail: instanceId,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,12 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { Observable, lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import {
 | 
			
		||||
  Observable,
 | 
			
		||||
  ReplaySubject,
 | 
			
		||||
  firstValueFrom,
 | 
			
		||||
  of,
 | 
			
		||||
  share,
 | 
			
		||||
  throwError,
 | 
			
		||||
} from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { ImplicitFlowCallbackService } from '../callback/implicit-flow-callback.service';
 | 
			
		||||
@ -28,6 +35,7 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
  let intervalService: IntervalService;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    vi.useFakeTimers();
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        SilentRenewService,
 | 
			
		||||
@ -54,6 +62,11 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
    intervalService = TestBed.inject(IntervalService);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    vi.useRealTimers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(silentRenewService).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
@ -149,7 +162,7 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
      const urlParts =
 | 
			
		||||
        'code=some-code&state=some-state&session_state=some-session-state';
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        silentRenewService.codeFlowCallbackSilentRenewIframe(
 | 
			
		||||
          [url, urlParts],
 | 
			
		||||
          config,
 | 
			
		||||
@ -188,7 +201,7 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
      const urlParts = 'error=some_error';
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          silentRenewService.codeFlowCallbackSilentRenewIframe(
 | 
			
		||||
            [url, urlParts],
 | 
			
		||||
            config,
 | 
			
		||||
@ -312,19 +325,31 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
      const eventData = { detail: 'detail?detail2' } as CustomEvent;
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
        silentRenewService.refreshSessionWithIFrameCompleted$
 | 
			
		||||
      const test$ = silentRenewService.refreshSessionWithIFrameCompleted$.pipe(
 | 
			
		||||
        share({
 | 
			
		||||
          connector: () => new ReplaySubject(1),
 | 
			
		||||
          resetOnError: false,
 | 
			
		||||
          resetOnComplete: false,
 | 
			
		||||
          resetOnRefCountZero: true,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
        refreshToken: 'callbackContext',
 | 
			
		||||
      } as CallbackContext);
 | 
			
		||||
 | 
			
		||||
      test$.subscribe();
 | 
			
		||||
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        silentRenewService.silentRenewEventHandler(
 | 
			
		||||
          eventData,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(1000);
 | 
			
		||||
 | 
			
		||||
      const result = await firstValueFrom(test$);
 | 
			
		||||
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
        refreshToken: 'callbackContext',
 | 
			
		||||
      } as CallbackContext);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('loggs and calls flowsDataService.resetSilentRenewRunning in case of an error', async () => {
 | 
			
		||||
@ -341,10 +366,12 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
      const eventData = { detail: 'detail?detail2' } as CustomEvent;
 | 
			
		||||
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        silentRenewService.silentRenewEventHandler(
 | 
			
		||||
          eventData,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(1000);
 | 
			
		||||
      expect(resetSilentRenewRunningSpy).toHaveBeenCalledTimes(1);
 | 
			
		||||
@ -360,17 +387,28 @@ describe('SilentRenewService  ', () => {
 | 
			
		||||
      const eventData = { detail: 'detail?detail2' } as CustomEvent;
 | 
			
		||||
      const allConfigs = [{ configId: 'configId1' }];
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
        silentRenewService.refreshSessionWithIFrameCompleted$
 | 
			
		||||
      const test$ = silentRenewService.refreshSessionWithIFrameCompleted$.pipe(
 | 
			
		||||
        share({
 | 
			
		||||
          connector: () => new ReplaySubject(1),
 | 
			
		||||
          resetOnError: false,
 | 
			
		||||
          resetOnComplete: false,
 | 
			
		||||
          resetOnRefCountZero: true,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBeNull();
 | 
			
		||||
 | 
			
		||||
      test$.subscribe();
 | 
			
		||||
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        silentRenewService.silentRenewEventHandler(
 | 
			
		||||
          eventData,
 | 
			
		||||
          allConfigs[0]!,
 | 
			
		||||
          allConfigs
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(1000);
 | 
			
		||||
 | 
			
		||||
      const result = await firstValueFrom(test$);
 | 
			
		||||
      expect(result).toBeNull();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import { HttpParams } from '@ngify/http';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, Subject, throwError } from 'rxjs';
 | 
			
		||||
import { catchError } from 'rxjs/operators';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, Subject, of, throwError } from 'rxjs';
 | 
			
		||||
import { catchError, map } from 'rxjs/operators';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { ImplicitFlowCallbackService } from '../callback/implicit-flow-callback.service';
 | 
			
		||||
import { IntervalService } from '../callback/interval.service';
 | 
			
		||||
@ -10,6 +9,7 @@ import type { CallbackContext } from '../flows/callback-context';
 | 
			
		||||
import { FlowsDataService } from '../flows/flows-data.service';
 | 
			
		||||
import { FlowsService } from '../flows/flows.service';
 | 
			
		||||
import { ResetAuthDataService } from '../flows/reset-auth-data.service';
 | 
			
		||||
import { HttpParams } from '../http';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
import { FlowHelper } from '../utils/flowHelper/flow-helper.service';
 | 
			
		||||
import { ValidationResult } from '../validation/validation-result';
 | 
			
		||||
@ -70,8 +70,9 @@ export class SilentRenewService {
 | 
			
		||||
    config: OpenIdConfiguration,
 | 
			
		||||
    allConfigs: OpenIdConfiguration[]
 | 
			
		||||
  ): Observable<CallbackContext> {
 | 
			
		||||
    // TODO: fix @ngify/http
 | 
			
		||||
    const params = new HttpParams(urlParts[1] || undefined);
 | 
			
		||||
    const params = new HttpParams({
 | 
			
		||||
      fromString: urlParts[1],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const errorParam = params.get('error');
 | 
			
		||||
 | 
			
		||||
@ -120,10 +121,10 @@ export class SilentRenewService {
 | 
			
		||||
    e: CustomEvent,
 | 
			
		||||
    config: OpenIdConfiguration,
 | 
			
		||||
    allConfigs: OpenIdConfiguration[]
 | 
			
		||||
  ): void {
 | 
			
		||||
  ): Observable<undefined> {
 | 
			
		||||
    this.loggerService.logDebug(config, 'silentRenewEventHandler');
 | 
			
		||||
    if (!e.detail) {
 | 
			
		||||
      return;
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let callback$: Observable<CallbackContext>;
 | 
			
		||||
@ -146,17 +147,19 @@ export class SilentRenewService {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    callback$.subscribe({
 | 
			
		||||
      next: (callbackContext) => {
 | 
			
		||||
    return callback$.pipe(
 | 
			
		||||
      map((callbackContext) => {
 | 
			
		||||
        this.refreshSessionWithIFrameCompletedInternal$.next(callbackContext);
 | 
			
		||||
        this.flowsDataService.resetSilentRenewRunning(config);
 | 
			
		||||
      },
 | 
			
		||||
      error: (err: unknown) => {
 | 
			
		||||
        return undefined;
 | 
			
		||||
      }),
 | 
			
		||||
      catchError((err: unknown) => {
 | 
			
		||||
        this.loggerService.logError(config, `Error: ${err}`);
 | 
			
		||||
        this.refreshSessionWithIFrameCompletedInternal$.next(null);
 | 
			
		||||
        this.flowsDataService.resetSilentRenewRunning(config);
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
        return of(undefined);
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getExistingIframe(): HTMLIFrameElement | null {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
// Public classes.
 | 
			
		||||
 | 
			
		||||
export { PassedInitialConfig } from './auth-config';
 | 
			
		||||
export type { PassedInitialConfig } from './auth-config';
 | 
			
		||||
export * from './auth-options';
 | 
			
		||||
export * from './auth-state/auth-result';
 | 
			
		||||
export * from './auth-state/auth-state';
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,3 @@
 | 
			
		||||
export { Module } from './module';
 | 
			
		||||
export type { Module } from './module';
 | 
			
		||||
export { APP_INITIALIZER } from './convention';
 | 
			
		||||
export { injectAbstractType } from './inject';
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,3 @@
 | 
			
		||||
import 'reflect-metadata';
 | 
			
		||||
import type { Injector } from 'injection-js';
 | 
			
		||||
 | 
			
		||||
export type Module = (parentInjector: Injector) => Injector;
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import {
 | 
			
		||||
  HttpTestingController,
 | 
			
		||||
  provideHttpClientTesting,
 | 
			
		||||
} from '@ngify/http/testing';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from '../auth-state/auth-state.service';
 | 
			
		||||
import { ConfigurationService } from '../config/config.service';
 | 
			
		||||
@ -106,7 +106,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -132,7 +132,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -160,7 +160,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        'thisIsAToken'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -185,7 +185,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -211,7 +211,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
      );
 | 
			
		||||
      vi.spyOn(authStateService, 'getAccessToken').mockReturnValue('');
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -229,7 +229,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        false
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -260,7 +260,7 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        matchingConfig: null,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      const response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
@ -286,10 +286,10 @@ describe('AuthHttpInterceptor', () => {
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      let response = await lastValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      let response = await firstValueFrom(httpClient.get(actionUrl));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      response = await lastValueFrom(httpClient.get(actionUrl2));
 | 
			
		||||
      response = await firstValueFrom(httpClient.get(actionUrl2));
 | 
			
		||||
      expect(response).toBeTruthy();
 | 
			
		||||
 | 
			
		||||
      const httpRequest = httpTestingController.expectOne(actionUrl);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { StoragePersistenceService } from '../storage/storage-persistence.service';
 | 
			
		||||
import { mockProvider } from '../testing/mock';
 | 
			
		||||
@ -83,7 +83,7 @@ describe('LoginService', () => {
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("should throw error if configuration is null and doesn't call loginPar or loginStandard", () => {
 | 
			
		||||
    it("should throw error if configuration is null and doesn't call loginPar or loginStandard", async () => {
 | 
			
		||||
      // arrange
 | 
			
		||||
      // biome-ignore lint/suspicious/noEvolvingTypes: <explanation>
 | 
			
		||||
      const config = null;
 | 
			
		||||
@ -91,13 +91,16 @@ describe('LoginService', () => {
 | 
			
		||||
      const standardLoginSpy = vi.spyOn(standardLoginService, 'loginStandard');
 | 
			
		||||
      const authOptions = { customParams: { custom: 'params' } };
 | 
			
		||||
 | 
			
		||||
      // act
 | 
			
		||||
      const fn = (): void => service.login(config, authOptions);
 | 
			
		||||
 | 
			
		||||
      // assert
 | 
			
		||||
      expect(fn).toThrow(
 | 
			
		||||
        new Error('Please provide a configuration before setting up the module')
 | 
			
		||||
      try {
 | 
			
		||||
        await firstValueFrom(service.login(config, authOptions));
 | 
			
		||||
        expect.fail('should be error');
 | 
			
		||||
      } catch (error: unknown) {
 | 
			
		||||
        expect(error).toEqual(
 | 
			
		||||
          new Error(
 | 
			
		||||
            'Please provide a configuration before setting up the module'
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      expect(loginParSpy).not.toHaveBeenCalled();
 | 
			
		||||
      expect(standardLoginSpy).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -115,7 +118,7 @@ describe('LoginService', () => {
 | 
			
		||||
        .mockReturnValue(of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      // act
 | 
			
		||||
      await lastValueFrom(service.loginWithPopUp(config, [config]));
 | 
			
		||||
      await firstValueFrom(service.loginWithPopUp(config, [config]));
 | 
			
		||||
      expect(loginWithPopUpPar).toHaveBeenCalledTimes(1);
 | 
			
		||||
      expect(loginWithPopUpStandardSpy).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -131,7 +134,7 @@ describe('LoginService', () => {
 | 
			
		||||
        .mockReturnValue(of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      // act
 | 
			
		||||
      await lastValueFrom(service.loginWithPopUp(config, [config]));
 | 
			
		||||
      await firstValueFrom(service.loginWithPopUp(config, [config]));
 | 
			
		||||
      expect(loginWithPopUpPar).not.toHaveBeenCalled();
 | 
			
		||||
      expect(loginWithPopUpStandardSpy).toHaveBeenCalledTimes(1);
 | 
			
		||||
    });
 | 
			
		||||
@ -150,7 +153,7 @@ describe('LoginService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // act
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.loginWithPopUp(config, [config], authOptions)
 | 
			
		||||
      );
 | 
			
		||||
      expect(storagePersistenceServiceSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -174,7 +177,7 @@ describe('LoginService', () => {
 | 
			
		||||
      vi.spyOn(popUpService, 'isCurrentlyInPopup').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      // act
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.loginWithPopUp(config, [config], authOptions)
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, of } from 'rxjs';
 | 
			
		||||
import { type Observable, of, throwError } from 'rxjs';
 | 
			
		||||
import type { AuthOptions } from '../auth-options';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
import { StoragePersistenceService } from '../storage/storage-persistence.service';
 | 
			
		||||
@ -27,10 +27,13 @@ export class LoginService {
 | 
			
		||||
  login(
 | 
			
		||||
    configuration: OpenIdConfiguration | null,
 | 
			
		||||
    authOptions?: AuthOptions
 | 
			
		||||
  ): void {
 | 
			
		||||
  ): Observable<void> {
 | 
			
		||||
    if (!configuration) {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
      return throwError(
 | 
			
		||||
        () =>
 | 
			
		||||
          new Error(
 | 
			
		||||
            'Please provide a configuration before setting up the module'
 | 
			
		||||
          )
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -45,10 +48,9 @@ export class LoginService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (usePushedAuthorisationRequests) {
 | 
			
		||||
      this.parLoginService.loginPar(configuration, authOptions);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.standardLoginService.loginStandard(configuration, authOptions);
 | 
			
		||||
      return this.parLoginService.loginPar(configuration, authOptions);
 | 
			
		||||
    }
 | 
			
		||||
    return this.standardLoginService.loginStandard(configuration, authOptions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loginWithPopUp(
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, spyOnProperty } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { CheckAuthService } from '../../auth-state/check-auth.service';
 | 
			
		||||
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
 | 
			
		||||
@ -65,7 +65,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
      ).mockReturnValue(false);
 | 
			
		||||
      const loggerSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(service.loginPar({}));
 | 
			
		||||
      const result = await firstValueFrom(service.loginPar({}));
 | 
			
		||||
 | 
			
		||||
      expect(result).toBeUndefined();
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -86,7 +86,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
        .spyOn(parService, 'postParRequest')
 | 
			
		||||
        .mockReturnValue(of({ requestUri: 'requestUri' } as ParResponse));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.loginPar({
 | 
			
		||||
          authWellknownEndpointUrl: 'authWellknownEndpoint',
 | 
			
		||||
          responseType: 'stubValue',
 | 
			
		||||
@ -116,7 +116,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
        .spyOn(parService, 'postParRequest')
 | 
			
		||||
        .mockReturnValue(of({ requestUri: 'requestUri' } as ParResponse));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.loginPar(config, {
 | 
			
		||||
          customParams: { some: 'thing' },
 | 
			
		||||
        })
 | 
			
		||||
@ -149,7 +149,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
      vi.spyOn(urlService, 'getAuthorizeParUrl').mockReturnValue('');
 | 
			
		||||
      const spy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(service.loginPar(config));
 | 
			
		||||
      const result = await firstValueFrom(service.loginPar(config));
 | 
			
		||||
 | 
			
		||||
      expect(result).toBeUndefined();
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(1);
 | 
			
		||||
@ -180,7 +180,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(redirectService, 'redirectTo');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(service.loginPar(config, authOptions));
 | 
			
		||||
      await firstValueFrom(service.loginPar(config, authOptions));
 | 
			
		||||
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('some-par-url');
 | 
			
		||||
    });
 | 
			
		||||
@ -212,7 +212,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
        spy(url);
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      service.loginPar(config, { urlHandler });
 | 
			
		||||
      await firstValueFrom(service.loginPar(config, { urlHandler }));
 | 
			
		||||
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('some-par-url');
 | 
			
		||||
      expect(redirectToSpy).not.toHaveBeenCalled();
 | 
			
		||||
@ -230,7 +230,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
      const allConfigs = [config];
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.loginWithPopUpPar(config, allConfigs));
 | 
			
		||||
        await firstValueFrom(service.loginWithPopUpPar(config, allConfigs));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
        expect(err.message).toBe('Invalid response type!');
 | 
			
		||||
@ -258,7 +258,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
        .mockReturnValue(of({ requestUri: 'requestUri' } as ParResponse));
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.loginWithPopUpPar(config, allConfigs));
 | 
			
		||||
        await firstValueFrom(service.loginWithPopUpPar(config, allConfigs));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(spy).toHaveBeenCalled();
 | 
			
		||||
        expect(err.message).toBe(
 | 
			
		||||
@ -288,7 +288,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
        .mockReturnValue(of({ requestUri: 'requestUri' } as ParResponse));
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.loginWithPopUpPar(config, allConfigs, {
 | 
			
		||||
            customParams: { some: 'thing' },
 | 
			
		||||
          })
 | 
			
		||||
@ -326,7 +326,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
      const spy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          service.loginWithPopUpPar(config, allConfigs, {
 | 
			
		||||
            customParams: { some: 'thing' },
 | 
			
		||||
          })
 | 
			
		||||
@ -369,7 +369,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(popupService, 'openPopUp');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(service.loginWithPopUpPar(config, allConfigs));
 | 
			
		||||
      await firstValueFrom(service.loginWithPopUpPar(config, allConfigs));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'some-par-url',
 | 
			
		||||
        undefined,
 | 
			
		||||
@ -419,7 +419,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
 | 
			
		||||
      spyOnProperty(popupService, 'result$').mockReturnValue(of(popupResult));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.loginWithPopUpPar(config, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(checkAuthSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -465,7 +465,7 @@ describe('ParLoginService', () => {
 | 
			
		||||
 | 
			
		||||
      spyOnProperty(popupService, 'result$').mockReturnValue(of(popupResult));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.loginWithPopUpPar(config, allConfigs)
 | 
			
		||||
      );
 | 
			
		||||
      expect(checkAuthSpy).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, of, throwError } from 'rxjs';
 | 
			
		||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
 | 
			
		||||
import { map, switchMap, take } from 'rxjs/operators';
 | 
			
		||||
import type { AuthOptions } from '../../auth-options';
 | 
			
		||||
import { CheckAuthService } from '../../auth-state/check-auth.service';
 | 
			
		||||
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
 | 
			
		||||
@ -47,7 +47,7 @@ export class ParLoginService {
 | 
			
		||||
    ) {
 | 
			
		||||
      this.loggerService.logError(configuration, 'Invalid response type!');
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.loggerService.logDebug(
 | 
			
		||||
@ -55,14 +55,13 @@ export class ParLoginService {
 | 
			
		||||
      'BEGIN Authorize OIDC Flow, no auth data'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const result$ = this.authWellKnownService
 | 
			
		||||
    return this.authWellKnownService
 | 
			
		||||
      .queryAndStoreAuthWellKnownEndPoints(configuration)
 | 
			
		||||
      .pipe(
 | 
			
		||||
        switchMap(() =>
 | 
			
		||||
          this.parService.postParRequest(configuration, authOptions)
 | 
			
		||||
        ),
 | 
			
		||||
        map(() => {
 | 
			
		||||
          (response) => {
 | 
			
		||||
        map((response) => {
 | 
			
		||||
          this.loggerService.logDebug(
 | 
			
		||||
            configuration,
 | 
			
		||||
            'par response: ',
 | 
			
		||||
@ -74,11 +73,7 @@ export class ParLoginService {
 | 
			
		||||
            configuration
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
            this.loggerService.logDebug(
 | 
			
		||||
              configuration,
 | 
			
		||||
              'par request url: ',
 | 
			
		||||
              url
 | 
			
		||||
            );
 | 
			
		||||
          this.loggerService.logDebug(configuration, 'par request url: ', url);
 | 
			
		||||
 | 
			
		||||
          if (!url) {
 | 
			
		||||
            this.loggerService.logError(
 | 
			
		||||
@ -94,14 +89,8 @@ export class ParLoginService {
 | 
			
		||||
          } else {
 | 
			
		||||
            this.redirectService.redirectTo(url);
 | 
			
		||||
          }
 | 
			
		||||
          };
 | 
			
		||||
        }),
 | 
			
		||||
        shareReplay(1)
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    result$.subscribe();
 | 
			
		||||
 | 
			
		||||
    return result$;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loginWithPopUpPar(
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../../api/data.service';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -20,6 +20,7 @@ describe('ParService', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        ParService,
 | 
			
		||||
        mockProvider(LoggerService),
 | 
			
		||||
        mockProvider(UrlService),
 | 
			
		||||
        mockProvider(DataService),
 | 
			
		||||
@ -48,7 +49,7 @@ describe('ParService', () => {
 | 
			
		||||
        () => null
 | 
			
		||||
      );
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
        await firstValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err.message).toBe(
 | 
			
		||||
          'Could not read PAR endpoint because authWellKnownEndPoints are not given'
 | 
			
		||||
@ -66,7 +67,7 @@ describe('ParService', () => {
 | 
			
		||||
        () => ({ some: 'thing' })
 | 
			
		||||
      );
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
        await firstValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err.message).toBe(
 | 
			
		||||
          'Could not read PAR endpoint from authWellKnownEndpoints'
 | 
			
		||||
@ -88,7 +89,7 @@ describe('ParService', () => {
 | 
			
		||||
        .spyOn(dataService, 'post')
 | 
			
		||||
        .mockReturnValue(of({}));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
      await firstValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
      expect(dataServiceSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'parEndpoint',
 | 
			
		||||
        'some-url123',
 | 
			
		||||
@ -109,7 +110,7 @@ describe('ParService', () => {
 | 
			
		||||
      vi.spyOn(dataService, 'post').mockReturnValue(
 | 
			
		||||
        of({ expires_in: 123, request_uri: 'request_uri' })
 | 
			
		||||
      );
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.postParRequest({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({ expiresIn: 123, requestUri: 'request_uri' });
 | 
			
		||||
@ -130,7 +131,7 @@ describe('ParService', () => {
 | 
			
		||||
      const loggerSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
        await firstValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err.message).toBe(
 | 
			
		||||
          'There was an error on ParService postParRequest'
 | 
			
		||||
@ -159,7 +160,7 @@ describe('ParService', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(
 | 
			
		||||
      const res = await firstValueFrom(
 | 
			
		||||
        service.postParRequest({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
@ -183,7 +184,7 @@ describe('ParService', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(
 | 
			
		||||
      const res = await firstValueFrom(
 | 
			
		||||
        service.postParRequest({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
@ -209,7 +210,7 @@ describe('ParService', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
        await firstValueFrom(service.postParRequest({ configId: 'configId1' }));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, spyOnProperty } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { CheckAuthService } from '../../auth-state/check-auth.service';
 | 
			
		||||
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
 | 
			
		||||
@ -60,7 +60,7 @@ describe('PopUpLoginService', () => {
 | 
			
		||||
      const loggerSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          popUpLoginService.loginWithPopUpStandard(config, [config])
 | 
			
		||||
        );
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
@ -91,7 +91,7 @@ describe('PopUpLoginService', () => {
 | 
			
		||||
        of({} as LoginResponse)
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        popUpLoginService.loginWithPopUpStandard(config, [config])
 | 
			
		||||
      );
 | 
			
		||||
      expect(urlService.getAuthorizeUrl).toHaveBeenCalled();
 | 
			
		||||
@ -120,7 +120,7 @@ describe('PopUpLoginService', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const popupSpy = vi.spyOn(popupService, 'openPopUp');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        popUpLoginService.loginWithPopUpStandard(config, [config])
 | 
			
		||||
      );
 | 
			
		||||
      expect(popupSpy).toHaveBeenCalled();
 | 
			
		||||
@ -160,7 +160,7 @@ describe('PopUpLoginService', () => {
 | 
			
		||||
 | 
			
		||||
      spyOnProperty(popupService, 'result$').mockReturnValue(of(popupResult));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        popUpLoginService.loginWithPopUpStandard(config, [config])
 | 
			
		||||
      );
 | 
			
		||||
      expect(checkAuthSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
@ -201,7 +201,7 @@ describe('PopUpLoginService', () => {
 | 
			
		||||
 | 
			
		||||
      spyOnProperty(popupService, 'result$').mockReturnValue(of(popupResult));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        popUpLoginService.loginWithPopUpStandard(config, [config])
 | 
			
		||||
      );
 | 
			
		||||
      expect(checkAuthSpy).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, spyOnProperty } from '@/testing';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { ReplaySubject, firstValueFrom, map, share } from 'rxjs';
 | 
			
		||||
import { type MockInstance, vi } from 'vitest';
 | 
			
		||||
import type { OpenIdConfiguration } from '../../config/openid-configuration';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
@ -18,6 +18,7 @@ describe('PopUpService', () => {
 | 
			
		||||
      providers: [
 | 
			
		||||
        mockProvider(StoragePersistenceService),
 | 
			
		||||
        mockProvider(LoggerService),
 | 
			
		||||
        PopUpService,
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
    storagePersistenceService = TestBed.inject(StoragePersistenceService);
 | 
			
		||||
@ -53,7 +54,11 @@ describe('PopUpService', () => {
 | 
			
		||||
      vi.spyOn(popUpService as any, 'canAccessSessionStorage').mockReturnValue(
 | 
			
		||||
        false
 | 
			
		||||
      );
 | 
			
		||||
      spyOnProperty(popUpService as any, 'windowInternal').mockReturnValue({
 | 
			
		||||
      spyOnProperty(
 | 
			
		||||
        popUpService as any,
 | 
			
		||||
        'windowInternal',
 | 
			
		||||
        'get'
 | 
			
		||||
      ).mockReturnValue({
 | 
			
		||||
        opener: {} as Window,
 | 
			
		||||
      });
 | 
			
		||||
      vi.spyOn(storagePersistenceService, 'read').mockReturnValue({
 | 
			
		||||
@ -113,10 +118,23 @@ describe('PopUpService', () => {
 | 
			
		||||
        receivedUrl: 'some-url1111',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(popUpService.result$);
 | 
			
		||||
      const test$ = popUpService.result$.pipe(
 | 
			
		||||
        map((result) => {
 | 
			
		||||
          expect(result).toBe(popupResult);
 | 
			
		||||
        }),
 | 
			
		||||
        share({
 | 
			
		||||
          connector: () => new ReplaySubject(1),
 | 
			
		||||
          resetOnError: false,
 | 
			
		||||
          resetOnComplete: false,
 | 
			
		||||
          resetOnRefCountZero: true,
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      test$.subscribe();
 | 
			
		||||
 | 
			
		||||
      (popUpService as any).resultInternal$.next(popupResult);
 | 
			
		||||
 | 
			
		||||
      await firstValueFrom(test$);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -183,7 +201,8 @@ describe('PopUpService', () => {
 | 
			
		||||
      let popupResult: PopupResult;
 | 
			
		||||
      let cleanUpSpy: MockInstance;
 | 
			
		||||
 | 
			
		||||
      beforeEach(async () => {
 | 
			
		||||
      beforeEach(() => {
 | 
			
		||||
        vi.useFakeTimers();
 | 
			
		||||
        popup = {
 | 
			
		||||
          closed: false,
 | 
			
		||||
          close: () => undefined,
 | 
			
		||||
@ -195,9 +214,15 @@ describe('PopUpService', () => {
 | 
			
		||||
 | 
			
		||||
        popupResult = {} as PopupResult;
 | 
			
		||||
 | 
			
		||||
        const result = await lastValueFrom(popUpService.result$);
 | 
			
		||||
        popUpService.result$.subscribe((result) => {
 | 
			
		||||
          popupResult = result;
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
      afterEach(() => {
 | 
			
		||||
        vi.useRealTimers();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it('message received with data', async () => {
 | 
			
		||||
        let listener: (event: MessageEvent) => void = () => {
 | 
			
		||||
@ -273,9 +298,18 @@ describe('PopUpService', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('sendMessageToMainWindow', () => {
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
      vi.useFakeTimers({});
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
      vi.useRealTimers();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('does nothing if window.opener is null', async () => {
 | 
			
		||||
      // arrange
 | 
			
		||||
      spyOnProperty(window, 'opener').mockReturnValue(null);
 | 
			
		||||
      spyOnProperty(window, 'opener', 'get', () => null);
 | 
			
		||||
 | 
			
		||||
      const sendMessageSpy = vi.spyOn(popUpService as any, 'sendMessage');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
 | 
			
		||||
import { FlowsDataService } from '../../flows/flows-data.service';
 | 
			
		||||
@ -20,6 +20,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
  let flowsDataService: FlowsDataService;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    vi.useFakeTimers();
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      imports: [],
 | 
			
		||||
      providers: [
 | 
			
		||||
@ -44,6 +45,11 @@ describe('StandardLoginService', () => {
 | 
			
		||||
    flowsDataService = TestBed.inject(FlowsDataService);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    vi.useRealTimers();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(standardLoginService).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
@ -56,7 +62,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
      ).mockReturnValue(false);
 | 
			
		||||
      const loggerSpy = vi.spyOn(loggerService, 'logError');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        standardLoginService.loginStandard({
 | 
			
		||||
          configId: 'configId1',
 | 
			
		||||
        })
 | 
			
		||||
@ -83,7 +89,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
      vi.spyOn(urlService, 'getAuthorizeUrl').mockReturnValue(of('someUrl'));
 | 
			
		||||
      const flowsDataSpy = vi.spyOn(flowsDataService, 'setCodeFlowInProgress');
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        standardLoginService.loginStandard(config)
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -107,7 +113,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
      ).mockReturnValue(of({}));
 | 
			
		||||
      vi.spyOn(urlService, 'getAuthorizeUrl').mockReturnValue(of('someUrl'));
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        standardLoginService.loginStandard(config)
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -131,7 +137,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
      vi.spyOn(urlService, 'getAuthorizeUrl').mockReturnValue(of('someUrl'));
 | 
			
		||||
      const redirectSpy = vi.spyOn(redirectService, 'redirectTo');
 | 
			
		||||
 | 
			
		||||
      standardLoginService.loginStandard(config);
 | 
			
		||||
      await firstValueFrom(standardLoginService.loginStandard(config));
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(0);
 | 
			
		||||
      expect(redirectSpy).toHaveBeenCalledExactlyOnceWith('someUrl');
 | 
			
		||||
    });
 | 
			
		||||
@ -159,7 +165,9 @@ describe('StandardLoginService', () => {
 | 
			
		||||
        spy(url);
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      standardLoginService.loginStandard(config, { urlHandler });
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        standardLoginService.loginStandard(config, { urlHandler })
 | 
			
		||||
      );
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(0);
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('someUrl');
 | 
			
		||||
      expect(redirectSpy).not.toHaveBeenCalled();
 | 
			
		||||
@ -185,7 +193,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
        'resetSilentRenewRunning'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      standardLoginService.loginStandard(config, {});
 | 
			
		||||
      await firstValueFrom(standardLoginService.loginStandard(config, {}));
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(0);
 | 
			
		||||
 | 
			
		||||
      expect(flowsDataSpy).toHaveBeenCalled();
 | 
			
		||||
@ -212,9 +220,11 @@ describe('StandardLoginService', () => {
 | 
			
		||||
        .spyOn(redirectService, 'redirectTo')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        standardLoginService.loginStandard(config, {
 | 
			
		||||
          customParams: { to: 'add', as: 'well' },
 | 
			
		||||
      });
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(0);
 | 
			
		||||
      expect(redirectSpy).toHaveBeenCalledExactlyOnceWith('someUrl');
 | 
			
		||||
      expect(getAuthorizeUrlSpy).toHaveBeenCalledExactlyOnceWith(config, {
 | 
			
		||||
@ -243,7 +253,7 @@ describe('StandardLoginService', () => {
 | 
			
		||||
        .spyOn(redirectService, 'redirectTo')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
 | 
			
		||||
      standardLoginService.loginStandard(config);
 | 
			
		||||
      await firstValueFrom(standardLoginService.loginStandard(config));
 | 
			
		||||
      await vi.advanceTimersByTimeAsync(0);
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, map, shareReplay, switchMap } from 'rxjs';
 | 
			
		||||
import { type Observable, map, of, switchMap } from 'rxjs';
 | 
			
		||||
import type { AuthOptions } from '../../auth-options';
 | 
			
		||||
import { AuthWellKnownService } from '../../config/auth-well-known/auth-well-known.service';
 | 
			
		||||
import type { OpenIdConfiguration } from '../../config/openid-configuration';
 | 
			
		||||
@ -28,7 +28,7 @@ export class StandardLoginService {
 | 
			
		||||
  loginStandard(
 | 
			
		||||
    configuration: OpenIdConfiguration,
 | 
			
		||||
    authOptions?: AuthOptions
 | 
			
		||||
  ): Observable<void> {
 | 
			
		||||
  ): Observable<undefined> {
 | 
			
		||||
    if (
 | 
			
		||||
      !this.responseTypeValidationService.hasConfigValidResponseType(
 | 
			
		||||
        configuration
 | 
			
		||||
@ -36,7 +36,7 @@ export class StandardLoginService {
 | 
			
		||||
    ) {
 | 
			
		||||
      this.loggerService.logError(configuration, 'Invalid response type!');
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
      return of(undefined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.loggerService.logDebug(
 | 
			
		||||
@ -45,7 +45,7 @@ export class StandardLoginService {
 | 
			
		||||
    );
 | 
			
		||||
    this.flowsDataService.setCodeFlowInProgress(configuration);
 | 
			
		||||
 | 
			
		||||
    const result$ = this.authWellKnownService
 | 
			
		||||
    return this.authWellKnownService
 | 
			
		||||
      .queryAndStoreAuthWellKnownEndPoints(configuration)
 | 
			
		||||
      .pipe(
 | 
			
		||||
        switchMap(() => {
 | 
			
		||||
@ -70,12 +70,8 @@ export class StandardLoginService {
 | 
			
		||||
          } else {
 | 
			
		||||
            this.redirectService.redirectTo(url);
 | 
			
		||||
          }
 | 
			
		||||
        }),
 | 
			
		||||
        shareReplay(1)
 | 
			
		||||
          return undefined;
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    result$.subscribe();
 | 
			
		||||
 | 
			
		||||
    return result$;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import type { HttpHeaders } from '@ngify/http';
 | 
			
		||||
import { Observable, lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { Observable, firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../api/data.service';
 | 
			
		||||
import { ResetAuthDataService } from '../flows/reset-auth-data.service';
 | 
			
		||||
@ -26,6 +26,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        LogoffRevocationService,
 | 
			
		||||
        mockProvider(DataService),
 | 
			
		||||
        mockProvider(LoggerService),
 | 
			
		||||
        mockProvider(StoragePersistenceService),
 | 
			
		||||
@ -120,7 +121,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const config = { configId: 'configId1' };
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      const result = await lastValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      const result = await firstValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      expect(result).toEqual({ data: 'anything' });
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -142,7 +143,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
        await firstValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
@ -167,7 +168,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      const res = await firstValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
      expect(res).toEqual({ data: 'anything' });
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -192,7 +193,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      const res = await firstValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
      expect(res).toEqual({ data: 'anything' });
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -219,7 +220,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
        await firstValueFrom(service.revokeAccessToken(config));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -297,7 +298,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const config = { configId: 'configId1' };
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      const result = await lastValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      const result = await firstValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      expect(result).toEqual({ data: 'anything' });
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -319,7 +320,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
        await firstValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
@ -344,7 +345,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      const res = await firstValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
      expect(res).toEqual({ data: 'anything' });
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -369,7 +370,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const res = await lastValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      const res = await firstValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      expect(res).toBeTruthy();
 | 
			
		||||
      expect(res).toEqual({ data: 'anything' });
 | 
			
		||||
      expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -396,7 +397,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
        await firstValueFrom(service.revokeRefreshToken(config));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
@ -419,7 +420,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const result$ = service.logoff(config, [config]);
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(serverStateChangedSpy).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -435,7 +436,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const result$ = service.logoff(config, [config]);
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(redirectSpy).not.toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -461,7 +462,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const result$ = service.logoff(config, [config], { urlHandler });
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(redirectSpy).not.toHaveBeenCalled();
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('someValue');
 | 
			
		||||
      expect(resetAuthorizationDataSpy).toHaveBeenCalled();
 | 
			
		||||
@ -482,7 +483,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const result$ = service.logoff(config, [config]);
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(redirectSpy).toHaveBeenCalledExactlyOnceWith('someValue');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -501,7 +502,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const result$ = service.logoff(config, [config], { logoffMethod: 'GET' });
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(redirectSpy).toHaveBeenCalledExactlyOnceWith('someValue');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -533,7 +534,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(redirectSpy).not.toHaveBeenCalled();
 | 
			
		||||
      expect(postSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'some-url',
 | 
			
		||||
@ -585,7 +586,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Assert
 | 
			
		||||
      await lastValueFrom(result$);
 | 
			
		||||
      await firstValueFrom(result$);
 | 
			
		||||
      expect(redirectSpy).not.toHaveBeenCalled();
 | 
			
		||||
      expect(postSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'some-url',
 | 
			
		||||
@ -647,7 +648,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
        .mockReturnValue(of({ any: 'thing' }));
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      await lastValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      await firstValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      expect(revokeRefreshTokenSpy).toHaveBeenCalled();
 | 
			
		||||
      expect(revokeAccessTokenSpy).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -676,7 +677,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
        await firstValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
@ -700,7 +701,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const config = { configId: 'configId1' };
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      await lastValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      await firstValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      expect(logoffSpy).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -722,7 +723,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      const config = { configId: 'configId1' };
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.logoffAndRevokeTokens(config, [config], { urlHandler })
 | 
			
		||||
      );
 | 
			
		||||
      expect(logoffSpy).toHaveBeenCalledExactlyOnceWith(config, [config], {
 | 
			
		||||
@ -749,7 +750,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
        .mockReturnValue(of({ any: 'thing' }));
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      await lastValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      await firstValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      expect(revokeRefreshTokenSpy).not.toHaveBeenCalled();
 | 
			
		||||
      expect(revokeAccessTokenSpy).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -774,7 +775,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
 | 
			
		||||
      // Act
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
        await firstValueFrom(service.logoffAndRevokeTokens(config, [config]));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(loggerSpy).toHaveBeenCalled();
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
@ -798,7 +799,7 @@ describe('Logout and Revoke Service', () => {
 | 
			
		||||
      // Assert
 | 
			
		||||
      expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
      expect(checkSessionServiceSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
      expect(resetAuthorizationDataSpy).toBeCalledWith([
 | 
			
		||||
      expect(resetAuthorizationDataSpy.mock.calls).toEqual([
 | 
			
		||||
        [allConfigs[0]!, allConfigs],
 | 
			
		||||
        [allConfigs[1], allConfigs],
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, spyOnProperty } from '@/testing';
 | 
			
		||||
import { Observable, lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { Observable, firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { type MockInstance, vi } from 'vitest';
 | 
			
		||||
import { AuthStateService } from './auth-state/auth-state.service';
 | 
			
		||||
import { CheckAuthService } from './auth-state/check-auth.service';
 | 
			
		||||
@ -89,36 +89,38 @@ describe('OidcSecurityService', () => {
 | 
			
		||||
    expect(oidcSecurityService).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('userData$', () => {
 | 
			
		||||
    it('calls userService.userData$', async () => {
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.userData$);
 | 
			
		||||
      // 1x from this subscribe
 | 
			
		||||
      // 1x by the signal property
 | 
			
		||||
      expect(userDataSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  // without signal
 | 
			
		||||
  // describe('userData$', () => {
 | 
			
		||||
  //   it('calls userService.userData$', async () => {
 | 
			
		||||
  //     await firstValueFrom(oidcSecurityService.userData());
 | 
			
		||||
  //     // 1x from this subscribe
 | 
			
		||||
  //     // 1x by the signal property
 | 
			
		||||
  //     expect(userDataSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
  //   });
 | 
			
		||||
  // });
 | 
			
		||||
 | 
			
		||||
  describe('userData', () => {
 | 
			
		||||
    it('calls userService.userData$', async () => {
 | 
			
		||||
      const _userdata = await lastValueFrom(oidcSecurityService.userData());
 | 
			
		||||
      const _userdata = await firstValueFrom(oidcSecurityService.userData$);
 | 
			
		||||
 | 
			
		||||
      expect(userDataSpy).toHaveBeenCalledTimes(1);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('isAuthenticated$', () => {
 | 
			
		||||
    it('calls authStateService.isAuthenticated$', async () => {
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.isAuthenticated$);
 | 
			
		||||
      // 1x from this subscribe
 | 
			
		||||
      // 1x by the signal property
 | 
			
		||||
      expect(authenticatedSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  // describe('isAuthenticated$', () => {
 | 
			
		||||
  //   it('calls authStateService.isAuthenticated$', async () => {
 | 
			
		||||
  //     await firstValueFrom(oidcSecurityService.isAuthenticated());
 | 
			
		||||
  //     // 1x from this subscribe
 | 
			
		||||
  //     // 1x by the signal property
 | 
			
		||||
  //     expect(authenticatedSpy).toHaveBeenCalledTimes(2);
 | 
			
		||||
  //   });
 | 
			
		||||
  // });
 | 
			
		||||
 | 
			
		||||
  // without signal
 | 
			
		||||
  describe('authenticated', () => {
 | 
			
		||||
    it('calls authStateService.isAuthenticated$', async () => {
 | 
			
		||||
      const _authenticated = await lastValueFrom(
 | 
			
		||||
        oidcSecurityService.authenticated()
 | 
			
		||||
      const _authenticated = await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.isAuthenticated$
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(authenticatedSpy).toHaveBeenCalledTimes(1);
 | 
			
		||||
@ -131,19 +133,20 @@ describe('OidcSecurityService', () => {
 | 
			
		||||
        checkSessionService,
 | 
			
		||||
        'checkSessionChanged$'
 | 
			
		||||
      ).mockReturnValue(of(true));
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.checkSessionChanged$);
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.checkSessionChanged$);
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(1);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('stsCallback$', () => {
 | 
			
		||||
    it('calls callbackService.stsCallback$', async () => {
 | 
			
		||||
    it('calls callbackService.stsCallback$', () => {
 | 
			
		||||
      const spy = spyOnProperty(
 | 
			
		||||
        callbackService,
 | 
			
		||||
        'stsCallback$'
 | 
			
		||||
      ).mockReturnValue(of());
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.stsCallback$);
 | 
			
		||||
      oidcSecurityService.stsCallback$.subscribe();
 | 
			
		||||
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(1);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -159,7 +162,7 @@ describe('OidcSecurityService', () => {
 | 
			
		||||
        .spyOn(authWellKnownService, 'queryAndStoreAuthWellKnownEndPoints')
 | 
			
		||||
        .mockReturnValue(of({}));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.preloadAuthWellKnownDocument());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.preloadAuthWellKnownDocument());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -210,7 +213,7 @@ describe('OidcSecurityService', () => {
 | 
			
		||||
          some: 'thing',
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getUserData('configId'));
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getUserData('configId'));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -225,7 +228,9 @@ describe('OidcSecurityService', () => {
 | 
			
		||||
        some: 'thing',
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(oidcSecurityService.getUserData('configId'));
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.getUserData('configId')
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -242,7 +247,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(checkAuthService, 'checkAuth')
 | 
			
		||||
        .mockReturnValue(of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.checkAuth());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.checkAuth());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config], undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -257,7 +262,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(checkAuthService, 'checkAuth')
 | 
			
		||||
        .mockReturnValue(of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.checkAuth('some-url'));
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.checkAuth('some-url'));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config], 'some-url');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -274,7 +279,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(checkAuthService, 'checkAuthMultiple')
 | 
			
		||||
        .mockReturnValue(of([{}] as LoginResponse[]));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.checkAuthMultiple());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.checkAuthMultiple());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith([config], undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -289,8 +294,8 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(checkAuthService, 'checkAuthMultiple')
 | 
			
		||||
        .mockReturnValue(of([{}] as LoginResponse[]));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.checkAuthMultiple('some-url'));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith([config], 'some-u-+rl');
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.checkAuthMultiple('some-url'));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith([config], 'some-url');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -306,7 +311,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(authStateService, 'isAuthenticated')
 | 
			
		||||
        .mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.isAuthenticated());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.isAuthenticated());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -323,7 +328,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(checkAuthService, 'checkAuthIncludingServer')
 | 
			
		||||
        .mockReturnValue(of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.checkAuthIncludingServer());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.checkAuthIncludingServer());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config]);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -340,7 +345,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(authStateService, 'getAccessToken')
 | 
			
		||||
        .mockReturnValue('');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getAccessToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getAccessToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -355,7 +360,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
 | 
			
		||||
      const spy = vi.spyOn(authStateService, 'getIdToken').mockReturnValue('');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getIdToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getIdToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -371,7 +376,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(authStateService, 'getRefreshToken')
 | 
			
		||||
        .mockReturnValue('');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getRefreshToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getRefreshToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -388,7 +393,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(authStateService, 'getAuthenticationResult')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getAuthenticationResult());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getAuthenticationResult());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -405,7 +410,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(tokenHelperService, 'getPayloadFromToken')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getPayloadFromIdToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getPayloadFromIdToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('some-token', false, config);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -420,7 +425,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(tokenHelperService, 'getPayloadFromToken')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getPayloadFromIdToken(true));
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getPayloadFromIdToken(true));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('some-token', true, config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -439,7 +444,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(tokenHelperService, 'getPayloadFromToken')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getPayloadFromAccessToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getPayloadFromAccessToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'some-access-token',
 | 
			
		||||
        false,
 | 
			
		||||
@ -460,7 +465,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(tokenHelperService, 'getPayloadFromToken')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getPayloadFromAccessToken(true));
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getPayloadFromAccessToken(true));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'some-access-token',
 | 
			
		||||
        true,
 | 
			
		||||
@ -478,7 +483,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(flowsDataService, 'setAuthStateControl');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.setState('anyString'));
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.setState('anyString'));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith('anyString', config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -492,7 +497,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(flowsDataService, 'getAuthStateControl');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getState());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getState());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -504,9 +509,11 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
      vi.spyOn(configurationService, 'getOpenIDConfiguration').mockReturnValue(
 | 
			
		||||
        of(config)
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(loginService, 'login');
 | 
			
		||||
      const spy = vi
 | 
			
		||||
        .spyOn(loginService, 'login')
 | 
			
		||||
        .mockReturnValue(of(undefined));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.authorize());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.authorize());
 | 
			
		||||
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, undefined);
 | 
			
		||||
    });
 | 
			
		||||
@ -517,9 +524,11 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
      vi.spyOn(configurationService, 'getOpenIDConfiguration').mockReturnValue(
 | 
			
		||||
        of(config)
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(loginService, 'login');
 | 
			
		||||
      const spy = vi
 | 
			
		||||
        .spyOn(loginService, 'login')
 | 
			
		||||
        .mockReturnValue(of(undefined));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.authorize('configId', {
 | 
			
		||||
          customParams: { some: 'param' },
 | 
			
		||||
        })
 | 
			
		||||
@ -542,7 +551,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(loginService, 'loginWithPopUp')
 | 
			
		||||
        .mockImplementation(() => of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.authorizeWithPopUp());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.authorizeWithPopUp());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        [config],
 | 
			
		||||
@ -564,7 +573,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(refreshSessionService, 'userForceRefreshSession')
 | 
			
		||||
        .mockReturnValue(of({} as LoginResponse));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.forceRefreshSession());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.forceRefreshSession());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config], undefined);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -580,7 +589,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(logoffRevocationService, 'logoffAndRevokeTokens')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.logoffAndRevokeTokens());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.logoffAndRevokeTokens());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config], undefined);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -596,7 +605,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(logoffRevocationService, 'logoff')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.logoff());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.logoff());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config], undefined);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -609,7 +618,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        of({ allConfigs: [config], currentConfig: config })
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(logoffRevocationService, 'logoffLocal');
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.logoffLocal());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.logoffLocal());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, [config]);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -623,7 +632,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
      );
 | 
			
		||||
      const spy = vi.spyOn(logoffRevocationService, 'logoffLocalMultiple');
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.logoffLocalMultiple());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.logoffLocalMultiple());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith([config]);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -639,7 +648,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(logoffRevocationService, 'revokeAccessToken')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.revokeAccessToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.revokeAccessToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -653,7 +662,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(logoffRevocationService, 'revokeAccessToken')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.revokeAccessToken('access_token')
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, 'access_token');
 | 
			
		||||
@ -671,7 +680,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(logoffRevocationService, 'revokeRefreshToken')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.revokeRefreshToken());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.revokeRefreshToken());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -685,7 +694,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(logoffRevocationService, 'revokeRefreshToken')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.revokeRefreshToken('refresh_token')
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, 'refresh_token');
 | 
			
		||||
@ -704,7 +713,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(urlService, 'getEndSessionUrl')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getEndSessionUrl());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getEndSessionUrl());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -719,7 +728,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(urlService, 'getEndSessionUrl')
 | 
			
		||||
        .mockReturnValue(null);
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.getEndSessionUrl({ custom: 'params' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, {
 | 
			
		||||
@ -740,7 +749,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(urlService, 'getAuthorizeUrl')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(oidcSecurityService.getAuthorizeUrl());
 | 
			
		||||
      await firstValueFrom(oidcSecurityService.getAuthorizeUrl());
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -755,7 +764,7 @@ expect(result).toEqual({ some: 'thing' });
 | 
			
		||||
        .spyOn(urlService, 'getAuthorizeUrl')
 | 
			
		||||
        .mockReturnValue(of(null));
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        oidcSecurityService.getAuthorizeUrl({ custom: 'params' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(config, {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import type { Observable } from 'rxjs';
 | 
			
		||||
import { concatMap, map, shareReplay } from 'rxjs/operators';
 | 
			
		||||
import { BehaviorSubject, type Observable } from 'rxjs';
 | 
			
		||||
import { concatMap, map, switchMap } from 'rxjs/operators';
 | 
			
		||||
import type { AuthOptions, LogoutAuthOptions } from './auth-options';
 | 
			
		||||
import type { AuthenticatedResult } from './auth-state/auth-result';
 | 
			
		||||
import { AuthStateService } from './auth-state/auth-state.service';
 | 
			
		||||
@ -20,6 +20,7 @@ import type { PopupOptions } from './login/popup/popup-options';
 | 
			
		||||
import { LogoffRevocationService } from './logoff-revoke/logoff-revocation.service';
 | 
			
		||||
import { UserService } from './user-data/user.service';
 | 
			
		||||
import type { UserDataResult } from './user-data/userdata-result';
 | 
			
		||||
import { MockUtil } from './utils/reflect/index';
 | 
			
		||||
import { TokenHelperService } from './utils/tokenHelper/token-helper.service';
 | 
			
		||||
import { UrlService } from './utils/url/url.service';
 | 
			
		||||
 | 
			
		||||
@ -160,6 +161,8 @@ export class OidcSecurityService {
 | 
			
		||||
   *
 | 
			
		||||
   * @returns An array of `LoginResponse` objects containing all information about the logins
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  @MockUtil({ implementation: () => new BehaviorSubject(undefined) })
 | 
			
		||||
  checkAuthMultiple(url?: string): Observable<LoginResponse[]> {
 | 
			
		||||
    return this.configurationService
 | 
			
		||||
      .getOpenIDConfigurations()
 | 
			
		||||
@ -336,16 +339,11 @@ export class OidcSecurityService {
 | 
			
		||||
   * @param authOptions The custom options for the the authentication request.
 | 
			
		||||
   */
 | 
			
		||||
  authorize(configId?: string, authOptions?: AuthOptions): Observable<void> {
 | 
			
		||||
    const result$ = this.configurationService
 | 
			
		||||
    return this.configurationService
 | 
			
		||||
      .getOpenIDConfiguration(configId)
 | 
			
		||||
      .pipe(
 | 
			
		||||
        map((config) => this.loginService.login(config, authOptions)),
 | 
			
		||||
        shareReplay(1)
 | 
			
		||||
        switchMap((config) => this.loginService.login(config, authOptions))
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    result$.subscribe();
 | 
			
		||||
 | 
			
		||||
    return result$;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@ -458,17 +456,13 @@ export class OidcSecurityService {
 | 
			
		||||
   *
 | 
			
		||||
   * @param configId The configId to perform the action in behalf of. If not passed, the first configs will be taken
 | 
			
		||||
   */
 | 
			
		||||
  logoffLocal(configId?: string): Observable<void> {
 | 
			
		||||
    const result$ = this.configurationService
 | 
			
		||||
      .getOpenIDConfigurations(configId)
 | 
			
		||||
      .pipe(
 | 
			
		||||
        map(({ allConfigs, currentConfig }) =>
 | 
			
		||||
          this.logoffRevocationService.logoffLocal(currentConfig, allConfigs)
 | 
			
		||||
        ),
 | 
			
		||||
        shareReplay(1)
 | 
			
		||||
  logoffLocal(configId?: string): Observable<undefined> {
 | 
			
		||||
    return this.configurationService.getOpenIDConfigurations(configId).pipe(
 | 
			
		||||
      map(({ allConfigs, currentConfig }) => {
 | 
			
		||||
        this.logoffRevocationService.logoffLocal(currentConfig, allConfigs);
 | 
			
		||||
        return undefined;
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    result$.subscribe();
 | 
			
		||||
    return result$;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@ -476,16 +470,13 @@ export class OidcSecurityService {
 | 
			
		||||
   * Use this method if you have _multiple_ configs enabled.
 | 
			
		||||
   */
 | 
			
		||||
  logoffLocalMultiple(): Observable<void> {
 | 
			
		||||
    const result$ = this.configurationService.getOpenIDConfigurations().pipe(
 | 
			
		||||
    return this.configurationService
 | 
			
		||||
      .getOpenIDConfigurations()
 | 
			
		||||
      .pipe(
 | 
			
		||||
        map(({ allConfigs }) =>
 | 
			
		||||
          this.logoffRevocationService.logoffLocalMultiple(allConfigs)
 | 
			
		||||
      ),
 | 
			
		||||
      shareReplay(1)
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    result$.subscribe();
 | 
			
		||||
 | 
			
		||||
    return result$;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
import { TestBed, createSpyObj } from '@/testing';
 | 
			
		||||
import { mockProvider } from '@/testing/mock';
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { mockClass, mockProvider } from '@/testing/mock';
 | 
			
		||||
import { APP_INITIALIZER } from 'oidc-client-rx';
 | 
			
		||||
import { of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { PASSED_CONFIG } from './auth-config';
 | 
			
		||||
import { ConfigurationService } from './config/config.service';
 | 
			
		||||
import {
 | 
			
		||||
@ -60,12 +61,13 @@ describe('provideAuth', () => {
 | 
			
		||||
 | 
			
		||||
  describe('features', () => {
 | 
			
		||||
    let oidcSecurityServiceMock: OidcSecurityService;
 | 
			
		||||
    let spy: any;
 | 
			
		||||
 | 
			
		||||
    beforeEach(async () => {
 | 
			
		||||
      oidcSecurityServiceMock = createSpyObj<OidcSecurityService>(
 | 
			
		||||
        'OidcSecurityService',
 | 
			
		||||
        ['checkAuthMultiple']
 | 
			
		||||
      );
 | 
			
		||||
      //@ts-ignore
 | 
			
		||||
 | 
			
		||||
      oidcSecurityServiceMock = new (mockClass(OidcSecurityService))();
 | 
			
		||||
      spy = vi.spyOn(oidcSecurityServiceMock, 'checkAuthMultiple');
 | 
			
		||||
      await TestBed.configureTestingModule({
 | 
			
		||||
        providers: [
 | 
			
		||||
          provideAuth(
 | 
			
		||||
@ -83,14 +85,11 @@ describe('provideAuth', () => {
 | 
			
		||||
 | 
			
		||||
    it('should provide APP_INITIALIZER config', () => {
 | 
			
		||||
      const config = TestBed.inject(APP_INITIALIZER);
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        config.length,
 | 
			
		||||
        'Expected an APP_INITIALIZER to be registered'
 | 
			
		||||
      ).toBe(1);
 | 
			
		||||
      expect(oidcSecurityServiceMock.checkAuthMultiple).toHaveBeenCalledTimes(
 | 
			
		||||
        1
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalledTimes(1);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import type { Provider } from 'injection-js';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import {
 | 
			
		||||
  PASSED_CONFIG,
 | 
			
		||||
  type PassedInitialConfig,
 | 
			
		||||
@ -65,7 +66,7 @@ export function withAppInitializerAuthCheck(): AuthFeature {
 | 
			
		||||
    ɵproviders: [
 | 
			
		||||
      {
 | 
			
		||||
        provide: APP_INITIALIZER,
 | 
			
		||||
        useFactory: (oidcSecurityService: OidcSecurityService) => () =>
 | 
			
		||||
        useFactory: (oidcSecurityService: OidcSecurityService) =>
 | 
			
		||||
          oidcSecurityService.checkAuthMultiple(),
 | 
			
		||||
        multi: true,
 | 
			
		||||
        deps: [OidcSecurityService],
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { filter } from 'rxjs/operators';
 | 
			
		||||
import { ReplaySubject, firstValueFrom, timer } from 'rxjs';
 | 
			
		||||
import { filter, share } from 'rxjs/operators';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { EventTypes } from './event-types';
 | 
			
		||||
import { PublicEventsService } from './public-events.service';
 | 
			
		||||
@ -20,47 +20,63 @@ describe('Events Service', () => {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('registering to single event with one event emit works', async () => {
 | 
			
		||||
    const firedEvent = await lastValueFrom(eventsService.registerForEvents());
 | 
			
		||||
    eventsService.fireEvent(EventTypes.ConfigLoaded, { myKey: 'myValue' });
 | 
			
		||||
 | 
			
		||||
    const firedEvent = await firstValueFrom(eventsService.registerForEvents());
 | 
			
		||||
    expect(firedEvent).toBeTruthy();
 | 
			
		||||
    expect(firedEvent).toEqual({
 | 
			
		||||
      type: EventTypes.ConfigLoaded,
 | 
			
		||||
      value: { myKey: 'myValue' },
 | 
			
		||||
    });
 | 
			
		||||
    eventsService.fireEvent(EventTypes.ConfigLoaded, { myKey: 'myValue' });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('registering to single event with multiple same event emit works', async () => {
 | 
			
		||||
    const spy = vi.fn()('spy');
 | 
			
		||||
    const spy = vi.fn();
 | 
			
		||||
 | 
			
		||||
    const firedEvent = await lastValueFrom(eventsService.registerForEvents());
 | 
			
		||||
    eventsService.registerForEvents().subscribe((firedEvent) => {
 | 
			
		||||
      spy(firedEvent);
 | 
			
		||||
      expect(firedEvent).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    eventsService.fireEvent(EventTypes.ConfigLoaded, { myKey: 'myValue' });
 | 
			
		||||
    eventsService.fireEvent(EventTypes.ConfigLoaded, { myKey: 'myValue2' });
 | 
			
		||||
 | 
			
		||||
    expect(spy.calls.count()).toBe(2);
 | 
			
		||||
    expect(spy.calls.first().args[0]).toEqual({
 | 
			
		||||
    expect(spy.mock.calls.length).toBe(2);
 | 
			
		||||
    expect(spy.mock.calls[0]?.[0]).toEqual({
 | 
			
		||||
      type: EventTypes.ConfigLoaded,
 | 
			
		||||
      value: { myKey: 'myValue' },
 | 
			
		||||
    });
 | 
			
		||||
    expect(spy.postSpy.mock.calls.at(-1)?.[0]).toEqual({
 | 
			
		||||
    expect(spy.mock.calls.at(-1)?.[0]).toEqual({
 | 
			
		||||
      type: EventTypes.ConfigLoaded,
 | 
			
		||||
      value: { myKey: 'myValue2' },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await firstValueFrom(timer(0));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('registering to single event with multiple emit works', async () => {
 | 
			
		||||
    const firedEvent = await lastValueFrom(
 | 
			
		||||
      eventsService
 | 
			
		||||
        .registerForEvents()
 | 
			
		||||
        .pipe(filter((x) => x.type === EventTypes.ConfigLoaded))
 | 
			
		||||
    const o$ = eventsService.registerForEvents().pipe(
 | 
			
		||||
      filter((x) => x.type === EventTypes.ConfigLoaded),
 | 
			
		||||
      share({
 | 
			
		||||
        connector: () => new ReplaySubject(1),
 | 
			
		||||
        resetOnError: false,
 | 
			
		||||
        resetOnComplete: false,
 | 
			
		||||
        resetOnRefCountZero: true,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    o$.subscribe((firedEvent) => {
 | 
			
		||||
      expect(firedEvent).toBeTruthy();
 | 
			
		||||
      expect(firedEvent).toEqual({
 | 
			
		||||
        type: EventTypes.ConfigLoaded,
 | 
			
		||||
        value: { myKey: 'myValue' },
 | 
			
		||||
      });
 | 
			
		||||
      return firedEvent;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    eventsService.fireEvent(EventTypes.ConfigLoaded, { myKey: 'myValue' });
 | 
			
		||||
    eventsService.fireEvent(EventTypes.NewAuthenticationResult, true);
 | 
			
		||||
 | 
			
		||||
    await firstValueFrom(o$);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ describe('BrowserStorageService', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        BrowserStorageService,
 | 
			
		||||
        mockProvider(LoggerService),
 | 
			
		||||
        {
 | 
			
		||||
          provide: AbstractSecurityStorage,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { inject, Injectable } from 'injection-js';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
import { injectAbstractType } from '../injection';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
import { AbstractSecurityStorage } from './abstract-security-storage';
 | 
			
		||||
 | 
			
		||||
@ -7,7 +8,9 @@ import { AbstractSecurityStorage } from './abstract-security-storage';
 | 
			
		||||
export class BrowserStorageService {
 | 
			
		||||
  private readonly loggerService = inject(LoggerService);
 | 
			
		||||
 | 
			
		||||
  private readonly abstractSecurityStorage = inject(AbstractSecurityStorage);
 | 
			
		||||
  private readonly abstractSecurityStorage = injectAbstractType(
 | 
			
		||||
    AbstractSecurityStorage
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  read(key: string, configuration: OpenIdConfiguration): any {
 | 
			
		||||
    const { configId } = configuration;
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,8 @@ describe('DefaultLocalStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('read', () => {
 | 
			
		||||
    it('should call localstorage.getItem', () => {
 | 
			
		||||
      const spy = vi.spyOn(localStorage, 'getItem');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'getItem');
 | 
			
		||||
 | 
			
		||||
      service.read('henlo');
 | 
			
		||||
 | 
			
		||||
@ -28,7 +29,8 @@ describe('DefaultLocalStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('write', () => {
 | 
			
		||||
    it('should call localstorage.setItem', () => {
 | 
			
		||||
      const spy = vi.spyOn(localStorage, 'setItem');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'setItem');
 | 
			
		||||
 | 
			
		||||
      service.write('henlo', 'furiend');
 | 
			
		||||
 | 
			
		||||
@ -38,7 +40,8 @@ describe('DefaultLocalStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('remove', () => {
 | 
			
		||||
    it('should call localstorage.removeItem', () => {
 | 
			
		||||
      const spy = vi.spyOn(localStorage, 'removeItem');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'removeItem');
 | 
			
		||||
 | 
			
		||||
      service.remove('henlo');
 | 
			
		||||
 | 
			
		||||
@ -48,7 +51,8 @@ describe('DefaultLocalStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('clear', () => {
 | 
			
		||||
    it('should call localstorage.clear', () => {
 | 
			
		||||
      const spy = vi.spyOn(localStorage, 'clear');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'clear');
 | 
			
		||||
 | 
			
		||||
      service.clear();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,8 @@ describe('DefaultSessionStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('read', () => {
 | 
			
		||||
    it('should call sessionstorage.getItem', () => {
 | 
			
		||||
      const spy = vi.spyOn(sessionStorage, 'getItem');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'getItem');
 | 
			
		||||
 | 
			
		||||
      service.read('henlo');
 | 
			
		||||
 | 
			
		||||
@ -28,7 +29,8 @@ describe('DefaultSessionStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('write', () => {
 | 
			
		||||
    it('should call sessionstorage.setItem', () => {
 | 
			
		||||
      const spy = vi.spyOn(sessionStorage, 'setItem');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'setItem');
 | 
			
		||||
 | 
			
		||||
      service.write('henlo', 'furiend');
 | 
			
		||||
 | 
			
		||||
@ -38,7 +40,8 @@ describe('DefaultSessionStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('remove', () => {
 | 
			
		||||
    it('should call sessionstorage.removeItem', () => {
 | 
			
		||||
      const spy = vi.spyOn(sessionStorage, 'removeItem');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'removeItem');
 | 
			
		||||
 | 
			
		||||
      service.remove('henlo');
 | 
			
		||||
 | 
			
		||||
@ -48,7 +51,8 @@ describe('DefaultSessionStorageService', () => {
 | 
			
		||||
 | 
			
		||||
  describe('clear', () => {
 | 
			
		||||
    it('should call sessionstorage.clear', () => {
 | 
			
		||||
      const spy = vi.spyOn(sessionStorage, 'clear');
 | 
			
		||||
      // https://github.com/jsdom/jsdom/issues/2318
 | 
			
		||||
      const spy = vi.spyOn(Storage.prototype, 'clear');
 | 
			
		||||
 | 
			
		||||
      service.clear();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { mockProvider } from '../testing/mock';
 | 
			
		||||
import { BrowserStorageService } from './browser-storage.service';
 | 
			
		||||
@ -10,7 +10,10 @@ describe('Storage Persistence Service', () => {
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [mockProvider(BrowserStorageService)],
 | 
			
		||||
      providers: [
 | 
			
		||||
        StoragePersistenceService,
 | 
			
		||||
        mockProvider(BrowserStorageService),
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
    service = TestBed.inject(StoragePersistenceService);
 | 
			
		||||
    securityStorage = TestBed.inject(BrowserStorageService);
 | 
			
		||||
@ -239,10 +242,16 @@ describe('Storage Persistence Service', () => {
 | 
			
		||||
      };
 | 
			
		||||
      const spy = vi.spyOn(securityStorage, 'read');
 | 
			
		||||
 | 
			
		||||
      spy
 | 
			
		||||
        .withArgs('reusable_refresh_token', config)
 | 
			
		||||
        .mockReturnValue(returnValue);
 | 
			
		||||
      spy.withArgs('authnResult', config).mockReturnValue(undefined);
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        spy,
 | 
			
		||||
        ['reusable_refresh_token', config],
 | 
			
		||||
        () => returnValue
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        spy,
 | 
			
		||||
        ['authnResult', config],
 | 
			
		||||
        () => undefined
 | 
			
		||||
      );
 | 
			
		||||
      const result = service.getRefreshToken(config);
 | 
			
		||||
 | 
			
		||||
      expect(result).toBe(returnValue.reusable_refresh_token);
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@ import { vi } from 'vitest';
 | 
			
		||||
 | 
			
		||||
// Create retriable observable stream to test retry / retryWhen. Credits to:
 | 
			
		||||
// https://stackoverflow.com/questions/51399819/how-to-create-a-mock-observable-to-test-http-rxjs-retry-retrywhen-in-angular
 | 
			
		||||
export const createRetriableStream = (...resp$: any): Observable<any> => {
 | 
			
		||||
  const fetchData = vi.fn()('fetchData');
 | 
			
		||||
export const createRetriableStream = (...resp$: any[]): Observable<any> => {
 | 
			
		||||
  const fetchData = vi.fn();
 | 
			
		||||
 | 
			
		||||
  fetchData.mockReturnValues(...resp$);
 | 
			
		||||
  for (const r of resp$) {
 | 
			
		||||
    fetchData.mockReturnValueOnce(r);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return of(null).pipe(switchMap((_) => fetchData()));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,11 @@
 | 
			
		||||
import { getTestBed } from '@/testing/testbed';
 | 
			
		||||
import {
 | 
			
		||||
  BrowserDynamicTestingModule,
 | 
			
		||||
  platformBrowserDynamicTesting,
 | 
			
		||||
} from '@angular/platform-browser-dynamic/testing';
 | 
			
		||||
import { TestBed } from '@/testing/testbed';
 | 
			
		||||
import { DOCUMENT } from 'oidc-client-rx/dom';
 | 
			
		||||
import 'reflect-metadata';
 | 
			
		||||
 | 
			
		||||
// First, initialize the Angular testing environment.
 | 
			
		||||
getTestBed().initTestEnvironment(
 | 
			
		||||
  BrowserDynamicTestingModule,
 | 
			
		||||
  platformBrowserDynamicTesting(),
 | 
			
		||||
TestBed.initTestEnvironment([
 | 
			
		||||
  {
 | 
			
		||||
    teardown: { destroyAfterEach: false },
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
    provide: DOCUMENT,
 | 
			
		||||
    useValue: document,
 | 
			
		||||
  },
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,10 @@
 | 
			
		||||
import type { Provider } from 'injection-js';
 | 
			
		||||
 | 
			
		||||
export function mockClass<T>(obj: new (...args: any[]) => T): any {
 | 
			
		||||
export function mockClass<T>(
 | 
			
		||||
  obj: new (...args: any[]) => T
 | 
			
		||||
): new (
 | 
			
		||||
  ...args: any[]
 | 
			
		||||
) => T {
 | 
			
		||||
  const keys = Object.getOwnPropertyNames(obj.prototype);
 | 
			
		||||
  const allMethods = keys.filter((key) => {
 | 
			
		||||
    try {
 | 
			
		||||
@ -14,9 +18,16 @@ export function mockClass<T>(obj: new (...args: any[]) => T): any {
 | 
			
		||||
  const mockedClass = class T {};
 | 
			
		||||
 | 
			
		||||
  for (const method of allMethods) {
 | 
			
		||||
    (mockedClass.prototype as any)[method] = (): void => {
 | 
			
		||||
    const mockImplementation = Reflect.getMetadata(
 | 
			
		||||
      'mock:implementation',
 | 
			
		||||
      obj.prototype,
 | 
			
		||||
      method
 | 
			
		||||
    );
 | 
			
		||||
    (mockedClass.prototype as any)[method] =
 | 
			
		||||
      mockImplementation ??
 | 
			
		||||
      ((): any => {
 | 
			
		||||
        return;
 | 
			
		||||
    };
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const method of allProperties) {
 | 
			
		||||
@ -28,12 +39,15 @@ export function mockClass<T>(obj: new (...args: any[]) => T): any {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return mockedClass;
 | 
			
		||||
  return mockedClass as any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function mockProvider<T>(obj: new (...args: any[]) => T): Provider {
 | 
			
		||||
export function mockProvider<T>(
 | 
			
		||||
  obj: new (...args: any[]) => T,
 | 
			
		||||
  token?: any
 | 
			
		||||
): Provider {
 | 
			
		||||
  return {
 | 
			
		||||
    provide: obj,
 | 
			
		||||
    provide: token ?? obj,
 | 
			
		||||
    useClass: mockClass(obj),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,23 +1,31 @@
 | 
			
		||||
import type { Provider } from 'injection-js';
 | 
			
		||||
import { JSDOM } from 'jsdom';
 | 
			
		||||
import { AbstractRouter, type Navigation, type UrlTree } from 'oidc-client-rx';
 | 
			
		||||
 | 
			
		||||
export class MockRouter extends AbstractRouter {
 | 
			
		||||
  dom = new JSDOM('', {
 | 
			
		||||
    url: 'http://localhost',
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  navigation: Navigation = {
 | 
			
		||||
    id: 1,
 | 
			
		||||
    extras: {},
 | 
			
		||||
    initialUrl: new URL('https://localhost/'),
 | 
			
		||||
    extractedUrl: new URL('https://localhost/'),
 | 
			
		||||
    initialUrl: this.parseUrl(this.dom.window.location.href),
 | 
			
		||||
    extractedUrl: this.parseUrl(this.dom.window.location.href),
 | 
			
		||||
    trigger: 'imperative',
 | 
			
		||||
    previousNavigation: null,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  navigateByUrl(url: string): void {
 | 
			
		||||
    const prevNavigation = this.navigation;
 | 
			
		||||
    this.dom.reconfigure({
 | 
			
		||||
      url: new URL(url, this.dom.window.location.href).href,
 | 
			
		||||
    });
 | 
			
		||||
    this.navigation = {
 | 
			
		||||
      id: prevNavigation.id + 1,
 | 
			
		||||
      extras: {},
 | 
			
		||||
      initialUrl: prevNavigation.initialUrl,
 | 
			
		||||
      extractedUrl: new URL(url),
 | 
			
		||||
      extractedUrl: this.parseUrl(this.dom.window.location.href),
 | 
			
		||||
      trigger: prevNavigation.trigger,
 | 
			
		||||
      previousNavigation: prevNavigation,
 | 
			
		||||
    };
 | 
			
		||||
@ -26,7 +34,8 @@ export class MockRouter extends AbstractRouter {
 | 
			
		||||
    return this.navigation;
 | 
			
		||||
  }
 | 
			
		||||
  parseUrl(url: string): UrlTree {
 | 
			
		||||
    return new URL(url);
 | 
			
		||||
    const u = new URL(url, this.dom.window.location.href);
 | 
			
		||||
    return `${u.pathname}${u.search}${u.hash}`;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,20 +35,80 @@ export function mockImplementationWhenArgsEqual<M extends MockInstance<any>>(
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function mockImplementationWhenArgs<M extends MockInstance<any>>(
 | 
			
		||||
  mockInstance: M,
 | 
			
		||||
  whenArgs: (
 | 
			
		||||
    ...args: Parameters<M extends MockInstance<infer T> ? T : never>
 | 
			
		||||
  ) => boolean,
 | 
			
		||||
  implementation: Exclude<ReturnType<M['getMockImplementation']>, undefined>
 | 
			
		||||
): M {
 | 
			
		||||
  const spyImpl = mockInstance.getMockImplementation()!;
 | 
			
		||||
type Procedure = (...args: any[]) => any;
 | 
			
		||||
type Methods<T> = keyof {
 | 
			
		||||
  [K in keyof T as T[K] extends Procedure ? K : never]: T[K];
 | 
			
		||||
};
 | 
			
		||||
type Classes<T> = {
 | 
			
		||||
  [K in keyof T]: T[K] extends new (...args: any[]) => any ? K : never;
 | 
			
		||||
}[keyof T] &
 | 
			
		||||
  (string | symbol);
 | 
			
		||||
 | 
			
		||||
export type MockInstanceWithOrigin<M extends Procedure> = MockInstance<M> & {
 | 
			
		||||
  getOriginImplementation?: () => any;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function spyOnWithOrigin<
 | 
			
		||||
  T,
 | 
			
		||||
  M extends Classes<Required<T>> | Methods<Required<T>>,
 | 
			
		||||
>(
 | 
			
		||||
  obj: T,
 | 
			
		||||
  methodName: M
 | 
			
		||||
): Required<T>[M] extends {
 | 
			
		||||
  new (...args: infer A): infer R;
 | 
			
		||||
}
 | 
			
		||||
  ? MockInstanceWithOrigin<(this: R, ...args: A) => R>
 | 
			
		||||
  : T[M] extends Procedure
 | 
			
		||||
    ? MockInstanceWithOrigin<T[M]>
 | 
			
		||||
    : never {
 | 
			
		||||
  let currentObj = obj;
 | 
			
		||||
  let origin:
 | 
			
		||||
    | (Required<T>[M] extends {
 | 
			
		||||
        new (...args: infer A): infer R;
 | 
			
		||||
      }
 | 
			
		||||
        ? (this: R, ...args: A) => R
 | 
			
		||||
        : T[M] extends Procedure
 | 
			
		||||
          ? T[M]
 | 
			
		||||
          : never)
 | 
			
		||||
    | undefined;
 | 
			
		||||
  while (currentObj) {
 | 
			
		||||
    origin = currentObj[methodName] as any;
 | 
			
		||||
    if (origin) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    currentObj = Object.getPrototypeOf(currentObj);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const spy = vi.spyOn(obj, methodName as any) as Required<T>[M] extends {
 | 
			
		||||
    new (...args: infer A): infer R;
 | 
			
		||||
  }
 | 
			
		||||
    ? MockInstanceWithOrigin<(this: R, ...args: A) => R>
 | 
			
		||||
    : T[M] extends Procedure
 | 
			
		||||
      ? MockInstanceWithOrigin<T[M]>
 | 
			
		||||
      : never;
 | 
			
		||||
 | 
			
		||||
  spy.getOriginImplementation = () => origin;
 | 
			
		||||
 | 
			
		||||
  return spy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function mockImplementationWhenArgs<T extends Procedure = Procedure>(
 | 
			
		||||
  mockInstance: MockInstance<T> & { getOriginImplementation?: () => T },
 | 
			
		||||
  whenArgs: (...args: Parameters<T>) => boolean,
 | 
			
		||||
  implementation: T
 | 
			
		||||
): MockInstance<T> {
 | 
			
		||||
  const spyImpl =
 | 
			
		||||
    mockInstance.getMockImplementation() ??
 | 
			
		||||
    mockInstance.getOriginImplementation?.();
 | 
			
		||||
 | 
			
		||||
  return mockInstance.mockImplementation((...args) => {
 | 
			
		||||
    if (isEqual(args, whenArgs)) {
 | 
			
		||||
    if (whenArgs(...args)) {
 | 
			
		||||
      return implementation(...args);
 | 
			
		||||
    }
 | 
			
		||||
    return spyImpl?.(...args);
 | 
			
		||||
    if (spyImpl) {
 | 
			
		||||
      return spyImpl(...args);
 | 
			
		||||
    }
 | 
			
		||||
    throw new Error('Mock implementation not defined for these arguments.');
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -58,46 +118,37 @@ export function mockImplementationWhenArgs<M extends MockInstance<any>>(
 | 
			
		||||
export function spyOnProperty<T, K extends keyof T>(
 | 
			
		||||
  obj: T,
 | 
			
		||||
  propertyKey: K,
 | 
			
		||||
  accessType: 'get' | 'set' = 'get',
 | 
			
		||||
  mockImplementation?: any
 | 
			
		||||
  accessType: 'get' | 'set' = 'get'
 | 
			
		||||
) {
 | 
			
		||||
  const originalDescriptor = Object.getOwnPropertyDescriptor(obj, propertyKey);
 | 
			
		||||
 | 
			
		||||
  if (!originalDescriptor) {
 | 
			
		||||
    throw new Error(
 | 
			
		||||
      `Property ${String(propertyKey)} does not exist on the object.`
 | 
			
		||||
    );
 | 
			
		||||
  const ownDescriptor = Object.getOwnPropertyDescriptor(obj, propertyKey);
 | 
			
		||||
  let finalDescriptor: PropertyDescriptor | undefined;
 | 
			
		||||
  let currentObj = obj;
 | 
			
		||||
  while (currentObj) {
 | 
			
		||||
    finalDescriptor = Object.getOwnPropertyDescriptor(currentObj, propertyKey);
 | 
			
		||||
    if (finalDescriptor) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    currentObj = Object.getPrototypeOf(currentObj);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const spy = vi.fn();
 | 
			
		||||
 | 
			
		||||
  let value: T[K] | undefined;
 | 
			
		||||
 | 
			
		||||
  if (accessType === 'get') {
 | 
			
		||||
    Object.defineProperty(obj, propertyKey, {
 | 
			
		||||
      get: mockImplementation
 | 
			
		||||
        ? () => {
 | 
			
		||||
            value = mockImplementation();
 | 
			
		||||
            return value;
 | 
			
		||||
          }
 | 
			
		||||
        : spy,
 | 
			
		||||
      get: spy,
 | 
			
		||||
      configurable: true,
 | 
			
		||||
    });
 | 
			
		||||
  } else if (accessType === 'set') {
 | 
			
		||||
    Object.defineProperty(obj, propertyKey, {
 | 
			
		||||
      set: mockImplementation
 | 
			
		||||
        ? (next) => {
 | 
			
		||||
            value = next;
 | 
			
		||||
          }
 | 
			
		||||
        : spy,
 | 
			
		||||
      set: spy,
 | 
			
		||||
      configurable: true,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 恢复原始属性
 | 
			
		||||
  spy.mockRestore = () => {
 | 
			
		||||
    if (originalDescriptor) {
 | 
			
		||||
      Object.defineProperty(obj, propertyKey, originalDescriptor);
 | 
			
		||||
    if (ownDescriptor) {
 | 
			
		||||
      Object.defineProperty(obj, propertyKey, ownDescriptor);
 | 
			
		||||
    } else {
 | 
			
		||||
      delete obj[propertyKey];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -13,21 +13,33 @@ export interface TestModuleMetadata {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class TestBed {
 | 
			
		||||
  static environmentInjector?: Injector;
 | 
			
		||||
  private injector: ReflectiveInjector;
 | 
			
		||||
  private providers: Provider[] = [];
 | 
			
		||||
  private imports: Injector[] = [];
 | 
			
		||||
 | 
			
		||||
  constructor(metadata: TestModuleMetadata = {}) {
 | 
			
		||||
  constructor(
 | 
			
		||||
    metadata: TestModuleMetadata = {},
 | 
			
		||||
    environmentInjector?: Injector
 | 
			
		||||
  ) {
 | 
			
		||||
    const providers = metadata.providers ?? [];
 | 
			
		||||
    const imports = metadata.imports ?? [];
 | 
			
		||||
    this.injector = ReflectiveInjector.resolveAndCreate(providers);
 | 
			
		||||
    this.injector = ReflectiveInjector.resolveAndCreate(
 | 
			
		||||
      providers,
 | 
			
		||||
      environmentInjector
 | 
			
		||||
    );
 | 
			
		||||
    this.imports = imports.map((importFn) => importFn(this.injector));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static #instance?: TestBed;
 | 
			
		||||
 | 
			
		||||
  static initTestEnvironment(providers: Provider[] = []) {
 | 
			
		||||
    TestBed.environmentInjector =
 | 
			
		||||
      ReflectiveInjector.resolveAndCreate(providers);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static configureTestingModule(metadata: TestModuleMetadata = {}) {
 | 
			
		||||
    const newTestBed = new TestBed(metadata);
 | 
			
		||||
    const newTestBed = new TestBed(metadata, TestBed.environmentInjector);
 | 
			
		||||
    TestBed.#instance = newTestBed;
 | 
			
		||||
 | 
			
		||||
    return newTestBed;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { Observable, lastValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { Observable, firstValueFrom, of, throwError } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { DataService } from '../api/data.service';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
@ -30,6 +30,7 @@ describe('User Service', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        UserService,
 | 
			
		||||
        mockProvider(StoragePersistenceService),
 | 
			
		||||
        mockProvider(LoggerService),
 | 
			
		||||
        mockProvider(DataService),
 | 
			
		||||
@ -70,7 +71,7 @@ describe('User Service', () => {
 | 
			
		||||
        userDataInstore
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const token = await lastValueFrom(
 | 
			
		||||
      const token = await firstValueFrom(
 | 
			
		||||
        userService.getAndPersistUserDataInStore(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -99,7 +100,7 @@ describe('User Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
      vi.spyOn(userService, 'setUserDataToStore');
 | 
			
		||||
 | 
			
		||||
      const token = await lastValueFrom(
 | 
			
		||||
      const token = await firstValueFrom(
 | 
			
		||||
        userService.getAndPersistUserDataInStore(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -129,7 +130,7 @@ describe('User Service', () => {
 | 
			
		||||
        userDataInstore
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const token = await lastValueFrom(
 | 
			
		||||
      const token = await firstValueFrom(
 | 
			
		||||
        userService.getAndPersistUserDataInStore(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -161,7 +162,7 @@ describe('User Service', () => {
 | 
			
		||||
        .spyOn(userService as any, 'getIdentityUserData')
 | 
			
		||||
        .mockReturnValue(of(userDataFromSts));
 | 
			
		||||
 | 
			
		||||
      const token = await lastValueFrom(
 | 
			
		||||
      const token = await firstValueFrom(
 | 
			
		||||
        userService.getAndPersistUserDataInStore(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -202,7 +203,7 @@ describe('User Service', () => {
 | 
			
		||||
        'accessToken'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const token = await lastValueFrom(
 | 
			
		||||
      const token = await firstValueFrom(
 | 
			
		||||
        userService.getAndPersistUserDataInStore(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -246,7 +247,7 @@ describe('User Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(
 | 
			
		||||
        await firstValueFrom(
 | 
			
		||||
          userService.getAndPersistUserDataInStore(
 | 
			
		||||
            config,
 | 
			
		||||
            [config],
 | 
			
		||||
@ -283,7 +284,7 @@ describe('User Service', () => {
 | 
			
		||||
        .spyOn(userService as any, 'getIdentityUserData')
 | 
			
		||||
        .mockReturnValue(of(userDataFromSts));
 | 
			
		||||
 | 
			
		||||
      const token = await lastValueFrom(
 | 
			
		||||
      const token = await firstValueFrom(
 | 
			
		||||
        userService.getAndPersistUserDataInStore(
 | 
			
		||||
          config,
 | 
			
		||||
          [config],
 | 
			
		||||
@ -539,7 +540,7 @@ describe('User Service', () => {
 | 
			
		||||
        () => null
 | 
			
		||||
      );
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(serviceAsAny.getIdentityUserData(config));
 | 
			
		||||
        await firstValueFrom(serviceAsAny.getIdentityUserData(config));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
      }
 | 
			
		||||
@ -560,7 +561,7 @@ describe('User Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await lastValueFrom(serviceAsAny.getIdentityUserData(config));
 | 
			
		||||
        await firstValueFrom(serviceAsAny.getIdentityUserData(config));
 | 
			
		||||
      } catch (err: any) {
 | 
			
		||||
        expect(err).toBeTruthy();
 | 
			
		||||
      }
 | 
			
		||||
@ -580,7 +581,7 @@ describe('User Service', () => {
 | 
			
		||||
        () => ({ userInfoEndpoint: 'userInfoEndpoint' })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(serviceAsAny.getIdentityUserData(config));
 | 
			
		||||
      await firstValueFrom(serviceAsAny.getIdentityUserData(config));
 | 
			
		||||
      expect(spy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        'userInfoEndpoint',
 | 
			
		||||
        config,
 | 
			
		||||
@ -607,7 +608,7 @@ describe('User Service', () => {
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const res = await lastValueFrom(
 | 
			
		||||
    const res = await firstValueFrom(
 | 
			
		||||
      (userService as any).getIdentityUserData(config)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -634,7 +635,7 @@ describe('User Service', () => {
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const res = await lastValueFrom(
 | 
			
		||||
    const res = await firstValueFrom(
 | 
			
		||||
      (userService as any).getIdentityUserData(config)
 | 
			
		||||
    );
 | 
			
		||||
    expect(res).toBeTruthy();
 | 
			
		||||
@ -662,7 +663,7 @@ describe('User Service', () => {
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      await lastValueFrom((userService as any).getIdentityUserData(config));
 | 
			
		||||
      await firstValueFrom((userService as any).getIdentityUserData(config));
 | 
			
		||||
    } catch (err: any) {
 | 
			
		||||
      expect(err).toBeTruthy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								src/utils/reflect/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/utils/reflect/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
/// <reference types="reflect-metadata" />
 | 
			
		||||
 | 
			
		||||
// biome-ignore lint/complexity/noBannedTypes: <explanation>
 | 
			
		||||
export function MockUtil<F = Function>(options: { implementation: F }) {
 | 
			
		||||
  return (
 | 
			
		||||
    targetClass: any,
 | 
			
		||||
    propertyKey: string,
 | 
			
		||||
    _descriptor?: TypedPropertyDescriptor<(...args: any[]) => any>
 | 
			
		||||
  ): void => {
 | 
			
		||||
    Reflect?.defineMetadata?.(
 | 
			
		||||
      'mock:implementation',
 | 
			
		||||
      options.implementation,
 | 
			
		||||
      targetClass,
 | 
			
		||||
      propertyKey
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -8,7 +8,7 @@ describe('Token Helper Service', () => {
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [mockProvider(LoggerService)],
 | 
			
		||||
      providers: [TokenHelperService, mockProvider(LoggerService)],
 | 
			
		||||
    });
 | 
			
		||||
    tokenHelperService = TestBed.inject(TokenHelperService);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ describe('CurrentUrlService with existing Url', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        CurrentUrlService,
 | 
			
		||||
        {
 | 
			
		||||
          provide: DOCUMENT,
 | 
			
		||||
          useValue: documentValue,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import type { OpenIdConfiguration } from '../../config/openid-configuration';
 | 
			
		||||
import { FlowsDataService } from '../../flows/flows-data.service';
 | 
			
		||||
@ -1041,14 +1041,14 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
  describe('getAuthorizeUrl', () => {
 | 
			
		||||
    it('returns null if no config is given', async () => {
 | 
			
		||||
      const url = await lastValueFrom(service.getAuthorizeUrl(null));
 | 
			
		||||
      const url = await firstValueFrom(service.getAuthorizeUrl(null));
 | 
			
		||||
      expect(url).toBeNull();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('returns null if current flow is code flow and no redirect url is defined', async () => {
 | 
			
		||||
      vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        service.getAuthorizeUrl({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(result).toBeNull();
 | 
			
		||||
@ -1062,7 +1062,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        redirectUrl: 'some-redirectUrl',
 | 
			
		||||
      } as OpenIdConfiguration;
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(service.getAuthorizeUrl(config));
 | 
			
		||||
      const result = await firstValueFrom(service.getAuthorizeUrl(config));
 | 
			
		||||
      expect(result).toBe('');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1090,7 +1090,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        () => ({ authorizationEndpoint })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(service.getAuthorizeUrl(config));
 | 
			
		||||
      const result = await firstValueFrom(service.getAuthorizeUrl(config));
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        'authorizationEndpoint?client_id=some-clientId&redirect_uri=some-redirectUrl&response_type=testResponseType&scope=testScope&nonce=undefined&state=undefined&code_challenge=some-code-challenge&code_challenge_method=S256'
 | 
			
		||||
      );
 | 
			
		||||
@ -1107,7 +1107,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        'createUrlImplicitFlowAuthorize'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      await lastValueFrom(service.getAuthorizeUrl({ configId: 'configId1' }));
 | 
			
		||||
      await firstValueFrom(service.getAuthorizeUrl({ configId: 'configId1' }));
 | 
			
		||||
      expect(spyCreateUrlCodeFlowAuthorize).not.toHaveBeenCalled();
 | 
			
		||||
      expect(spyCreateUrlImplicitFlowAuthorize).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
@ -1119,18 +1119,20 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        .mockReturnValue('');
 | 
			
		||||
      const resultObs$ = service.getAuthorizeUrl({ configId: 'configId1' });
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
      expect(result).toBe('');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('getRefreshSessionSilentRenewUrl', () => {
 | 
			
		||||
    it('calls createUrlCodeFlowWithSilentRenew if current flow is code flow', () => {
 | 
			
		||||
    it('calls createUrlCodeFlowWithSilentRenew if current flow is code flow', async () => {
 | 
			
		||||
      vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
 | 
			
		||||
      const spy = vi.spyOn(service as any, 'createUrlCodeFlowWithSilentRenew');
 | 
			
		||||
 | 
			
		||||
      service.getRefreshSessionSilentRenewUrl({ configId: 'configId1' });
 | 
			
		||||
      await firstValueFrom(
 | 
			
		||||
        service.getRefreshSessionSilentRenewUrl({ configId: 'configId1' })
 | 
			
		||||
      );
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1159,7 +1161,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        configId: 'configId1',
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(spy).toHaveBeenCalled();
 | 
			
		||||
      expect(result).toBe('');
 | 
			
		||||
    });
 | 
			
		||||
@ -1344,7 +1346,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        redirectUrl: '',
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(null);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1372,7 +1374,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = service.createBodyForParCodeFlowRequest(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256'
 | 
			
		||||
      );
 | 
			
		||||
@ -1402,7 +1404,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = service.createBodyForParCodeFlowRequest(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam'
 | 
			
		||||
      );
 | 
			
		||||
@ -1432,7 +1434,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = service.createBodyForParCodeFlowRequest(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam&any=thing'
 | 
			
		||||
      );
 | 
			
		||||
@ -1466,7 +1468,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        'client_id=testClientId&redirect_uri=testRedirectUrl&response_type=testResponseType&scope=testScope&nonce=testNonce&state=testState&code_challenge=testCodeChallenge&code_challenge_method=S256&hd=testHdParam&any=thing&any=otherThing'
 | 
			
		||||
      );
 | 
			
		||||
@ -1596,7 +1598,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = serviceAsAny.createUrlCodeFlowWithSilentRenew(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe('');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1639,7 +1641,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = serviceAsAny.createUrlCodeFlowWithSilentRenew(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        `authorizationEndpoint?client_id=${clientId}&redirect_uri=http%3A%2F%2Fany-url.com&response_type=${responseType}&scope=${scope}&nonce=${nonce}&state=${state}&prompt=none`
 | 
			
		||||
      );
 | 
			
		||||
@ -1680,7 +1682,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = serviceAsAny.createUrlCodeFlowWithSilentRenew(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe('');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -1796,7 +1798,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = serviceAsAny.createUrlCodeFlowAuthorize(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBeNull();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1838,7 +1840,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = serviceAsAny.createUrlCodeFlowAuthorize(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        `authorizationEndpoint?client_id=clientId&redirect_uri=http%3A%2F%2Fany-url.com&response_type=${responseType}&scope=${scope}&nonce=${nonce}&state=${state}`
 | 
			
		||||
      );
 | 
			
		||||
@ -1887,7 +1889,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
        customParams: { to: 'add', as: 'well' },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe(
 | 
			
		||||
        `authorizationEndpoint?client_id=clientId&redirect_uri=http%3A%2F%2Fany-url.com&response_type=${responseType}&scope=${scope}&nonce=${nonce}&state=${state}&to=add&as=well`
 | 
			
		||||
      );
 | 
			
		||||
@ -1924,7 +1926,7 @@ describe('UrlService Tests', () => {
 | 
			
		||||
 | 
			
		||||
      const resultObs$ = serviceAsAny.createUrlCodeFlowAuthorize(config);
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(resultObs$);
 | 
			
		||||
      const result = await firstValueFrom(resultObs$);
 | 
			
		||||
      expect(result).toBe('');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import { HttpParams } from '@ngify/http';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { type Observable, of } from 'rxjs';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import type { AuthOptions } from '../../auth-options';
 | 
			
		||||
import type { OpenIdConfiguration } from '../../config/openid-configuration';
 | 
			
		||||
import { FlowsDataService } from '../../flows/flows-data.service';
 | 
			
		||||
import { HttpParams } from '../../http';
 | 
			
		||||
import { LoggerService } from '../../logging/logger.service';
 | 
			
		||||
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
 | 
			
		||||
import { JwtWindowCryptoService } from '../../validation/jwt-window-crypto.service';
 | 
			
		||||
@ -873,8 +873,8 @@ export class UrlService {
 | 
			
		||||
  private createHttpParams(existingParams?: string): HttpParams {
 | 
			
		||||
    existingParams = existingParams ?? '';
 | 
			
		||||
 | 
			
		||||
    // @TODO @ngify/http
 | 
			
		||||
    return new HttpParams(existingParams || undefined, {
 | 
			
		||||
    return new HttpParams({
 | 
			
		||||
      fromString: existingParams,
 | 
			
		||||
      encoder: new UriEncoder(),
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom } from 'rxjs';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { CryptoService } from '../utils/crypto/crypto.service';
 | 
			
		||||
import { JwtWindowCryptoService } from './jwt-window-crypto.service';
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ describe('JwtWindowCryptoService', () => {
 | 
			
		||||
        '44445543344242132145455aaabbdc3b4'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const value = await lastValueFrom(observable);
 | 
			
		||||
      const value = await firstValueFrom(observable);
 | 
			
		||||
      expect(value).toBe(outcome);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,14 @@
 | 
			
		||||
import { inject, Injectable } from 'injection-js';
 | 
			
		||||
import { from, type Observable } from 'rxjs';
 | 
			
		||||
import { Injectable, inject } from 'injection-js';
 | 
			
		||||
import { BehaviorSubject, type Observable, from } from 'rxjs';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import { CryptoService } from '../utils/crypto/crypto.service';
 | 
			
		||||
import { MockUtil } from '../utils/reflect';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class JwtWindowCryptoService {
 | 
			
		||||
  private readonly cryptoService = inject(CryptoService);
 | 
			
		||||
 | 
			
		||||
  @MockUtil({ implementation: () => new BehaviorSubject(undefined) })
 | 
			
		||||
  generateCodeChallenge(codeVerifier: string): Observable<string> {
 | 
			
		||||
    return this.calcHash(codeVerifier).pipe(
 | 
			
		||||
      map((challengeRaw: string) => this.base64UrlEncode(challengeRaw))
 | 
			
		||||
 | 
			
		||||
@ -2,22 +2,12 @@ import { ValidationResult } from './validation-result';
 | 
			
		||||
 | 
			
		||||
export class StateValidationResult {
 | 
			
		||||
  constructor(
 | 
			
		||||
    // biome-ignore lint/style/noParameterProperties: <explanation>
 | 
			
		||||
    // biome-ignore lint/nursery/useConsistentMemberAccessibility: <explanation>
 | 
			
		||||
    public accessToken = '',
 | 
			
		||||
    // biome-ignore lint/style/noParameterProperties: <explanation>
 | 
			
		||||
    // biome-ignore lint/nursery/useConsistentMemberAccessibility: <explanation>
 | 
			
		||||
    public idToken = '',
 | 
			
		||||
    // biome-ignore lint/style/noParameterProperties: <explanation>
 | 
			
		||||
    // biome-ignore lint/nursery/useConsistentMemberAccessibility: <explanation>
 | 
			
		||||
    public authResponseIsValid = false,
 | 
			
		||||
    // biome-ignore lint/style/noParameterProperties: <explanation>
 | 
			
		||||
    // biome-ignore lint/nursery/useConsistentMemberAccessibility: <explanation>
 | 
			
		||||
    public decodedIdToken: any = {
 | 
			
		||||
      at_hash: '',
 | 
			
		||||
    },
 | 
			
		||||
    // biome-ignore lint/style/noParameterProperties: <explanation>
 | 
			
		||||
    // biome-ignore lint/nursery/useConsistentMemberAccessibility: <explanation>
 | 
			
		||||
    public state: ValidationResult = ValidationResult.NotSet
 | 
			
		||||
  ) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import {
 | 
			
		||||
  mockImplementationWhenArgs,
 | 
			
		||||
  mockImplementationWhenArgsEqual,
 | 
			
		||||
} from '@/testing/spy';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import type { AuthWellKnownEndpoints } from '../config/auth-well-known/auth-well-known-endpoints';
 | 
			
		||||
import type { OpenIdConfiguration } from '../config/openid-configuration';
 | 
			
		||||
@ -28,6 +32,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        StateValidationService,
 | 
			
		||||
        mockProvider(StoragePersistenceService),
 | 
			
		||||
        mockProvider(TokenValidationService),
 | 
			
		||||
        mockProvider(LoggerService),
 | 
			
		||||
@ -687,8 +692,8 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const isValid = await lastValueFrom(isValidObs$);
 | 
			
		||||
      expect(isValid.authResponseIsValid).toBe(false);
 | 
			
		||||
      const isValid = await firstValueFrom(isValidObs$);
 | 
			
		||||
      expect(isValid.authResponseIsValid).toBeFalsy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should return invalid context error', async () => {
 | 
			
		||||
@ -723,7 +728,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const isValid = await lastValueFrom(isValidObs$);
 | 
			
		||||
      const isValid = await firstValueFrom(isValidObs$);
 | 
			
		||||
      expect(isValid.authResponseIsValid).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -787,13 +792,23 @@ describe('State Validation Service', () => {
 | 
			
		||||
      ).mockReturnValue(false);
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
@ -818,7 +833,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback id token expired'
 | 
			
		||||
@ -832,12 +847,18 @@ describe('State Validation Service', () => {
 | 
			
		||||
    it('should return invalid result if validateStateFromHashCallback is false', async () => {
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      vi.spyOn(
 | 
			
		||||
        tokenValidationService,
 | 
			
		||||
        'validateStateFromHashCallback'
 | 
			
		||||
@ -870,7 +891,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        tokenValidationService.validateStateFromHashCallback
 | 
			
		||||
      ).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback incorrect state'
 | 
			
		||||
@ -940,13 +961,23 @@ describe('State Validation Service', () => {
 | 
			
		||||
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const callbackContext = {
 | 
			
		||||
        code: 'fdffsdfsdf',
 | 
			
		||||
@ -967,7 +998,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(state.accessToken).toBe('access_tokenTEST');
 | 
			
		||||
      expect(state.idToken).toBe('id_tokenTEST');
 | 
			
		||||
      expect(state.decodedIdToken).toBe('decoded_id_token');
 | 
			
		||||
@ -990,12 +1021,16 @@ describe('State Validation Service', () => {
 | 
			
		||||
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      const logDebugSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logDebug')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1020,8 +1055,8 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      expect(logDebugSpy).toBeCalledWith([
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logDebugSpy.mock.calls).toEqual([
 | 
			
		||||
        [config, 'authCallback Signature validation failed id_token'],
 | 
			
		||||
        [config, 'authCallback token(s) invalid'],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -1049,13 +1084,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
@ -1080,7 +1123,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback incorrect nonce, did you call the checkAuth() method multiple times?'
 | 
			
		||||
@ -1118,13 +1161,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      ).mockReturnValue(false);
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logDebugSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logDebug')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1148,7 +1199,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logDebugSpy).toHaveBeenCalledWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback Validation, one of the REQUIRED properties missing from id_token'
 | 
			
		||||
@ -1193,13 +1244,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1223,7 +1282,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback Validation, iat rejected id_token was issued too far away from the current time'
 | 
			
		||||
@ -1271,13 +1330,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      );
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1301,7 +1368,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback incorrect iss does not match authWellKnownEndpoints issuer'
 | 
			
		||||
@ -1339,11 +1406,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.maxIdTokenIatOffsetAllowedInSeconds = 0;
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy.withArgs('authWellKnownEndPoints', config).mockReturnValue(null);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => null
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1367,7 +1444,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authWellKnownEndpoints is undefined'
 | 
			
		||||
@ -1414,13 +1491,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.clientId = '';
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1444,7 +1529,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback incorrect aud'
 | 
			
		||||
@ -1494,13 +1579,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.clientId = '';
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1524,7 +1617,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback missing azp'
 | 
			
		||||
@ -1579,13 +1672,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.clientId = '';
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1609,7 +1710,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback incorrect azp'
 | 
			
		||||
@ -1668,13 +1769,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.clientId = '';
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
        .mockImplementation(() => undefined);
 | 
			
		||||
@ -1698,7 +1807,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback pre, post id_token claims do not match in refresh'
 | 
			
		||||
@ -1769,13 +1878,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.autoCleanStateAfterAuthentication = false;
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const logDebugSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logDebug')
 | 
			
		||||
@ -1801,7 +1918,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logDebugSpy).toHaveBeenCalledWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback token(s) validated, continue'
 | 
			
		||||
@ -1875,13 +1992,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const logWarningSpy = vi
 | 
			
		||||
        .spyOn(loggerService, 'logWarning')
 | 
			
		||||
@ -1906,7 +2031,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(logWarningSpy).toHaveBeenCalledExactlyOnceWith(
 | 
			
		||||
        config,
 | 
			
		||||
        'authCallback incorrect at_hash'
 | 
			
		||||
@ -1974,13 +2099,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
      config.responseType = 'id_token token';
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const logDebugSpy = vi.spyOn(loggerService, 'logDebug'); // .mockImplementation(() => undefined);
 | 
			
		||||
 | 
			
		||||
@ -2003,8 +2136,9 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      expect(logDebugSpy).toBeCalledWith([
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
 | 
			
		||||
      expect(logDebugSpy.mock.calls).toEqual([
 | 
			
		||||
        [config, 'iss validation is turned off, this is not recommended!'],
 | 
			
		||||
        [config, 'authCallback token(s) validated, continue'],
 | 
			
		||||
      ]);
 | 
			
		||||
@ -2060,13 +2194,21 @@ describe('State Validation Service', () => {
 | 
			
		||||
 | 
			
		||||
      const readSpy = vi.spyOn(storagePersistenceService, 'read');
 | 
			
		||||
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authWellKnownEndPoints', config)
 | 
			
		||||
        .mockReturnValue(authWellKnownEndpoints);
 | 
			
		||||
      readSpy
 | 
			
		||||
        .withArgs('authStateControl', config)
 | 
			
		||||
        .mockReturnValue('authStateControl');
 | 
			
		||||
      readSpy.withArgs('authNonce', config).mockReturnValue('authNonce');
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authWellKnownEndPoints', config],
 | 
			
		||||
        () => authWellKnownEndpoints
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authStateControl', config],
 | 
			
		||||
        () => 'authStateControl'
 | 
			
		||||
      );
 | 
			
		||||
      mockImplementationWhenArgsEqual(
 | 
			
		||||
        readSpy,
 | 
			
		||||
        ['authNonce', config],
 | 
			
		||||
        () => 'authNonce'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const callbackContext = {
 | 
			
		||||
        code: 'fdffsdfsdf',
 | 
			
		||||
@ -2088,7 +2230,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const state = await lastValueFrom(stateObs$);
 | 
			
		||||
      const state = await firstValueFrom(stateObs$);
 | 
			
		||||
      expect(state.accessToken).toBe('access_tokenTEST');
 | 
			
		||||
      expect(state.idToken).toBe('');
 | 
			
		||||
      expect(state.decodedIdToken).toBeDefined();
 | 
			
		||||
@ -2127,7 +2269,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const isValid = await lastValueFrom(isValidObs$);
 | 
			
		||||
      const isValid = await firstValueFrom(isValidObs$);
 | 
			
		||||
      expect(isValid.state).toBe(ValidationResult.Ok);
 | 
			
		||||
      expect(isValid.authResponseIsValid).toBe(true);
 | 
			
		||||
    });
 | 
			
		||||
@ -2164,7 +2306,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const isValid = await lastValueFrom(isValidObs$);
 | 
			
		||||
      const isValid = await firstValueFrom(isValidObs$);
 | 
			
		||||
      expect(isValid.state).toBe(ValidationResult.Ok);
 | 
			
		||||
      expect(isValid.authResponseIsValid).toBe(true);
 | 
			
		||||
    });
 | 
			
		||||
@ -2201,7 +2343,7 @@ describe('State Validation Service', () => {
 | 
			
		||||
        config
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const isValid = await lastValueFrom(isValidObs$);
 | 
			
		||||
      const isValid = await firstValueFrom(isValidObs$);
 | 
			
		||||
      expect(isValid.state).toBe(ValidationResult.Ok);
 | 
			
		||||
      expect(isValid.authResponseIsValid).toBe(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TestBed } from '@/testing';
 | 
			
		||||
import { lastValueFrom, of } from 'rxjs';
 | 
			
		||||
import { firstValueFrom, of } from 'rxjs';
 | 
			
		||||
import { vi } from 'vitest';
 | 
			
		||||
import { JwkExtractor } from '../extractors/jwk.extractor';
 | 
			
		||||
import { LoggerService } from '../logging/logger.service';
 | 
			
		||||
@ -503,7 +503,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueFalse = await lastValueFrom(valueFalse$);
 | 
			
		||||
      const valueFalse = await firstValueFrom(valueFalse$);
 | 
			
		||||
      expect(valueFalse).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -514,7 +514,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueFalse = await lastValueFrom(valueFalse$);
 | 
			
		||||
      const valueFalse = await firstValueFrom(valueFalse$);
 | 
			
		||||
      expect(valueFalse).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -525,7 +525,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueFalse = await lastValueFrom(valueFalse$);
 | 
			
		||||
      const valueFalse = await firstValueFrom(valueFalse$);
 | 
			
		||||
      expect(valueFalse).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -542,7 +542,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueFalse = await lastValueFrom(valueFalse$);
 | 
			
		||||
      const valueFalse = await firstValueFrom(valueFalse$);
 | 
			
		||||
      expect(valueFalse).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -561,7 +561,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueFalse = await lastValueFrom(valueFalse$);
 | 
			
		||||
      const valueFalse = await firstValueFrom(valueFalse$);
 | 
			
		||||
      expect(valueFalse).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -597,7 +597,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueFalse = await lastValueFrom(valueFalse$);
 | 
			
		||||
      const valueFalse = await firstValueFrom(valueFalse$);
 | 
			
		||||
      expect(valueFalse).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -634,7 +634,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const valueTrue = await lastValueFrom(valueTrue$);
 | 
			
		||||
      const valueTrue = await firstValueFrom(valueTrue$);
 | 
			
		||||
      expect(valueTrue).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@ -651,7 +651,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(result$);
 | 
			
		||||
      const result = await firstValueFrom(result$);
 | 
			
		||||
      expect(result).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -660,7 +660,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE1ODkyMTAwODYsIm5iZiI6MTU4OTIwNjQ4NiwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9kYW1pZW5ib2QuYjJjbG9naW4uY29tL2EwOTU4ZjQ1LTE5NWItNDAzNi05MjU5LWRlMmY3ZTU5NGRiNi92Mi4wLyIsInN1YiI6ImY4MzZmMzgwLTNjNjQtNDgwMi04ZGJjLTAxMTk4MWMwNjhmNSIsImF1ZCI6ImYxOTM0YTZlLTk1OGQtNDE5OC05ZjM2LTYxMjdjZmM0Y2RiMyIsIm5vbmNlIjoiMDA3YzQxNTNiNmEwNTE3YzBlNDk3NDc2ZmIyNDk5NDhlYzVjbE92UVEiLCJpYXQiOjE1ODkyMDY0ODYsImF1dGhfdGltZSI6MTU4OTIwNjQ4NiwibmFtZSI6ImRhbWllbmJvZCIsImVtYWlscyI6WyJkYW1pZW5AZGFtaWVuYm9kLm9ubWljcm9zb2Z0LmNvbSJdLCJ0ZnAiOiJCMkNfMV9iMmNwb2xpY3lkYW1pZW4iLCJhdF9oYXNoIjoiWmswZktKU19wWWhPcE04SUJhMTJmdyJ9.E5Z-0kOzNU7LBkeVHHMyNoER8TUapGzUUfXmW6gVu4v6QMM5fQ4sJ7KC8PHh8lBFYiCnaDiTtpn3QytUwjXEFnLDAX5qcZT1aPoEgL_OmZMC-8y-4GyHp35l7VFD4iNYM9fJmLE8SYHTVl7eWPlXSyz37Ip0ciiV0Fd6eoksD_aVc-hkIqngDfE4fR8ZKfv4yLTNN_SfknFfuJbZ56yN-zIBL4GkuHsbQCBYpjtWQ62v98p1jO7NhHKV5JP2ec_Ge6oYc_bKTrE6OIX38RJ2rIm7zU16mtdjnl_350Nw3ytHcTPnA1VpP_VLElCfe83jr5aDHc_UQRYaAcWlOgvmVg';
 | 
			
		||||
      const atHash = 'bad';
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(
 | 
			
		||||
      const result = await firstValueFrom(
 | 
			
		||||
        tokenValidationService.validateIdTokenAtHash(
 | 
			
		||||
          accessToken,
 | 
			
		||||
          atHash,
 | 
			
		||||
@ -688,7 +688,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(result$);
 | 
			
		||||
      const result = await firstValueFrom(result$);
 | 
			
		||||
      expect(result).toEqual(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -704,7 +704,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(result$);
 | 
			
		||||
      const result = await firstValueFrom(result$);
 | 
			
		||||
      expect(result).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -720,7 +720,7 @@ describe('TokenValidationService', () => {
 | 
			
		||||
        { configId: 'configId1' }
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const result = await lastValueFrom(result$);
 | 
			
		||||
      const result = await firstValueFrom(result$);
 | 
			
		||||
      expect(result).toEqual(false);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -390,7 +390,8 @@ export class TokenValidationService {
 | 
			
		||||
    localState: any,
 | 
			
		||||
    configuration: OpenIdConfiguration
 | 
			
		||||
  ): boolean {
 | 
			
		||||
    if ((state as string) !== (localState as string)) {
 | 
			
		||||
    console.error(state, localState, `${state}`, `${localState}`);
 | 
			
		||||
    if (`${state}` !== `${localState}`) {
 | 
			
		||||
      this.loggerService.logDebug(
 | 
			
		||||
        configuration,
 | 
			
		||||
        `ValidateStateFromHashCallback failed, state: ${state} local_state:${localState}`
 | 
			
		||||
 | 
			
		||||
@ -1,437 +0,0 @@
 | 
			
		||||
import { test, expect, type Page } from '@playwright/test';
 | 
			
		||||
 | 
			
		||||
test.beforeEach(async ({ page }) => {
 | 
			
		||||
  await page.goto('https://demo.playwright.dev/todomvc');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const TODO_ITEMS = [
 | 
			
		||||
  'buy some cheese',
 | 
			
		||||
  'feed the cat',
 | 
			
		||||
  'book a doctors appointment'
 | 
			
		||||
] as const;
 | 
			
		||||
 | 
			
		||||
test.describe('New Todo', () => {
 | 
			
		||||
  test('should allow me to add todo items', async ({ page }) => {
 | 
			
		||||
    // create a new todo locator
 | 
			
		||||
    const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
 | 
			
		||||
    // Create 1st todo.
 | 
			
		||||
    await newTodo.fill(TODO_ITEMS[0]);
 | 
			
		||||
    await newTodo.press('Enter');
 | 
			
		||||
 | 
			
		||||
    // Make sure the list only has one todo item.
 | 
			
		||||
    await expect(page.getByTestId('todo-title')).toHaveText([
 | 
			
		||||
      TODO_ITEMS[0]
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Create 2nd todo.
 | 
			
		||||
    await newTodo.fill(TODO_ITEMS[1]);
 | 
			
		||||
    await newTodo.press('Enter');
 | 
			
		||||
 | 
			
		||||
    // Make sure the list now has two todo items.
 | 
			
		||||
    await expect(page.getByTestId('todo-title')).toHaveText([
 | 
			
		||||
      TODO_ITEMS[0],
 | 
			
		||||
      TODO_ITEMS[1]
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 2);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should clear text input field when an item is added', async ({ page }) => {
 | 
			
		||||
    // create a new todo locator
 | 
			
		||||
    const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
 | 
			
		||||
    // Create one todo item.
 | 
			
		||||
    await newTodo.fill(TODO_ITEMS[0]);
 | 
			
		||||
    await newTodo.press('Enter');
 | 
			
		||||
 | 
			
		||||
    // Check that input is empty.
 | 
			
		||||
    await expect(newTodo).toBeEmpty();
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 1);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should append new items to the bottom of the list', async ({ page }) => {
 | 
			
		||||
    // Create 3 items.
 | 
			
		||||
    await createDefaultTodos(page);
 | 
			
		||||
 | 
			
		||||
    // create a todo count locator
 | 
			
		||||
    const todoCount = page.getByTestId('todo-count')
 | 
			
		||||
  
 | 
			
		||||
    // Check test using different methods.
 | 
			
		||||
    await expect(page.getByText('3 items left')).toBeVisible();
 | 
			
		||||
    await expect(todoCount).toHaveText('3 items left');
 | 
			
		||||
    await expect(todoCount).toContainText('3');
 | 
			
		||||
    await expect(todoCount).toHaveText(/3/);
 | 
			
		||||
 | 
			
		||||
    // Check all items in one call.
 | 
			
		||||
    await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 3);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Mark all as completed', () => {
 | 
			
		||||
  test.beforeEach(async ({ page }) => {
 | 
			
		||||
    await createDefaultTodos(page);
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test.afterEach(async ({ page }) => {
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to mark all items as completed', async ({ page }) => {
 | 
			
		||||
    // Complete all todos.
 | 
			
		||||
    await page.getByLabel('Mark all as complete').check();
 | 
			
		||||
 | 
			
		||||
    // Ensure all todos have 'completed' class.
 | 
			
		||||
    await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to clear the complete state of all items', async ({ page }) => {
 | 
			
		||||
    const toggleAll = page.getByLabel('Mark all as complete');
 | 
			
		||||
    // Check and then immediately uncheck.
 | 
			
		||||
    await toggleAll.check();
 | 
			
		||||
    await toggleAll.uncheck();
 | 
			
		||||
 | 
			
		||||
    // Should be no completed classes.
 | 
			
		||||
    await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
 | 
			
		||||
    const toggleAll = page.getByLabel('Mark all as complete');
 | 
			
		||||
    await toggleAll.check();
 | 
			
		||||
    await expect(toggleAll).toBeChecked();
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 3);
 | 
			
		||||
 | 
			
		||||
    // Uncheck first todo.
 | 
			
		||||
    const firstTodo = page.getByTestId('todo-item').nth(0);
 | 
			
		||||
    await firstTodo.getByRole('checkbox').uncheck();
 | 
			
		||||
 | 
			
		||||
    // Reuse toggleAll locator and make sure its not checked.
 | 
			
		||||
    await expect(toggleAll).not.toBeChecked();
 | 
			
		||||
 | 
			
		||||
    await firstTodo.getByRole('checkbox').check();
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 3);
 | 
			
		||||
 | 
			
		||||
    // Assert the toggle all is checked again.
 | 
			
		||||
    await expect(toggleAll).toBeChecked();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Item', () => {
 | 
			
		||||
 | 
			
		||||
  test('should allow me to mark items as complete', async ({ page }) => {
 | 
			
		||||
    // create a new todo locator
 | 
			
		||||
    const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
 | 
			
		||||
    // Create two items.
 | 
			
		||||
    for (const item of TODO_ITEMS.slice(0, 2)) {
 | 
			
		||||
      await newTodo.fill(item);
 | 
			
		||||
      await newTodo.press('Enter');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check first item.
 | 
			
		||||
    const firstTodo = page.getByTestId('todo-item').nth(0);
 | 
			
		||||
    await firstTodo.getByRole('checkbox').check();
 | 
			
		||||
    await expect(firstTodo).toHaveClass('completed');
 | 
			
		||||
 | 
			
		||||
    // Check second item.
 | 
			
		||||
    const secondTodo = page.getByTestId('todo-item').nth(1);
 | 
			
		||||
    await expect(secondTodo).not.toHaveClass('completed');
 | 
			
		||||
    await secondTodo.getByRole('checkbox').check();
 | 
			
		||||
 | 
			
		||||
    // Assert completed class.
 | 
			
		||||
    await expect(firstTodo).toHaveClass('completed');
 | 
			
		||||
    await expect(secondTodo).toHaveClass('completed');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to un-mark items as complete', async ({ page }) => {
 | 
			
		||||
    // create a new todo locator
 | 
			
		||||
    const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
 | 
			
		||||
    // Create two items.
 | 
			
		||||
    for (const item of TODO_ITEMS.slice(0, 2)) {
 | 
			
		||||
      await newTodo.fill(item);
 | 
			
		||||
      await newTodo.press('Enter');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const firstTodo = page.getByTestId('todo-item').nth(0);
 | 
			
		||||
    const secondTodo = page.getByTestId('todo-item').nth(1);
 | 
			
		||||
    const firstTodoCheckbox = firstTodo.getByRole('checkbox');
 | 
			
		||||
 | 
			
		||||
    await firstTodoCheckbox.check();
 | 
			
		||||
    await expect(firstTodo).toHaveClass('completed');
 | 
			
		||||
    await expect(secondTodo).not.toHaveClass('completed');
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
 | 
			
		||||
 | 
			
		||||
    await firstTodoCheckbox.uncheck();
 | 
			
		||||
    await expect(firstTodo).not.toHaveClass('completed');
 | 
			
		||||
    await expect(secondTodo).not.toHaveClass('completed');
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to edit an item', async ({ page }) => {
 | 
			
		||||
    await createDefaultTodos(page);
 | 
			
		||||
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    const secondTodo = todoItems.nth(1);
 | 
			
		||||
    await secondTodo.dblclick();
 | 
			
		||||
    await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
 | 
			
		||||
    await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
 | 
			
		||||
    await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
 | 
			
		||||
 | 
			
		||||
    // Explicitly assert the new text value.
 | 
			
		||||
    await expect(todoItems).toHaveText([
 | 
			
		||||
      TODO_ITEMS[0],
 | 
			
		||||
      'buy some sausages',
 | 
			
		||||
      TODO_ITEMS[2]
 | 
			
		||||
    ]);
 | 
			
		||||
    await checkTodosInLocalStorage(page, 'buy some sausages');
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Editing', () => {
 | 
			
		||||
  test.beforeEach(async ({ page }) => {
 | 
			
		||||
    await createDefaultTodos(page);
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should hide other controls when editing', async ({ page }) => {
 | 
			
		||||
    const todoItem = page.getByTestId('todo-item').nth(1);
 | 
			
		||||
    await todoItem.dblclick();
 | 
			
		||||
    await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
 | 
			
		||||
    await expect(todoItem.locator('label', {
 | 
			
		||||
      hasText: TODO_ITEMS[1],
 | 
			
		||||
    })).not.toBeVisible();
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should save edits on blur', async ({ page }) => {
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    await todoItems.nth(1).dblclick();
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
 | 
			
		||||
 | 
			
		||||
    await expect(todoItems).toHaveText([
 | 
			
		||||
      TODO_ITEMS[0],
 | 
			
		||||
      'buy some sausages',
 | 
			
		||||
      TODO_ITEMS[2],
 | 
			
		||||
    ]);
 | 
			
		||||
    await checkTodosInLocalStorage(page, 'buy some sausages');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should trim entered text', async ({ page }) => {
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    await todoItems.nth(1).dblclick();
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('    buy some sausages    ');
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
 | 
			
		||||
 | 
			
		||||
    await expect(todoItems).toHaveText([
 | 
			
		||||
      TODO_ITEMS[0],
 | 
			
		||||
      'buy some sausages',
 | 
			
		||||
      TODO_ITEMS[2],
 | 
			
		||||
    ]);
 | 
			
		||||
    await checkTodosInLocalStorage(page, 'buy some sausages');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should remove the item if an empty text string was entered', async ({ page }) => {
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    await todoItems.nth(1).dblclick();
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
 | 
			
		||||
 | 
			
		||||
    await expect(todoItems).toHaveText([
 | 
			
		||||
      TODO_ITEMS[0],
 | 
			
		||||
      TODO_ITEMS[2],
 | 
			
		||||
    ]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should cancel edits on escape', async ({ page }) => {
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    await todoItems.nth(1).dblclick();
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
 | 
			
		||||
    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
 | 
			
		||||
    await expect(todoItems).toHaveText(TODO_ITEMS);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Counter', () => {
 | 
			
		||||
  test('should display the current number of todo items', async ({ page }) => {
 | 
			
		||||
    // create a new todo locator
 | 
			
		||||
    const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
    
 | 
			
		||||
    // create a todo count locator
 | 
			
		||||
    const todoCount = page.getByTestId('todo-count')
 | 
			
		||||
 | 
			
		||||
    await newTodo.fill(TODO_ITEMS[0]);
 | 
			
		||||
    await newTodo.press('Enter');
 | 
			
		||||
 | 
			
		||||
    await expect(todoCount).toContainText('1');
 | 
			
		||||
 | 
			
		||||
    await newTodo.fill(TODO_ITEMS[1]);
 | 
			
		||||
    await newTodo.press('Enter');
 | 
			
		||||
    await expect(todoCount).toContainText('2');
 | 
			
		||||
 | 
			
		||||
    await checkNumberOfTodosInLocalStorage(page, 2);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Clear completed button', () => {
 | 
			
		||||
  test.beforeEach(async ({ page }) => {
 | 
			
		||||
    await createDefaultTodos(page);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should display the correct text', async ({ page }) => {
 | 
			
		||||
    await page.locator('.todo-list li .toggle').first().check();
 | 
			
		||||
    await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should remove completed items when clicked', async ({ page }) => {
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    await todoItems.nth(1).getByRole('checkbox').check();
 | 
			
		||||
    await page.getByRole('button', { name: 'Clear completed' }).click();
 | 
			
		||||
    await expect(todoItems).toHaveCount(2);
 | 
			
		||||
    await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should be hidden when there are no items that are completed', async ({ page }) => {
 | 
			
		||||
    await page.locator('.todo-list li .toggle').first().check();
 | 
			
		||||
    await page.getByRole('button', { name: 'Clear completed' }).click();
 | 
			
		||||
    await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Persistence', () => {
 | 
			
		||||
  test('should persist its data', async ({ page }) => {
 | 
			
		||||
    // create a new todo locator
 | 
			
		||||
    const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
 | 
			
		||||
    for (const item of TODO_ITEMS.slice(0, 2)) {
 | 
			
		||||
      await newTodo.fill(item);
 | 
			
		||||
      await newTodo.press('Enter');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const todoItems = page.getByTestId('todo-item');
 | 
			
		||||
    const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
 | 
			
		||||
    await firstTodoCheck.check();
 | 
			
		||||
    await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
 | 
			
		||||
    await expect(firstTodoCheck).toBeChecked();
 | 
			
		||||
    await expect(todoItems).toHaveClass(['completed', '']);
 | 
			
		||||
 | 
			
		||||
    // Ensure there is 1 completed item.
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
 | 
			
		||||
 | 
			
		||||
    // Now reload.
 | 
			
		||||
    await page.reload();
 | 
			
		||||
    await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
 | 
			
		||||
    await expect(firstTodoCheck).toBeChecked();
 | 
			
		||||
    await expect(todoItems).toHaveClass(['completed', '']);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test.describe('Routing', () => {
 | 
			
		||||
  test.beforeEach(async ({ page }) => {
 | 
			
		||||
    await createDefaultTodos(page);
 | 
			
		||||
    // make sure the app had a chance to save updated todos in storage
 | 
			
		||||
    // before navigating to a new view, otherwise the items can get lost :(
 | 
			
		||||
    // in some frameworks like Durandal
 | 
			
		||||
    await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to display active items', async ({ page }) => {
 | 
			
		||||
    const todoItem = page.getByTestId('todo-item');
 | 
			
		||||
    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
 | 
			
		||||
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
 | 
			
		||||
    await page.getByRole('link', { name: 'Active' }).click();
 | 
			
		||||
    await expect(todoItem).toHaveCount(2);
 | 
			
		||||
    await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should respect the back button', async ({ page }) => {
 | 
			
		||||
    const todoItem = page.getByTestId('todo-item'); 
 | 
			
		||||
    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
 | 
			
		||||
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
 | 
			
		||||
 | 
			
		||||
    await test.step('Showing all items', async () => {
 | 
			
		||||
      await page.getByRole('link', { name: 'All' }).click();
 | 
			
		||||
      await expect(todoItem).toHaveCount(3);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await test.step('Showing active items', async () => {
 | 
			
		||||
      await page.getByRole('link', { name: 'Active' }).click();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await test.step('Showing completed items', async () => {
 | 
			
		||||
      await page.getByRole('link', { name: 'Completed' }).click();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await expect(todoItem).toHaveCount(1);
 | 
			
		||||
    await page.goBack();
 | 
			
		||||
    await expect(todoItem).toHaveCount(2);
 | 
			
		||||
    await page.goBack();
 | 
			
		||||
    await expect(todoItem).toHaveCount(3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to display completed items', async ({ page }) => {
 | 
			
		||||
    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
 | 
			
		||||
    await page.getByRole('link', { name: 'Completed' }).click();
 | 
			
		||||
    await expect(page.getByTestId('todo-item')).toHaveCount(1);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should allow me to display all items', async ({ page }) => {
 | 
			
		||||
    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
 | 
			
		||||
    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
 | 
			
		||||
    await page.getByRole('link', { name: 'Active' }).click();
 | 
			
		||||
    await page.getByRole('link', { name: 'Completed' }).click();
 | 
			
		||||
    await page.getByRole('link', { name: 'All' }).click();
 | 
			
		||||
    await expect(page.getByTestId('todo-item')).toHaveCount(3);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('should highlight the currently applied filter', async ({ page }) => {
 | 
			
		||||
    await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
 | 
			
		||||
    
 | 
			
		||||
    //create locators for active and completed links
 | 
			
		||||
    const activeLink = page.getByRole('link', { name: 'Active' });
 | 
			
		||||
    const completedLink = page.getByRole('link', { name: 'Completed' });
 | 
			
		||||
    await activeLink.click();
 | 
			
		||||
 | 
			
		||||
    // Page change - active items.
 | 
			
		||||
    await expect(activeLink).toHaveClass('selected');
 | 
			
		||||
    await completedLink.click();
 | 
			
		||||
 | 
			
		||||
    // Page change - completed items.
 | 
			
		||||
    await expect(completedLink).toHaveClass('selected');
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function createDefaultTodos(page: Page) {
 | 
			
		||||
  // create a new todo locator
 | 
			
		||||
  const newTodo = page.getByPlaceholder('What needs to be done?');
 | 
			
		||||
 | 
			
		||||
  for (const item of TODO_ITEMS) {
 | 
			
		||||
    await newTodo.fill(item);
 | 
			
		||||
    await newTodo.press('Enter');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
 | 
			
		||||
  return await page.waitForFunction(e => {
 | 
			
		||||
    return JSON.parse(localStorage['react-todos']).length === e;
 | 
			
		||||
  }, expected);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
 | 
			
		||||
  return await page.waitForFunction(e => {
 | 
			
		||||
    return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
 | 
			
		||||
  }, expected);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function checkTodosInLocalStorage(page: Page, title: string) {
 | 
			
		||||
  return await page.waitForFunction(t => {
 | 
			
		||||
    return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
 | 
			
		||||
  }, title);
 | 
			
		||||
}
 | 
			
		||||
@ -19,6 +19,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "files": [],
 | 
			
		||||
  "include": [],
 | 
			
		||||
  "exclude": ["node_modules"],
 | 
			
		||||
  "references": [
 | 
			
		||||
    {
 | 
			
		||||
      "path": "./tsconfig.lib.json"
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,8 @@
 | 
			
		||||
    "rootDir": ".",
 | 
			
		||||
    "outDir": "./dist/tsc-lib",
 | 
			
		||||
    "lib": ["dom", "es2018"],
 | 
			
		||||
    "experimentalDecorators": true,
 | 
			
		||||
    "emitDecoratorMetadata": true,
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "injection-js": ["./node_modules/injection-js/lib/index.ts"]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,8 @@
 | 
			
		||||
    "noUncheckedIndexedAccess": true,
 | 
			
		||||
    "outDir": "./dist/tsc-test",
 | 
			
		||||
    "types": ["vitest/globals", "node"],
 | 
			
		||||
    "experimentalDecorators": true,
 | 
			
		||||
    "emitDecoratorMetadata": true,
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "@/testing": ["./src/testing"],
 | 
			
		||||
      "@/testing/*": ["./src/testing/*"],
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,29 @@
 | 
			
		||||
import swc from 'unplugin-swc';
 | 
			
		||||
import tsconfigPaths from 'vite-tsconfig-paths';
 | 
			
		||||
import { defineConfig } from 'vitest/config';
 | 
			
		||||
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  cacheDir: '.vitest',
 | 
			
		||||
  test: {
 | 
			
		||||
    include: ['src/**/*.spec.ts', 'tests-examples'],
 | 
			
		||||
    setupFiles: ['src/testing/init-test.ts'],
 | 
			
		||||
    environment: 'jsdom',
 | 
			
		||||
    include: ['src/**/*.spec.ts'],
 | 
			
		||||
    globals: true,
 | 
			
		||||
    browser: {
 | 
			
		||||
      provider: 'playwright', // or 'webdriverio'
 | 
			
		||||
      enabled: true,
 | 
			
		||||
      // at least one instance is required
 | 
			
		||||
      instances: [{ browser: 'chromium' }],
 | 
			
		||||
    restoreMocks: true,
 | 
			
		||||
    // browser: {
 | 
			
		||||
    //   provider: 'playwright', // or 'webdriverio'
 | 
			
		||||
    //   enabled: true,
 | 
			
		||||
    //   instances: [{ browser: 'chromium' }],
 | 
			
		||||
    // },
 | 
			
		||||
  },
 | 
			
		||||
  },
 | 
			
		||||
  plugins: [tsconfigPaths({})],
 | 
			
		||||
  plugins: [
 | 
			
		||||
    tsconfigPaths(),
 | 
			
		||||
    swc.vite({
 | 
			
		||||
      include: /\.[mc]?[jt]sx?$/,
 | 
			
		||||
      exclude: [
 | 
			
		||||
        /node_modules\/(?!injection-js|\.pnpm)/,
 | 
			
		||||
        /node_modules\/\.pnpm\/(?!injection-js)/,
 | 
			
		||||
      ] as any,
 | 
			
		||||
    }),
 | 
			
		||||
  ],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user