import { describe, it, expect } from 'vitest' import { cleanError, describeDownloadError } from '../src/main/log' describe('cleanError', () => { it('returns the last ERROR line, stripped of its prefix', () => { const stderr = [ '[youtube] gHiZA4O5PQM: Downloading webpage', 'ERROR: [youtube] gHiZA4O5PQM: Video unavailable. This video is private' ].join('\n') expect(cleanError(stderr)).toBe( '[youtube] gHiZA4O5PQM: Video unavailable. This video is private' ) }) it('falls back to the last line when nothing matches /error/', () => { expect(cleanError('one\ntwo\nthree')).toBe('three') }) it('returns empty string for blank stderr', () => { expect(cleanError('')).toBe('') }) }) describe('describeDownloadError', () => { // A real non-verbose SponsorBlock-remove failure: yt-dlp emits only the // [ModifyChapters] step lines and the bare wrapper error. const modifyChaptersFailure = [ '[info] qkUTB65OfAc: Downloading 1 format(s): 137+251', '[SponsorBlock] Found 1 segments in the SponsorBlock database', '[Merger] Merging formats into "video.mp4"', 'Deleting original file video.f251.webm (pass -k to keep)', 'Deleting original file video.f137.mp4 (pass -k to keep)', '[ModifyChapters] Re-encoding "video.mp4" with appropriate keyframes', '[ModifyChapters] Removing chapters from video.mp4', 'ERROR: Postprocessing: Conversion failed!' ].join('\n') it('names the failing post-processing step for the bare "Conversion failed!" wrapper', () => { const msg = describeDownloadError(modifyChaptersFailure) expect(msg.startsWith('Postprocessing: Conversion failed!')).toBe(true) expect(msg).toContain('ModifyChapters') }) it('skips [download] progress and "Deleting original file" bookkeeping as context', () => { const msg = describeDownloadError(modifyChaptersFailure) expect(msg).not.toContain('Deleting original file') expect(msg).not.toMatch(/\[download\]/) }) it('leaves a self-explanatory (non-postprocessing) error unchanged', () => { const stderr = [ '[youtube] gHiZA4O5PQM: Downloading webpage', 'ERROR: [youtube] gHiZA4O5PQM: Video unavailable. This video is private' ].join('\n') expect(describeDownloadError(stderr)).toBe(cleanError(stderr)) }) it('does not echo the headline back as its own context', () => { const stderr = 'ERROR: Postprocessing: Conversion failed!' expect(describeDownloadError(stderr)).toBe('Postprocessing: Conversion failed!') }) it('caps an over-long context line', () => { const longPath = 'x'.repeat(400) const stderr = [ `[ModifyChapters] Re-encoding "${longPath}" with appropriate keyframes`, 'ERROR: Postprocessing: Conversion failed!' ].join('\n') const msg = describeDownloadError(stderr) expect(msg).toContain('…') // headline + separator + a single capped context line, nowhere near 400 chars. expect(msg.length).toBeLessThan(300) }) it('returns empty string for blank stderr (falls through to the caller fallback)', () => { expect(describeDownloadError('')).toBe('') }) })