Phase C (T2-1..T3-10): command palette, reader mode (real Readability.js, E2E), boost force-dark, screenshot, split view, vertical tabs, Glance (Alt+click), tab groups (6 colors), workspaces, speed dial, InPrivate tabs, quick find, source view, print, PiP, TTS (94 tests GREEN)
This commit is contained in:
parent
0caa1111e7
commit
406957088f
14 changed files with 3496 additions and 7 deletions
85
VideoDownloader.Tests/E2E/ReaderModeE2ETests.cs
Normal file
85
VideoDownloader.Tests/E2E/ReaderModeE2ETests.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
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;
|
||||
|
||||
/// <summary>
|
||||
/// E2E (T2-4): 실제 WebView2 + 번들된 Mozilla Readability.js 로
|
||||
/// 기사 페이지에서 본문을 추출해 리더 뷰로 교체하고 원본 URL 복귀까지 검증.
|
||||
/// </summary>
|
||||
[Collection("E2E")]
|
||||
public class ReaderModeE2ETests
|
||||
{
|
||||
private const string ArticleHtml = """
|
||||
<html><head><title>테스트 기사</title></head><body>
|
||||
<nav>광고 광고 광고 내비게이션</nav>
|
||||
<article>
|
||||
<h1>리더 모드 E2E 기사</h1>
|
||||
<p>첫 번째 문단입니다. 이 문단은 Readability 가 본문으로 추출해야 합니다. 충분한 길이의 텍스트가 필요하므로 몇 번 더 반복합니다. 본문 본문 본문.</p>
|
||||
<p>두 번째 문단입니다. 리더 모드는 광고와 내비게이션을 제거하고 본문만 남깁니다. 추출 결과에 이 텍스트가 포함되어야 합니다.</p>
|
||||
<p>세 번째 문단입니다. 문단이 세 개 이상이면 Readability 의 휴리스틱이 본문으로 판정합니다. 마커텍스트-고유문구-레인보우4747.</p>
|
||||
</article>
|
||||
<footer>푸터 링크 모음</footer>
|
||||
</body></html>
|
||||
""";
|
||||
|
||||
[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<CoreWebView2NavigationCompletedEventArgs>? h = null;
|
||||
h = (_, _) => { cv.NavigationCompleted -= h; tcs.TrySetResult(); };
|
||||
cv.NavigationCompleted += h;
|
||||
return tcs.Task.WaitAsync(TimeSpan.FromSeconds(15));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue