XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using XFESystem.Core;
using XFESystem.Desktop;
using Xunit;

namespace XFESystem.Desktop.Tests;

public sealed class BrowserAddressDraftTests
{
    private const string Bing = "https://www.bing.com/";
    private const string Redirected = "https://cn.bing.com/";

    [Fact]
    public void InternalNavigationRepaintsTheAddressBarAfterTheSceneCacheHasSettled()
    {
        var f = new Fixture(); var window = f.Desktop.Open(DesktopApplication.Browser);
        f.Desktop.Render(); f.Desktop.AdvanceFeedback(2000); f.Desktop.Render(); f.Canvas.Damages.Clear();
        const string search = "https://cn.bing.com/search?q=test";
        f.Page.Publish(search, "Actual search results"); f.Desktop.AdvanceFeedback(3000); f.Desktop.Render();
        Assert.Equal(search, window.Location);
        Assert.Contains(f.Canvas.Damages, damage => damage.Contains(window.Bounds.X + 38, window.Bounds.Y + 43));
    }

    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public void DraftSurvivesEveryBackgroundSnapshotAndTheCtrlLRelease(bool pointerFocus)
    {
        var f = new Fixture(); var window = f.Desktop.Open(DesktopApplication.Browser);
        f.FocusAddress(window, pointerFocus);
        Assert.False(f.Desktop.BrowserKey(new("keyup", "Control", "ControlLeft")));
        string draft = "";
        foreach (char character in "about:Start")
        {
            f.Desktop.Key(DesktopKey.Character, character); draft += character;
            f.Page.Publish(Redirected, "Timer snapshot " + draft.Length);
            f.Desktop.AdvanceFeedback(20 * draft.Length);
            Assert.Equal(draft, window.Location);
            Assert.False(f.Desktop.HasBrowserContentFocus);
        }
        Assert.Empty(f.Page.Keys);
        Assert.Single(f.Page.Navigations);
        f.Desktop.Key(DesktopKey.Enter);
        Assert.Equal("about:Start", f.Page.Navigations[^1]);
        Assert.Equal("about:Start", window.Location);
        f.Page.Publish("xfe-app://Start/index.html", "Loaded local page");
        f.Desktop.AdvanceFeedback(400);
        Assert.Equal("xfe-app://Start/index.html", window.Location);
    }

    [Fact]
    public void EscapeCancelsToTheLatestCommittedUrlWithoutClosingTheWindow()
    {
        var f = new Fixture(); var window = f.Desktop.Open(DesktopApplication.Browser);
        f.FocusAddress(window); f.Type("about:St");
        f.Page.Publish(Redirected, "The original navigation redirected"); f.Desktop.AdvanceFeedback(20);
        Assert.Equal("about:St", window.Location);
        f.Desktop.Key(DesktopKey.Escape);
        Assert.Contains(window, f.Desktop.Windows);
        Assert.Equal(Redirected, window.Location);
        Assert.True(f.Desktop.HasBrowserContentFocus);
        f.Page.Publish("https://cn.bing.com/new", "Another page snapshot"); f.Desktop.AdvanceFeedback(40);
        Assert.Equal("https://cn.bing.com/new", window.Location);
        Assert.Single(f.Page.Navigations);
    }

    [Fact]
    public void ClickingContentCancelsTheDraftAndRestoresActualPageKeyboardInput()
    {
        var f = new Fixture(); var window = f.Desktop.Open(DesktopApplication.Browser);
        f.FocusAddress(window); f.Type("about:Draft");
        f.Page.Publish(Redirected, "Current page"); f.Desktop.AdvanceFeedback(20);
        f.Click(window.Bounds.X + 70, window.Bounds.Y + 120);
        Assert.Equal(Redirected, window.Location);
        Assert.True(f.Desktop.HasBrowserContentFocus);
        f.Desktop.Key(DesktopKey.Character, 'z');
        Assert.Contains(f.Page.Keys, input => input.Type == "keydown" && input.Text == "z");
        Assert.Single(f.Page.Navigations);
    }

    [Fact]
    public void CompletingPendingNavigationKeepsTheDraftAndDoesNotStartAnUnsubmittedRequest()
    {
        var f = new Fixture(background: true); var window = f.Desktop.Open(DesktopApplication.Browser);
        var original = Assert.Single(f.Service.Operations);
        f.FocusAddress(window); f.Type("about:St");
        var page = new Page(Redirected); original.Complete(page); f.Desktop.AdvanceFeedback(20);
        Assert.Same(page, window.BrowserSession);
        Assert.Equal("about:St", window.Location);
        Assert.False(f.Desktop.HasBrowserContentFocus);
        Assert.False(f.Desktop.BrowserKey(new("keyup", "Control", "ControlLeft")));
        Assert.Single(f.Service.Operations);
        f.Type("art"); page.Publish(Redirected, "An animation snapshot"); f.Desktop.AdvanceFeedback(40);
        Assert.Equal("about:Start", window.Location);
        f.Desktop.Key(DesktopKey.Enter);
        Assert.Equal(2, f.Service.Operations.Count);
        var submitted = f.Service.Operations[^1];
        Assert.Equal(EditorPreparationKind.Navigation, submitted.Request.Kind);
        Assert.Equal("about:Start", submitted.Request.Path);
        Assert.True(page.Released);
        var local = new Page("xfe-app://Start/index.html"); submitted.Complete(local); f.Desktop.AdvanceFeedback(60);
        Assert.Equal(local.CurrentPage.Address, window.Location);
        Assert.True(f.Desktop.HasBrowserContentFocus);
    }

