using XFESystem.Browser; using XFESystem.Core; using XFESystem.Desktop; using System.Text; using System.Text.Json; using Xunit; namespace XFESystem.Desktop.Tests; /// Production desktop β†’ page factory β†’ DOM/Jint/Monaco. Host-only tests; /// no browser mocks, native guest or physical-disk claims. 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="// \n"+ (language=="csharp"?"public class Widget { public string Text = \" & \\\"quoted\\\"\"; }":"globalThis.executionCanary=1; const value = '';\n"); var window=fixture.Open(name,source); var page=Assert.IsType(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(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(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(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(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(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(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(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\n"; var editor=fixture.Open("Website.md",source); Assert.Null(editor.BrowserSession); fixture.Desktop.Key(DesktopKey.Run);fixture.Pump(); var page=Assert.IsType(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(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")); } }