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(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url1'
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});
|
||||
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(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url1'
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
{ customParams: { custom: 'param' } }
|
||||
);
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call `checkSavedRedirectRouteAndNavigate` if authenticated already', async () => {
|
||||
@ -146,16 +145,17 @@ 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(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
)
|
||||
);
|
||||
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(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url1'
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});
|
||||
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(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url1'
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
{ customParams: { custom: 'param' } }
|
||||
);
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call `checkSavedRedirectRouteAndNavigate` if authenticated already', async () => {
|
||||
@ -237,16 +235,17 @@ 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(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
)
|
||||
);
|
||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
|
||||
expect(loginSpy).not.toHaveBeenCalled();
|
||||
expect(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
});
|
||||
});
|
||||
|
||||
@ -265,15 +264,15 @@ expect(
|
||||
);
|
||||
const loginSpy = vi.spyOn(loginService, 'login');
|
||||
|
||||
await lastValueFrom(guard.canLoad());
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);;
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});;
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
await firstValueFrom(guard.canLoad());
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should save current route (with router extractedUrl) and call `login` if not authenticated already', async () => {
|
||||
@ -301,15 +300,15 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
trigger: 'imperative',
|
||||
});
|
||||
|
||||
await lastValueFrom(guard.canLoad());
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url12/with/some-param?queryParam=true'
|
||||
);;
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});;
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
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();
|
||||
});
|
||||
|
||||
it('should call `checkSavedRedirectRouteAndNavigate` if authenticated already', async () => {
|
||||
@ -326,12 +325,12 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
);
|
||||
const loginSpy = vi.spyOn(loginService, 'login');
|
||||
|
||||
await lastValueFrom(guard.canLoad());
|
||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();;
|
||||
expect(loginSpy).not.toHaveBeenCalled();;
|
||||
expect(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
await firstValueFrom(guard.canLoad());
|
||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
|
||||
expect(loginSpy).not.toHaveBeenCalled();
|
||||
expect(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -383,15 +382,15 @@ expect(
|
||||
autoLoginPartialRoutesGuard
|
||||
);
|
||||
|
||||
await lastValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);;
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});;
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
await firstValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should save current route (with router extractedUrl) and call `login` if not authenticated already', async () => {
|
||||
@ -423,15 +422,15 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
autoLoginPartialRoutesGuard
|
||||
);
|
||||
|
||||
await lastValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url12/with/some-param?queryParam=true'
|
||||
);;
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});;
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
await firstValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
'some-url12/with/some-param?queryParam=true'
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should save current route and call `login` if not authenticated already and add custom params', async () => {
|
||||
@ -454,16 +453,16 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
} as unknown as ActivatedRouteSnapshot)
|
||||
);
|
||||
|
||||
await lastValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);;
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
{ customParams: { custom: 'param' } }
|
||||
);;
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
await firstValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
{ customParams: { custom: 'param' } }
|
||||
);
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call `checkSavedRedirectRouteAndNavigate` if authenticated already', async () => {
|
||||
@ -484,12 +483,12 @@ expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
autoLoginPartialRoutesGuard
|
||||
);
|
||||
|
||||
await lastValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();;
|
||||
expect(loginSpy).not.toHaveBeenCalled();;
|
||||
expect(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
await firstValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).not.toHaveBeenCalled();
|
||||
expect(loginSpy).not.toHaveBeenCalled();
|
||||
expect(
|
||||
checkSavedRedirectRouteAndNavigateSpy
|
||||
).toHaveBeenCalledExactlyOnceWith({ configId: 'configId1' });
|
||||
});
|
||||
});
|
||||
|
||||
@ -537,15 +536,15 @@ expect(
|
||||
autoLoginPartialRoutesGuardWithConfig('configId1')
|
||||
);
|
||||
|
||||
await lastValueFrom(guard$);
|
||||
expect(saveRedirectRouteSpy).toHaveBeenCalledExactlyOnceWith(
|
||||
{ configId: 'configId1' },
|
||||
''
|
||||
);;
|
||||
expect(loginSpy).toHaveBeenCalledExactlyOnceWith({
|
||||
configId: 'configId1',
|
||||
});;
|
||||
expect(checkSavedRedirectRouteAndNavigateSpy).not.toHaveBeenCalled();
|
||||
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,
|
||||
})
|
||||
);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
test$.subscribe();
|
||||
|
||||
expect(
|
||||
periodicallyTokenCheckService.startTokenValidationPeriodically
|
||||
).toThrowError();
|
||||
expect(resetSilentRenewRunning).toHaveBeenCalledExactlyOnceWith(
|
||||
configs[0]
|
||||
);
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
|
||||
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,
|
||||
})
|
||||
);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
test$.subscribe();
|
||||
|
||||
expect(
|
||||
periodicallyTokenCheckService.startTokenValidationPeriodically
|
||||
).toThrowError();
|
||||
expect(publicEventsServiceSpy).toBeCalledWith([
|
||||
[EventTypes.SilentRenewStarted],
|
||||
[EventTypes.SilentRenewFailed, new Error('error')],
|
||||
]);
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
|
||||
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,13 +226,11 @@ export class CheckSessionService implements OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
this.zone.runOutsideAngular(() => {
|
||||
this.scheduledHeartBeatRunning =
|
||||
this.document?.defaultView?.setTimeout(
|
||||
() => this.zone.run(pollServerSessionRecur),
|
||||
this.heartBeatInterval
|
||||
) ?? null;
|
||||
});
|
||||
this.scheduledHeartBeatRunning =
|
||||
this.document?.defaultView?.setTimeout(
|
||||
pollServerSessionRecur,
|
||||
this.heartBeatInterval
|
||||
) ?? null;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
allConfigs[0]!,
|
||||
allConfigs
|
||||
);
|
||||
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',
|
||||
'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)
|
||||
const oidcSilentRenewInit$ = fromEventPattern(
|
||||
(handler) =>
|
||||
this.document.defaultView.window.addEventListener(
|
||||
'oidc-silent-renew-init',
|
||||
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,
|
||||
})
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
silentRenewService.silentRenewEventHandler(
|
||||
eventData,
|
||||
allConfigs[0]!,
|
||||
allConfigs
|
||||
);
|
||||
await vi.advanceTimersByTimeAsync(1000);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
silentRenewService.silentRenewEventHandler(
|
||||
eventData,
|
||||
allConfigs[0]!,
|
||||
allConfigs
|
||||
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();
|
||||
|
||||
silentRenewService.silentRenewEventHandler(
|
||||
eventData,
|
||||
allConfigs[0]!,
|
||||
allConfigs
|
||||
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(
|
||||
'Please provide a configuration before setting up the module'
|
||||
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,53 +55,42 @@ 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) => {
|
||||
this.loggerService.logDebug(
|
||||
map((response) => {
|
||||
this.loggerService.logDebug(
|
||||
configuration,
|
||||
'par response: ',
|
||||
response
|
||||
);
|
||||
|
||||
const url = this.urlService.getAuthorizeParUrl(
|
||||
response.requestUri,
|
||||
configuration
|
||||
);
|
||||
|
||||
this.loggerService.logDebug(configuration, 'par request url: ', url);
|
||||
|
||||
if (!url) {
|
||||
this.loggerService.logError(
|
||||
configuration,
|
||||
'par response: ',
|
||||
response
|
||||
`Could not create URL with param ${response.requestUri}: '${url}'`
|
||||
);
|
||||
|
||||
const url = this.urlService.getAuthorizeParUrl(
|
||||
response.requestUri,
|
||||
configuration
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.loggerService.logDebug(
|
||||
configuration,
|
||||
'par request url: ',
|
||||
url
|
||||
);
|
||||
|
||||
if (!url) {
|
||||
this.loggerService.logError(
|
||||
configuration,
|
||||
`Could not create URL with param ${response.requestUri}: '${url}'`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (authOptions?.urlHandler) {
|
||||
authOptions.urlHandler(url);
|
||||
} else {
|
||||
this.redirectService.redirectTo(url);
|
||||
}
|
||||
};
|
||||
}),
|
||||
shareReplay(1)
|
||||
if (authOptions?.urlHandler) {
|
||||
authOptions.urlHandler(url);
|
||||
} else {
|
||||
this.redirectService.redirectTo(url);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
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$);
|
||||
expect(result).toBe(popupResult);
|
||||
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,8 +214,14 @@ describe('PopUpService', () => {
|
||||
|
||||
popupResult = {} as PopupResult;
|
||||
|
||||
const result = await lastValueFrom(popUpService.result$);
|
||||
popupResult = result;
|
||||
popUpService.result$.subscribe((result) => {
|
||||
popupResult = result;
|
||||
});
|
||||
});
|
||||
|
||||
// biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('message received with data', async () => {
|
||||
@ -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);
|
||||
|
||||
standardLoginService.loginStandard(config, {
|
||||
customParams: { to: 'add', as: 'well' },
|
||||
});
|
||||
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,8 +228,10 @@ describe('OidcSecurityService', () => {
|
||||
some: 'thing',
|
||||
});
|
||||
|
||||
const result = await lastValueFrom(oidcSecurityService.getUserData('configId'));
|
||||
expect(result).toEqual({ some: 'thing' });
|
||||
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)
|
||||
);
|
||||
result$.subscribe();
|
||||
return result$;
|
||||
logoffLocal(configId?: string): Observable<undefined> {
|
||||
return this.configurationService.getOpenIDConfigurations(configId).pipe(
|
||||
map(({ allConfigs, currentConfig }) => {
|
||||
this.logoffRevocationService.logoffLocal(currentConfig, allConfigs);
|
||||
return undefined;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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(
|
||||
map(({ allConfigs }) =>
|
||||
this.logoffRevocationService.logoffLocalMultiple(allConfigs)
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
result$.subscribe();
|
||||
|
||||
return result$;
|
||||
return this.configurationService
|
||||
.getOpenIDConfigurations()
|
||||
.pipe(
|
||||
map(({ allConfigs }) =>
|
||||
this.logoffRevocationService.logoffLocalMultiple(allConfigs)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
|
||||
eventsService.registerForEvents().subscribe((firedEvent) => {
|
||||
spy(firedEvent);
|
||||
expect(firedEvent).toBeTruthy();
|
||||
});
|
||||
|
||||
const firedEvent = await lastValueFrom(eventsService.registerForEvents());
|
||||
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,
|
||||
})
|
||||
);
|
||||
expect(firedEvent).toBeTruthy();
|
||||
expect(firedEvent).toEqual({
|
||||
type: EventTypes.ConfigLoaded,
|
||||
value: { myKey: 'myValue' },
|
||||
|
||||
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 => {
|
||||
return;
|
||||
};
|
||||
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