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

XFESystem

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

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

namespace XFESystem.Desktop.Tests;

/// <summary>Production desktop → page factory → DOM/Jint/Monaco. Host-only tests;
/// no browser mocks, native guest or physical-disk claims.</summary>
public sealed class RealWebEditorTests
{
    private sealed class Fixture : IDisposable
    {
        public readonly MemoryWorkspace Storage=new(seed:false);
        public readonly WindowsTextCanvas Canvas=new(Path.Combine(AppContext.BaseDirectory,"TestAssets/Font.ttf"),1280,800);
        public readonly DesktopSession Desktop;
        private long now;
        public Fixture() => Desktop=new(Canvas,Storage,new BrowserService(),monotonicMilliseconds:()=>now);
        public DesktopWindow Open(string name,string source)
        {
            string path=SystemPaths.Combine(SystemPaths.Documents,name);Storage.WriteText(path,source);
            var window=Desktop.Open(DesktopApplication.Editor,path);Pump();return window;
        }
        public void Pump()
        {
            // Local previews now require a real completed presentation before
            // their ordered enhancement phases. Pump the actual desktop path,
            // rather than assuming synchronous JavaScript during F5 itself.
            for(int frame=0;frame<64;frame++)
            {
                Desktop.Render(); now+=17; Desktop.AdvanceFeedback(now);
                if(frame>=7 && Desktop.ActiveWindow?.BrowserSession is not BrowserService { DocumentEnhancementsPending:true }) break;
            }
            Desktop.Render();
        }
        public void Dispose()=>Canvas.Dispose();
    }

    [Theory]
    [InlineData("Example.cs","csharp")]
    [InlineData("Example.js","javascript")]
    public void CodeFileOpensItsActualOriginalMonacoModelWithoutExecutingSource(string name,string language)
    {
        using var fixture=new Fixture();
        string source="// </script><script>globalThis.injectionCanary=1</script>\n"+
            (language=="csharp"?"public class Widget { public string Text = \"<tag> & \\\"quoted\\\"\"; }":"globalThis.executionCanary=1; const value = '</script>';\n");
        var window=fixture.Open(name,source);
        var page=Assert.IsType<BrowserService>(window.BrowserSession);
        Assert.Equal(source,page.ReadEditorText());
        Assert.Equal(language,page.ExecuteJavaScript("xfeMonacoEditor.getModel().getLanguageId()"));
        Assert.Equal("undefined",page.ExecuteJavaScript("typeof injectionCanary"));
        Assert.Equal("undefined",page.ExecuteJavaScript("typeof executionCanary"));
        Assert.Contains("Monaco",window.Status,StringComparison.Ordinal);
    }

    [Theory]
    [InlineData("Preserved.cs", "CSharpLanguage", "csharp")]
    [InlineData("Preserved.cs", "JavaScriptLanguage", "javascript")]
    [InlineData("Preserved.js", "CSharpLanguage", "csharp")]
    [InlineData("Preserved.js", "JavaScriptLanguage", "javascript")]
    public void FileEditorLanguageButtonChangesGrammarWithoutReplacingOrSavingOverOriginalSource(string name,string buttonId,string language)
    {
        using var fixture = new Fixture();
        string source = "// Unique original 19C3B875 \0 \u001b 中文😀\r\n" +
            (name.EndsWith(".cs",StringComparison.Ordinal) ? "public class UserDocument { public string Preserve = \"DO NOT REPLACE\"; }\r\n" :
                "const preserved = 'DO NOT REPLACE';\r\n");
        var window = fixture.Open(name,source);
        var page = Assert.IsType<BrowserService>(window.BrowserSession);
        Assert.Equal(source,page.ReadEditorText());
        Assert.False(window.IsDirty);

        ClickLanguage(fixture,window,page,buttonId);

        Assert.Equal(language,page.ExecuteJavaScript("xfeMonacoEditor.getModel().getLanguageId()"));
        Assert.Equal(source,page.ReadEditorText());
        Assert.Equal(source,window.Content);
        Assert.False(window.IsDirty);
        fixture.Desktop.Key(DesktopKey.Save);
        Assert.Equal(source,fixture.Storage.ReadText(window.Location));
        Assert.Equal(Encoding.UTF8.GetBytes(source),fixture.Storage.ReadBytes(window.Location));
        Assert.False(window.IsDirty);
        Assert.Contains("Saved to RAM live",window.Status,StringComparison.Ordinal);
    }

