using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Paca.Browser.Content; using Paca.Browser.Interaction; using Xunit; namespace Paca.Tests; public sealed class ModernBrowsingMilestone3Phase103Tests : IDisposable { private readonly string _tempDir; public ModernBrowsingMilestone3Phase103Tests() { _tempDir = Path.Combine(Path.GetTempPath(), "paca_m3_test_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_tempDir); } public void Dispose() { try { if (Directory.Exists(_tempDir)) Directory.Delete(_tempDir, true); } catch { } } [Fact] public void Gate116_ImmersiveReaderExtractor_ExtractsArticleAndFormatsReaderDocument() { // Arrange var extractor = new ImmersiveReaderExtractor(); var rawHtml = @" Breaking Tech News - PACA Browser

The Future of Web Navigation

By Jane Doe

Modern web browsers require high performance and deep privacy protection.

Readers deserve an uncompromised experience without intrusive trackers.

"; // Act 1: Extract article var article = extractor.Extract(rawHtml, "https://example.com/tech-news"); // Assert 1 Assert.NotNull(article); Assert.Equal("The Future of Web Navigation", article.Title); Assert.Equal("Jane Doe", article.Byline); Assert.DoesNotContain("Buy now 50% off!", article.CleanedContentHtml); Assert.Contains("Modern web browsers require high performance", article.CleanedContentHtml); Assert.True(article.WordCount > 10); Assert.True(article.EstimatedReadingMinutes >= 1); // Act 2: Format for reader view var readerHtml = extractor.FormatReaderDocument( article, theme: ReaderTheme.Sepia, fontSizePx: 18, fontFamily: "Georgia", columnWidthPx: 680); // Assert 2 Assert.Contains("#FBF0D9", readerHtml); // Sepia background Assert.Contains("Georgia", readerHtml); Assert.Contains("680px", readerHtml); Assert.Contains("The Future of Web Navigation", readerHtml); } [Fact] public void Gate117_ReadAloudSpeechCoordinator_ChunksSentencesAndTracksSpeechState() { // Arrange var coordinator = new ReadAloudSpeechCoordinator(); var text = "Hello world! This is PACA browser. Welcome to the modern web."; // Act coordinator.SetText(text); // Assert 1: Sentence chunking Assert.Equal(3, coordinator.TotalSentences); Assert.Equal("Hello world!", coordinator.GetSentence(0)); Assert.Equal("This is PACA browser.", coordinator.GetSentence(1)); Assert.Equal("Welcome to the modern web.", coordinator.GetSentence(2)); // Assert 2: Playback state coordinator.Play(); Assert.True(coordinator.IsPlaying); Assert.False(coordinator.IsPaused); Assert.Equal(0, coordinator.CurrentIndex); // Step forward coordinator.NextSentence(); Assert.Equal(1, coordinator.CurrentIndex); Assert.Equal("This is PACA browser.", coordinator.CurrentSentence); // Playback rate coordinator.SetPlaybackRate(1.5); Assert.Equal(1.5, coordinator.PlaybackRate); // Pause & Resume & Stop coordinator.Pause(); Assert.True(coordinator.IsPaused); coordinator.Resume(); Assert.False(coordinator.IsPaused); coordinator.Stop(); Assert.False(coordinator.IsPlaying); Assert.Equal(0, coordinator.CurrentIndex); } [Fact] public void Gate118_PictureInPictureCoordinator_ManagesFloatingPiPSession() { // Arrange var coordinator = new PictureInPictureCoordinator(); // Act 1: Open PiP var session = coordinator.RequestPiP( tabId: "tab-media-42", videoSource: "https://example.com/stream.mp4", videoBounds: new ScreenRect(100, 100, 640, 360)); // Assert 1 Assert.NotNull(session); Assert.True(session.IsTopMost); Assert.True(session.IsPlaying); Assert.Equal("tab-media-42", session.TabId); Assert.Equal("https://example.com/stream.mp4", session.VideoSource); // Act 2: Controls coordinator.TogglePlayPause(session.SessionId); var active = coordinator.GetSession(session.SessionId); Assert.NotNull(active); Assert.False(active.IsPlaying); coordinator.SetVolume(session.SessionId, 0.75); active = coordinator.GetSession(session.SessionId); Assert.NotNull(active); Assert.Equal(0.75, active.Volume); coordinator.Seek(session.SessionId, 45.5); active = coordinator.GetSession(session.SessionId); Assert.NotNull(active); Assert.Equal(45.5, active.CurrentPositionSeconds); // Act 3: Close PiP var closeResult = coordinator.ClosePiP(session.SessionId); Assert.True(closeResult.Success); Assert.Equal(45.5, closeResult.LastPositionSeconds); Assert.Null(coordinator.GetSession(session.SessionId)); } [Fact] public async Task Gate119_WebTranslationService_DetectsLanguageAndTranslatesSegments() { // Arrange var service = new WebTranslationService(); // Act 1: Detect language var langEn = service.DetectLanguage("This is a technology article about distributed systems."); var langKo = service.DetectLanguage("이것은 분산 시스템에 관한 기술 기사입니다."); var langJa = service.DetectLanguage("これは分散システムに関する技術記事です。"); // Assert 1 Assert.Equal("en", langEn); Assert.Equal("ko", langKo); Assert.Equal("ja", langJa); // Act 2: Translate segments var segments = new List { "Hello world", "Privacy first browser" }; var results = await service.TranslateSegmentsAsync(segments, sourceLang: "en", targetLang: "ko"); // Assert 2 Assert.Equal(2, results.Count); Assert.All(results, r => Assert.False(string.IsNullOrWhiteSpace(r.TranslatedText))); } [Fact] public void Gate120_ForceContentDarkModeEngine_GeneratesInversionCssAndManagesWhitelist() { // Arrange var configPath = Path.Combine(_tempDir, "darkmode_policy.json"); var engine = new ForceContentDarkModeEngine(configPath); // Act 1: Generate CSS var css = engine.GenerateDarkModeCss(); Assert.Contains("invert(", css); Assert.Contains("hue-rotate(180deg)", css); Assert.Contains("img", css); Assert.Contains("video", css); // Act 2: Site policies engine.SetSiteMode("example.com", ForceDarkSiteMode.Enabled); engine.SetSiteMode("github.com", ForceDarkSiteMode.Disabled); // Assert 2 Assert.True(engine.ShouldForceDark("https://example.com/some/article")); Assert.False(engine.ShouldForceDark("https://github.com/trending")); Assert.True(engine.ShouldForceDark("https://unknown-bright-site.org")); // Default is enabled // Act 3: Save & Reload engine.Save(); var reloaded = new ForceContentDarkModeEngine(configPath); Assert.True(reloaded.ShouldForceDark("https://example.com/some/article")); Assert.False(reloaded.ShouldForceDark("https://github.com/trending")); } [Fact] public void Gate121_MhtmlWebArchivePackager_PackagesAndParsesSingleFileArchive() { // Arrange var packager = new MhtmlWebArchivePackager(); var html = "Archive Test

Saved Offline

"; var resources = new List { new("https://example.com/style.css", "text/css", System.Text.Encoding.UTF8.GetBytes("body { color: red; }")) }; var packageRequest = new WebArchivePackageRequest( Title: "Archive Test", Url: "https://example.com/page", HtmlContent: html, Resources: resources); // Act: Package var mhtmlString = packager.Package(packageRequest); // Assert 1: MHTML format verification Assert.Contains("multipart/related", mhtmlString); Assert.Contains("Snapshot-Content-Location: https://example.com/page", mhtmlString); Assert.Contains("text/html", mhtmlString); Assert.Contains("Saved Offline", mhtmlString); // Act 2: Unpack var unpacked = packager.Unpack(mhtmlString); Assert.Equal("Archive Test", unpacked.Title); Assert.Equal("https://example.com/page", unpacked.LocationUrl); Assert.Contains("Saved Offline", unpacked.HtmlContent); Assert.Single(unpacked.Resources); Assert.Equal("text/css", unpacked.Resources[0].MimeType); } [Fact] public void Gate122_PdfViewerToolbarCoordinator_ControlsPageLayoutAndAnnotations() { // Arrange var coordinator = new PdfViewerToolbarCoordinator(totalPages: 12); // Act 1: Page Navigation Assert.Equal(1, coordinator.CurrentPage); coordinator.NextPage(); Assert.Equal(2, coordinator.CurrentPage); coordinator.GoToPage(10); Assert.Equal(10, coordinator.CurrentPage); coordinator.GoToPage(999); // Clamping Assert.Equal(12, coordinator.CurrentPage); // Act 2: Rotation Assert.Equal(0, coordinator.RotationDegrees); coordinator.RotateClockwise(); Assert.Equal(90, coordinator.RotationDegrees); coordinator.RotateClockwise(); Assert.Equal(180, coordinator.RotationDegrees); // Act 3: Zoom coordinator.SetZoom(1.5); Assert.Equal(1.5, coordinator.ZoomLevel); // Act 4: View Mode coordinator.SetViewMode(PdfViewMode.TwoPages); Assert.Equal(PdfViewMode.TwoPages, coordinator.ViewMode); // Act 5: Highlight Annotation coordinator.AddHighlight(pageNumber: 3, new ScreenRect(50, 100, 200, 20), "#FFFF00"); var annotations = coordinator.GetAnnotations(3); Assert.Single(annotations); Assert.Equal("#FFFF00", annotations[0].ColorHex); } [Fact] public void Gate123_SiteAutoMutePolicy_ManagesMuteRulesAndPersistence() { // Arrange var policyPath = Path.Combine(_tempDir, "auto_mute_rules.json"); var policy = new SiteAutoMutePolicy(policyPath); // Act 1: Add rules policy.AddAutoMutePattern("*.noisy-news.com"); policy.AddAutoMutePattern("annoying-media.org"); // Assert 1: Match Assert.True(policy.IsAutoMute("https://live.noisy-news.com/stream")); Assert.True(policy.IsAutoMute("https://annoying-media.org/video")); Assert.False(policy.IsAutoMute("https://youtube.com/watch")); // Act 2: Remove rule policy.RemoveAutoMutePattern("annoying-media.org"); Assert.False(policy.IsAutoMute("https://annoying-media.org/video")); // Act 3: Persistence var reloaded = new SiteAutoMutePolicy(policyPath); Assert.True(reloaded.IsAutoMute("https://live.noisy-news.com/stream")); Assert.False(reloaded.IsAutoMute("https://annoying-media.org/video")); } }