Files
TREK/client/tests/unit/utils/fileDownload.native.test.ts
T
Maurice 8b6a65023f Merge dev into feat/native-apps
Brings the native apps branch up to dev (NestJS-only server, MikroORM
repositories, tracked sessions, per-domain locales, the lint ratchets) and
ports its new code onto that architecture:

- server: /api/auth/native moves onto injected services. The session is
  minted by the auth domain's new LoginSessionService (login bookkeeping and
  SessionsService.issue in one transaction), the one-time codes live behind a
  NativeHandoffStore port with a capped in-memory implementation that drops
  expired codes on write instead of running a timer, both routes declare
  response contracts, and the exchange answers { success: true }. Unit and
  e2e suites run on createTestModule and the ORM factories.
- shared: native strings for az, et, sk and th, native wired into every
  locale, the address placeholder as a full URL.
- client: the account and export changes go into dev's shared hooks
  (useAccountSettings, useTripExport), the native server card is a
  desktop/phone pair on useNativeServer, the hand-off call moves out of the
  page hook into native/, best-effort native failures go through
  reportError instead of the console, and the address screen is known to
  knip and lint:i18n-keys.
- package-lock.json regenerated on Linux.
2026-10-25 17:04:15 +01:00

48 lines
2.1 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { downloadBlob, downloadFile, openFile } from '../../../src/utils/fileDownload'
import { shareFile } from '../../../src/native/nativeFiles'
vi.mock('../../../src/db/offlineDb', () => ({ getCachedBlob: vi.fn() }))
vi.mock('../../../src/native/platform', () => ({ isNativeApp: () => true }))
vi.mock('../../../src/native/nativeFiles', async () => {
const actual = await vi.importActual<typeof import('../../../src/native/nativeFiles')>('../../../src/native/nativeFiles')
return { ...actual, shareFile: vi.fn() }
})
const pdf = new Blob(['%PDF'], { type: 'application/pdf' })
beforeEach(() => {
vi.mocked(shareFile).mockReset().mockResolvedValue()
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true, status: 200, blob: () => Promise.resolve(pdf) }))
})
describe('fileDownload inside the native app', () => {
it('hands an in-memory blob to the share sheet instead of an anchor', () => {
const click = vi.spyOn(HTMLAnchorElement.prototype, 'click')
downloadBlob(pdf, 'report.pdf')
expect(shareFile).toHaveBeenCalledWith(pdf, 'report.pdf')
expect(click).not.toHaveBeenCalled()
})
it('logs a share that failed rather than losing it', async () => {
const report = vi.fn()
vi.stubGlobal('reportError', report)
vi.mocked(shareFile).mockRejectedValue(new Error('no space'))
downloadBlob(pdf, 'report.pdf')
await vi.waitFor(() => expect(report).toHaveBeenCalledWith(expect.any(Error)))
})
it('shares a protected file under its given name, or the one from its URL', async () => {
await downloadFile('/api/trips/1/files/2/download', 'Boarding pass.pdf')
expect(shareFile).toHaveBeenLastCalledWith(pdf, 'Boarding pass.pdf')
await downloadFile('/uploads/files/ticket%20A.pdf')
expect(shareFile).toHaveBeenLastCalledWith(pdf, 'ticket A.pdf')
})
it('opens files through the share sheet too, since there is no tab to open them in', async () => {
await openFile('/api/trips/1/files/2/download')
expect(shareFile).toHaveBeenLastCalledWith(pdf, 'download')
})
})