releases/tests/Paca.Tests/ModernBrowsingMilestone2Phase102Tests.cs

251 lines
10 KiB
C#

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,<h1>Hello</h1>", new ScreenRect(50, 50, 200, 20));
Assert.False(sessionData.CanPeek);
var sessionBlank = coordinator.TryCreatePeekSession("about:blank", new ScreenRect(50, 50, 200, 20));
Assert.False(sessionBlank.CanPeek);
// Act & Assert 3: Promote to tab
var promoteEvent = coordinator.PromoteToTab(sessionValid.SessionId);
Assert.True(promoteEvent.Success);
Assert.Equal("https://example.org/article", promoteEvent.TargetUrl);
}
[Fact]
public void Gate113_CustomShortcutRemapper_DetectsConflictsAndBindsActions()
{
// Arrange
var mappingPath = Path.Combine(_tempDir, "shortcuts.json");
var remapper = new CustomShortcutRemapper(mappingPath);
// Act 1: Verify defaults
Assert.Equal(BrowserActionCommand.NewTab, remapper.GetCommand("Ctrl+T"));
Assert.Equal(BrowserActionCommand.CloseTab, remapper.GetCommand("Ctrl+W"));
// Act 2: Detect conflict
var conflict = remapper.FindConflict("Ctrl+T");
Assert.NotNull(conflict);
Assert.Equal(BrowserActionCommand.NewTab, conflict.ExistingCommand);
// Act 3: Bind custom shortcut
remapper.BindShortcut("Ctrl+Alt+N", BrowserActionCommand.NewTab);
Assert.Equal(BrowserActionCommand.NewTab, remapper.GetCommand("Ctrl+Alt+N"));
// Act 4: Save & Reload
remapper.Save();
var reloaded = new CustomShortcutRemapper(mappingPath);
Assert.Equal(BrowserActionCommand.NewTab, reloaded.GetCommand("Ctrl+Alt+N"));
}
[Fact]
public void Gate114_ClipboardPasteAndGoSuggester_InspectsUrlPayload()
{
// Arrange
var suggester = new ClipboardPasteAndGoSuggester(searchEngineUrlTemplate: "https://www.google.com/search?q={0}");
// Act & Assert 1: URL in clipboard -> Navigate suggestion
var urlSug = suggester.EvaluateClipboard("https://paca.dev/guide");
Assert.NotNull(urlSug);
Assert.Equal(ClipboardSuggestionType.Navigate, urlSug.SuggestionType);
Assert.Equal("https://paca.dev/guide", urlSug.TargetUrl);
Assert.Contains("paca.dev", urlSug.DisplayLabel);
// Act & Assert 2: Text in clipboard -> Search suggestion
var textSug = suggester.EvaluateClipboard("modern browser features");
Assert.NotNull(textSug);
Assert.Equal(ClipboardSuggestionType.Search, textSug.SuggestionType);
Assert.Equal("https://www.google.com/search?q=modern%20browser%20features", textSug.TargetUrl);
Assert.Contains("modern browser features", textSug.DisplayLabel);
// Act & Assert 3: Empty / whitespace -> null
var emptySug = suggester.EvaluateClipboard(" ");
Assert.Null(emptySug);
// Act & Assert 4: Overly long text gets truncated safely
var longText = new string('A', 120);
var longSug = suggester.EvaluateClipboard(longText);
Assert.NotNull(longSug);
Assert.True(longSug.DisplayLabel.Length <= 70);
Assert.Contains("...", longSug.DisplayLabel);
}
[Fact]
public void Gate115_TaskbarJumpListBuilder_ConstructsShellJumpList()
{
// Arrange
var builder = new TaskbarJumpListBuilder();
// Act
builder.AddFrequentSite("GitHub", "https://github.com");
builder.AddFrequentSite("YouTube", "https://youtube.com");
var jumpList = builder.Build();
// Assert 1: Standard Tasks category
var tasksCategory = jumpList.Categories.FirstOrDefault(c => c.CategoryType == JumpListCategoryType.Tasks);
Assert.NotNull(tasksCategory);
Assert.Contains(tasksCategory.Items, i => i.Arguments.Contains("--new-window"));
Assert.Contains(tasksCategory.Items, i => i.Arguments.Contains("--inprivate"));
Assert.Contains(tasksCategory.Items, i => i.Arguments.Contains("--downloads"));
Assert.Contains(tasksCategory.Items, i => i.Arguments.Contains("--history"));
// Assert 2: Frequent Sites category
var frequentCategory = jumpList.Categories.FirstOrDefault(c => c.CategoryType == JumpListCategoryType.Frequent);
Assert.NotNull(frequentCategory);
Assert.Equal(2, frequentCategory.Items.Count);
Assert.Equal("GitHub", frequentCategory.Items[0].Title);
Assert.Equal("https://github.com", frequentCategory.Items[0].TargetUrl);
}
}