feat(ads/seo/release): v1.2.0 with live Google Ads, isolated ad profile, ads.txt, and SEO/AEO/GEO optimization
This commit is contained in:
parent
a88aafa1e5
commit
0e568f1f0a
975 changed files with 130593 additions and 16783 deletions
|
|
@ -1,249 +0,0 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using VideoDownloader.App;
|
||||
using VideoDownloader.App.Browser.Features;
|
||||
using VideoDownloader.App.Core;
|
||||
using VideoDownloader.App.Services;
|
||||
using VideoDownloader.App.ViewModels;
|
||||
using VideoDownloader.Core.Config;
|
||||
using VideoDownloader.Tests.Infrastructure;
|
||||
using WpfApp = VideoDownloader.App.App;
|
||||
|
||||
namespace VideoDownloader.Tests.E2E;
|
||||
|
||||
/// <summary>
|
||||
/// E2E: "UI 복잡 버전"(docs/UI_USECASES.md §A~§J) GREEN 구현의 실창 실증 —
|
||||
/// MainWindowSmokeE2ETests 와 동일한 StaDispatcher/WpfTestApp 패턴으로 실제 윈도우를 띄워
|
||||
/// ① 신규 유즈케이스 컨트롤이 실 트리에 존재하는지 ② 테마가 재시작 없이 라이브 전환되는지(UC-T03/T04)
|
||||
/// ③ 다운로드 일시정지/재개 상태 전이(UC-D06/D07) ④ 설정창 검색 필터·테마 즉시 적용(UC-S01)
|
||||
/// ⑤ 토스트 방해 금지 억제·알림센터 히스토리(UC-N14/N15)를 검증한다.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class UiUsecaseLiveE2ETests
|
||||
{
|
||||
private static string TempDataDir()
|
||||
{
|
||||
var dir = Path.Combine(Path.GetTempPath(), $"vd_uc_e2e_{Guid.NewGuid():N}");
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MainWindow_유즈케이스_컨트롤_실존과_라이브_테마_전환_E2E()
|
||||
{
|
||||
var dataDir = TempDataDir();
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
var ok = await sta.InvokeAsync(async () =>
|
||||
{
|
||||
var window = new MainWindow();
|
||||
window.Show();
|
||||
var first = await Wait.Until(
|
||||
() => Task.FromResult(window.Tabs.ActiveTab),
|
||||
t => t?.Core != null, 30000);
|
||||
|
||||
// ── §C/§D/§E/§I/§J 신규 컨트롤이 실 트리에 존재(최상위 x:Name 필드) ──
|
||||
var mustExist = new FrameworkElement?[]
|
||||
{
|
||||
// 툴바
|
||||
window.CompactModeBtn, window.LibraryBtn, window.PairingBtn,
|
||||
window.DownloadsBtnBadge, window.UpdateBadge,
|
||||
// 탭 스트립
|
||||
window.WorkspaceIndicator, window.WorkspaceSwitcher, window.NewTabMoreBtn,
|
||||
window.ArchiveBtn, window.TabInsertIndicator,
|
||||
// 다운로드 패널
|
||||
window.DownloadFilterTabs, window.DownloadSearchBox, window.QueueSummaryText,
|
||||
window.GlobalSpeedText, window.SpeedSparkline,
|
||||
// 상태바·오버레이
|
||||
window.StatusRight, window.ZoomIndicator, window.ServerStatusIcon,
|
||||
window.NotificationCenter, window.ShortcutHelpBtn, window.AggregateProgress,
|
||||
window.ToastHost, window.ErrorBanner, window.ErrorDetailExpander,
|
||||
window.ClipboardHint, window.Onboarding,
|
||||
// 팝업 본문
|
||||
window.ArchivePopup, window.ArchiveSearchBox,
|
||||
window.LibraryPopup, window.LibrarySearchBox, window.LibraryViewToggle, window.LibrarySortMenu,
|
||||
window.BookmarkSearchBox, window.HistorySearchBox, window.NotesSearchBox,
|
||||
window.PlaylistSearchBox, window.PlaylistShuffle,
|
||||
window.ShortcutHelpPopup, window.NotificationCenterPopup, window.TabVolumePopup,
|
||||
};
|
||||
var missing = mustExist.Count(e => e == null);
|
||||
|
||||
// ── §J 초기 상태 계약: 온보딩 표시(세션 없음)·오류배너·클립보드힌트 숨김 ──
|
||||
var onboardingShown = window.Onboarding.Visibility == Visibility.Visible;
|
||||
var errorHidden = window.ErrorBanner.Visibility == Visibility.Collapsed;
|
||||
var clipboardHidden = window.ClipboardHint.Visibility == Visibility.Collapsed;
|
||||
|
||||
// ── §A 라이브 테마 전환(UC-T03/T04) — 창을 다시 만들지 않고 즉시 ──
|
||||
window.ApplyTheme("light");
|
||||
var lightApplied = WpfApp.CurrentTheme == "light" && WpfApp.IsLightTheme;
|
||||
var aliveInLight = window.IsLoaded;
|
||||
window.ApplyTheme("dark");
|
||||
var darkApplied = WpfApp.CurrentTheme == "dark" && !WpfApp.IsLightTheme;
|
||||
var aliveInDark = window.IsLoaded;
|
||||
var lightRestore = WpfApp.CurrentTheme == "dark"; // 원복 확인
|
||||
|
||||
window.Close();
|
||||
await Task.Delay(800); // WebView2 파일 핸들 해제 여유(smoke 테스트 패턴)
|
||||
var fails = new List<string>();
|
||||
if (missing != 0) fails.Add($"컨트롤 누락 {missing}개");
|
||||
if (!onboardingShown) fails.Add("온보딩 미표시");
|
||||
if (!errorHidden) fails.Add("오류배너 초기 미숨김");
|
||||
if (!clipboardHidden) fails.Add("클립보드힌트 초기 미숨김");
|
||||
if (!lightApplied) fails.Add("라이트 테마 미적용");
|
||||
if (!aliveInLight) fails.Add("라이트 전환 후 창 사망");
|
||||
if (!darkApplied) fails.Add("다크 원복 미적용");
|
||||
if (!aliveInDark) fails.Add("다크 전환 후 창 사망");
|
||||
return string.Join(" | ", fails);
|
||||
});
|
||||
Assert.True(string.IsNullOrEmpty(ok), $"MainWindow 유즈케이스 E2E 실패: {ok}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", null);
|
||||
try { Directory.Delete(dataDir, recursive: true); } catch { } // WebView2 파일 점유 시 남김(smoke 테스트 패턴)
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DownloadManager_전체_일시정지_및_재개_상태전이_E2E()
|
||||
{
|
||||
var dataDir = TempDataDir();
|
||||
var outDir = Path.Combine(dataDir, "out");
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
var ok = sta.Invoke(() =>
|
||||
{
|
||||
var config = new AppConfig { OutputFolder = outDir, MaxConcurrentDownloads = 2 };
|
||||
var manager = new DownloadManager(
|
||||
config,
|
||||
_ => Task.FromResult(new List<Microsoft.Web.WebView2.Core.CoreWebView2Cookie>()),
|
||||
_ => { });
|
||||
|
||||
var running = new DownloadItemViewModel("진행 중 항목")
|
||||
{
|
||||
Status = "다운로드 중",
|
||||
Started = true,
|
||||
MediaUrl = new Uri("https://127.0.0.1:9/video.mp4"), // 비라우팅 — 재개가 네트워크를 치지 않게
|
||||
OutputBase = Path.Combine(outDir, "video"),
|
||||
};
|
||||
var queued = new DownloadItemViewModel("대기 항목")
|
||||
{
|
||||
Status = "대기",
|
||||
MediaUrl = new Uri("https://example.com/video2.mp4"),
|
||||
OutputBase = Path.Combine(outDir, "video2"),
|
||||
};
|
||||
manager.Items.Add(running);
|
||||
manager.Items.Add(queued);
|
||||
|
||||
// UC-D06: 전체 일시정지 — 진행 중만 "일시정지" 전이, 대기 항목은 그대로
|
||||
manager.PauseAll();
|
||||
var pausedStatus = running.Status == "일시정지" && running.PausedForResume;
|
||||
var queuedUntouched = queued.Status == "대기";
|
||||
|
||||
// UC-D07: 전체 재개 — 일시정지 항목만 재투입(PausedForResume 해제 + Started 재설정)
|
||||
manager.ResumeAll();
|
||||
var resumed = !running.PausedForResume && running.Started; // 재시작 큐 진입
|
||||
manager.Cancel(running); // 실측 목적 달성 — 원격 접속 시도 중단
|
||||
var queuedStillQueued = queued.Started == false && queued.PausedForResume == false;
|
||||
|
||||
var fails = new List<string>();
|
||||
if (!pausedStatus) fails.Add($"PauseAll 후 상태={running.Status}/PausedForResume={running.PausedForResume}");
|
||||
if (!queuedUntouched) fails.Add($"대기 항목이 건드려짐(상태={queued.Status})");
|
||||
if (!resumed) fails.Add($"ResumeAll 후 재투입 안 됨(Started={running.Started}/PausedForResume={running.PausedForResume})");
|
||||
if (!queuedStillQueued) fails.Add("대기 항목이 재개 대상이 됨");
|
||||
return string.Join(" | ", fails);
|
||||
});
|
||||
Assert.True(string.IsNullOrEmpty(ok), $"일시정지/재개 E2E 실패: {ok}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", null);
|
||||
try { Directory.Delete(dataDir, recursive: true); } catch { } // WebView2 파일 점유 시 남김(smoke 테스트 패턴)
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SettingsWindow_검색_필터와_테마_즉시적용_E2E()
|
||||
{
|
||||
var dataDir = TempDataDir();
|
||||
try
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
var ok = sta.Invoke(() =>
|
||||
{
|
||||
var config = new AppConfig(); // 기본 테마 dark
|
||||
var win = new SettingsWindow(config);
|
||||
|
||||
// 생성 시점 로드: 테마 dark 반영 + 검색창 비어 전 섹션 표시
|
||||
var initialTheme = WpfApp.CurrentTheme == "dark";
|
||||
var allVisible = win.SectionFont.Visibility == Visibility.Visible
|
||||
&& win.SectionDownload.Visibility == Visibility.Visible;
|
||||
|
||||
// UC-T01/T02: 콤보박스로 라이트 선택 → 앱 테마가 즉시 전환(재시작 없음)
|
||||
win.ThemeModeBox.SelectedValue = "light";
|
||||
var lightLive = WpfApp.CurrentTheme == "light" && WpfApp.IsLightTheme;
|
||||
win.ThemeModeBox.SelectedValue = "dark";
|
||||
var backToDark = WpfApp.CurrentTheme == "dark";
|
||||
|
||||
// UC-S01: 설정 검색 — 해당 섹션만 노출
|
||||
win.SettingsSearchBox.Text = "글꼴";
|
||||
var fontOnly = win.SectionFont.Visibility == Visibility.Visible
|
||||
&& win.SectionDownload.Visibility == Visibility.Collapsed
|
||||
&& win.SectionNotify.Visibility == Visibility.Collapsed;
|
||||
win.SettingsSearchBox.Text = "알림";
|
||||
var downloadOnly = win.SectionNotify.Visibility == Visibility.Visible
|
||||
&& win.SectionDownload.Visibility == Visibility.Collapsed
|
||||
&& win.SectionAccessibility.Visibility == Visibility.Collapsed;
|
||||
win.SettingsSearchBox.Text = "";
|
||||
var restored = win.SectionFont.Visibility == Visibility.Visible
|
||||
&& win.SectionDownload.Visibility == Visibility.Visible;
|
||||
|
||||
win.Close();
|
||||
var fails = new List<string>();
|
||||
if (!initialTheme) fails.Add($"초기 테마 dark 아님(현재 {WpfApp.CurrentTheme})");
|
||||
if (!allVisible) fails.Add("초기 전 섹션 표시 아님");
|
||||
if (!lightLive) fails.Add($"라이트 즉시 적용 실패(현재 {WpfApp.CurrentTheme}/{WpfApp.IsLightTheme})");
|
||||
if (!backToDark) fails.Add($"다크 원복 실패(현재 {WpfApp.CurrentTheme})");
|
||||
if (!fontOnly) fails.Add("'글꼴' 검색 시 글꼴 섹션만 표시 아님");
|
||||
if (!downloadOnly) fails.Add("'알림' 검색 시 알림 섹션만 표시 아님");
|
||||
if (!restored) fails.Add("빈 검색어로 복원 안 됨");
|
||||
return string.Join(" | ", fails);
|
||||
});
|
||||
Assert.True(string.IsNullOrEmpty(ok), $"설정창 E2E 실패: {ok}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("VD_DATA_DIR", null);
|
||||
try { Directory.Delete(dataDir, recursive: true); } catch { } // WebView2 파일 점유 시 남김(smoke 테스트 패턴)
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToastNotifier_방해금지_억제와_알림센터_히스토리_E2E()
|
||||
{
|
||||
using var sta = StaDispatcher.Start();
|
||||
WpfTestApp.Ensure(sta.Dispatcher);
|
||||
var ok = sta.Invoke(() =>
|
||||
{
|
||||
var host = new StackPanel();
|
||||
var toast = new ToastNotifier(host) { Duration = TimeSpan.FromMilliseconds(500) };
|
||||
|
||||
// 일반 표시: 토스트 1장 + 히스토리 1건(UC-N02)
|
||||
toast.Show("완료: 첫 번째", "success");
|
||||
var shown = host.Children.Count == 1 && toast.History.Count == 1;
|
||||
|
||||
// UC-N15 방해 금지: 표시는 억제되고 히스토리에만 기록(UC-N14 알림 센터 원천)
|
||||
toast.DoNotDisturb = true;
|
||||
toast.Show("실패: 두 번째", "error");
|
||||
var suppressed = host.Children.Count == 1 && toast.History.Count == 2;
|
||||
|
||||
return shown && suppressed;
|
||||
});
|
||||
Assert.True(ok, "토스트 방해 금지/히스토리 계약 위반");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue