1559 lines
68 KiB
C#
1559 lines
68 KiB
C#
using Paca.App;
|
|
using Paca.App.Browser.Features;
|
|
using Paca.App.ViewModels;
|
|
using Paca.Browser;
|
|
using Paca.Core.Config;
|
|
using Paca.Tests.Infrastructure;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.E2E;
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 통합 RED 계약 메가 파이프라인 (docs/UI_USECASES.md §A~§Y, 150+ 전수 계약).
|
|
/// </summary>
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §G 접근성 유즈케이스(docs/UI_USECASES.md §G, UC-A01~A20).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseAccessibilityRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_A01_자동화_속성이_하나라도_사용된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("AutomationProperties"),
|
|
"UC-A01", "화면 낭독기 지원을 위한 AutomationProperties 사용이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A02_주요_컨트롤에_자동화_이름이_부여된다()
|
|
{
|
|
var count = System.Text.RegularExpressions.Regex.Matches(SourceTree.MainWindowXaml, "AutomationProperties\\.Name").Count;
|
|
SourceTree.Usecase(
|
|
count >= 15,
|
|
$"UC-A02", $"주요 컨트롤 15개 이상에 AutomationProperties.Name 이 필요하다 (현재 {count}개)");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_A03_상태_변화가_낭독기에_자동_전달된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LiveSetting"),
|
|
"UC-A03", "상태바·진행률의 LiveSetting 선언이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A04_시스템_고대비_모드를_감지한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("HighContrast"),
|
|
"UC-A04", "SystemParameters.HighContrast 감지 처리가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A05_키보드_포커스_비주얼이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SharedXaml.Contains("FocusVisualStyle"),
|
|
"UC-A05", "공용 FocusVisualStyle 정의가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A06_Tab_순서가_지정된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("TabIndex"),
|
|
"UC-A06", "논리적 Tab 이동을 위한 TabIndex 지정이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A07_ToolTipService_설정이_사용된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ToolTipService"),
|
|
"UC-A07", "툴팁 표시 시간 등 ToolTipService 설정이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A08_접근_키_AccessText가_사용된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("AccessText"),
|
|
"UC-A08", "Alt 접근 키를 위한 AccessText 사용이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A09_사용자_지정_자동화_피어가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("AutomationPeer"),
|
|
"UC-A09", "목록·탭 컨트롤용 AutomationPeer 구현이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A10_HelpText가_제공된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("AutomationProperties.HelpText"),
|
|
"UC-A10", "컨트롤 추가 설명 AutomationProperties.HelpText 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A11_완료_상태가_색과_무관하게_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("SuccessIcon"),
|
|
"UC-A11", "완료 상태 아이콘(SuccessIcon)으로 색 무의존 표시가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A12_접근성_설정_섹션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("접근성"),
|
|
"UC-A12", "설정창에 '접근성' 섹션이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A13_UI_확대_배율을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "UiFontScale"),
|
|
"UC-A13", "저시력 지원용 AppConfig.UiFontScale 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A14_팝업_키보드_탐색이_설정된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("KeyboardNavigation.TabNavigation"),
|
|
"UC-A14", "팝업 내 키보드 탐색(KeyboardNavigation) 설정이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A15_자막_표시_옵션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "ShowCaptions"),
|
|
"UC-A15", "AppConfig.ShowCaptions 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A16_애니메이션_감소_설정이_스타일에_반영된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SharedXaml.Contains("ReduceMotion"),
|
|
"UC-A16", "Shared.xaml 이 ReduceMotion 설정을 스타일에 반영해야 한다");
|
|
|
|
[Fact]
|
|
public void UC_A17_리더_모드_HTML에_언어가_선언된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.ReaderModeCs.Contains("lang="),
|
|
"UC-A17", "리더 모드 HTML 에 lang=\"ko\" 선언이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A18_접근성_선언_문서가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.DocFileExists("ACCESSIBILITY.md"),
|
|
"UC-A18", "접근성 지원 범위 문서(docs/ACCESSIBILITY.md)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_A19_라이트_진행_트랙이_배경과_구별된다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeLightXaml, "TrackBrush");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeLightXaml, "PanelBg");
|
|
Assert.False(fg is null || bg is null, "UC-A19 — 테마 브러시 파싱 실패");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 3.0, $"UC-A19 RED — 라이트 TrackBrush/PanelBg 대비 {ratio:F2}:1 < 3:1 (비텍스트 구성요소 기준)");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_A20_버튼_테두리가_카드_배경과_구별된다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "BorderBrush");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "CardBg");
|
|
Assert.False(fg is null || bg is null, "UC-A20 — 테마 브러시 파싱 실패");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 3.0, $"UC-A20 RED — 다크 BorderBrush/CardBg 대비 {ratio:F2}:1 < 3:1 (클릭 대상 식별 기준)");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §E 다운로드 관리자 유즈케이스(docs/UI_USECASES.md §E, UC-D01~D30).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseDownloadsRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_D01_다운로드_정렬_순서를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "DownloadSortOrder"),
|
|
"UC-D01", "AppConfig.DownloadSortOrder 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D02_진행중_완료_실패_필터_탭이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("DownloadFilterTabs"),
|
|
"UC-D02", "다운로드 필터 탭(DownloadFilterTabs) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D03_다운로드_목록을_검색할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("DownloadSearchBox"),
|
|
"UC-D03", "다운로드 검색 상자(DownloadSearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D04_남은_시간_ETA가_카드에_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "RemainingText"),
|
|
"UC-D04", "다운로드 카드 VM 에 RemainingText(남은 시간) 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D05_현재_속도가_카드에_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "SpeedText"),
|
|
"UC-D05", "다운로드 카드 VM 에 SpeedText 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D06_전체_일시정지를_할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "PauseAllDownloads"),
|
|
"UC-D06", "MainWindow.PauseAllDownloads() 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D07_전체_재개를_할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "ResumeAllDownloads"),
|
|
"UC-D07", "MainWindow.ResumeAllDownloads() 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D08_완료_항목_자동_치움을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "AutoClearCompleted"),
|
|
"UC-D08", "AppConfig.AutoClearCompleted 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D09_재시도_횟수가_카드에_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "RetryCount"),
|
|
"UC-D09", "다운로드 카드 VM 에 RetryCount 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D10_세그먼트_진행이_시각화된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "SegmentMap"),
|
|
"UC-D10", "세그먼트별 진행 맵(SegmentMap) 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D11_토스트_알림_컴포넌트가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("ToastNotifier") is not null,
|
|
"UC-D11", "다운로드 완료 알림을 담당하는 ToastNotifier 컴포넌트가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D12_알림_소리를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "NotifySound"),
|
|
"UC-D12", "AppConfig.NotifySound 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D13_완료_시_폴더_자동_열기를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "OpenFolderOnComplete"),
|
|
"UC-D13", "AppConfig.OpenFolderOnComplete 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D14_카드_우클릭_컨텍스트_메뉴가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("CardContextMenu"),
|
|
"UC-D14", "다운로드 카드 컨텍스트 메뉴(CardContextMenu)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D15_카드를_펼쳐_상세_정보를_본다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("CardDetailExpander"),
|
|
"UC-D15", "카드 상세 펼침(CardDetailExpander) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D16_대기_현황_요약이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("QueueSummaryText"),
|
|
"UC-D16", "패널의 대기 현황 요약(QueueSummaryText)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D17_속도_스파크라인이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("SpeedSparkline"),
|
|
"UC-D17", "속도 추이 스파크라인(SpeedSparkline) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D18_전체_속도_합계가_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("GlobalSpeedText"),
|
|
"UC-D18", "전체 다운로드 속도(GlobalSpeedText) 표시가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D19_파일_총_크기가_카드에_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "TotalSizeText"),
|
|
"UC-D19", "카드 VM 에 TotalSizeText 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D20_자막_언어를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("SubtitleLangBox"),
|
|
"UC-D20", "설정창에 자막 언어 선택(SubtitleLangBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D21_다운로드_스케줄러가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("DownloadScheduler") is not null,
|
|
"UC-D21", "시간대 예약을 담당하는 DownloadScheduler 컴포넌트가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D22_속도_상한을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "MaxDownloadSpeedKbps"),
|
|
"UC-D22", "AppConfig.MaxDownloadSpeedKbps 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D23_목록을_CSV로_내보낼_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "ExportDownloads"),
|
|
"UC-D23", "MainWindow.ExportDownloads() 내보내기 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D24_항목_우선순위를_정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "Priority"),
|
|
"UC-D24", "카드 VM 에 대기열 우선순위(Priority) 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D25_중복_다운로드_경고를_받는다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "ConfirmDuplicateDownload"),
|
|
"UC-D25", "MainWindow.ConfirmDuplicateDownload() 확인 절차가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D26_최종_저장_파일명이_카드에_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(DownloadItemViewModel), "FinalFileName"),
|
|
"UC-D26", "카드 VM 에 충돌 회피 후 파일명(FinalFileName)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D27_오류_메시지_복사_버튼이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("CopyErrorButton"),
|
|
"UC-D27", "오류 복사 버튼(CopyErrorButton) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D28_페이지_다운로드도_전체_일시정지가_된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(PageDownloadTracker), "PauseAll"),
|
|
"UC-D28", "PageDownloadTracker.PauseAll() 메서드가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D29_미디어_종류_배지가_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("MediaKindBadge"),
|
|
"UC-D29", "감지 미디어 종류 배지(MediaKindBadge) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_D30_사이트별_그룹_헤더가_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("SiteGroupHeader"),
|
|
"UC-D30", "사이트별 묶음 헤더(SiteGroupHeader) 요소가 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §H 키보드·마우스 인터랙션 유즈케이스(docs/UI_USECASES.md §H, UC-K01~K20).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseInteractionRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_K01_단축키_설정_항목이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("단축키"),
|
|
"UC-K01", "설정창에 '단축키' 항목이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K02_단축키를_사용자가_바꿀_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "Shortcuts"),
|
|
"UC-K02", "AppConfig.Shortcuts 사용자 지정 단축키 맵이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K03_단축키_관리_컴포넌트가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("ShortcutManager") is not null,
|
|
"UC-K03", "충돌 검증을 포함한 ShortcutManager 컴포넌트가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K04_단축키_전체_목록_팝업이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ShortcutHelpPopup"),
|
|
"UC-K04", "단축키 도움말 팝업(ShortcutHelpPopup)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K05_마우스_제스처로_탐색할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("MouseGesture") || SourceTree.MainWindowCs.Contains("XButton"),
|
|
"UC-K05", "마우스 제스처(XButton 등) 뒤로/앞처 처리가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K06_휠_클릭으로_새_탭을_연다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("MiddleButton"),
|
|
"UC-K06", "링크 휠 클릭(MiddleButton) 새 탭 처리가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K07_붙여넣기_즉시_이동_옵션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "PasteAndGo"),
|
|
"UC-K07", "AppConfig.PasteAndGo 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K08_주소창_포커스_시_전체_선택_옵션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "FocusSelectsAddress"),
|
|
"UC-K08", "AppConfig.FocusSelectsAddress 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K09_모두_일시정지_빠른_명령이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("모두 일시정지"),
|
|
"UC-K09", "팔레트/버튼의 '모두 일시정지' 명령이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K10_Esc로_모든_팝업을_한번에_닫는다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("CloseAllPopups") || SourceTree.MainWindowCs.Contains("CloseAllOverlays"),
|
|
"UC-K10", "열린 팝업 일괄 닫기(CloseAllPopups) 처리가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K11_다운로드_중_종료_확인을_받는다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("종료하시겠습니까"),
|
|
"UC-K11", "다운로드 진행 중 종료 확인 대화상자가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K12_F12_개발자_도구를_연다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("OpenDevTools"),
|
|
"UC-K12", "F12 개발자 도구(OpenDevTools) 처리가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K13_프로필_전환_기능이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("프로필 전환"),
|
|
"UC-K13", "세션 프로필 전환 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K14_현재_줌_배율이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ZoomIndicator"),
|
|
"UC-K14", "줌 배율 표시(ZoomIndicator) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K15_기본_줌을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "DefaultZoomFactor"),
|
|
"UC-K15", "AppConfig.DefaultZoomFactor 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K16_즐겨찾기_관리_창이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("BookmarkManagerWindow") is not null,
|
|
"UC-K16", "즐겨찾기 관리 창(BookmarkManagerWindow)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K17_방문_기록_개별_삭제가_된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("DeleteHistory_Click"),
|
|
"UC-K17", "방문 기록 개별 삭제(DeleteHistory_Click) 처리기가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K18_영역을_지정해_캡처할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("RegionCapture"),
|
|
"UC-K18", "영역 지정 캡처(RegionCapture) 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K19_노트를_Ctrl_S로_저장한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("NotesSave_Click"),
|
|
"UC-K19", "노트 저장(NotesSave_Click) 처리기가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_K20_링크_우클릭으로_바로_받는다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("링크 다운로드"),
|
|
"UC-K20", "페이지 링크 우클릭 '링크 다운로드' 메뉴가 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §C 레이아웃·밀도·창 상태 유즈케이스(docs/UI_USECASES.md §C, UC-L01~L26).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseLayoutRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_L01_UI_밀도_설정이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "UiDensity"),
|
|
"UC-L01", "컴팩트/보통/여유 밀도를 고르는 AppConfig.UiDensity 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L02_설정창에서_밀도를_바꿀_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("밀도"),
|
|
"UC-L02", "설정창에 '밀도' 항목이 노출되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L03_작은_간격_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Spacing.S"),
|
|
"UC-L03", "간격 토큰 Spacing.S 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L04_중간_간격_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Spacing.M"),
|
|
"UC-L04", "간격 토큰 Spacing.M 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L05_큰_간격_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Spacing.L"),
|
|
"UC-L05", "간격 토큰 Spacing.L 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L06_카드_모서리_둥글기_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "CornerRadius.Card"),
|
|
"UC-L06", "카드 둥근 모서리 토큰 CornerRadius.Card 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L07_컴팩트_모드_상태가_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "CompactMode"),
|
|
"UC-L07", "AppConfig.CompactMode 로 재시작 후에도 컴팩트 상태가 유지되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L08_툴바에_컴팩트_토글_버튼이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("Name=\"CompactModeBtn\""),
|
|
"UC-L08", "툴바의 CompactModeBtn 로 컴팩트 모드를 빠르게 켜고 끌 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L09_세로_탭_기본_여부를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "VerticalTabsDefault"),
|
|
"UC-L09", "AppConfig.VerticalTabsDefault 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L10_분할_화면_비율이_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "SplitViewRatio"),
|
|
"UC-L10", "분할 화면 비율(AppConfig.SplitViewRatio)이 저장·복원되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L11_창_위치_크기가_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "WindowBounds"),
|
|
"UC-L11", "AppConfig.WindowBounds 로 창 위치·크기가 복원되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L12_최대화_상태가_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "WindowMaximized"),
|
|
"UC-L12", "AppConfig.WindowMaximized 로 최대화 여부가 복원되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L13_루트_요소가_레이아웃_반올림을_켠다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("UseLayoutRounding"),
|
|
"UC-L13", "MainWindow 가 UseLayoutRounding 으로 고DPI 흐림을 방지해야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L14_공용_스타일이_픽셀_정렬을_켠다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SharedXaml.Contains("SnapsToDevicePixels"),
|
|
"UC-L14", "Shared.xaml 스타일이 SnapsToDevicePixels 로 선명도를 보장해야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L15_상태바_자동_숨김을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "StatusBarAutoHide"),
|
|
"UC-L15", "AppConfig.StatusBarAutoHide 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L16_툴바_버튼_노출을_직접_고른다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "ToolbarButtons"),
|
|
"UC-L16", "AppConfig.ToolbarButtons 로 버튼 표시 여부를 지정할 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L17_북마크바_표시_여부가_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "BookmarkBarVisible"),
|
|
"UC-L17", "AppConfig.BookmarkBarVisible 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L18_탭_스트립_위치를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "TabStripPosition"),
|
|
"UC-L18", "AppConfig.TabStripPosition 로 탭 스트립 위치를 고를 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L19_패널_폭을_더블클릭으로_초기화한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "ResetPanelWidth"),
|
|
"UC-L19", "MainWindow.ResetPanelWidth 로 패널 폭을 기본값으로 되돌릴 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L20_패널_최소_폭을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "RightPanelMinWidth"),
|
|
"UC-L20", "AppConfig.RightPanelMinWidth 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L21_애니메이션_축소_설정이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "ReduceMotion"),
|
|
"UC-L21", "AppConfig.ReduceMotion 로 애니메이션을 줄일 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L22_빠른_애니메이션_지속시간_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Duration.Fast"),
|
|
"UC-L22", "지속 시간 토큰 Duration.Fast 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L23_일반_애니메이션_지속시간_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Duration.Normal"),
|
|
"UC-L23", "지속 시간 토큰 Duration.Normal 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L24_컴팩트_엣지_호버_반응_속도를_설정한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "EdgeRevealDelayMs"),
|
|
"UC-L24", "AppConfig.EdgeRevealDelayMs 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_L25_UI_전체_배율을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "UiScale"),
|
|
"UC-L25", "AppConfig.UiScale 로 UI 전체 배율을 조절할 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_L26_전체화면_여부가_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "FullScreen"),
|
|
"UC-L26", "AppConfig.FullScreen 속성이 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §I 라이브러리·재생목록·즐겨찾기·기록·노트 유즈케이스(docs/UI_USECASES.md §I, UC-M01~M22).
|
|
/// 데스크톱 앱에는 아직 라이브러리 뷰가 없으므로 전부 RED 다.
|
|
/// </summary>
|
|
public class UiUsecaseLibraryRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_M01_라이브러리_뷰가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LibraryPopup") || SourceTree.MainWindowXaml.Contains("LibraryBtn"),
|
|
"UC-M01", "받은 파일을 보는 라이브러리 뷰(LibraryPopup/LibraryBtn)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M02_라이브러리를_그리드_리스트로_전환한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LibraryViewToggle"),
|
|
"UC-M02", "그리드/리스트 전환(LibraryViewToggle)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M03_라이브러리를_검색할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LibrarySearchBox"),
|
|
"UC-M03", "라이브러리 검색 상자(LibrarySearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M04_라이브러리를_정렬할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LibrarySortMenu"),
|
|
"UC-M04", "라이브러리 정렬 메뉴(LibrarySortMenu)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M05_항목에_추가일이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("AddedDateText"),
|
|
"UC-M05", "라이브러리 항목의 추가일 표시(AddedDateText)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M06_항목에_썸네일이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LibraryThumbnail"),
|
|
"UC-M06", "라이브러리 썸네일(LibraryThumbnail)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M07_항목을_더블클릭해_재생한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("LibraryPlay_Click"),
|
|
"UC-M07", "라이브러리 재생(LibraryPlay_Click) 처리기가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M08_항목을_삭제할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("LibraryDelete_Click"),
|
|
"UC-M08", "라이브러리 삭제(LibraryDelete_Click) 처리기가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M09_항목_폴더를_열_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("LibraryOpenFolder_Click"),
|
|
"UC-M09", "라이브러리 폴더 열기(LibraryOpenFolder_Click)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M10_라이브러리_빈_상태_안내가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("LibraryEmptyHint"),
|
|
"UC-M10", "라이브러리 빈 상태 안내(LibraryEmptyHint)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M11_재생목록_셔플이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("PlaylistShuffle"),
|
|
"UC-M11", "재생목록 셔플(PlaylistShuffle) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M12_반복_모드가_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "PlaylistLoopMode"),
|
|
"UC-M12", "AppConfig.PlaylistLoopMode 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M13_재생목록을_파일로_저장_불러온다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("PlaylistExport"),
|
|
"UC-M13", "재생목록 내보내기/불러오기(PlaylistExport)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M14_재생목록_순서를_바꾼다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("PlaylistMoveUp"),
|
|
"UC-M14", "재생목록 순서 변경(PlaylistMoveUp) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M15_재생목록을_검색한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("PlaylistSearchBox"),
|
|
"UC-M15", "재생목록 검색 상자(PlaylistSearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M16_즐겨찾기를_폴더로_정리한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("BookmarkFolder"),
|
|
"UC-M16", "즐겨찾기 폴더(BookmarkFolder) 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M17_즐겨찾기를_편집한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("EditBookmark"),
|
|
"UC-M17", "즐겨찾기 편집(EditBookmark) 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M18_즐겨찾기를_검색한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("BookmarkSearchBox"),
|
|
"UC-M18", "즐겨찾기 검색 상자(BookmarkSearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M19_즐겨찾기를_HTML로_내보낸다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("ExportBookmarks"),
|
|
"UC-M19", "즐겨찾기 내보내기(ExportBookmarks) 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M20_방문_기록을_날짜별로_묶는다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("GroupHistoryByDate"),
|
|
"UC-M20", "방문 기록 날짜 묶음(GroupHistoryByDate) 기능이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M21_방문_기록을_검색한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("HistorySearchBox"),
|
|
"UC-M21", "방문 기록 검색 상자(HistorySearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_M22_노트를_검색한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("NotesSearchBox"),
|
|
"UC-M22", "노트 검색 상자(NotesSearchBox)가 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §F 설정 창 유즈케이스(docs/UI_USECASES.md §F, UC-S01~S20).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseSettingsRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_S01_설정을_검색으로_찾을_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("SettingsSearchBox"),
|
|
"UC-S01", "설정 검색 상자(SettingsSearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S02_카테고리_내비게이션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("SettingsNavList"),
|
|
"UC-S02", "좌측 카테고리 내비게이션(SettingsNavList)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S03_재시작_필요_변경을_배너로_알린다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("RestartBanner"),
|
|
"UC-S03", "재시작 필요 알림 배너(RestartBanner)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S04_기본값으로_한번에_되돌릴_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("ResetDefaultsButton"),
|
|
"UC-S04", "기본값 복원 버튼(ResetDefaultsButton)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S05_설정을_내보낼_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("ExportSettingsButton"),
|
|
"UC-S05", "설정 내보내기 버튼(ExportSettingsButton)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S06_설정을_가져올_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("ImportSettingsButton"),
|
|
"UC-S06", "설정 가져오기 버튼(ImportSettingsButton)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S07_고급_항목을_접을_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("AdvancedSection"),
|
|
"UC-S07", "고급 섹션 접힘(AdvancedSection)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S08_Ctrl_S로_저장할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowCs.Contains("Key.S"),
|
|
"UC-S08", "설정창 코드비하인드가 Ctrl+S(Key.S) 저장을 처리해야 한다");
|
|
|
|
[Fact]
|
|
public void UC_S09_취소_시_폐기_확인을_받는다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(SettingsWindow), "ConfirmDiscardChanges"),
|
|
"UC-S09", "SettingsWindow.ConfirmDiscardChanges() 확인 절차가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S10_설정창_크기_위치가_기억된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "SettingsWindowBounds"),
|
|
"UC-S10", "AppConfig.SettingsWindowBounds 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S11_재시도_횟수를_UI에서_바꾼다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"RetriesBox\""),
|
|
"UC-S11", "재시도 횟수 입력(RetriesBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S12_UserAgent를_UI에서_바꾼다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"UserAgentBox\""),
|
|
"UC-S12", "User-Agent 입력(UserAgentBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S13_프록시를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"ProxyBox\""),
|
|
"UC-S13", "프록시 설정(ProxyBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S14_UI_언어를_고를_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"LanguageBox\""),
|
|
"UC-S14", "언어 선택(LanguageBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S15_자동_실행을_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"AutoStartBox\""),
|
|
"UC-S15", "시작 시 자동 실행(AutoStartBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S16_트레이로_최소화할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("MinimizeToTrayBox"),
|
|
"UC-S16", "트레이 최소화 옵션(MinimizeToTrayBox)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S17_알림_설정_섹션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("알림"),
|
|
"UC-S17", "설정창에 '알림' 섹션이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S18_실험적_기능_섹션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("실험적"),
|
|
"UC-S18", "설정창에 '실험적' 기능 섹션이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S19_실시간_검증_힌트가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("ValidationHint"),
|
|
"UC-S19", "입력 검증 힌트(ValidationHint) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_S20_버전_정보가_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"VersionText\""),
|
|
"UC-S20", "앱 버전 표시(VersionText)가 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §J 상태 표시·알림·피드백 유즈케이스(docs/UI_USECASES.md §J, UC-N01~N20).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseStatusRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_N01_상태바에_전체_다운로드_진행률이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("AggregateProgress"),
|
|
"UC-N01", "상태바 통합 진행률(AggregateProgress) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N02_토스트_호스트가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ToastHost"),
|
|
"UC-N02", "알림 토스트 컨테이너(ToastHost)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N03_토스트_표시_시간을_설정한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "ToastDurationMs"),
|
|
"UC-N03", "AppConfig.ToastDurationMs 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N04_치명_오류_배너가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ErrorBanner"),
|
|
"UC-N04", "상단 고정 오류 배너(ErrorBanner)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N05_오류_상세를_펼쳐_볼_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ErrorDetailExpander"),
|
|
"UC-N05", "오류 상세 펼침(ErrorDetailExpander) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N06_클립보드_URL_감지_제안이_뜬다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ClipboardHint"),
|
|
"UC-N06", "클립보드 URL 감지 배지(ClipboardHint)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N07_원격_서버_상태가_상시_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ServerStatusIcon"),
|
|
"UC-N07", "서버 상태 아이콘(ServerStatusIcon)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N08_툴바에서_QR_페어링을_바로_연다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("PairingBtn"),
|
|
"UC-N08", "툴바 페어링 버튼(PairingBtn)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N09_업데이트_가능_배지가_뜬다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("UpdateBadge"),
|
|
"UC-N09", "새 버전 알림 배지(UpdateBadge)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N10_차단_도메인별_통계가_보인다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ShieldDomainStats"),
|
|
"UC-N10", "도메인별 차단 통계(ShieldDomainStats)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N11_네트워크_끊김이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("NetworkChange"),
|
|
"UC-N11", "NetworkChange 오프라인 감지 처리가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N12_절전_모드_진입_시_일시정지_옵션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "PauseOnPowerSaver"),
|
|
"UC-N12", "AppConfig.PauseOnPowerSaver 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N13_상태바_텍스트를_클릭해_복사한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowCs.Contains("CopyStatus_Click"),
|
|
"UC-N13", "상태 복사(CopyStatus_Click) 처리기가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N14_알림_센터가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("NotificationCenter"),
|
|
"UC-N14", "지나간 알림 목록(NotificationCenter)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N15_방해_금지_모드를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "DoNotDisturb"),
|
|
"UC-N15", "AppConfig.DoNotDisturb 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N16_온보딩_안내가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("Onboarding") || SourceTree.MainWindowCs.Contains("Onboarding"),
|
|
"UC-N16", "첫 실행 온보딩(Onboarding) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N17_빈_상태_공용_스타일이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "EmptyStateStyle"),
|
|
"UC-N17", "빈 상태 공용 스타일(EmptyStateStyle) 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N18_크래시_리포트_대화상자가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("CrashReportDialog") is not null,
|
|
"UC-N18", "충돌 리포트 대화상자(CrashReportDialog)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N19_다운로드_버튼에_진행_개수_배지가_뜬다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("DownloadsBtnBadge"),
|
|
"UC-N19", "툴바 다운로드 버튼 배지(DownloadsBtnBadge)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_N20_상태바_우측_보조_정보_영역이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("StatusRight"),
|
|
"UC-N20", "상태바 우측 정보 영역(StatusRight)이 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §D 탭 고급 관리 유즈케이스(docs/UI_USECASES.md §D, UC-B01~B24).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseTabsRedTests
|
|
{
|
|
private static readonly Type TabItemType = typeof(Paca.Browser.Tabs.TabItem);
|
|
|
|
[Fact]
|
|
public void UC_B01_탭을_잠글_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(TabItemType, "IsLocked"),
|
|
"UC-B01", "탭 잠금(IsLocked)으로 실수로 닫는 것을 막을 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_B02_탭마다_사용자_지정_색을_달_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(TabItemType, "CustomColor"),
|
|
"UC-B02", "탭 사용자 샆(CustomColor) 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B03_탭_그룹을_접을_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(TabItemType, "IsGroupCollapsed"),
|
|
"UC-B03", "그룹 접음(IsGroupCollapsed) 상태가 탭 항목에 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_B04_탭_그룹_이름을_변경할_수_있다()
|
|
{
|
|
var ext = SourceTree.FindType("TabManagerGroupExtensions");
|
|
SourceTree.Usecase(
|
|
ext is not null && SourceTree.MethodExists(ext, "RenameGroup"),
|
|
"UC-B04", "그룹 이름 변경(RenameGroup) API 가 필요하다");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_B05_탭_호버_미리보기_컴포넌트가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("TabPreview") is not null,
|
|
"UC-B05", "탭 호버 미리보기를 담당하는 TabPreview 컴포넌트가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B06_잠자기_탭이_시각적으로_구분된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("IsSuspended"),
|
|
"UC-B06", "탭 템플릿이 잠자기 상태(IsSuspended)를 시각적으로 표시해야 한다");
|
|
|
|
[Fact]
|
|
public void UC_B07_탭_번호_배지가_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("TabIndexBadge"),
|
|
"UC-B07", "탭 번호 배지(TabIndexBadge) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B08_탭_드래그_삽입_표시기가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("TabInsertIndicator"),
|
|
"UC-B08", "드래그 중 삽입 위치 표시기(TabInsertIndicator)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B09_다중_탭_닫기_확인_옵션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "ConfirmCloseMultipleTabs"),
|
|
"UC-B09", "AppConfig.ConfirmCloseMultipleTabs 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B10_휠로_탭을_전환하는_옵션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "TabWheelSwitch"),
|
|
"UC-B10", "AppConfig.TabWheelSwitch 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B11_탭_최대_개수를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "MaxTabs"),
|
|
"UC-B11", "AppConfig.MaxTabs 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B12_세션_복원을_잠자기_상태로_할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "RestoreTabsSuspended"),
|
|
"UC-B12", "AppConfig.RestoreTabsSuspended 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B13_닫은_탭_아카이브_전용_팝업이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("Name=\"ArchivePopup\""),
|
|
"UC-B13", "아카이브를 보여주는 ArchivePopup 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B14_아카이브를_검색할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("ArchiveSearchBox"),
|
|
"UC-B14", "아카이브 검색 상자(ArchiveSearchBox)가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B15_아카이브를_한번에_비울_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(TabArchive), "Clear"),
|
|
"UC-B15", "TabArchive.Clear() 메서드가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B16_아카이브_항목을_복원할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(TabArchive), "Restore"),
|
|
"UC-B16", "TabArchive.Restore() 메서드가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B17_탭_그룹_색_팔레트가_테마로_관리된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "GroupPalette"),
|
|
"UC-B17", "그룹 색 팔레트(GroupPalette)가 공용 리소스로 정의되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_B18_현재_작업공간_이름이_표시된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("WorkspaceIndicator"),
|
|
"UC-B18", "현재 작업공간 표시(WorkspaceIndicator) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B19_작업공간_이름을_직접_지을_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "WorkspaceNames"),
|
|
"UC-B19", "AppConfig.WorkspaceNames 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B20_작업공간_전환_UI가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("WorkspaceSwitcher"),
|
|
"UC-B20", "작업공간 전환기(WorkspaceSwitcher) 요소가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B21_새_탭_버튼에_보조_메뉴가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("NewTabMore_Click"),
|
|
"UC-B21", "새 탭 버튼의 보조 메뉴(최근 페이지·즐겨찾기) 핸들러가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B22_탭_제목을_두_줄로_볼_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "TabTitleMultiline"),
|
|
"UC-B22", "AppConfig.TabTitleMultiline 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B23_탭에서_볼륨_슬라이더를_쓸_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("TabVolumePopup"),
|
|
"UC-B23", "탭 볼륨 조절 팝업(TabVolumePopup)이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B24_탭_그룹을_즐겨찾기로_저장할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "BookmarkGroup"),
|
|
"UC-B24", "그룹 전체를 즐겨찾기로 저장하는 MainWindow.BookmarkGroup() 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_B25_탭_스트립이_브라우저_열에_위치하여_다운로드창_리사이즈와_동기화된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("x:Name=\"TabStripRow\" Grid.Row=\"2\" Grid.Column=\"0\"") ||
|
|
SourceTree.MainWindowXaml.Contains("x:Name=\"TabStripRow\" Grid.Column=\"0\""),
|
|
"UC-B25", "TabStripRow가 브라우저 열(Grid.Column=0)에 위치해야 다운로드 패널 리사이즈 시 너비가 함께 조절된다");
|
|
|
|
[Fact]
|
|
public void UC_B26_브라우저_탭_너비가_동적으로_리사이즈된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(MainWindow), "TabItemWidth") &&
|
|
SourceTree.MethodExists(typeof(MainWindow), "UpdateDynamicTabWidth"),
|
|
"UC-B26", "MainWindow에 TabItemWidth 및 UpdateDynamicTabWidth가 있어 가용 폭에 따라 탭이 동적 리사이즈되어야 한다");
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §A 테마·색상·대비 유즈케이스(docs/UI_USECASES.md §A, UC-T01~T30).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다. 계약은 해당 문서의 "계약" 열을 따른다.
|
|
/// </summary>
|
|
public class UiUsecaseThemeRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_T01_시스템_테마를_따라가는_옵션이_설정에_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Tag=\"system\""),
|
|
"UC-T01", "OS 테마(다크/라이트)를 자동으로 따라가는 'system' 옵션이 설정창에 노출된다");
|
|
|
|
[Fact]
|
|
public void UC_T02_테마를_콤보박스로_다중_옵션_중_고른다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"ThemeModeBox\""),
|
|
"UC-T02", "테마 선택이 체크박스(라이트만)가 아니라 다크/라이트/시스템/고대비 콤보박스여야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T03_테마_전환에_재시작이_필요하다는_문구가_없어야_한다()
|
|
=> SourceTree.Usecase(
|
|
!SourceTree.SettingsWindowXaml.Contains("재시작"),
|
|
"UC-T03", "테마는 즉시 적용되어야 하며 설정창에 '재시작' 안내가 남아 있지 않아야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T04_MainWindow에_라이브_테마_적용_API가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "ApplyTheme"),
|
|
"UC-T04", "테마 전환 후 열려 있는 MainWindow 자체가 리소스를 다시 적용하는 API 가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T05_고대비_테마_사전_파일이_존재한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.AppFileExists("Themes/HighContrast.xaml"),
|
|
"UC-T05", "고대비 사용자를 위한 Themes/HighContrast.xaml 리소스 사전이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T06_설정에서_고대비_테마를_직접_고를_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Tag=\"highcontrast\""),
|
|
"UC-T06", "설정창 테마 옵션에 'highcontrast' 값이 제공되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T07_사용자_지정_강조색이_설정에_저장된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "AccentColor"),
|
|
"UC-T07", "AppConfig 에 강조색(AccentColor) 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T08_강조색을_즉시_적용하는_API가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(Paca.App.App), "ApplyAccent"),
|
|
"UC-T08", "App.ApplyAccent 로 재시작 없이 강조색이 전체 컨트롤에 반영되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T09_강조색_프리셋_스와치가_테마에_준비된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "Accent.Blue"),
|
|
"UC-T09", "다크 테마에 Accent.Blue 등 강조색 프리셋 키가 정의되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T10_진행바_스타일에_하드코딩된_강조색이_없어야_한다()
|
|
=> SourceTree.Usecase(
|
|
!SourceTree.SharedXaml.Contains("Color=\"#6366F1\""),
|
|
"UC-T10", "AccentProgressBar 템플릿이 하드코딩 색 대신 테마 강조색 리소스를 참조해야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T11_대기_상태색_PendingBrush가_실제_UI에서_쓰인다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MainWindowXaml.Contains("PendingBrush"),
|
|
"UC-T11", "테마에 정의만 된 PendingBrush 가 대기 상태 표시에 실제로 사용되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T12_사용자_테마_파일_경로를_설정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "CustomThemePath"),
|
|
"UC-T12", "AppConfig.CustomThemePath 로 사용자 테마 파일을 불러올 수 있어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T13_시스템_테마_판별_컴포넌트가_존재한다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.FindType("ThemeResolver") is not null,
|
|
"UC-T13", "OS 테마 값을 판별하는 ThemeResolver 컴포넌트가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T14_다크_TextMuted가_CardBg에서_AA대비를_만족한다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "TextMuted");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "CardBg");
|
|
Assert.False(fg is null || bg is null, "UC-T14 — 테마 브러시 파싱 실패(테마 파일 구조 변경?)");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 4.5, $"UC-T14 RED — 다크 TextMuted/CardBg 대비 {ratio:F2}:1 < 4.5:1 (WCAG AA)");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_T15_다크_TextMuted가_카드_호버_배경에서도_AA대비를_만족한다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "TextMuted");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "CardBgHover");
|
|
Assert.False(fg is null || bg is null, "UC-T15 — 테마 브러시 파싱 실패");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 4.5, $"UC-T15 RED — 다크 TextMuted/CardBgHover 대비 {ratio:F2}:1 < 4.5:1");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_T16_라이트_TextMuted가_CardBg에서_AA대비를_만족한다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeLightXaml, "TextMuted");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeLightXaml, "CardBg");
|
|
Assert.False(fg is null || bg is null, "UC-T16 — 테마 브러시 파싱 실패");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 4.5, $"UC-T16 RED — 라이트 TextMuted/CardBg 대비 {ratio:F2}:1 < 4.5:1");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_T17_라이트_TextMuted가_GlassBg에서_AA대비를_만족한다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeLightXaml, "TextMuted");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeLightXaml, "GlassBg");
|
|
Assert.False(fg is null || bg is null, "UC-T17 — 테마 브러시 파싱 실패");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 4.5, $"UC-T17 RED — 라이트 TextMuted/GlassBg 대비 {ratio:F2}:1 < 4.5:1");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_T18_다크_강조색_텍스트가_CardBg에서_AA대비를_만족한다()
|
|
{
|
|
var fg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "AccentBrush");
|
|
var bg = SourceTree.BrushHex(SourceTree.ThemeDarkXaml, "CardBg");
|
|
Assert.False(fg is null || bg is null, "UC-T18 — 테마 브러시 파싱 실패");
|
|
var ratio = SourceTree.ContrastRatio(fg!, bg!);
|
|
Assert.True(ratio >= 4.5, $"UC-T18 RED — 다크 AccentBrush/CardBg 대비 {ratio:F2}:1 < 4.5:1 (포맷 배지 텍스트로 사용됨)");
|
|
}
|
|
|
|
[Fact]
|
|
public void UC_T19_비활성_텍스트_토큰이_양_테마에_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "TextDisabled")
|
|
&& SourceTree.HasKey(SourceTree.ThemeLightXaml, "TextDisabled"),
|
|
"UC-T19", "비활성 컨트롤 텍스트 샆(TextDisabled) 토큰이 다크·라이트 모두에 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T20_선택_영역_브러시_토큰이_테마에_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "SelectionBrush"),
|
|
"UC-T20", "텍스트 선택 영역 색(SelectionBrush)이 테마 토큰으로 관리되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T21_포커스_링_브러시_토큰이_테마에_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "FocusAdornerBrush"),
|
|
"UC-T21", "키보드 포커스 링 샆(FocusAdornerBrush) 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T22_스크롤바_썸_전용_색_토큰이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "ScrollBarThumbBrush"),
|
|
"UC-T22", "스크롤바 썸 샆(ScrollBarThumbBrush)이 공용 토큰으로 정의되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_T23_툴팁_그림자_리소스가_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "TooltipShadow"),
|
|
"UC-T23", "툴팁 그림자(TooltipShadow) 리소스가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T24_팝업_그림자_리소스가_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "PopupShadow"),
|
|
"UC-T24", "플라이아웃 공용 그림자(PopupShadow) 리소스가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T25_경고_상태색_토큰이_대기색과_구분돼_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "WarningBrush"),
|
|
"UC-T25", "경고 샆(WarningBrush) 토큰이 PendingBrush 와 별도로 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T26_링크_색_토큰이_테마에_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "LinkBrush"),
|
|
"UC-T26", "링크 텍스트 샆(LinkBrush) 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T27_방문한_링크_색_토큰이_테마에_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "VisitedLinkBrush"),
|
|
"UC-T27", "방문 링크 샆(VisitedLinkBrush) 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T28_정보_안내색_토큰이_테마에_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "InfoBrush"),
|
|
"UC-T28", "정보성 안내 샆(InfoBrush) 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T29_강조_텍스트_전용_브러시_토큰이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.ThemeDarkXaml, "AccentTextBrush"),
|
|
"UC-T29", "강조색 중 텍스트 용도(AccentTextBrush) 토큰이 별도로 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_T30_타이틀바_테마를_즉시_갱신하는_API가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(MainWindow), "UpdateTitleBarTheme"),
|
|
"UC-T30", "테마 전환 시 DWM 타이틀바 특성을 다시 적용하는 MainWindow.UpdateTitleBarTheme 이 필요하다");
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// UI 복잡 버전 RED 스펙 — §B 타이포그래피·폰트 유즈케이스(docs/UI_USECASES.md §B, UC-F01~F22).
|
|
/// RED 단계: 전부 실패하는 것이 정상이다.
|
|
/// </summary>
|
|
public class UiUsecaseTypographyRedTests
|
|
{
|
|
[Fact]
|
|
public void UC_F01_UI_기본_폰트를_설정으로_고를_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "UiFontFamily"),
|
|
"UC-F01", "AppConfig.UiFontFamily 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F02_UI_폰트_크기를_설정으로_바꿀_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "UiFontSize"),
|
|
"UC-F02", "AppConfig.UiFontSize 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F03_한국어_폴백_폰트를_직접_지정할_수_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.PropertyExists(typeof(AppConfig), "UiFontKoreanFallback"),
|
|
"UC-F03", "AppConfig.UiFontKoreanFallback 속성이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F04_기본_폰트_크기_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "FontSize.Base"),
|
|
"UC-F04", "Shared.xaml 에 FontSize.Base 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F05_작은_폰트_크기_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "FontSize.Small"),
|
|
"UC-F05", "FontSize.Small 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F06_큰_폰트_크기_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "FontSize.Large"),
|
|
"UC-F06", "FontSize.Large 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F07_초대형_폰트_크기_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "FontSize.XLarge"),
|
|
"UC-F07", "FontSize.XLarge 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F08_제목_H1_스타일이_공용으로_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Type.H1"),
|
|
"UC-F08", "Type.H1 스타일 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F09_본문_스타일이_공용으로_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Type.Body"),
|
|
"UC-F09", "Type.Body 스타일 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F10_캡션_스타일이_공용으로_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Type.Caption"),
|
|
"UC-F10", "Type.Caption 스타일 토큰이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F11_등폭_숫자_스타일이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Type.Mono"),
|
|
"UC-F11", "진행률·속도 숫자용 Type.Mono 스타일이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F12_한국어_가독성_프리셋_폰트가_후보에_포함된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SharedXaml.Contains("Pretendard"),
|
|
"UC-F12", "UiFont 폰트 후보에 Pretendard 가 포함되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_F13_설정창에_글꼴_섹션이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("글꼴"),
|
|
"UC-F13", "설정창에 '글꼴' 섹션이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F14_글꼴_미리보기_컨트롤이_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"FontPreviewText\""),
|
|
"UC-F14", "설정창에 FontPreviewText 미리보기 컨트롤이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F15_폰트_크기_프리셋_콤보박스가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.SettingsWindowXaml.Contains("Name=\"FontSizeBox\""),
|
|
"UC-F15", "설정창에 FontSizeBox 콤보박스가 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F16_폰트_설정을_즉시_적용하는_API가_있다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.MethodExists(typeof(Paca.App.App), "ApplyFontSettings"),
|
|
"UC-F16", "App.ApplyFontSettings 로 폰트 설정이 재시작 없이 반영되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_F17_리더_모드에_폰트가_하드코딩되지_않는다()
|
|
=> SourceTree.Usecase(
|
|
!SourceTree.ReaderModeCs.Contains("Malgun Gothic"),
|
|
"UC-F17", "리더 모드 HTML 이 폰트를 하드코딩 대신 앱 설정에서 주입받아야 한다");
|
|
|
|
[Fact]
|
|
public void UC_F18_새_탭_다이얼에_폰트가_하드코딩되지_않는다()
|
|
=> SourceTree.Usecase(
|
|
!SourceTree.MainWindowCs.Contains("Malgun Gothic"),
|
|
"UC-F18", "새 탭 다이얼 인라인 CSS 의 폰트 폴백이 앱 공용 설정으로 통일되어야 한다");
|
|
|
|
[Fact]
|
|
public void UC_F19_탭_제목_공용_스타일이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "TabTitleStyle"),
|
|
"UC-F19", "탭 제목(말줄임 등)의 공용 스타일 TabTitleStyle 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F20_상태바_텍스트_공용_스타일이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "StatusTextStyle"),
|
|
"UC-F20", "상태바 텍스트 공용 스타일 StatusTextStyle 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F21_아이콘_크기_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "IconSize.Small"),
|
|
"UC-F21", "아이콘 크기 토큰 IconSize.Small 이 필요하다");
|
|
|
|
[Fact]
|
|
public void UC_F22_줄간격_토큰이_정의된다()
|
|
=> SourceTree.Usecase(
|
|
SourceTree.HasKey(SourceTree.SharedXaml, "Type.LineHeight"),
|
|
"UC-F22", "본문 줄간격 토큰 Type.LineHeight 가 필요하다");
|
|
}
|
|
|