    [Fact]
    public void AboutMonacoLanguageButtonsStillLoadTheirRealDemonstrationSamples()
    {
        using var fixture = new Fixture();
        var window = fixture.Desktop.Open(DesktopApplication.Browser,"about:Monaco");
        fixture.Pump();
        var page = Assert.IsType<BrowserService>(window.BrowserSession);
        const string javascript = "// XFESystem | XFEstudio\nconst answer = 6 * 7;\nconsole.log(answer);";
        Assert.Equal(javascript,page.ReadEditorText());

        ClickLanguage(fixture,window,page,"CSharpLanguage");

        string csharp = page.ReadEditorText()!;
        Assert.StartsWith("using System;\nnamespace XFEstudio;",csharp,StringComparison.Ordinal);
        Assert.Contains("public sealed class Preview",csharp,StringComparison.Ordinal);
        Assert.Contains("Math.Max(value, 42)",csharp,StringComparison.Ordinal);
        Assert.Equal("csharp",page.ExecuteJavaScript("xfeMonacoEditor.getModel().getLanguageId()"));

        ClickLanguage(fixture,window,page,"JavaScriptLanguage");

        Assert.Equal(javascript,page.ReadEditorText());
        Assert.Equal("javascript",page.ExecuteJavaScript("xfeMonacoEditor.getModel().getLanguageId()"));
        Assert.DoesNotContain("namespace XFEstudio",page.ReadEditorText()!,StringComparison.Ordinal);
    }

    private static void ClickLanguage(Fixture fixture,DesktopWindow window,BrowserService page,string buttonId)
    {
        var button = page.Document!.GetElementById(buttonId);
        Assert.NotNull(button);
        var rectangle = page.Layout(new(window.Bounds.Width-4,window.Bounds.Height-96)).GetBoundingClientRect(button);
        Assert.True(rectangle.Width>0 && rectangle.Height>0,"The language button must be a real visible pointer target.");
        int x = window.Bounds.X+2+(int)Math.Round(rectangle.X+rectangle.Width/2);
        int y = window.Bounds.Y+67+(int)Math.Round(rectangle.Y+rectangle.Height/2);
        fixture.Desktop.Pointer(x,y,false);
        fixture.Desktop.Pointer(x,y,true);
        fixture.Desktop.Pointer(x,y,false);
        fixture.Pump();
        Assert.Contains("|BUTTON|"+buttonId+"|Same|True|ClickAllowed|True",page.LastInputTrace,StringComparison.Ordinal);
    }

    [Fact]
    public void OpeningCodeEditorAcceptsActualKeyboardAndControlSaveReadsTheModel()
    {
        using var fixture=new Fixture();
        const string source="using System;\npublic class Widget { }";
        var window=fixture.Open("Keyboard.cs",source);
        var page=Assert.IsType<BrowserService>(window.BrowserSession);
        fixture.Desktop.BrowserKey(new("keydown","/","Slash","/"));fixture.Desktop.BrowserKey(new("keyup","/","Slash"));
        fixture.Pump();
        Assert.Equal("/"+source,page.ReadEditorText());
        fixture.Desktop.Key(DesktopKey.Save);
        Assert.Equal("/"+source,fixture.Storage.ReadText(window.Location));
        Assert.False(window.IsDirty);Assert.Contains("Saved to RAM live",window.Status,StringComparison.Ordinal);
    }

