Files
AeroFetch/test/profileResolve.test.ts
T
debont80 ed1daf8bef feat(pinchflat): Media Profiles — named download presets per Library source
A MediaProfile bundles { kind, quality, options, outputTemplate, extraArgs }
that a Source points at via Source.profileId, overriding the global defaults
field-by-field for that source's downloads.

Backend mirrors CommandTemplate: profiles.ts (createJsonStore, PROFILES_MAX,
isProfileLike + sanitize), CRUD IPC (profiles:list/save/remove) + preload +
useProfiles store; Source.profileId + sources:set-profile. Pure resolution
in @shared/profileResolve.ts (profile → global, per field; unit-tested).
Applied in the sources-store enqueueItems; a per-download collectionOutput-
Template override threads to buildArgs so a profile's outputTemplate wins.

UI: ProfileManager (mirrors TemplateManager) in a new Settings → Advanced →
Media profiles card, plus a per-source Profile picker in the Library source
detail. Both live-verified in the preview; 354 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 16:02:07 -04:00

68 lines
2.5 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { resolveProfile, type ProfileDefaults } from '../src/shared/profileResolve'
import { DEFAULT_DOWNLOAD_OPTIONS, type MediaProfile } from '@shared/ipc'
const DEFAULTS: ProfileDefaults = {
kind: 'video',
videoQuality: '1080p',
audioQuality: 'Best',
options: DEFAULT_DOWNLOAD_OPTIONS,
outputTemplate: ''
}
describe('resolveProfile (Media Profiles precedence)', () => {
it('no profile → pure global defaults', () => {
expect(resolveProfile(null, DEFAULTS)).toEqual({
kind: 'video',
quality: '1080p',
options: DEFAULT_DOWNLOAD_OPTIONS,
outputTemplate: '',
extraArgs: ''
})
// undefined behaves the same as null
expect(resolveProfile(undefined, DEFAULTS).kind).toBe('video')
})
it('a profile field overrides its global default', () => {
const p: MediaProfile = { id: 'p', name: 'Music', kind: 'audio', quality: 'Best' }
const r = resolveProfile(p, DEFAULTS)
expect(r.kind).toBe('audio')
expect(r.quality).toBe('Best')
})
it('an audio-kind profile without its own quality picks up the global AUDIO quality', () => {
const p: MediaProfile = { id: 'p', name: 'Audio', kind: 'audio' }
expect(resolveProfile(p, DEFAULTS).quality).toBe('Best') // not the video 1080p
})
it('a video-kind profile without its own quality picks up the global VIDEO quality', () => {
const p: MediaProfile = { id: 'p', name: 'Vid', kind: 'video' }
expect(resolveProfile(p, DEFAULTS).quality).toBe('1080p')
})
it('carries the profile outputTemplate + extraArgs; unset extraArgs is ""', () => {
const p: MediaProfile = {
id: 'p',
name: 'Custom',
outputTemplate: '%(uploader)s/%(title)s.%(ext)s'
}
const r = resolveProfile(p, DEFAULTS)
expect(r.outputTemplate).toBe('%(uploader)s/%(title)s.%(ext)s')
expect(r.extraArgs).toBe('')
})
it('a profile options override replaces the global options object', () => {
const customOptions = { ...DEFAULT_DOWNLOAD_OPTIONS, embedSubtitles: true }
const p: MediaProfile = { id: 'p', name: 'Subs', options: customOptions }
expect(resolveProfile(p, DEFAULTS).options).toBe(customOptions)
})
it('a profile that pins only quality keeps the global kind + options', () => {
const p: MediaProfile = { id: 'p', name: 'HiQ', quality: '720p' }
const r = resolveProfile(p, DEFAULTS)
expect(r.kind).toBe('video')
expect(r.quality).toBe('720p')
expect(r.options).toBe(DEFAULT_DOWNLOAD_OPTIONS)
})
})