import { describe, expect, it } from 'vitest'; import { type DownloadMode, buildDownloadArgs, expectedExtension, parseProgress, } from '../src/services/downloadPlan.js'; describe('expectedExtension', () => { it('retourne mp4 pour le mode vidéo', () => { const mode: DownloadMode = { kind: 'video', options: { subtitles: false, embedThumbnail: false }, }; expect(expectedExtension(mode)).toBe('mp4'); }); it('retourne le format demandé pour le mode audio', () => { expect(expectedExtension({ kind: 'audio', format: 'opus' })).toBe('opus'); }); it('retourne jpg pour le mode miniature', () => { expect(expectedExtension({ kind: 'thumbnail' })).toBe('jpg'); }); }); describe('buildDownloadArgs', () => { const url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; const outputDir = '/tmp/abc123'; it('mode vidéo sans options : pas de sous-titres ni de miniature embarquée', () => { const args = buildDownloadArgs( url, { kind: 'video', options: { subtitles: false, embedThumbnail: false } }, outputDir, ); expect(args).toEqual([ '--newline', '--no-playlist', '--add-metadata', '-f', 'bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]/bv*+ba/b', '--merge-output-format', 'mp4', '-o', `${outputDir}/%(title)s.%(ext)s`, '--', url, ]); }); it('mode vidéo avec sous-titres et miniature embarquée', () => { const args = buildDownloadArgs( url, { kind: 'video', options: { subtitles: true, embedThumbnail: true } }, outputDir, ); expect(args).toContain('--embed-subs'); expect(args).toContain('--embed-thumbnail'); expect(args).toEqual( expect.arrayContaining(['--write-subs', '--write-auto-subs', '--sub-langs', 'fr,en']), ); }); it('mode audio : extraction au format demandé', () => { const args = buildDownloadArgs(url, { kind: 'audio', format: 'opus' }, outputDir); expect(args).toEqual([ '--newline', '--no-playlist', '--add-metadata', '-x', '--audio-format', 'opus', '-o', `${outputDir}/%(title)s.%(ext)s`, '--', url, ]); }); it('mode miniature : téléchargement de la vidéo ignoré', () => { const args = buildDownloadArgs(url, { kind: 'thumbnail' }, outputDir); expect(args).toEqual([ '--newline', '--no-playlist', '--add-metadata', '--skip-download', '--write-thumbnail', '--convert-thumbnails', 'jpg', '-o', `thumbnail:${outputDir}/%(title)s`, '--', url, ]); }); it("place toujours l'URL après un séparateur `--`, quel que soit le mode", () => { for (const mode of [ { kind: 'video', options: { subtitles: false, embedThumbnail: false } }, { kind: 'audio', format: 'mp3' }, { kind: 'thumbnail' }, ] satisfies DownloadMode[]) { const args = buildDownloadArgs(url, mode, outputDir); const separatorIndex = args.indexOf('--'); expect(separatorIndex).toBeGreaterThanOrEqual(0); expect(args[separatorIndex + 1]).toBe(url); expect(args).toHaveLength(separatorIndex + 2); } }); }); describe('parseProgress', () => { it('extrait un pourcentage entier', () => { expect(parseProgress('[download] 42% of 10.00MiB at 1.00MiB/s ETA 00:05')).toBe(42); }); it('extrait un pourcentage décimal', () => { expect(parseProgress('[download] 99.5% of 10.00MiB')).toBe(99.5); }); it('retourne null pour une ligne sans progression', () => { expect(parseProgress('[info] Writing video metadata as JSON')).toBeNull(); }); it('borne le résultat entre 0 et 100', () => { expect(parseProgress('[download] 100.0% of 10.00MiB')).toBe(100); }); });