8ab85da67e
H5: HistoryEntry now stores formatId+formatHasAudio; redownload passes them
back to addFromUrl so the original yt-dlp format is reused, not guessed.
Compound quality labels stripped to the preset prefix for old history.
L72: thumbnail now passed in redownload so re-queued rows keep their art.
H6: TerminalView log capped at MAX_LOG_LINES=2000 to prevent unbounded growth.
M21: --audio-quality omitted for lossless audioFormats (flac/wav).
L166: fmtEta hour rollover fixed in all 3 locations -- 2h00:00 not 120:00.
L165: queueStats formatSpeed precision aligned with fmtBytes.
L55: QueueItem suppresses sizeLabel when already in probed-format quality label.
L47: probeMeta null sends empty meta event so Resolving clears via SR6.
L68: notifiedBackground resets on window show for subsequent close-while-downloading.
L76: dup warning falls back to URL when title is still a placeholder.
L91: SettingsView onFormatSelect uses indexOf instead of unsafe tuple cast.
L98: Embed chapters field gets a hint text.
L108: BrowserWindow created with explicit title AeroFetch.
L37: pure formatters extracted to src/main/lib/formatters.ts and unit-tested.
L38: buildArgs test covers webm thumbnail suppression.
L39: CROP_SQUARE_PPA test is structural not exact-string.
L40: looksLikeUrl/looksLikeSingleVideo extracted to src/renderer/src/lib/urlHelpers.ts.
typecheck + 242 tests + eslint + prettier green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
// Import from the pure formatter module directly — no electron import chain (L37).
|
|
import { fmtBytes, fmtEta, parseProgress } from '../src/main/lib/formatters'
|
|
|
|
describe('fmtBytes', () => {
|
|
it('formats bytes under 1 KB verbatim', () => {
|
|
expect(fmtBytes(0)).toBe('0 B')
|
|
expect(fmtBytes(512)).toBe('512 B')
|
|
expect(fmtBytes(1023)).toBe('1023 B')
|
|
})
|
|
|
|
it('switches to KB at 1024', () => {
|
|
expect(fmtBytes(1024)).toBe('1.0 KB')
|
|
expect(fmtBytes(1536)).toBe('1.5 KB')
|
|
})
|
|
|
|
it('uses one decimal below 100, none above', () => {
|
|
expect(fmtBytes(99 * 1024)).toBe('99.0 KB')
|
|
expect(fmtBytes(100 * 1024)).toBe('100 KB')
|
|
expect(fmtBytes(150 * 1024)).toBe('150 KB')
|
|
})
|
|
|
|
it('switches to MB and GB', () => {
|
|
expect(fmtBytes(1024 * 1024)).toBe('1.0 MB')
|
|
expect(fmtBytes(1024 * 1024 * 1024)).toBe('1.0 GB')
|
|
})
|
|
})
|
|
|
|
describe('fmtEta', () => {
|
|
it('returns undefined for null / undefined input', () => {
|
|
expect(fmtEta(undefined)).toBeUndefined()
|
|
expect(fmtEta(null as unknown as number)).toBeUndefined()
|
|
})
|
|
|
|
it('formats seconds to M:SS', () => {
|
|
expect(fmtEta(0)).toBe('0:00')
|
|
expect(fmtEta(59)).toBe('0:59')
|
|
expect(fmtEta(60)).toBe('1:00')
|
|
expect(fmtEta(90)).toBe('1:30')
|
|
expect(fmtEta(3599)).toBe('59:59')
|
|
})
|
|
|
|
it('formats hours to H:MM:SS (L166)', () => {
|
|
expect(fmtEta(3600)).toBe('1:00:00')
|
|
expect(fmtEta(3661)).toBe('1:01:01')
|
|
expect(fmtEta(7260)).toBe('2:01:00')
|
|
expect(fmtEta(7384)).toBe('2:03:04')
|
|
})
|
|
|
|
it('clamps negative input to 0', () => {
|
|
expect(fmtEta(-10)).toBe('0:00')
|
|
})
|
|
})
|
|
|
|
describe('parseProgress', () => {
|
|
const makeRaw = (parts: string[]): string => parts.join('|')
|
|
|
|
it('returns null when there are fewer than 6 fields', () => {
|
|
expect(parseProgress('')).toBeNull()
|
|
expect(parseProgress('downloading|100|200|200')).toBeNull()
|
|
})
|
|
|
|
it('parses a normal progress line', () => {
|
|
// status | downloaded | total | totalEst | speed | eta
|
|
const raw = makeRaw(['downloading', '524288', '1048576', '1048576', '262144', '2'])
|
|
const p = parseProgress(raw)
|
|
expect(p).not.toBeNull()
|
|
expect(p!.progress).toBeCloseTo(0.5)
|
|
expect(p!.eta).toBe('0:02')
|
|
expect(p!.sizeLabel).toBe('1.0 MB')
|
|
expect(p!.sizeUnknown).toBe(false)
|
|
})
|
|
|
|
it('falls back to totalEst when total is NA', () => {
|
|
const raw = makeRaw(['downloading', '100', 'NA', '1000', '50', '18'])
|
|
const p = parseProgress(raw)
|
|
expect(p).not.toBeNull()
|
|
expect(p!.sizeLabel).toBe('1000 B')
|
|
expect(p!.sizeUnknown).toBe(false)
|
|
})
|
|
|
|
it('marks sizeUnknown when both total and totalEst are NA', () => {
|
|
const raw = makeRaw(['downloading', '100', 'NA', 'NA', 'NA', 'NA'])
|
|
const p = parseProgress(raw)
|
|
expect(p).not.toBeNull()
|
|
expect(p!.sizeUnknown).toBe(true)
|
|
expect(p!.eta).toBeUndefined()
|
|
expect(p!.speed).toBeUndefined()
|
|
})
|
|
|
|
it('clamps progress to 1.0', () => {
|
|
const raw = makeRaw(['downloading', '2000', '1000', '1000', '500', '0'])
|
|
const p = parseProgress(raw)
|
|
expect(p!.progress).toBe(1)
|
|
})
|
|
|
|
it('formats an ETA over 1 hour correctly (L166)', () => {
|
|
const raw = makeRaw(['downloading', '100', '1000', '1000', '10', '3661'])
|
|
const p = parseProgress(raw)
|
|
expect(p!.eta).toBe('1:01:01')
|
|
})
|
|
})
|