    [Fact]
    public void SubmitWhilePendingRetiresOnlyTheOldRequestAndQueuesTheCompleteDraft()
    {
        var f = new Fixture(background: true); var window = f.Desktop.Open(DesktopApplication.Browser);
        var original = Assert.Single(f.Service.Operations);
        f.FocusAddress(window); f.Type("about:Start"); f.Desktop.AdvanceFeedback(20);
        Assert.Single(f.Service.Operations);
        f.Desktop.Key(DesktopKey.Enter);
        Assert.True(original.Cancelled);
        Assert.Equal(2, f.Service.Operations.Count);
        Assert.Equal("about:Start", f.Service.Operations[^1].Request.Path);
        f.Service.Operations[^1].Complete(new Page("xfe-app://Start/index.html")); f.Desktop.AdvanceFeedback(40);
        Assert.Equal("xfe-app://Start/index.html", window.Location);
        Assert.Equal(2, f.Service.Operations.Count);
    }

    [Fact]
    public void EscapeWhilePendingRestoresSubmittedUrlAndStillAcceptsItsLaterRedirect()
    {
        var f = new Fixture(background: true); var window = f.Desktop.Open(DesktopApplication.Browser);
        f.FocusAddress(window); f.Type("about:Draft"); f.Desktop.Key(DesktopKey.Escape);
        Assert.Contains(window, f.Desktop.Windows);
        Assert.Equal(Bing, window.Location);
        var operation = Assert.Single(f.Service.Operations);
        operation.Complete(new Page(Redirected)); f.Desktop.AdvanceFeedback(20);
        Assert.Equal(Redirected, window.Location);
        Assert.True(f.Desktop.HasBrowserContentFocus);
        Assert.Single(f.Service.Operations);
    }

    private sealed class Fixture
    {
        public readonly Page Page = new(Bing);
        public readonly QueuedService Service = new();
        public readonly DesktopSession Desktop;
        public readonly Canvas Canvas = new();
        public Fixture(bool background = false) => Desktop = new(Canvas, new MemoryWorkspace(seed: false), new Backend(Page), editorPreparation: background ? Service : null);
        public void Type(string text) { foreach (char character in text) Desktop.Key(DesktopKey.Character, character); }
        public void FocusAddress(DesktopWindow window, bool pointer = false)
        { if (pointer) Click(window.Bounds.X + 150, window.Bounds.Y + 48); else Desktop.Key(DesktopKey.FocusAddress); }
        public void Click(int x, int y) { Desktop.Pointer(x, y, false); Desktop.Pointer(x, y, true); Desktop.Pointer(x, y, false); }
    }
    private sealed class Backend(Page page) : IBrowserService, IInteractiveBrowserFactory
    {
        public IInteractiveBrowserSession CreatePageSession() => page;
        public BrowserPage Navigate(string address) => throw new InvalidOperationException("Must use the explicit page session");
        public string ExecuteJavaScript(string source) => throw new NotSupportedException();
        public string PreviewMarkdown(string source) => throw new NotSupportedException();
    }
    private sealed class QueuedService : IEditorPreparationService
    {
        public readonly List<Operation> Operations = [];
        public IEditorPreparationOperation Begin(EditorPreparationRequest request) { var operation = new Operation(request); Operations.Add(operation); return operation; }
    }
    private sealed class Operation(EditorPreparationRequest request) : IEditorPreparationOperation
    {
        public EditorPreparationRequest Request => request;
        private EditorPreparationResult? result;
        public bool Cancelled;
        public void Complete(Page page) => result = new(page, null, 42);
        public void Cancel() { Cancelled = true; result = null; }
        public bool TryTakeResult(out EditorPreparationResult value) { value = result!; result = null; return value is not null; }
    }
    private sealed class Page(string address) : IInteractiveBrowserSession
    {
        private BrowserPage current = new(address, "Page", "Page content");
        private bool pending;
        public readonly List<string> Navigations = [];
        public readonly List<BrowserKeyboardInput> Keys = [];
        public bool Released;
        public BrowserPage CurrentPage => current;
        public long Revision { get; private set; }
        public double DocumentHeight => 400;
        public BrowserPage Navigate(string target) { Navigations.Add(target); Publish(target, "Page content"); return current; }
        public void Publish(string target, string content) { current = new(target, "Page", content); pending = true; Revision++; }
        public void Paint(IDesktopCanvas canvas, DesktopBounds viewport, double scrollY) { }
        public bool Pointer(BrowserPointerInput input) => false;
        public bool Key(BrowserKeyboardInput input) { Keys.Add(input); return true; }
        public bool AdvanceTime(long milliseconds) { bool changed = pending; pending = false; return changed; }
        public void Release() => Released = true;
    }
    private sealed class Canvas : IDesktopCanvas
    {
        public readonly List<DesktopBounds> Damages = [];
        public bool SupportsSceneCache => true;
        public void Present(DesktopBounds damage) => Damages.Add(damage);
        public int Width => 1280; public int Height => 800;
        public void Fill(uint color, int x, int y, int width, int height) { }
        public void Line(uint color, int x1, int y1, int x2, int y2) { }
        public void Text(string text, uint color, int x, int y) { }
        public void Present() { }
    }
}