using System.Windows; using Microsoft.Web.WebView2.Core; using VideoDownloader.App.Browser.Features; using VideoDownloader.Browser.Tabs; using VideoDownloader.Tests.Infrastructure; namespace VideoDownloader.Tests.E2E; /// /// E2E (T2-4): 실제 WebView2 + 번들된 Mozilla Readability.js 로 /// 기사 페이지에서 본문을 추출해 리더 뷰로 교체하고 원본 URL 복귀까지 검증. /// [Collection("E2E")] public class ReaderModeE2ETests { private const string ArticleHtml = """ 테스트 기사

리더 모드 E2E 기사

첫 번째 문단입니다. 이 문단은 Readability 가 본문으로 추출해야 합니다. 충분한 길이의 텍스트가 필요하므로 몇 번 더 반복합니다. 본문 본문 본문.

두 번째 문단입니다. 리더 모드는 광고와 내비게이션을 제거하고 본문만 남깁니다. 추출 결과에 이 텍스트가 포함되어야 합니다.

세 번째 문단입니다. 문단이 세 개 이상이면 Readability 의 휴리스틱이 본문으로 판정합니다. 마커텍스트-고유문구-레인보우4747.

"""; [Fact] public async Task 기사를_리더로_추출하고_원본으로_복귀한다() { using var sta = StaDispatcher.Start(); var udf = Path.Combine(Path.GetTempPath(), $"vd_reader_{Guid.NewGuid():N}"); var ok = await sta.InvokeAsync(async () => { var window = new Window(); var browser = new Microsoft.Web.WebView2.Wpf.WebView2(); window.Content = browser; window.Show(); var env = await CoreWebView2Environment.CreateAsync(userDataFolder: udf); await browser.EnsureCoreWebView2Async(env); var cv = browser.CoreWebView2; cv.NavigateToString(ArticleHtml); await WaitNav(cv); var tab = new TabItem { View = browser }; // 1) 리더 진입 var entered = await ReaderMode.ToggleAsync(tab, lightTheme: false); await Wait.Until(() => Task.FromResult(cv.DocumentTitle ?? ""), t => t.Contains("테스트 기사"), 15000); var titleAfter = cv.DocumentTitle ?? ""; var bodyText = await cv.ExecuteScriptAsync("document.body.innerText"); var hasMarker = bodyText.Contains("마커텍스트-고유문구-레인보우4747"); var hasNav = bodyText.Contains("광고 광고 광고"); var isReader = tab.IsReader; var originalSaved = tab.ReaderOriginalUrl; // 2) 복귀 var exited = await ReaderMode.ToggleAsync(tab, lightTheme: false); var restoredReader = tab.IsReader == false; window.Close(); return (entered: entered == true, titleAfter, hasMarker, navRemoved: !hasNav, isReader, originalSaved, exited: exited == false, restoredReader); }); try { Directory.Delete(udf, true); } catch { } Assert.True(ok.entered, "리더 진입 실패"); Assert.Contains("테스트 기사", ok.titleAfter); Assert.True(ok.hasMarker, "본문 마커 누락"); Assert.True(ok.navRemoved, "내비/광고 제거 안됨"); Assert.True(ok.isReader); Assert.True(ok.exited); Assert.True(ok.restoredReader); } private static Task WaitNav(CoreWebView2 cv) { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); EventHandler? h = null; h = (_, _) => { cv.NavigationCompleted -= h; tcs.TrySetResult(); }; cv.NavigationCompleted += h; return tcs.Task.WaitAsync(TimeSpan.FromSeconds(15)); } }