    [Fact]
    public void SaveUsesTheCurrentModelEvenBeforeTheNextDesktopPump()
    {
        using var fixture=new Fixture();
        var window=fixture.Open("Saved.cs","public class Before { }");
        var page=Assert.IsType<BrowserService>(window.BrowserSession);
        page.ExecuteJavaScript("xfeMonacoEditor.setValue('public class After { public int Value = 42; }')");
        fixture.Desktop.Key(DesktopKey.Save);
        Assert.Equal("public class After { public int Value = 42; }",fixture.Storage.ReadText(window.Location));
    }

    [Fact]
    public void TwoCodeWindowsHaveSeparateRealModelsDomAndJavaScriptGlobals()
    {
        using var fixture=new Fixture();
        var first=fixture.Open("First.cs","public class First { }");
        var firstPage=Assert.IsType<BrowserService>(first.BrowserSession);
        firstPage.ExecuteJavaScript("globalThis.privateEditorCanary=42;xfeMonacoEditor.setValue('public class Changed { }')");
        var second=fixture.Open("Second.js","const second = 2;");
        var secondPage=Assert.IsType<BrowserService>(second.BrowserSession);
        Assert.NotSame(firstPage,secondPage);
        Assert.Equal("undefined",secondPage.ExecuteJavaScript("typeof privateEditorCanary"));
        Assert.Equal("const second = 2;",secondPage.ReadEditorText());
        Assert.Equal("public class Changed { }",firstPage.ReadEditorText());
        fixture.Desktop.Key(DesktopKey.Save);
        Assert.Equal("const second = 2;",fixture.Storage.ReadText(second.Location));
        Assert.Equal("public class First { }",fixture.Storage.ReadText(first.Location));
    }

    [Theory]
    [InlineData("Preview.cs","public class Preview { public int Value = 42; }","csharp")]
    [InlineData("Preview.js","globalThis.fileExecutionCanary=1; const answer = 42;","javascript")]
    public void F5RendersARealWebsiteCodePreviewWithSharedStylesAndScriptHighlighting(string name,string source,string language)
    {
        using var fixture=new Fixture();
        var editor=fixture.Open(name,source);
        fixture.Desktop.Key(DesktopKey.Run);fixture.Pump();
        var preview=fixture.Desktop.ActiveWindow!;
        Assert.Equal(DesktopApplication.Browser,preview.Application);
        Assert.NotSame(editor,preview);
        var page=Assert.IsType<BrowserService>(preview.BrowserSession);
        var code=page.Document!.QuerySelector("code.language-"+language);
        Assert.NotNull(code);Assert.Equal(source,code.TextContent);
        Assert.NotEmpty(code.QuerySelectorAll("span[class^='hljs-']"));
        Assert.Contains(page.LoadedResources,resource=>resource.Kind=="style"&&resource.Address.EndsWith("/css/docs/code-style.css",StringComparison.Ordinal));
        Assert.Contains(page.LoadedResources,resource=>resource.Kind=="script"&&resource.Address.EndsWith("/js/docs/highlight.min.js",StringComparison.Ordinal));
        Assert.Equal("undefined",page.ExecuteJavaScript("typeof fileExecutionCanary"));
        string before=page.Document.DocumentElement!.OuterHtml;
        using var proof=JsonDocument.Parse(page.ReadWebsiteDocumentProof());
        Assert.Equal("ActualWebsiteDocumentDomReadback",proof.RootElement.GetProperty("Kind").GetString());
        Assert.True(proof.RootElement.GetProperty("ReadOnly").GetBoolean());
        Assert.Equal(source,proof.RootElement.GetProperty("CodeBlocks")[0].GetProperty("CodeText").GetString());
        Assert.Equal(before,page.Document.DocumentElement.OuterHtml);
    }

