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
{
[Fact]
public void MarkdownEditorAdapterUsesMonacosActualPlainTextLanguageAndPreservesTheSource()
{
var browser = new BrowserService();
const string source = "# Original markdown\n\n**bold** and `code` 中文.\n";
try
{
browser.NavigateEditor(source, "Original.md");
Assert.Equal("plaintext", browser.ExecuteJavaScript("xfeMonacoEditor.getModel().getLanguageId()"));
Assert.Equal(source, browser.ReadEditorText());
Assert.Equal("{}", browser.ReadMonacoProof());
}
finally { browser.Release(); }
}
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 } && Desktop.ActiveWindow?.PreviewSession is not BrowserService { DocumentEnhancementsPending:true }) break;
}
Desktop.Render();
}
public void Dispose()
{
foreach (var window in Desktop.Windows)
{ window.BrowserSession?.Release(); window.PreviewSession?.Release(); }
Canvas.Dispose();
}
}
[Fact]
public async Task ConcurrentRealFileEditorOpensAlwaysPublishTheOriginalModel()
{
// Reproduces an observed 100 ms regex timeout in a 27-character color
// name during actual Monaco startup. Each page/font/model is independent.
await Task.WhenAll(Enumerable.Range(0, 4).Select(worker => Task.Run(() =>
{
for (int pass = 0; pass < 16; pass++)
{
using var fixture = new Fixture();
string source = "// Original \0 \u001b 中文😀\r\nconst preserved = '" + worker + ":" + pass + "';\r\n";
var window = fixture.Open("Preserved.js", source);
Assert.True(window.BrowserSession is BrowserService, window.Status);
Assert.Equal(source, ((BrowserService)window.BrowserSession!).ReadEditorText());
Assert.Equal(source, fixture.Storage.ReadText(window.Location));
}
})));
}
[Fact]
public void FailedPackagedInitializationReportsItsFirstErrorBeforeTheMissingEditor()
{
var page = new BrowserService(resources: new FailedCoreResource());
try
{
var error = Assert.Throws<IOException>(() => page.NavigateEditor("const preserved = 42;", "Preserved.js"));
Assert.Contains("OriginalStartupFailure", error.Message, StringComparison.Ordinal);
Assert.DoesNotContain("xfeMonacoEditor is not defined", error.Message, StringComparison.Ordinal);
}
finally { page.Release(); }
}
private sealed class FailedCoreResource : IBrowserResourceSource
{
private readonly PackagedBrowserResources resources = new();
public bool TryGet(Uri address, out WebResponse response)
{
if (address.AbsolutePath.EndsWith("/EditorCore.js", StringComparison.Ordinal))
{ response = new(address, 200, "application/javascript", "throw new Error('OriginalStartupFailure')"); return true; }
return resources.TryGet(address, out response);
}
}
[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);
Assert.True(window.BrowserSession is not null, window.Status);
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 MarkdownDefaultsToTheRealWebDocumentPreviewAndKeepsUntrustedMarkupInert()
{
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);
Assert.Equal(MarkdownViewMode.Preview, editor.MarkdownView);
var initialPage = editor.PreviewSession;
fixture.Desktop.Key(DesktopKey.Run);fixture.Pump();
Assert.Same(editor, fixture.Desktop.ActiveWindow);
Assert.Same(initialPage, editor.PreviewSession);
var page=Assert.IsType<BrowserService>(editor.PreviewSession);
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"));
}
}
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
{
[Fact]
public void MarkdownEditorAdapterUsesMonacosActualPlainTextLanguageAndPreservesTheSource()
{
var browser = new BrowserService();
const string source = "# Original markdown\n\n**bold** and `code` 中文.\n";
try
{
browser.NavigateEditor(source, "Original.md");
Assert.Equal("plaintext", browser.ExecuteJavaScript("xfeMonacoEditor.getModel().getLanguageId()"));
Assert.Equal(source, browser.ReadEditorText());
Assert.Equal("{}", browser.ReadMonacoProof());
}
finally { browser.Release(); }
}
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 } && Desktop.ActiveWindow?.PreviewSession is not BrowserService { DocumentEnhancementsPending:true }) break;
}
Desktop.Render();
}
public void Dispose()
{
foreach (var window in Desktop.Windows)
{ window.BrowserSession?.Release(); window.PreviewSession?.Release(); }
Canvas.Dispose();
}
}
[Fact]
public async Task ConcurrentRealFileEditorOpensAlwaysPublishTheOriginalModel()
{
// Reproduces an observed 100 ms regex timeout in a 27-character color
// name during actual Monaco startup. Each page/font/model is independent.
await Task.WhenAll(Enumerable.Range(0, 4).Select(worker => Task.Run(() =>
{
for (int pass = 0; pass < 16; pass++)
{
using var fixture = new Fixture();
string source = "// Original \0 \u001b 中文😀\r\nconst preserved = '" + worker + ":" + pass + "';\r\n";
var window = fixture.Open("Preserved.js", source);
Assert.True(window.BrowserSession is BrowserService, window.Status);
Assert.Equal(source, ((BrowserService)window.BrowserSession!).ReadEditorText());
Assert.Equal(source, fixture.Storage.ReadText(window.Location));
}
})));
}
[Fact]
public void FailedPackagedInitializationReportsItsFirstErrorBeforeTheMissingEditor()
{
var page = new BrowserService(resources: new FailedCoreResource());
try
{
var error = Assert.Throws<IOException>(() => page.NavigateEditor("const preserved = 42;", "Preserved.js"));
Assert.Contains("OriginalStartupFailure", error.Message, StringComparison.Ordinal);
Assert.DoesNotContain("xfeMonacoEditor is not defined", error.Message, StringComparison.Ordinal);
}
finally { page.Release(); }
}
private sealed class FailedCoreResource : IBrowserResourceSource
{
private readonly PackagedBrowserResources resources = new();
public bool TryGet(Uri address, out WebResponse response)
{
if (address.AbsolutePath.EndsWith("/EditorCore.js", StringComparison.Ordinal))
{ response = new(address, 200, "application/javascript", "throw new Error('OriginalStartupFailure')"); return true; }
return resources.TryGet(address, out response);
}
}
[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);
Assert.True(window.BrowserSession is not null, window.Status);
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 MarkdownDefaultsToTheRealWebDocumentPreviewAndKeepsUntrustedMarkupInert()
{
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);
Assert.Equal(MarkdownViewMode.Preview, editor.MarkdownView);
var initialPage = editor.PreviewSession;
fixture.Desktop.Key(DesktopKey.Run);fixture.Pump();
Assert.Same(editor, fixture.Desktop.ActiveWindow);
Assert.Same(initialPage, editor.PreviewSession);
var page=Assert.IsType<BrowserService>(editor.PreviewSession);
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"));
}
}