From ec4962c1c27c51428e0783eb8001b343cc37764b Mon Sep 17 00:00:00 2001 From: abrha Date: Mon, 13 Nov 2023 16:58:50 +0100 Subject: [PATCH 1/5] [KAIZEN-0] utbetalings period filter --- .../infotabs/utbetalinger/UtbetalingerContainer.tsx | 3 ++- .../__snapshots__/UtbetalingerContainer.test.tsx.snap | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/personside/infotabs/utbetalinger/UtbetalingerContainer.tsx b/src/app/personside/infotabs/utbetalinger/UtbetalingerContainer.tsx index 0f8ce79a3..5c2145db0 100644 --- a/src/app/personside/infotabs/utbetalinger/UtbetalingerContainer.tsx +++ b/src/app/personside/infotabs/utbetalinger/UtbetalingerContainer.tsx @@ -28,9 +28,10 @@ const UtbetalingerStyle = styled.div` `; const FiltreringSection = styled.section` + height: fit-content; width: 100%; @media (min-width: ${theme.media.utbetalinger.minWidth}) { - width: 19.5rem; + width: 22.5rem; } `; diff --git a/src/app/personside/infotabs/utbetalinger/__snapshots__/UtbetalingerContainer.test.tsx.snap b/src/app/personside/infotabs/utbetalinger/__snapshots__/UtbetalingerContainer.test.tsx.snap index 17ef88dcf..dd41d6334 100644 --- a/src/app/personside/infotabs/utbetalinger/__snapshots__/UtbetalingerContainer.test.tsx.snap +++ b/src/app/personside/infotabs/utbetalinger/__snapshots__/UtbetalingerContainer.test.tsx.snap @@ -481,6 +481,9 @@ exports[`Viser utbetalingercontainer med alt innhold 1`] = ` } .c4 { + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; width: 100%; } @@ -572,7 +575,7 @@ exports[`Viser utbetalingercontainer med alt innhold 1`] = ` @media (min-width:1328px) { .c4 { - width: 19.5rem; + width: 22.5rem; } } From 560e201518a1f9317d8d471c4600b8dac022bbed Mon Sep 17 00:00:00 2001 From: Jesper Paulsen Date: Thu, 16 Nov 2023 10:15:21 +0100 Subject: [PATCH 2/5] [FAGSYSTEM-304086] Flytte login logikk til webworker --- src/app/AppContainer.tsx | 14 ++- src/components/featureToggle/toggleIDs.ts | 1 + src/login/AcitivityMonitor.ts | 11 +++ src/login/LoginStateManager.ts | 100 +++++++++++++++++++++ src/login/WebWorkerCommunicator.ts | 86 ++++++++++++++++++ src/login/constants.ts | 8 ++ src/login/metaUrl.ts | 2 + src/login/timeToExpiration.ts | 9 ++ src/login/types.ts | 6 ++ src/loginWebWorker.ts | 43 +++++++++ src/setupTests.ts | 4 + src/utils/hooks/use-persistent-ww-login.ts | 82 +++++++++++++++++ 12 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 src/login/AcitivityMonitor.ts create mode 100644 src/login/LoginStateManager.ts create mode 100644 src/login/WebWorkerCommunicator.ts create mode 100644 src/login/constants.ts create mode 100644 src/login/metaUrl.ts create mode 100644 src/login/timeToExpiration.ts create mode 100644 src/login/types.ts create mode 100644 src/loginWebWorker.ts create mode 100644 src/utils/hooks/use-persistent-ww-login.ts diff --git a/src/app/AppContainer.tsx b/src/app/AppContainer.tsx index 16feec6df..b2b618e08 100644 --- a/src/app/AppContainer.tsx +++ b/src/app/AppContainer.tsx @@ -16,10 +16,13 @@ import Routing from './Routing'; import styled from 'styled-components'; import { useOnMount } from '../utils/customHooks'; import VelgEnhet from './VelgEnhet'; -import usePersistentLogin from '../utils/hooks/use-persistent-login'; import LoggetUtModal from './LoggetUtModal'; import { useValgtenhet, ValgtEnhetProvider } from '../context/valgtenhet-state'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { usePersistentWWLogin } from '../utils/hooks/use-persistent-ww-login'; +import usePersistentLogin from '../utils/hooks/use-persistent-login'; +import useFeatureToggle from '../components/featureToggle/useFeatureToggle'; +import { FeatureToggles } from '../components/featureToggle/toggleIDs'; const AppStyle = styled.div` height: 100vh; @@ -42,9 +45,16 @@ const ContentStyle = styled.div` const store = createStore(reducers, composeWithDevTools(applyMiddleware(thunk))); function App() { - const loginState = usePersistentLogin(); + const loginStateOld = usePersistentLogin(); + const loginStateNew = usePersistentWWLogin(); + const { isOn: newLoginStateToggleIsOn } = useFeatureToggle(FeatureToggles.BrukWebworkerPaaInnLogging); const valgtEnhet = useValgtenhet().enhetId; + let loginState = loginStateOld; + if (newLoginStateToggleIsOn) { + loginState = loginStateNew; + } + if (!valgtEnhet) { /** * valgt enhet hentes fra modiacontextholder, og mellomlagres i localStorage diff --git a/src/components/featureToggle/toggleIDs.ts b/src/components/featureToggle/toggleIDs.ts index f13c13185..de439873a 100644 --- a/src/components/featureToggle/toggleIDs.ts +++ b/src/components/featureToggle/toggleIDs.ts @@ -1,5 +1,6 @@ export enum FeatureToggles { BrukSoknadsstatus = 'modiapersonoversikt.soknadsstatus-api', BrukUtvidetUtbetalingsSporring = 'modiapersonoversikt.utvidet-utbetalings-sporring', + BrukWebworkerPaaInnLogging = 'modiapersonoversikt.web-worker-på-innlogging', DebugMeldingsFunksjonalitet = 'modiapersonoversikt.ny-send-melding-container' } diff --git a/src/login/AcitivityMonitor.ts b/src/login/AcitivityMonitor.ts new file mode 100644 index 000000000..46177cc67 --- /dev/null +++ b/src/login/AcitivityMonitor.ts @@ -0,0 +1,11 @@ +export class ActivityMonitor { + private lastActivity: number = new Date().getTime(); + + public update() { + this.lastActivity = new Date().getTime(); + } + + public timeSinceLastActivity(): number { + return new Date().getTime() - this.lastActivity; + } +} diff --git a/src/login/LoginStateManager.ts b/src/login/LoginStateManager.ts new file mode 100644 index 000000000..da014a34d --- /dev/null +++ b/src/login/LoginStateManager.ts @@ -0,0 +1,100 @@ +import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; +import { ActivityMonitor } from './AcitivityMonitor'; +import { INACTIVITY_LIMIT_IN_MS, PREEMPTIVE_REFRESH_TIME_IN_MS, RECALC_LOGIN_STATUS_INTERVAL_IN_MS } from './constants'; +import { timeToExpiration } from './timeToExpiration'; + +export class LoginStateManager { + private timeout: ReturnType | null = null; + private interval: ReturnType | null = null; + private activityMonitor = new ActivityMonitor(); + private _refreshToken?: () => void; + private _onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; + + initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { + this._refreshToken = refreshToken; + this._onLoginStateUpdate = onLoginStateUpdate; + }; + + private setupTokenRefresher = (timeToRefresh: number) => { + this.timeout = this.getTokenRefreshTimeout(this.activityMonitor, timeToRefresh); + }; + + private get refreshToken() { + if (!this._refreshToken) { + throw new Error('[LoginStateManager] var ikke initialisert med en metode for å refreshe token'); + } + return this._refreshToken; + } + + private get onLoginStateUpdate() { + if (!this._onLoginStateUpdate) { + throw new Error('[LoginStateManager] var ikke initialisert med en metode for å sende login oppdateringer'); + } + return this._onLoginStateUpdate; + } + + private getTokenRefreshTimeout = (activityMonitor: ActivityMonitor, timeToExpiration: number) => { + return setTimeout(() => { + if (activityMonitor.timeSinceLastActivity() < INACTIVITY_LIMIT_IN_MS) { + if (!this.refreshToken) { + throw new Error('[LoginStateManager] Var ikke initialisert med '); + } + if (this.refreshToken) { + this.refreshToken(); + } + } + }, [timeToExpiration - PREEMPTIVE_REFRESH_TIME_IN_MS]); + }; + + private getLoginStateInterval = (auth: AuthIntropectionDTO) => { + return setInterval(() => { + const timeLeft = timeToExpiration(auth.expirationDate); + if (this.onLoginStateUpdate) { + this.onLoginStateUpdate({ isLoggedIn: timeLeft > 0 }); + } + }, RECALC_LOGIN_STATUS_INTERVAL_IN_MS); + }; + + private onAuthStateUpdate = (auth: AuthIntropectionDTO) => { + this.stopTokenRefresher(); + const timeToRefresh = timeToExpiration(auth.expirationDate); + if (timeToRefresh === 0) { + if (this.refreshToken) { + this.refreshToken(); + } + return; + } + this.setupTokenRefresher(timeToRefresh); + }; + + private setupLoginStateNotifier = (auth: AuthIntropectionDTO) => { + this.stopLoginStateNotifier(); + this.interval = this.getLoginStateInterval(auth); + }; + + private stopTokenRefresher = () => { + if (this.timeout) { + clearTimeout(this.timeout); + } + }; + + private stopLoginStateNotifier = () => { + if (this.interval) { + clearInterval(this.interval); + } + }; + + onUserActive = () => { + this.activityMonitor.update(); + }; + + onUpdate = (auth: AuthIntropectionDTO) => { + this.onAuthStateUpdate(auth); + this.setupLoginStateNotifier(auth); + }; + + stopWork = () => { + this.stopTokenRefresher(); + this.stopLoginStateNotifier(); + }; +} diff --git a/src/login/WebWorkerCommunicator.ts b/src/login/WebWorkerCommunicator.ts new file mode 100644 index 000000000..4e8d9b369 --- /dev/null +++ b/src/login/WebWorkerCommunicator.ts @@ -0,0 +1,86 @@ +import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; +import { LoginStateManager } from './LoginStateManager'; +import { META_URL } from './metaUrl'; +import { WWMessage, OutgoingMessageType, IncommingMessageType } from './types'; + +export interface IWebWorkerCom { + initialize: (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => void; + onAuthChange: (newState: AuthIntropectionDTO) => void; + stop: () => void; + onUserActive: () => void; +} + +export class WebWorkerCommunicator implements IWebWorkerCom { + private worker: Worker; + refreshToken?: () => void; + onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; + + constructor() { + this.worker = new window.Worker(new URL('../loginWebWorker', META_URL)); + } + + initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { + this.refreshToken = refreshToken; + this.onLoginStateUpdate = onLoginStateUpdate; + this.worker.onmessage = (message: MessageEvent>) => { + const { type, payload } = message.data; + this.onMessage(type as OutgoingMessageType, payload); + }; + }; + + private sendMessage = (type: IncommingMessageType, payload?: any) => { + const message: WWMessage = { + type, + payload + }; + this.worker.postMessage(message); + }; + + private onMessage = (type: OutgoingMessageType, payload?: any) => { + switch (type) { + case 'REFRESH_TOKEN': { + if (!this.refreshToken) { + throw new Error('WebWorker was not initialized before being called'); + } + this.refreshToken(); + return; + } + case 'LOGIN_STATE_UPDATE': { + if (!this.onLoginStateUpdate) { + throw new Error('WebWorker was not initialized before being called'); + } + this.onLoginStateUpdate({ isLoggedIn: payload }); + return; + } + } + }; + + onUserActive = () => { + this.sendMessage('USER_ACTIVE'); + }; + + onAuthChange = (newState: AuthIntropectionDTO) => { + this.sendMessage('AUTH_STATE_UPDATE', newState); + }; + + stop = () => this.sendMessage('STOP_WORKER'); +} + +export class NoWorkerCommunicator implements IWebWorkerCom { + private loginStateManager = new LoginStateManager(); + + initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { + this.loginStateManager.initialize(refreshToken, onLoginStateUpdate); + }; + + onAuthChange = (newState: AuthIntropectionDTO) => { + this.loginStateManager.onUpdate(newState); + }; + stop = () => { + this.loginStateManager.stopWork(); + }; + + onUserActive = () => { + this.loginStateManager.onUserActive(); + }; +} diff --git a/src/login/constants.ts b/src/login/constants.ts new file mode 100644 index 000000000..6ec659ef5 --- /dev/null +++ b/src/login/constants.ts @@ -0,0 +1,8 @@ +export const SECOND_IN_MS = 1000; +export const MINUTE_IN_MS = 60 * SECOND_IN_MS; + +export const RECALC_LOGIN_STATUS_INTERVAL_IN_MS = 30 * SECOND_IN_MS; +export const INACTIVITY_LIMIT_IN_MS = 10 * MINUTE_IN_MS; +export const PREEMPTIVE_REFRESH_TIME_IN_MS = 120 * SECOND_IN_MS; +export const ESTIMATED_EXPIRATION_IN_MS = 3600 * SECOND_IN_MS; +export const INVALID_EXPIRATION_DATE = -1; diff --git a/src/login/metaUrl.ts b/src/login/metaUrl.ts new file mode 100644 index 000000000..fed684f7c --- /dev/null +++ b/src/login/metaUrl.ts @@ -0,0 +1,2 @@ +// Jest doesn't support import.meta, so this is extracted here to mock it during tests. +export const META_URL = import.meta.url; diff --git a/src/login/timeToExpiration.ts b/src/login/timeToExpiration.ts new file mode 100644 index 000000000..a8a8e3de9 --- /dev/null +++ b/src/login/timeToExpiration.ts @@ -0,0 +1,9 @@ +import { ESTIMATED_EXPIRATION_IN_MS, INVALID_EXPIRATION_DATE } from './constants'; + +export const timeToExpiration = (expirationDate: number): number => { + if (expirationDate === INVALID_EXPIRATION_DATE) { + return ESTIMATED_EXPIRATION_IN_MS; + } + const currentDate = new Date().getTime(); + return expirationDate - currentDate; +}; diff --git a/src/login/types.ts b/src/login/types.ts new file mode 100644 index 000000000..fcbf199f8 --- /dev/null +++ b/src/login/types.ts @@ -0,0 +1,6 @@ +export type OutgoingMessageType = 'LOGIN_STATE_UPDATE' | 'REFRESH_TOKEN'; +export type IncommingMessageType = 'AUTH_STATE_UPDATE' | 'STOP_WORKER' | 'USER_ACTIVE'; +export interface WWMessage { + type: IncommingMessageType | OutgoingMessageType; + payload: T; +} diff --git a/src/loginWebWorker.ts b/src/loginWebWorker.ts new file mode 100644 index 000000000..7b961c6a0 --- /dev/null +++ b/src/loginWebWorker.ts @@ -0,0 +1,43 @@ +/* eslint-disable no-restricted-globals */ +import { LoginStateManager } from './login/LoginStateManager'; +import { IncommingMessageType, OutgoingMessageType } from './login/types'; + +const loginStateManager = new LoginStateManager(); + +const register = () => { + console.log('Bruker webworker for å kontrollere inlogging'); + self.addEventListener('message', handleEventMessage); +}; + +const handleEventMessage = (event: MessageEvent<{ type: IncommingMessageType; payload: any }>) => { + const { type, payload } = event.data; + switch (type) { + case 'STOP_WORKER': + loginStateManager.stopWork(); + console.log('[loginWebWorker] Mottok melding: ', type); + return; + case 'AUTH_STATE_UPDATE': + loginStateManager.onUpdate(payload); + console.log(`[loginWebWorker] Mottok melding: ${type}, med: ${payload.expirationDate}`); + return; + case 'USER_ACTIVE': + loginStateManager.onUserActive(); + return; + } +}; + +const sendRefreshMessage = () => { + sendMessage('REFRESH_TOKEN'); +}; + +const sendIsLoggedIn = ({ isLoggedIn }: { isLoggedIn: boolean }) => { + sendMessage('LOGIN_STATE_UPDATE', isLoggedIn); +}; + +const sendMessage = (type: OutgoingMessageType, payload?: T) => { + self.postMessage({ type, payload }); +}; + +loginStateManager.initialize(sendRefreshMessage, sendIsLoggedIn); + +register(); diff --git a/src/setupTests.ts b/src/setupTests.ts index ddf3d4ab8..ccf5ee85e 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -39,5 +39,9 @@ jest.mock('react-collapse', () => { }; }); +jest.mock('./login/metaUrl.ts', () => ({ + META_URL: 'https://mock.com' +})); + beforeEach(EnzymeContainer.beforeEachHandler); afterEach(EnzymeContainer.afterEachHandler); diff --git a/src/utils/hooks/use-persistent-ww-login.ts b/src/utils/hooks/use-persistent-ww-login.ts new file mode 100644 index 000000000..3ba0656b7 --- /dev/null +++ b/src/utils/hooks/use-persistent-ww-login.ts @@ -0,0 +1,82 @@ +import { useEffect, useMemo, useState } from 'react'; +import { + AuthIntropectionDTO, + ErrorReason, + INVALID_EXPIRATION_DATE, + PersistentLoginState +} from './use-persistent-login'; +import { UseQueryResult, useQuery } from '@tanstack/react-query'; +import { FetchError, get } from '../../api/api'; +import { apiBaseUri } from '../../api/config'; +import { IWebWorkerCom, NoWorkerCommunicator, WebWorkerCommunicator } from '../../login/WebWorkerCommunicator'; + +const authResource = { + useFetch(): UseQueryResult { + return useQuery(['auth'], () => get(`${apiBaseUri}/tilgang/auth`)); + } +}; + +const errorHandling = (auth: UseQueryResult): ErrorReason | undefined => { + if (auth.isError) { + return ErrorReason.FETCH_ERROR; + } else if (auth.data && auth.data.expirationDate === INVALID_EXPIRATION_DATE) { + return ErrorReason.INVALID_EXPIRATION_DATE; + } + return undefined; +}; + +const useAuthStateLogin = (auth: UseQueryResult) => { + const [isLoggedIn, setIsLoggedIn] = useState(true); + const [webWorkerCom] = useState(() => { + const worker = persistentLoginWebworkerFactory(); + worker.initialize(auth.refetch, ({ isLoggedIn }) => setIsLoggedIn(isLoggedIn)); + return worker; + }); + + useEffect(() => { + if (webWorkerCom) { + document.addEventListener('mousemove', webWorkerCom.onUserActive); + document.addEventListener('keydown', webWorkerCom.onUserActive); + } + return () => { + if (webWorkerCom) { + document.removeEventListener('mousemove', webWorkerCom.onUserActive); + document.removeEventListener('keydown', webWorkerCom.onUserActive); + } + }; + }, [webWorkerCom]); + + useEffect(() => { + if (auth.status === 'success') { + webWorkerCom.onAuthChange(auth.data); + return; + } else if (auth.status === 'error') { + setIsLoggedIn(false); + webWorkerCom.stop(); + } + }, [auth, webWorkerCom]); + + return isLoggedIn; +}; + +export const usePersistentWWLogin = (): PersistentLoginState => { + const auth = authResource.useFetch(); + const errorStatus = errorHandling(auth); + const isLoggedIn = useAuthStateLogin(auth); + + return useMemo( + () => ({ + isLoggedIn, + errorStatus + }), + [isLoggedIn, errorStatus] + ); +}; + +const persistentLoginWebworkerFactory = (): IWebWorkerCom => { + if (!window.Worker) { + console.warn('Webworker is not supported by the browser. Will fall back to browser intervals'); + return new NoWorkerCommunicator(); + } + return new WebWorkerCommunicator(); +}; From 88f5ea63467ab4562fe3e022d361dd3771b515e5 Mon Sep 17 00:00:00 2001 From: Jesper Paulsen Date: Thu, 16 Nov 2023 13:19:32 +0100 Subject: [PATCH 3/5] [KAIZEN-0] Korrigere navn i toggleID --- src/components/featureToggle/toggleIDs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/featureToggle/toggleIDs.ts b/src/components/featureToggle/toggleIDs.ts index de439873a..30c7eedee 100644 --- a/src/components/featureToggle/toggleIDs.ts +++ b/src/components/featureToggle/toggleIDs.ts @@ -1,6 +1,6 @@ export enum FeatureToggles { BrukSoknadsstatus = 'modiapersonoversikt.soknadsstatus-api', BrukUtvidetUtbetalingsSporring = 'modiapersonoversikt.utvidet-utbetalings-sporring', - BrukWebworkerPaaInnLogging = 'modiapersonoversikt.web-worker-på-innlogging', + BrukWebworkerPaaInnLogging = 'modiapersonoversikt.web-worker-paa-innlogging', DebugMeldingsFunksjonalitet = 'modiapersonoversikt.ny-send-melding-container' } From 7ac084ab4ef9cd299a79cdef457a5d26399c840e Mon Sep 17 00:00:00 2001 From: Jesper Paulsen Date: Thu, 16 Nov 2023 13:32:00 +0100 Subject: [PATCH 4/5] =?UTF-8?q?[KAIZEN-0]=20Fallback=20til=20vanlig=20innl?= =?UTF-8?q?oggingslogikk=20om=20vi=20ikke=20klarer=20=C3=A5=20lage=20worke?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/login/WebWorkerCommunicator.ts | 5 ++--- src/utils/hooks/use-persistent-ww-login.ts | 10 +++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/login/WebWorkerCommunicator.ts b/src/login/WebWorkerCommunicator.ts index 4e8d9b369..4a1f2de39 100644 --- a/src/login/WebWorkerCommunicator.ts +++ b/src/login/WebWorkerCommunicator.ts @@ -1,6 +1,5 @@ import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; import { LoginStateManager } from './LoginStateManager'; -import { META_URL } from './metaUrl'; import { WWMessage, OutgoingMessageType, IncommingMessageType } from './types'; export interface IWebWorkerCom { @@ -15,8 +14,8 @@ export class WebWorkerCommunicator implements IWebWorkerCom { refreshToken?: () => void; onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; - constructor() { - this.worker = new window.Worker(new URL('../loginWebWorker', META_URL)); + constructor(worker: Worker) { + this.worker = worker; } initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { diff --git a/src/utils/hooks/use-persistent-ww-login.ts b/src/utils/hooks/use-persistent-ww-login.ts index 3ba0656b7..d739cb6b2 100644 --- a/src/utils/hooks/use-persistent-ww-login.ts +++ b/src/utils/hooks/use-persistent-ww-login.ts @@ -9,6 +9,7 @@ import { UseQueryResult, useQuery } from '@tanstack/react-query'; import { FetchError, get } from '../../api/api'; import { apiBaseUri } from '../../api/config'; import { IWebWorkerCom, NoWorkerCommunicator, WebWorkerCommunicator } from '../../login/WebWorkerCommunicator'; +import { META_URL } from '../../login/metaUrl'; const authResource = { useFetch(): UseQueryResult { @@ -78,5 +79,12 @@ const persistentLoginWebworkerFactory = (): IWebWorkerCom => { console.warn('Webworker is not supported by the browser. Will fall back to browser intervals'); return new NoWorkerCommunicator(); } - return new WebWorkerCommunicator(); + let worker: Worker; + try { + worker = new window.Worker(new URL('../loginWebWorker', META_URL)); + return new WebWorkerCommunicator(worker); + } catch (e) { + console.log(e); + return new NoWorkerCommunicator(); + } }; From 0fa63017649f9e5738739959e9ace97400d8208c Mon Sep 17 00:00:00 2001 From: Jesper Paulsen Date: Thu, 16 Nov 2023 15:31:44 +0100 Subject: [PATCH 5/5] [KAIZEN-0] Mocke ut factory metode som setter opp webworker i test Webpack krever at import.meta.url brukes i kallet til new Worker(), og kan ikke lagres i en variabel. --- src/app/AppContainer.tsx | 2 +- src/login/WebWorkerCommunicator.ts | 2 ++ src/login/metaUrl.ts | 2 -- src/login/persistentLoginWebWorkerFactory.ts | 16 +++++++++++++ .../use-persistent-ww-login.ts | 24 ++++--------------- src/setupTests.ts | 18 ++++++++++++-- 6 files changed, 39 insertions(+), 25 deletions(-) delete mode 100644 src/login/metaUrl.ts create mode 100644 src/login/persistentLoginWebWorkerFactory.ts rename src/{utils/hooks => login}/use-persistent-ww-login.ts (74%) diff --git a/src/app/AppContainer.tsx b/src/app/AppContainer.tsx index b2b618e08..03036d3e5 100644 --- a/src/app/AppContainer.tsx +++ b/src/app/AppContainer.tsx @@ -19,7 +19,7 @@ import VelgEnhet from './VelgEnhet'; import LoggetUtModal from './LoggetUtModal'; import { useValgtenhet, ValgtEnhetProvider } from '../context/valgtenhet-state'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { usePersistentWWLogin } from '../utils/hooks/use-persistent-ww-login'; +import { usePersistentWWLogin } from '../login/use-persistent-ww-login'; import usePersistentLogin from '../utils/hooks/use-persistent-login'; import useFeatureToggle from '../components/featureToggle/useFeatureToggle'; import { FeatureToggles } from '../components/featureToggle/toggleIDs'; diff --git a/src/login/WebWorkerCommunicator.ts b/src/login/WebWorkerCommunicator.ts index 4a1f2de39..a15aea252 100644 --- a/src/login/WebWorkerCommunicator.ts +++ b/src/login/WebWorkerCommunicator.ts @@ -41,6 +41,7 @@ export class WebWorkerCommunicator implements IWebWorkerCom { if (!this.refreshToken) { throw new Error('WebWorker was not initialized before being called'); } + console.log(new Date().valueOf(), 'Refresh token message'); this.refreshToken(); return; } @@ -48,6 +49,7 @@ export class WebWorkerCommunicator implements IWebWorkerCom { if (!this.onLoginStateUpdate) { throw new Error('WebWorker was not initialized before being called'); } + console.log(new Date().valueOf(), 'Login state update'); this.onLoginStateUpdate({ isLoggedIn: payload }); return; } diff --git a/src/login/metaUrl.ts b/src/login/metaUrl.ts deleted file mode 100644 index fed684f7c..000000000 --- a/src/login/metaUrl.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Jest doesn't support import.meta, so this is extracted here to mock it during tests. -export const META_URL = import.meta.url; diff --git a/src/login/persistentLoginWebWorkerFactory.ts b/src/login/persistentLoginWebWorkerFactory.ts new file mode 100644 index 000000000..a550bce37 --- /dev/null +++ b/src/login/persistentLoginWebWorkerFactory.ts @@ -0,0 +1,16 @@ +import { IWebWorkerCom, NoWorkerCommunicator, WebWorkerCommunicator } from './WebWorkerCommunicator'; + +export const persistentLoginWebworkerFactory = (): IWebWorkerCom => { + if (!Worker) { + console.warn('WebWorker er ikke støttet av nettleseren. Kjører innlogging logikk i hovedtråden.'); + return new NoWorkerCommunicator(); + } + let worker: Worker; + try { + worker = new Worker(new URL('../loginWebWorker', import.meta.url)); + return new WebWorkerCommunicator(worker); + } catch (e) { + console.log(e); + return new NoWorkerCommunicator(); + } +}; diff --git a/src/utils/hooks/use-persistent-ww-login.ts b/src/login/use-persistent-ww-login.ts similarity index 74% rename from src/utils/hooks/use-persistent-ww-login.ts rename to src/login/use-persistent-ww-login.ts index d739cb6b2..a57f0286f 100644 --- a/src/utils/hooks/use-persistent-ww-login.ts +++ b/src/login/use-persistent-ww-login.ts @@ -4,12 +4,11 @@ import { ErrorReason, INVALID_EXPIRATION_DATE, PersistentLoginState -} from './use-persistent-login'; +} from '../utils/hooks/use-persistent-login'; import { UseQueryResult, useQuery } from '@tanstack/react-query'; -import { FetchError, get } from '../../api/api'; -import { apiBaseUri } from '../../api/config'; -import { IWebWorkerCom, NoWorkerCommunicator, WebWorkerCommunicator } from '../../login/WebWorkerCommunicator'; -import { META_URL } from '../../login/metaUrl'; +import { FetchError, get } from '../api/api'; +import { apiBaseUri } from '../api/config'; +import { persistentLoginWebworkerFactory } from './persistentLoginWebWorkerFactory'; const authResource = { useFetch(): UseQueryResult { @@ -73,18 +72,3 @@ export const usePersistentWWLogin = (): PersistentLoginState => { [isLoggedIn, errorStatus] ); }; - -const persistentLoginWebworkerFactory = (): IWebWorkerCom => { - if (!window.Worker) { - console.warn('Webworker is not supported by the browser. Will fall back to browser intervals'); - return new NoWorkerCommunicator(); - } - let worker: Worker; - try { - worker = new window.Worker(new URL('../loginWebWorker', META_URL)); - return new WebWorkerCommunicator(worker); - } catch (e) { - console.log(e); - return new NoWorkerCommunicator(); - } -}; diff --git a/src/setupTests.ts b/src/setupTests.ts index ccf5ee85e..aa399e934 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -7,6 +7,7 @@ import 'dayjs/locale/nb'; import 'jest-enzyme'; import 'jest-styled-components'; import './extra-polyfills'; +import { IWebWorkerCom } from './login/WebWorkerCommunicator'; dayjs.locale('nb'); configure({ adapter: new EnzymeReactAdapter17() }); @@ -31,6 +32,8 @@ window.matchMedia = (query: string) => { return querylist as unknown as MediaQueryList; }; +global['Worker'] = undefined; + // Mock react collapse sin UnmountClosed jest.mock('react-collapse', () => { return { @@ -39,8 +42,19 @@ jest.mock('react-collapse', () => { }; }); -jest.mock('./login/metaUrl.ts', () => ({ - META_URL: 'https://mock.com' +/** + * Jest har ikke støtte for import.meta som må brukes for å kunne lage WebWorker med Webpack p.t. + * I framtiden burde man fjerne denne mocken når man skriver seg bort fra legacy pakker. + */ + +const workerMock: IWebWorkerCom = { + initialize: () => null, + onAuthChange: () => null, + onUserActive: () => null, + stop: () => null +}; +jest.mock('./login/persistentLoginWebWorkerFactory.ts', () => ({ + persistentLoginWebworkerFactory: () => workerMock })); beforeEach(EnzymeContainer.beforeEachHandler);