releases/VideoDownloader.Tests/E2E/UiUsecaseThemeRedTests.cs

213 lines
11 KiB
C#

using VideoDownloader.App;
using VideoDownloader.Core.Config;
using VideoDownloader.Tests.Infrastructure;
namespace VideoDownloader.Tests.E2E;
/// <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(VideoDownloader.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 이 필요하다");
}