    [Fact]
    public void F5MarkdownUsesTheRealWebDocumentComponentAndKeepsUntrustedMarkupInert()
    {
        using var fixture=new Fixture();
        const string source="# Website preview\n\n> Native Markdown quote\n\n```csharp\npublic class Widget { public int Value = 42; }\n```\n\n<script>globalThis.markdownCanary=1</script>\n";
        var editor=fixture.Open("Website.md",source);
        Assert.Null(editor.BrowserSession);
        fixture.Desktop.Key(DesktopKey.Run);fixture.Pump();
        var page=Assert.IsType<BrowserService>(fixture.Desktop.ActiveWindow!.BrowserSession);
        Assert.Equal("Website preview",page.Document!.QuerySelector("h1")!.TextContent);
        Assert.Contains("Native Markdown quote",page.Document.QuerySelector("blockquote")!.TextContent,StringComparison.Ordinal);
        Assert.NotNull(page.Document.QuerySelector("code.language-csharp .hljs-keyword"));
        Assert.Contains(page.LoadedResources,resource=>resource.Address.EndsWith("/css/docs/markdown-style.css",StringComparison.Ordinal));
        Assert.Equal("undefined",page.ExecuteJavaScript("typeof markdownCanary"));
        using var proof=JsonDocument.Parse(page.ReadWebsiteDocumentProof());
        Assert.Equal("Website preview",proof.RootElement.GetProperty("Heading").GetProperty("Text").GetString());
        Assert.True(proof.RootElement.GetProperty("WebsiteCodeToolsAvailable").GetBoolean());
    }

    [Fact]
    public void WebsiteProofDoesNotClaimAnOrdinaryBrowserPageIsAGeneratedDocument()
    {
        var browser=new BrowserService();browser.Navigate("about:Start");
        Assert.Equal("{}",browser.ReadWebsiteDocumentProof());
    }

    [Fact]
    public void MonacoColorsMatchActualWebsiteMarkdownWebFinalCascade()
    {
        using var fixture=new Fixture();
        const string source="using System;\npublic class Palette { public void Run(int count) { var name = \"normal\"; for(var i=0;i<count;i++) Console.WriteLine(name); } }";
        var editor=fixture.Open("Palette.cs",source);
        var monaco=Assert.IsType<BrowserService>(editor.BrowserSession);
        var website=new BrowserService();website.SetCanvas(fixture.Canvas);website.ResizeViewport(800,600);
        website.NavigateDocument(source,"Palette.cs");
        var layout=website.Layout(new(800,600));
        foreach(var sample in new[]{("code.language-csharp","xfe"),(".hljs-namespace","xfe.namespace"),(".hljs-string","xfe.string"),(".hljs-number","xfe.number"),(".hljs-csharp-keyword","xfe.csharp_keyword"),(".hljs-type","xfe.type")})
        {
            var element=website.Document!.QuerySelector(sample.Item1);Assert.NotNull(element);
            var style=layout.GetComputedStyle(element);Assert.NotNull(style);
            string expected=style.Color.Rgb.ToString("x6");
            string actual=monaco.ExecuteJavaScript("xfeWebsiteHighlight.themeRules.find(rule=>rule.token==='"+sample.Item2+"').foreground.toLowerCase()");
            Assert.Equal(expected,actual);
        }
        Assert.Equal("ce9178",monaco.ExecuteJavaScript("xfeWebsiteHighlight.themeRules.find(rule=>rule.token==='xfe.string').foreground"));
        Assert.Equal("b5cea8",monaco.ExecuteJavaScript("xfeWebsiteHighlight.themeRules.find(rule=>rule.token==='xfe.number').foreground"));
        Assert.Equal("f1f1f4",monaco.ExecuteJavaScript("xfeWebsiteHighlight.themeRules.find(rule=>rule.token==='xfe').foreground"));
        Assert.Equal("d69d85",monaco.ExecuteJavaScript("xfeWebsiteHighlight.themeRules.find(rule=>rule.token==='xfe.meta.string').foreground"));
    }
}