1
0
Fork 0

refactor(P5b-1): CommunityToolkit.Mvvm 도입 + OptionsViewModel 추출 (옵션 로직 테스트화)

MVVM 첫 슬라이스 — 테스트 불가능했던 코드비하인드 BuildOptions의 순수 옵션 구성 로직을
헤드리스 단위테스트 가능한 OptionsViewModel.ToConvertOptions로 추출. XAML 바인딩 미변경(런타임 위험 0).

- CommunityToolkit.Mvvm 8.2.2(CPM) + App.csproj. App/ViewModels/OptionsViewModel.cs:
  ObservableObject + [ObservableProperty](Quality/CustomOutputDirectory/ConflictRule/AiTaskIndex/
  TargetLanguage/VideoPreferGpu) → 향후 옵션 패널 직접 바인딩 토대. ToConvertOptions()는 순수.
- MainWindow.BuildOptions: 컨트롤 값을 VM에 반영 후 ToConvertOptions() 위임(동작 동일).
- Tests → App ProjectReference 추가(App 뷰모델 헤드리스 테스트 가능화).
- OptionsViewModelTests 10케이스(품질매핑·AVIF clamp·커스텀폴더 trim·AI작업·대상언어·충돌/GPU).

71 → 81개 테스트 전부 그린, 빌드 0경고/0오류.
This commit is contained in:
Yun Chan 2026-06-02 09:51:49 +09:00
parent 729b9b699d
commit ba5738e705
6 changed files with 136 additions and 29 deletions

View file

@ -9,6 +9,7 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Everything2Everything.App.Shell;
using Everything2Everything.App.ViewModels;
using Everything2Everything.Core;
using LossClass = Everything2Everything.Core.Providers.LossClass;
@ -18,6 +19,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
{
private readonly ConversionEngine _engine;
private readonly ISettingsStore _settings;
private readonly OptionsViewModel _options = new();
private readonly ObservableCollection<QueueItem> _activeQueue = new();
private readonly ObservableCollection<DateGroup> _pastResults = new();
private CancellationTokenSource? _cts;
@ -278,35 +280,14 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
private ConvertOptions BuildOptions()
{
var q = (int)QualitySlider.Value;
var custom = OutputPathTextBox.Text?.Trim();
var hasCustom = !string.IsNullOrEmpty(custom);
// AI 작업 종류 (txt↔txt / md↔md 등 텍스트 변환 시 LlmProvider가 사용)
var aiTask = (AiTaskCombo?.SelectedIndex ?? 0) switch
{
1 => "translate",
2 => "proofread",
_ => "summarize",
};
var targetLang = AiTargetLangBox?.Text?.Trim();
return new ConvertOptions
{
OnCollision = _conflictRule,
OutputLocation = hasCustom ? OutputLocation.Custom : OutputLocation.SubfolderBesideSource,
CustomOutputDirectory = hasCustom ? custom : null,
Jpeg = new JpegEncodingOptions { Quality = q },
Webp = new WebpEncodingOptions { Quality = q },
Avif = new AvifEncodingOptions { Quality = Math.Clamp(q - 30, 1, 100) },
Ai = new AiOptions
{
Task = aiTask,
TargetLanguage = string.IsNullOrEmpty(targetLang) ? null : targetLang,
},
VideoPreferGpu = _settings.Get("video.gpu") != "false",
};
// 뷰의 컨트롤 값을 OptionsViewModel에 반영한 뒤, 불변 ConvertOptions 구성은 VM의 순수 메서드에 위임한다.
_options.Quality = (int)QualitySlider.Value;
_options.CustomOutputDirectory = OutputPathTextBox.Text?.Trim();
_options.ConflictRule = _conflictRule;
_options.AiTaskIndex = AiTaskCombo?.SelectedIndex ?? 0;
_options.TargetLanguage = AiTargetLangBox?.Text?.Trim();
_options.VideoPreferGpu = _settings.Get("video.gpu") != "false";
return _options.ToConvertOptions();
}
// ============== Process queue ==============