diff --git a/.eslintrc b/.eslintrc index 0c776ec027a..015b41f4bdf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,8 +18,8 @@ "no-restricted-syntax": [ "error", { - "selector": "AssignmentExpression[left.property.name='href'][right.type=/(Template)?Literal/]", - "message": "Do not assign window.location.href to a string or string template to avoid losing i18n parameters" + "selector": "AssignmentExpression[left.property.name='href'][right.type=/(Template)?Literal/],NewExpression[callee.name=URL][arguments.0.type=/(Template)?Literal/]", + "message": "Avoid hard-coded string URLs, since they will not include the current locale" } ], "react-hooks/exhaustive-deps": "error" @@ -42,6 +42,7 @@ "packageDir": "." } ], + "no-restricted-syntax": "off", "testing-library/await-async-events": "error", "testing-library/await-async-queries": "error", "testing-library/await-async-utils": "error", diff --git a/app/javascript/packages/session/requests.spec.ts b/app/javascript/packages/session/requests.spec.ts index 2793584b50a..24607acb2c3 100644 --- a/app/javascript/packages/session/requests.spec.ts +++ b/app/javascript/packages/session/requests.spec.ts @@ -1,9 +1,11 @@ import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; import type { SetupServer } from 'msw/node'; -import { SESSIONS_URL, requestSessionStatus, extendSession } from './requests'; +import { requestSessionStatus, extendSession } from './requests'; import type { SessionLiveStatusResponse, SessionTimedOutStatusResponse } from './requests'; +const SESSIONS_URL = '/api/internal/sessions'; + describe('requestSessionStatus', () => { let server: SetupServer; @@ -22,7 +24,7 @@ describe('requestSessionStatus', () => { }); it('resolves to the status', async () => { - const result = await requestSessionStatus(); + const result = await requestSessionStatus(SESSIONS_URL); expect(result).to.deep.equal({ isLive: false }); }); @@ -46,7 +48,7 @@ describe('requestSessionStatus', () => { }); it('resolves to the status', async () => { - const result = await requestSessionStatus(); + const result = await requestSessionStatus(SESSIONS_URL); expect(result).to.deep.equal({ isLive: true, timeout: new Date(timeout) }); }); @@ -65,7 +67,7 @@ describe('requestSessionStatus', () => { }); it('resolves to the status', async () => { - const result = await requestSessionStatus(); + const result = await requestSessionStatus(SESSIONS_URL); expect(result).to.deep.equal({ isLive: false }); }); @@ -84,7 +86,7 @@ describe('requestSessionStatus', () => { }); it('throws an error', async () => { - await expect(requestSessionStatus()).to.be.rejected(); + await expect(requestSessionStatus(SESSIONS_URL)).to.be.rejected(); }); }); }); @@ -109,7 +111,7 @@ describe('extendSession', () => { }); it('resolves to the status', async () => { - const result = await extendSession(); + const result = await extendSession(SESSIONS_URL); expect(result).to.deep.equal({ isLive: true, timeout: new Date(timeout) }); }); @@ -128,7 +130,7 @@ describe('extendSession', () => { }); it('resolves to the status', async () => { - const result = await extendSession(); + const result = await extendSession(SESSIONS_URL); expect(result).to.deep.equal({ isLive: false }); }); @@ -147,7 +149,7 @@ describe('extendSession', () => { }); it('throws an error', async () => { - await expect(extendSession()).to.be.rejected(); + await expect(extendSession(SESSIONS_URL)).to.be.rejected(); }); }); }); diff --git a/app/javascript/packages/session/requests.ts b/app/javascript/packages/session/requests.ts index 048834b6ca5..a04e2cc213f 100644 --- a/app/javascript/packages/session/requests.ts +++ b/app/javascript/packages/session/requests.ts @@ -52,8 +52,6 @@ interface SessionTimedOutStatus { export type SessionStatus = SessionLiveStatus | SessionTimedOutStatus; -export const SESSIONS_URL = new URL('/api/internal/sessions', window.location.href).toString(); - function mapSessionStatusResponse( response: R, ): SessionLiveStatus; @@ -83,10 +81,11 @@ function handleUnauthorizedStatusResponse(error: ResponseError) { /** * Request the current session status. Returns a promise resolving to the current session status. * + * @param sessionsURL The URL for the session API * @return A promise resolving to the current session status */ -export const requestSessionStatus = (): Promise => - request(SESSIONS_URL) +export const requestSessionStatus = (sessionsURL: string): Promise => + request(sessionsURL) .catch(handleUnauthorizedStatusResponse) .then(mapSessionStatusResponse); @@ -94,9 +93,10 @@ export const requestSessionStatus = (): Promise => * Request that the current session be kept alive. Returns a promise resolving to the updated * session status. * + * @param sessionsURL The URL for the session API * @return A promise resolving to the updated session status. */ -export const extendSession = (): Promise => - request(SESSIONS_URL, { method: 'PUT' }) +export const extendSession = (sessionsURL: string): Promise => + request(sessionsURL, { method: 'PUT' }) .catch(handleUnauthorizedStatusResponse) .then(mapSessionStatusResponse); diff --git a/app/javascript/packs/document-capture.tsx b/app/javascript/packs/document-capture.tsx index 2cc3f153a23..2cf4b4bafeb 100644 --- a/app/javascript/packs/document-capture.tsx +++ b/app/javascript/packs/document-capture.tsx @@ -40,6 +40,7 @@ interface AppRootData { previousStepUrl: string; docAuthSelfieDesktopTestMode: string; locationsUrl: string; + addressSearchUrl: string; } const appRoot = document.getElementById('document-capture-form')!; @@ -109,6 +110,7 @@ const { previousStepUrl, docAuthSelfieDesktopTestMode, locationsUrl: locationsURL, + addressSearchUrl: addressSearchURL, } = appRoot.dataset as DOMStringMap & AppRootData; let parsedUsStatesTerritories = []; @@ -125,7 +127,7 @@ const App = composeComponents( value: { inPersonURL, locationsURL, - addressSearchURL: new URL('/api/addresses', window.location.href).toString(), + addressSearchURL, inPersonOutageMessageEnabled: inPersonOutageMessageEnabled === 'true', inPersonOutageExpectedUpdateDate, inPersonFullAddressEntryEnabled: inPersonFullAddressEntryEnabled === 'true', diff --git a/app/javascript/packs/session-timeout-ping.ts b/app/javascript/packs/session-timeout-ping.ts index 339ce097c86..a86f60254b5 100644 --- a/app/javascript/packs/session-timeout-ping.ts +++ b/app/javascript/packs/session-timeout-ping.ts @@ -11,7 +11,8 @@ const defaultTime = '60'; const frequency = parseInt(warningEl?.dataset.frequency || defaultTime, 10) * 1000; const warning = parseInt(warningEl?.dataset.warning || defaultTime, 10) * 1000; const start = parseInt(warningEl?.dataset.start || defaultTime, 10) * 1000; -const timeoutUrl = warningEl?.dataset.timeoutUrl!; +const timeoutURL = warningEl?.dataset.timeoutUrl!; +const sessionsURL = warningEl?.dataset.sessionsUrl!; const modal = document.querySelector('lg-modal.session-timeout-modal')!; const keepaliveEl = document.getElementById('session-keepalive-btn'); @@ -19,8 +20,8 @@ const countdownEls: NodeListOf = modal.querySelectorAll('lg-co function success({ isLive, timeout }: SessionStatus) { if (!isLive) { - if (timeoutUrl) { - forceRedirect(timeoutUrl); + if (timeoutURL) { + forceRedirect(timeoutURL); } return; } @@ -43,12 +44,12 @@ function success({ isLive, timeout }: SessionStatus) { setTimeout(ping, nextPingTimeout); } -const ping = () => requestSessionStatus().then(success); +const ping = () => requestSessionStatus(sessionsURL).then(success); function keepalive() { modal.hide(); countdownEls.forEach((countdownEl) => countdownEl.stop()); - extendSession(); + extendSession(sessionsURL); } keepaliveEl?.addEventListener('click', keepalive, false); diff --git a/app/views/idv/shared/_document_capture.html.erb b/app/views/idv/shared/_document_capture.html.erb index 4c0b25b78ae..8a67edf5393 100644 --- a/app/views/idv/shared/_document_capture.html.erb +++ b/app/views/idv/shared/_document_capture.html.erb @@ -43,6 +43,7 @@ how_to_verify_url: idv_how_to_verify_url, previous_step_url: @previous_step_url, locations_url: idv_in_person_usps_locations_url, + address_search_url: '', } %> <%= simple_form_for( :doc_auth, diff --git a/app/views/session_timeout/_ping.html.erb b/app/views/session_timeout/_ping.html.erb index 9c600645c3d..4a8aaa1b8fb 100644 --- a/app/views/session_timeout/_ping.html.erb +++ b/app/views/session_timeout/_ping.html.erb @@ -1,6 +1,7 @@ <%= tag.div id: 'session-timeout-cntnr', data: { timeout_url: new_user_session_url(timeout: :session, request_id: sp_session[:request_id]), + sessions_url: api_internal_sessions_path, warning: warning, start: start, frequency: frequency,