using VideoDownloader.App.Browser.Features; using VideoDownloader.Browser; namespace VideoDownloader.Tests.Browser; /// 데스크톱 T3 잔여 — 워크스페이스 라우팅(T3-1), 탭 아카이브(T3-2), 노트(T3-12). public class DesktopRemainderTests : IDisposable { private readonly string _dir = Path.Combine(Path.GetTempPath(), $"vd_t3_{Guid.NewGuid():N}"); // ── T3-1: 도메인 → 워크스페이스 자동 라우팅 규칙 ── [Fact] public void 라우팅_규칙_도메인_매칭() { var rules = new WorkspaceRoutingRules(Path.Combine(_dir, "routing.json")); rules.Add("youtube.com", "영상"); Assert.Equal("영상", rules.Match("https://www.youtube.com/watch?v=1")); // www 정규화 Assert.Equal("영상", rules.Match("https://m.youtube.com/watch?v=1")); // 서브도메인 Assert.Null(rules.Match("https://example.com")); // 무관 도메인 Assert.Null(rules.Match("about:blank")); // 비 HTTP } [Fact] public void 라우팅_규칙_영속_및_제거() { var p = Path.Combine(_dir, "routing.json"); new WorkspaceRoutingRules(p).Add("x.com", "SNS"); var reloaded = new WorkspaceRoutingRules(p); // 재시작 Assert.Equal("SNS", reloaded.Match("https://x.com/home")); reloaded.Remove("x.com"); Assert.Null(reloaded.Match("https://x.com/home")); } // ── T3-2: 탭 아카이브(닫힘 이력 영속·검색·중복 제거) ── [Fact] public void 아카이브_기록_검색_중복제거() { var arc = new TabArchive(Path.Combine(_dir, "archive.json")); arc.Add("영상1", "https://a.com/1"); arc.Add("영상1 다시", "https://a.com/1"); // 같은 URL 연속 → 제목만 갱신 arc.Add("문서", "https://b.com/doc"); Assert.Single(arc.Search("a.com/1")); Assert.Equal("영상1 다시", arc.Search("a.com/1")[0].Title); Assert.Single(arc.Search("문서")); Assert.Equal(2, arc.Search("").Count); } [Fact] public void 아카이브_영속_삭제() { var p = Path.Combine(_dir, "archive.json"); var arc = new TabArchive(p); arc.Add("t", "https://x.com"); var re = new TabArchive(p); Assert.Single(re.Search("x.com")); re.Remove("https://x.com"); Assert.Empty(new TabArchive(p).Search("x.com")); } [Fact] public void 아카이브_최대_500_제한() { var arc = new TabArchive(Path.Combine(_dir, "archive.json")); for (var i = 0; i < 520; i++) arc.Add($"t{i}", $"https://x.com/{i}"); Assert.Equal(500, arc.Search("").Count); Assert.Equal("t519", arc.Search("")[0].Title); // 최신이 앞 } // ── T3-12: 노트 저장소 ── [Fact] public void 노트_추가_목록_삭제_영속() { var p = Path.Combine(_dir, "notes.json"); var notes = new NotesStore(p); var id = notes.Add("제목", "본문 내용"); Assert.Single(notes.List()); Assert.Equal(("제목", "본문 내용"), (notes.List()[0].Title, notes.List()[0].Body)); var re = new NotesStore(p); // 재시작 Assert.Single(re.List()); re.Delete(id); Assert.Empty(new NotesStore(p).List()); } [Fact] public void 노트_검색() { var notes = new NotesStore(Path.Combine(_dir, "notes.json")); notes.Add("유튜브 모음", "링크들"); notes.Add("회의록", "유튜브 언급됨"); Assert.Equal(2, notes.Search("유튜브").Count); Assert.Single(notes.Search("회의")); } // ── T3-6: 페이지 액션 — 흑백/세피아 CSS 필터(Vivaldi Page Actions, Boost 통합) ── [Fact] public void 페이지액션_흑백_CSS_생성() { var js = VideoDownloader.App.Browser.Features.BoostCssBuilder.BuildScript( "https://x.com", new VideoDownloader.App.Browser.Features.BoostSettings(false, "", PageFilter.Grayscale)); Assert.Contains("grayscale(1)", js); Assert.DoesNotContain("invert(1)", js); } [Fact] public void 페이지액션_세피아_CSS_생성() { var js = VideoDownloader.App.Browser.Features.BoostCssBuilder.BuildScript( "https://x.com", new VideoDownloader.App.Browser.Features.BoostSettings(false, "", PageFilter.Sepia)); Assert.Contains("sepia(", js); } [Fact] public void 페이지액션_미적용_시_빈_스크립트() { var js = VideoDownloader.App.Browser.Features.BoostCssBuilder.BuildScript( "https://x.com", new VideoDownloader.App.Browser.Features.BoostSettings(false, "", PageFilter.None)); Assert.Equal("", js); } public void Dispose() { try { Directory.Delete(_dir, true); } catch { } } }