diff --git a/apps/endringsmelding-pleiepenger/mockServiceWorker.js b/apps/endringsmelding-pleiepenger/mockServiceWorker.js index ee3cc288dd..dbc5b1d0cf 100644 --- a/apps/endringsmelding-pleiepenger/mockServiceWorker.js +++ b/apps/endringsmelding-pleiepenger/mockServiceWorker.js @@ -8,144 +8,149 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.2.14'; -const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'; -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse'); -const activeClientIds = new Set(); +const PACKAGE_VERSION = '2.6.8' +const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() self.addEventListener('install', function () { - self.skipWaiting(); -}); + self.skipWaiting() +}) self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()); -}); + event.waitUntil(self.clients.claim()) +}) self.addEventListener('message', async function (event) { - const clientId = event.source.id; + const clientId = event.source.id - if (!clientId || !self.clients) { - return; - } + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } - const client = await self.clients.get(clientId); + const allClients = await self.clients.matchAll({ + type: 'window', + }) - if (!client) { - return; + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break } - const allClients = await self.clients.matchAll({ - type: 'window', - }); - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }); - break; - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, - }); - break; - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId); - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: true, - }); - break; - } - - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId); - break; - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId); - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId; - }); - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister(); - } - - break; - } + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break } -}); -self.addEventListener('fetch', function (event) { - const { request } = event; + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) - // Bypass navigation requests. - if (request.mode === 'navigate') { - return; + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break } - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return; + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break } - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return; + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break } + } +}) - // Generate unique request ID. - const requestId = crypto.randomUUID(); - event.respondWith(handleRequest(event, requestId)); -}); +self.addEventListener('fetch', function (event) { + const { request } = event + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId)) +}) async function handleRequest(event, requestId) { - const client = await resolveMainClient(event); - const response = await getResponse(event, client, requestId); - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - (async function () { - const responseClone = response.clone(); - - sendToClient( - client, - { - type: 'RESPONSE', - payload: { - requestId, - isMockedResponse: IS_MOCKED_RESPONSE in response, - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - body: responseClone.body, - headers: Object.fromEntries(responseClone.headers.entries()), - }, - }, - [responseClone.body], - ); - })(); - } + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const responseClone = response.clone() - return response; + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body], + ) + })() + } + + return response } // Resolve the main client for the given event. @@ -153,129 +158,150 @@ async function handleRequest(event, requestId) { // that registered the worker. It's with the latter the worker should // communicate with during the response resolving phase. async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId); - - if (client?.frameType === 'top-level') { - return client; - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }); - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible'; - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id); - }); + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) } async function getResponse(event, client, requestId) { - const { request } = event; - - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const requestClone = request.clone(); - - function passthrough() { - const headers = Object.fromEntries(requestClone.headers.entries()); - - // Remove internal MSW request header so the passthrough request - // complies with any potential CORS preflight checks on the server. - // Some servers forbid unknown request headers. - delete headers['x-msw-intention']; - - return fetch(requestClone, { headers }); + const { request } = event + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } } - // Bypass mocking when the client is not active. - if (!client) { - return passthrough(); + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer() + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) } - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough(); + case 'PASSTHROUGH': { + return passthrough() } + } - // Notify the client that a request has been intercepted. - const requestBuffer = await request.arrayBuffer(); - const clientMessage = await sendToClient( - client, - { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - mode: request.mode, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: requestBuffer, - keepalive: request.keepalive, - }, - }, - [requestBuffer], - ); - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data); - } - - case 'PASSTHROUGH': { - return passthrough(); - } - } - - return passthrough(); + return passthrough() } function sendToClient(client, message, transferrables = []) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel(); + return new Promise((resolve, reject) => { + const channel = new MessageChannel() - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error); - } + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } - resolve(event.data); - }; + resolve(event.data) + } - client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean))); - }); + client.postMessage( + message, + [channel.port2].concat(transferrables.filter(Boolean)), + ) + }) } async function respondWithMock(response) { - // Setting response status code to 0 is a no-op. - // However, when responding with a "Response.error()", the produced Response - // instance will have status code set to 0. Since it's not possible to create - // a Response instance with status code 0, handle that use-case separately. - if (response.status === 0) { - return Response.error(); - } - - const mockedResponse = new Response(response.body, response); - - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, - }); - - return mockedResponse; + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse } diff --git a/apps/endringsmelding-pleiepenger/src/app/App.tsx b/apps/endringsmelding-pleiepenger/src/app/App.tsx index 33be74cba3..33a5d649b4 100644 --- a/apps/endringsmelding-pleiepenger/src/app/App.tsx +++ b/apps/endringsmelding-pleiepenger/src/app/App.tsx @@ -29,18 +29,17 @@ const isE2E = getMaybeEnv('E2E_TEST') === 'true'; ensureBaseNameForReactRouter(PUBLIC_PATH); -function prepare() { +async function prepare() { if (isDevMode()) { const envNow = getMaybeEnv('NOW'); if (envNow) { MockDate.set(new Date(envNow)); } if (getMaybeEnv('MSW') === 'on' && isE2E !== undefined) { - return import('../mocks/msw/browser').then(({ worker }) => { - worker.start({ - onUnhandledRequest: 'bypass', - quiet: false, - }); + const { worker } = await import('../mocks/msw/browser'); + return worker.start({ + onUnhandledRequest: 'bypass', + quiet: false, }); } } diff --git a/apps/endringsmelding-pleiepenger/src/app/dev/scenarioer.ts b/apps/endringsmelding-pleiepenger/src/app/dev/scenarioer.ts index 152eed26ec..4fc96bd1c9 100644 --- a/apps/endringsmelding-pleiepenger/src/app/dev/scenarioer.ts +++ b/apps/endringsmelding-pleiepenger/src/app/dev/scenarioer.ts @@ -65,6 +65,11 @@ export const scenarioer: Scenario[] = [ value: 'ingen-sak', harTilgang: false, }, + { + name: 'Debug', + value: 'debug', + harTilgang: true, + }, { name: 'Arbeidsaktivitet uten arbeidsgiver', value: 'arbeidsaktivitet-uten-arbeidsgiver', diff --git a/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/Debug.ts b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/Debug.ts new file mode 100644 index 0000000000..e11d62348f --- /dev/null +++ b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/Debug.ts @@ -0,0 +1,10 @@ +import { ScenarioData } from '..'; +import arbeidsgiver from './arbeidsgiver-mock'; +import sak from './sak-mock'; +import søker from './søker-mock'; + +export const Debug: ScenarioData = { + sak, + arbeidsgiver, + søker, +}; diff --git a/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/arbeidsgiver-mock.ts b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/arbeidsgiver-mock.ts new file mode 100644 index 0000000000..a5c1abb86b --- /dev/null +++ b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/arbeidsgiver-mock.ts @@ -0,0 +1,5 @@ +export default { + organisasjoner: [], + privateArbeidsgivere: null, + frilansoppdrag: null, +}; diff --git a/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/sak-mock.ts b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/sak-mock.ts new file mode 100644 index 0000000000..b341db01ac --- /dev/null +++ b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/sak-mock.ts @@ -0,0 +1,90 @@ +export default [ + { + barn: { + fødselsdato: '2008-07-27', + fornavn: 'RAVGUL', + mellomnavn: null, + etternavn: 'LØVETANN', + aktørId: '2175638020356', + identitetsnummer: '27870899799', + }, + søknad: { + søknadId: 'generert', + versjon: '1.0.0', + mottattDato: '2024-12-10T08:47:11.929Z', + søker: { + norskIdentitetsnummer: '00000000000', + }, + språk: null, + ytelse: { + type: 'PLEIEPENGER_SYKT_BARN', + barn: { + norskIdentitetsnummer: '00000000000', + fødselsdato: null, + }, + søknadsperiode: ['2024-12-03/2024-12-05'], + endringsperiode: [], + trekkKravPerioder: [], + opptjeningAktivitet: {}, + dataBruktTilUtledning: null, + annetDataBruktTilUtledning: null, + infoFraPunsj: null, + bosteder: { + perioder: {}, + perioderSomSkalSlettes: {}, + }, + utenlandsopphold: { + perioder: {}, + perioderSomSkalSlettes: {}, + }, + beredskap: { + perioder: {}, + perioderSomSkalSlettes: {}, + }, + nattevåk: { + perioder: {}, + perioderSomSkalSlettes: {}, + }, + tilsynsordning: { + perioder: { + '2024-12-03/2024-12-05': { + etablertTilsynTimerPerDag: 'PT0S', + }, + }, + }, + lovbestemtFerie: { + perioder: { + '2024-12-04/2024-12-05': { + skalHaFerie: false, + }, + }, + }, + arbeidstid: { + arbeidstakerList: [], + frilanserArbeidstidInfo: { + perioder: { + '2024-12-03/2024-12-05': { + jobberNormaltTimerPerDag: 'PT0S', + faktiskArbeidTimerPerDag: 'PT0S', + }, + }, + }, + selvstendigNæringsdrivendeArbeidstidInfo: null, + }, + uttak: { + perioder: {}, + }, + omsorg: { + relasjonTilBarnet: null, + beskrivelseAvOmsorgsrollen: null, + }, + erSammenMedBarnet: null, + }, + journalposter: [], + begrunnelseForInnsending: { + tekst: null, + }, + kildesystem: null, + }, + }, +]; diff --git "a/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/s\303\270ker-mock.ts" "b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/s\303\270ker-mock.ts" new file mode 100644 index 0000000000..b80d44748e --- /dev/null +++ "b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/debug/s\303\270ker-mock.ts" @@ -0,0 +1,9 @@ +export default { + aktørId: '2486083225079', + fødselsdato: '1987-10-09', + fødselsnummer: '09908799647', + fornavn: 'NORA', + mellomnavn: null, + etternavn: 'Etternavn', + myndig: true, +}; diff --git a/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/index.ts b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/index.ts index c2f120bae9..d07e3ca605 100644 --- a/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/index.ts +++ b/apps/endringsmelding-pleiepenger/src/mocks/data/scenario/index.ts @@ -3,6 +3,7 @@ import { ArbeidsaktivitetUtenArbeidsgiver } from './arbeidsaktivitet-uten-arbeid import { ArbeidsgiverIkkeISak } from './arbeidsgiver-ikke-i-sak/ArbeidsgiverIkkeISak'; import { ArbeidsgiverOgFrilanser } from './arbeidsgiver-og-frilanser/ArbeidsgiverOgFrilanser'; import { ArbeidsgivereOgFrilanser } from './arbeidsgivere-og-frilanser/ArbeidsgivereOgFrilanser'; +import { Debug } from './debug/Debug'; import { EnArbeidsgiverEnPeriode } from './en-arbeidsgiver-en-periode/EnArbeidsgiverEnPeriode'; import { EnArbeidsgiverToPerioder } from './en-arbeidsgiver-to-perioder/EnArbeidsgiverToPerioder'; import { FlereSaker } from './flere-saker/FlereSaker'; @@ -22,6 +23,7 @@ export const mockData: ScenarioMap = { ['selvstendig-næringsdrivende']: SelvstendigNæringsdrivende, ['flere-saker']: FlereSaker, ['ingen-sak']: IngenSak, + ['debug']: Debug, ['arbeidsgiver-ikke-i-sak']: ArbeidsgiverIkkeISak, ['arbeidsaktivitet-uten-arbeidsgiver']: ArbeidsaktivitetUtenArbeidsgiver, ['ugyldig-k9-format']: UgyldigK9Format, diff --git a/apps/endringsmelding-pleiepenger/src/mocks/msw/handlers.ts b/apps/endringsmelding-pleiepenger/src/mocks/msw/handlers.ts index 1b6ee250cf..95e5bc66f6 100644 --- a/apps/endringsmelding-pleiepenger/src/mocks/msw/handlers.ts +++ b/apps/endringsmelding-pleiepenger/src/mocks/msw/handlers.ts @@ -11,6 +11,7 @@ export const getHandlers = () => { const { sak, arbeidsgiver, søker } = getScenarioMockData(scenario.value); const handlers = [ + http.post('*amplitude*', () => new HttpResponse(null, { status: 200 })), http.get(`${baseUrl}/health/isAlive`, () => new HttpResponse(null, { status: 200 })), http.get(`${baseUrl}/health/isReady`, () => new HttpResponse(null, { status: 200 })), http.get(`${baseUrl}/oppslag/soker`, () => {