using System; using System.Collections.Generic; using System.IO; using System.Linq; using Paca.Browser.Interaction; using Xunit; namespace Paca.Tests; public sealed class ModernBrowsingMilestone2Phase102Tests : IDisposable { private readonly string _tempDir; public ModernBrowsingMilestone2Phase102Tests() { _tempDir = Path.Combine(Path.GetTempPath(), "paca_m2_test_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_tempDir); } public void Dispose() { try { if (Directory.Exists(_tempDir)) Directory.Delete(_tempDir, true); } catch { } } [Fact] public void Gate109_MouseGestureEngine_RecognizesGesturesCorrectly() { // Arrange var engine = new MouseGestureEngine(minStrokeDistance: 15); // Act & Assert 1: Right drag -> Forward engine.BeginStroke(100, 100); engine.MoveStroke(160, 100); var resRight = engine.EndStroke(); Assert.Equal("R", resRight.Pattern); Assert.Equal(BrowserGestureAction.Forward, resRight.Action); // Act & Assert 2: Left drag -> Back engine.BeginStroke(200, 100); engine.MoveStroke(130, 100); var resLeft = engine.EndStroke(); Assert.Equal("L", resLeft.Pattern); Assert.Equal(BrowserGestureAction.Back, resLeft.Action); // Act & Assert 3: Down then Right -> CloseTab (DR) engine.BeginStroke(100, 100); engine.MoveStroke(100, 160); engine.MoveStroke(160, 160); var resDownRight = engine.EndStroke(); Assert.Equal("DR", resDownRight.Pattern); Assert.Equal(BrowserGestureAction.CloseTab, resDownRight.Action); // Act & Assert 4: Custom gesture registration engine.RegisterGesture("U", BrowserGestureAction.Refresh); engine.BeginStroke(100, 100); engine.MoveStroke(100, 50); var resUp = engine.EndStroke(); Assert.Equal("U", resUp.Pattern); Assert.Equal(BrowserGestureAction.Refresh, resUp.Action); // Act & Assert 5: Too short gesture -> None engine.BeginStroke(100, 100); engine.MoveStroke(105, 102); var resNone = engine.EndStroke(); Assert.Equal(string.Empty, resNone.Pattern); Assert.Equal(BrowserGestureAction.None, resNone.Action); } [Fact] public void Gate110_SuperDragDropHandler_DeterminesLinkAndTextActions() { // Arrange var handler = new SuperDragDropHandler(searchUrlTemplate: "https://www.google.com/search?q={0}"); // Act & Assert 1: Dragging a URL upward -> Open in background var linkBg = handler.EvaluateDragDrop("https://example.com/docs", deltaX: 0, deltaY: -60); Assert.Equal(SuperDragActionType.OpenLinkInBackground, linkBg.ActionType); Assert.Equal("https://example.com/docs", linkBg.Payload); // Act & Assert 2: Dragging a URL downward -> Open in foreground var linkFg = handler.EvaluateDragDrop("https://example.com/docs", deltaX: 0, deltaY: 60); Assert.Equal(SuperDragActionType.OpenLinkInForeground, linkFg.ActionType); Assert.Equal("https://example.com/docs", linkFg.Payload); // Act & Assert 3: Dragging text -> Search text var textSearch = handler.EvaluateDragDrop("csharp modern browsing", deltaX: 40, deltaY: 0); Assert.Equal(SuperDragActionType.SearchText, textSearch.ActionType); Assert.Equal("https://www.google.com/search?q=csharp%20modern%20browsing", textSearch.Payload); // Act & Assert 4: Empty string or whitespace -> None var emptyAction = handler.EvaluateDragDrop(" ", deltaX: 10, deltaY: 10); Assert.Equal(SuperDragActionType.None, emptyAction.ActionType); Assert.Empty(emptyAction.Payload); } [Fact] public void Gate111_SelectionMiniToolbarCoordinator_ExtractsActionsForText() { // Arrange var coordinator = new SelectionMiniToolbarCoordinator(); // Act 1: Plain text var textModel = coordinator.BuildToolbar( selectedText: "PACA modern browser", selectionBounds: new ScreenRect(100, 100, 120, 24), viewportBounds: new ScreenRect(0, 0, 1920, 1080)); // Assert 1 Assert.True(textModel.IsVisible); Assert.Contains(textModel.Actions, a => a.Kind == SelectionActionKind.Copy); Assert.Contains(textModel.Actions, a => a.Kind == SelectionActionKind.WebSearch); Assert.Contains(textModel.Actions, a => a.Kind == SelectionActionKind.Translate); Assert.Contains(textModel.Actions, a => a.Kind == SelectionActionKind.TextToSpeech); Assert.DoesNotContain(textModel.Actions, a => a.Kind == SelectionActionKind.Navigate); // Act 2: URL text var urlModel = coordinator.BuildToolbar( selectedText: "https://github.com/dotnet", selectionBounds: new ScreenRect(200, 200, 180, 24), viewportBounds: new ScreenRect(0, 0, 1920, 1080)); // Assert 2 Assert.True(urlModel.IsVisible); Assert.Contains(urlModel.Actions, a => a.Kind == SelectionActionKind.Navigate); // Act 3: Whitespace or empty var emptyModel = coordinator.BuildToolbar( selectedText: " ", selectionBounds: new ScreenRect(200, 200, 10, 10), viewportBounds: new ScreenRect(0, 0, 1920, 1080)); Assert.False(emptyModel.IsVisible); Assert.Empty(emptyModel.Actions); } [Fact] public void Gate112_PeekPreviewCoordinator_ValidatesPeekableUrls() { // Arrange var coordinator = new PeekPreviewCoordinator(); // Act & Assert 1: Valid HTTP/HTTPS URLs can be peeked var sessionValid = coordinator.TryCreatePeekSession("https://example.org/article", new ScreenRect(50, 50, 200, 20)); Assert.True(sessionValid.CanPeek); Assert.Equal("https://example.org/article", sessionValid.Url); Assert.NotNull(sessionValid.SessionId); // Act & Assert 2: Dangerous or non-web schemes cannot be peeked var sessionJs = coordinator.TryCreatePeekSession("javascript:alert(1)", new ScreenRect(50, 50, 200, 20)); Assert.False(sessionJs.CanPeek); var sessionData = coordinator.TryCreatePeekSession("data:text/html,