import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import uploaderUtils from './uploader-utils' const { clampOneLevel, composeName, extOf, createNoResumeFingerprint, createNoResumeStorage, fmt, isDetailedError, isImageFile, isLikelyMobileUA, isVideoFile, normalizeRows, sanitizeDesc, sanitizeFolderName, stemOf, } = uploaderUtils describe('Uploader helpers', () => { let logSpy: ReturnType beforeAll(() => { logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) }) afterAll(() => { logSpy.mockRestore() }) it('sanitizes description and folder names', () => { expect(sanitizeDesc(' Hello world! ')).toBe('Hello_world_') expect(sanitizeDesc('')).toBe('upload') expect(sanitizeFolderName(' /my folder/with/depth ')).toBe('my_folder') expect(sanitizeFolderName('bad#$name')).toBe('bad_name') }) it('computes extension, stem, and composed final name', () => { expect(extOf('clip.MP4')).toBe('mp4') expect(extOf('noext')).toBe('') expect(stemOf('My.Video.File.mp4')).toBe('My_Video_File') expect(composeName('2026-04-09', 'Trip Clip', 'My.Video.File.mp4')).toBe( '2026.04.09.Trip_Clip.My_Video_File.mp4', ) }) it('clamps one-level paths and normalizes row payload shapes', () => { expect(clampOneLevel('/photos/2026/trip/')).toBe('photos') expect(clampOneLevel('')).toBe('') const rows = normalizeRows([ { Name: 'a', Path: 'a', IsDir: true, Size: 12, Mtime: 55 }, { name: 'b', path: 'b', is_dir: false, size: 3, mtime: 99 }, null, ] as any) expect(rows[0]).toEqual({ name: 'a', path: 'a', is_dir: true, size: 12, mtime: 55 }) expect(rows[1]).toEqual({ name: 'b', path: 'b', is_dir: false, size: 3, mtime: 99 }) expect(rows[2]).toEqual({ name: '', path: '', is_dir: false, size: 0, mtime: 0 }) }) it('detects video/image files by type and extension', () => { const mkFile = (name: string, type: string) => new File(['x'], name, { type }) expect(isVideoFile(mkFile('x.mp4', 'video/mp4'))).toBe(true) expect(isVideoFile(mkFile('x.mkv', ''))).toBe(true) expect(isVideoFile(mkFile('x.txt', 'text/plain'))).toBe(false) expect(isImageFile(mkFile('x.jpg', 'image/jpeg'))).toBe(true) expect(isImageFile(mkFile('x.heic', ''))).toBe(true) expect(isImageFile(mkFile('x.mp4', 'video/mp4'))).toBe(false) }) it('identifies detailed tus errors and mobile UA heuristics', () => { expect(isDetailedError({ originalRequest: {} })).toBe(true) expect(isDetailedError({ originalResponse: {} })).toBe(true) expect(isDetailedError({})).toBe(false) expect(typeof isLikelyMobileUA()).toBe('boolean') }) it('covers the mobile UA and size formatting branches', () => { const originalMatchMedia = window.matchMedia Object.defineProperty(window, 'matchMedia', { configurable: true, value: () => ({ matches: true }), }) expect(isLikelyMobileUA()).toBe(true) Object.defineProperty(window, 'matchMedia', { configurable: true, value: () => ({ matches: false }), }) expect(isLikelyMobileUA()).toBe(false) if (originalMatchMedia) { Object.defineProperty(window, 'matchMedia', { configurable: true, value: originalMatchMedia, }) } else { delete (window as any).matchMedia } expect(fmt(1024 * 1024 * 1024)).toBe('1.0 GB') }) it('formats sizes and exposes no-resume upload helpers', async () => { expect(fmt(0)).toBe('0 B') expect(fmt(1536)).toBe('1.5 KB') const storage = createNoResumeStorage() await expect(storage.addUpload({})).resolves.toBeUndefined() await expect(storage.removeUpload({})).resolves.toBeUndefined() await expect(storage.listUploads()).resolves.toEqual([]) await expect(storage.findUploadsByFingerprint('abc')).resolves.toEqual([]) await expect(createNoResumeFingerprint()).resolves.toMatch(/^noresume-/) }) it('returns false without a window and normalizes non-array rows', () => { const originalWindow = window vi.stubGlobal('window', undefined as any) expect(isLikelyMobileUA()).toBe(false) vi.stubGlobal('window', originalWindow as any) expect(normalizeRows('bad input' as any)).toEqual([]) }) })