import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('@tauri-apps/api/path', () => ({ join: vi.fn(async (...parts: string[]) => parts .map((part, index) => index === 0 ? part.replace(/[\\/]+$/, '') : part.replace(/^[\\/]+|[\\/]+$/g, '')) .join('/') ) })); import { downloadLocationEquals, DEFAULT_CATEGORY_SUBFOLDERS, deriveBatchFolderName, formatDerivedCategoryPath, normalizeCategorySubfolder, normalizeDownloadLocationSettings, resolveInitialAddWindowLocation, resolveCategoryDestination, resolveSubfolderDestination, sanitizeBatchFolderName, subfolderFromDerivedCategoryPath } from './downloadLocations'; describe('download locations', () => { it('matches backend platform path case semantics', () => { expect(downloadLocationEquals('D:\\Downloads', 'Movie.MP4', 'd:/downloads', 'movie.mp4', 'windows')).toBe(true); expect(downloadLocationEquals('/Users/Test', 'Movie.MP4', '/users/test', 'movie.mp4', 'macos')).toBe(false); expect(downloadLocationEquals('/home/Test', 'Movie.MP4', '/home/test', 'movie.mp4', 'linux')).toBe(false); }); it('uses a remembered Add-window directory only when the setting is enabled', () => { expect(resolveInitialAddWindowLocation( 'D:\\Downloads', true, 'D:\\Course_Videos' )).toEqual({ path: 'D:\\Course_Videos', isManual: true }); expect(resolveInitialAddWindowLocation( 'D:\\Downloads', false, 'D:\\Course_Videos' )).toEqual({ path: 'D:\\Downloads', isManual: false }); }); it('falls back to the normalized base folder when no directory was remembered', () => { expect(resolveInitialAddWindowLocation(' ', true, null)) .toEqual({ path: '~/Downloads', isManual: false }); }); it('derives safe batch folder names from title, referer, and timestamp fallback', () => { expect(deriveBatchFolderName( 'Gallery / Chapter: 1', 'https://example.com/gallery' )).toBe('Gallery - Chapter- 1'); expect(deriveBatchFolderName( 'New Tab', 'https://example.com/gallery/part-1?token=secret' )).toBe('example.com-gallery-part-1'); expect(deriveBatchFolderName( 'New Tab', 'https://example.com/gallery', new Date('2026-07-20T12:34:56.789Z'), ['example.part1.rar', 'example.part2.rar'] )).toBe('example'); expect(deriveBatchFolderName( 'CON', null, new Date('2026-07-20T12:34:56.789Z') )).toBe('batch-CON'); expect(deriveBatchFolderName( '', null, new Date('2026-07-20T12:34:56.789Z') )).toBe('firelink-batch-2026-07-20-12-34-56-789'); expect(deriveBatchFolderName( '', null, new Date('2026-07-20T12:34:56.789Z'), ['example.part1.rar', 'example.part2.rar'] )).toBe('example'); expect(sanitizeBatchFolderName('../Example: Parts')).toBe('Example- Parts'); expect(sanitizeBatchFolderName('😀'.repeat(100))).toBe('😀'.repeat(96)); }); it('places the optional folder below an existing category destination', async () => { expect(await resolveSubfolderDestination( '/Users/test/Downloads/Compressed', 'Example: Parts' )).toBe('/Users/test/Downloads/Compressed/Example- Parts'); }); beforeEach(() => { vi.clearAllMocks(); }); it('migrates legacy derived directories without creating overrides', () => { const settings = normalizeDownloadLocationSettings({ defaultDownloadPath: '/Users/test/Downloads', downloadDirectories: { Movies: '/Users/test/Downloads/Movies', Documents: '/Users/test/Downloads/Documents' } }); expect(settings.baseDownloadFolder).toBe('/Users/test/Downloads'); expect(settings.categorySubfolders).toEqual(DEFAULT_CATEGORY_SUBFOLDERS); expect(settings.categoryDirectoryOverrides).toEqual({}); }); it('preserves legacy custom category directories as overrides', () => { const settings = normalizeDownloadLocationSettings({ defaultDownloadPath: '/Users/test/Downloads', downloadDirectories: { Video: '/Volumes/Media/Movies' } }); expect(settings.categoryDirectoryOverrides.Movies).toBe('/Volumes/Media/Movies'); }); it('resolves automatic and overridden category destinations', async () => { const automatic = normalizeDownloadLocationSettings({ baseDownloadFolder: '/Users/test/Downloads', categorySubfolders: { Movies: 'Video Files' } }); expect(await resolveCategoryDestination(automatic, 'Movies')) .toBe('/Users/test/Downloads/Video Files'); automatic.categoryDirectoryOverrides.Movies = '/Volumes/Media'; expect(await resolveCategoryDestination(automatic, 'Movies')).toBe('/Volumes/Media'); }); it('defaults Torrent downloads to the Torrents folder and respects overrides', async () => { const settings = normalizeDownloadLocationSettings({ baseDownloadFolder: '/Users/test/Downloads' }); expect(settings.categorySubfolders.Torrents).toBe('Torrents'); expect(await resolveCategoryDestination(settings, 'Torrents')) .toBe('/Users/test/Downloads/Torrents'); settings.categoryDirectoryOverrides.Torrents = '/Volumes/Archive/Torrents'; expect(await resolveCategoryDestination(settings, 'Torrents')) .toBe('/Volumes/Archive/Torrents'); settings.categorySubfoldersEnabled = false; expect(await resolveCategoryDestination(settings, 'Torrents')) .toBe('/Users/test/Downloads'); }); it('defaults category subfolders on and sends every category to the base folder when disabled', async () => { const automatic = normalizeDownloadLocationSettings({ baseDownloadFolder: '/Users/test/Downloads' }); expect(automatic.categorySubfoldersEnabled).toBe(true); const disabled = normalizeDownloadLocationSettings({ baseDownloadFolder: '/Users/test/Downloads', categorySubfoldersEnabled: false, categorySubfolders: { Movies: 'Video Files' }, categoryDirectoryOverrides: { Movies: '/Volumes/Media' } }); expect(disabled.categorySubfoldersEnabled).toBe(false); expect(await resolveCategoryDestination(disabled, 'Movies')) .toBe('/Users/test/Downloads'); expect(await resolveCategoryDestination(disabled, 'Documents')) .toBe('/Users/test/Downloads'); }); it('keeps an explicit empty category subfolder as the base folder', async () => { const settings = normalizeDownloadLocationSettings({ baseDownloadFolder: '/Users/test/Downloads', categorySubfolders: { Movies: '' } }); expect(settings.categorySubfolders.Movies).toBe(''); expect(settings.categorySubfolders.Documents).toBe('Documents'); expect(formatDerivedCategoryPath('/Users/test/Downloads', '')).toBe('/Users/test/Downloads'); expect(await resolveCategoryDestination(settings, 'Movies')).toBe('/Users/test/Downloads'); }); it('keeps category subfolders relative and permits nested folders', () => { expect(normalizeCategorySubfolder('../Media/./Movies', 'Movies')).toBe('Media/Movies'); expect(normalizeCategorySubfolder('C:\\Media\\Movies', 'Movies')).toBe('Media/Movies'); expect(normalizeCategorySubfolder('../../', 'Movies')).toBe('Movies'); }); it('formats and parses derived category paths with platform path separators', () => { expect(formatDerivedCategoryPath('/Users/test/Downloads', 'Video Files')) .toBe('/Users/test/Downloads/Video Files'); expect(formatDerivedCategoryPath('D:\\Downloads', 'Video Files')) .toBe('D:\\Downloads\\Video Files'); expect(subfolderFromDerivedCategoryPath( 'D:\\Downloads\\Video Files', 'd:\\downloads' )).toBe('Video Files'); expect(subfolderFromDerivedCategoryPath( '/Users/test/Downloads/Video Files', '/Users/test/Downloads' )).toBe('Video Files'); expect(subfolderFromDerivedCategoryPath('/Volumes/Media', '/Users/test/Downloads')) .toBeNull(); }); });