From a3b04887a166847b2368882ec447fbe54e7ef9d1 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sun, 25 Oct 2026 17:16:02 +0100 Subject: [PATCH] fix(native): new lint warnings and the MapLibre protocol type after the dev merge Awaits that sat in loops in the native-auth tests run together, the hand-off hook marks its navigations as ignored, the native tests drop their non-null assertions, and the offline protocol answers with the data type MapLibre 6.15 expects. --- client/src/native/browserSignIn.test.ts | 6 +++--- client/src/native/nativeApp.test.ts | 3 ++- client/src/native/offlineTiles.ts | 6 ++++-- client/src/pages/nativeHandoff/useNativeHandoff.ts | 4 ++-- server/tests/e2e/native-auth.e2e.test.ts | 4 +--- server/tests/unit/nest/native-auth.service.test.ts | 9 +++++---- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/client/src/native/browserSignIn.test.ts b/client/src/native/browserSignIn.test.ts index 29a489e83..b9236915a 100644 --- a/client/src/native/browserSignIn.test.ts +++ b/client/src/native/browserSignIn.test.ts @@ -43,11 +43,11 @@ describe('signInThroughBrowser', () => { await signInThroughBrowser('oidc') - const opened = new URL(vi.mocked(TrekShell.authenticate).mock.calls[0]![0].url) + const opened = new URL(vi.mocked(TrekShell.authenticate).mock.calls[0][0].url) expect(opened.pathname).toBe('/native-handoff') expect(opened.searchParams.get('start')).toBe('oidc') - expect(vi.mocked(TrekShell.authenticate).mock.calls[0]![0].callbackScheme).toBe('com.liketrek.trek') - const { code, verifier } = vi.mocked(nativeAuthApi.exchange).mock.calls[0]![0] + expect(vi.mocked(TrekShell.authenticate).mock.calls[0][0].callbackScheme).toBe('com.liketrek.trek') + const { code, verifier } = vi.mocked(nativeAuthApi.exchange).mock.calls[0][0] expect(code).toBe('abc') expect(bytesToHex(sha256(new TextEncoder().encode(verifier)))).toBe(opened.searchParams.get('challenge')) }) diff --git a/client/src/native/nativeApp.test.ts b/client/src/native/nativeApp.test.ts index 06809976c..c76c647e9 100644 --- a/client/src/native/nativeApp.test.ts +++ b/client/src/native/nativeApp.test.ts @@ -18,7 +18,8 @@ vi.mock('../sync/networkMode', () => ({ isEffectivelyOffline: vi.fn() })) function backButtonHandler(): (event: { canGoBack: boolean }) => void { const call = vi.mocked(App.addListener).mock.calls.find(([name]) => name === 'backButton') - return call![1] as unknown as (event: { canGoBack: boolean }) => void + if (!call) throw new Error('no backButton listener') + return call[1] as unknown as (event: { canGoBack: boolean }) => void } beforeEach(() => { diff --git a/client/src/native/offlineTiles.ts b/client/src/native/offlineTiles.ts index 3a6248878..87e6a87c8 100644 --- a/client/src/native/offlineTiles.ts +++ b/client/src/native/offlineTiles.ts @@ -37,6 +37,8 @@ export function offlineStyle(style: T): T { } type Doc = Record +/** What a protocol handler may hand MapLibre back. */ +type ProtocolData = Awaited>['data'] const isDoc = (value: unknown): value is Doc => typeof value === 'object' && value !== null && !Array.isArray(value) const rewriteList = (value: unknown): unknown => (Array.isArray(value) ? value.map((v) => (typeof v === 'string' ? toOfflineUrl(v) : v)) : value) @@ -90,11 +92,11 @@ async function load(url: string, networkFirst: boolean, signal: AbortSignal): Pr throw new Error(`Not available offline: ${url}`) } -export const offlineProtocol: AddProtocolAction = async (params: RequestParameters, abortController: AbortController): Promise> => { +export const offlineProtocol: AddProtocolAction = async (params: RequestParameters, abortController: AbortController): Promise> => { const url = fromOfflineUrl(params.url) const response = await load(url, params.type === 'json', abortController.signal) if (!response.ok) throw new Error(`HTTP ${response.status} for ${url}`) - if (params.type === 'json') return { data: rewriteDocument(await response.json()) } + if (params.type === 'json') return { data: rewriteDocument(await response.json()) as ProtocolData } if (params.type === 'string') return { data: await response.text() } return { data: await response.arrayBuffer() } } diff --git a/client/src/pages/nativeHandoff/useNativeHandoff.ts b/client/src/pages/nativeHandoff/useNativeHandoff.ts index 2660884a8..58ad20da9 100644 --- a/client/src/pages/nativeHandoff/useNativeHandoff.ts +++ b/client/src/pages/nativeHandoff/useNativeHandoff.ts @@ -36,7 +36,7 @@ export function useNativeHandoff() { window.location.replace('/api/auth/oidc/login?remember=1') return } - navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true }) + void navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true }) }, [valid, isLoading, isAuthenticated, params, returnPath, navigate]) const openApp = async (): Promise => { @@ -51,7 +51,7 @@ export function useNativeHandoff() { const switchAccount = async (): Promise => { await logout() - navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true }) + void navigate(`/login?redirect=${encodeURIComponent(returnPath)}`, { replace: true }) } return { diff --git a/server/tests/e2e/native-auth.e2e.test.ts b/server/tests/e2e/native-auth.e2e.test.ts index a3bf88d99..68674a34f 100644 --- a/server/tests/e2e/native-auth.e2e.test.ts +++ b/server/tests/e2e/native-auth.e2e.test.ts @@ -129,9 +129,7 @@ describe('Native sign-in hand-off e2e', () => { }); it('NATIVE-E2E-005: rate-limits the exchange per ip', async () => { - for (let i = 0; i < 20; i++) { - await exchange({ code: `nope-${i}`, verifier }); - } + await Promise.all(Array.from({ length: 20 }, (_, i) => exchange({ code: `nope-${i}`, verifier }))); const blocked = await exchange({ code: 'nope', verifier }); expect(blocked.status).toBe(429); }); diff --git a/server/tests/unit/nest/native-auth.service.test.ts b/server/tests/unit/nest/native-auth.service.test.ts index 7e95b1b4c..92dd96c93 100644 --- a/server/tests/unit/nest/native-auth.service.test.ts +++ b/server/tests/unit/nest/native-auth.service.test.ts @@ -130,16 +130,17 @@ describe('InMemoryNativeHandoffStore', () => { it('NATIVE-STORE-001: holds at most its cap of live codes', async () => { const store = new InMemoryNativeHandoffStore(); const later = Date.now() + 60_000; - for (let i = 0; i < MAX_PENDING_HANDOFFS; i++) { - expect(await store.put(`c${i}`, entry(later))).toBe(true); - } + const stored = await Promise.all( + Array.from({ length: MAX_PENDING_HANDOFFS }, (_, i) => store.put(`c${i}`, entry(later))), + ); + expect(stored.every(Boolean)).toBe(true); expect(await store.put('one-too-many', entry(later))).toBe(false); }); it('NATIVE-STORE-002: expired codes are dropped on the next write and make room', async () => { const store = new InMemoryNativeHandoffStore(); const past = Date.now() - 1; - for (let i = 0; i < MAX_PENDING_HANDOFFS; i++) await store.put(`c${i}`, entry(past)); + await Promise.all(Array.from({ length: MAX_PENDING_HANDOFFS }, (_, i) => store.put(`c${i}`, entry(past)))); expect(await store.put('fresh', entry(Date.now() + 60_000))).toBe(true); expect(await store.take('c0')).toBeNull();