using System.Reflection; using System.Xml.Linq; using OpenAC.MosswartMassacre.FlagTracker; using OpenAC.MosswartMassacre.Metas; using OpenAC.MosswartMassacre.Tests.Fakes; using OpenAC.MosswartMassacre.Views; namespace OpenAC.MosswartMassacre.Tests.Views; /// /// The plugin shows as the original did: one button on the plugin bar, "MM" /// with the original window's icon, opening one tabbed window. The Mossy /// Tracker tab opens the tracker window, which has no bar button of its own, /// and the Metas tab holds the meta and nav downloads. /// /// /// The tabs are driven the way the host drives them: each binding is looked /// up by the name the markup gives it and read or invoked by reflection. /// public sealed class OneWindowTests { private const uint OriginalIcon = 0x06001E37; private static PluginHarness WithUi() { var harness = new PluginHarness(arrange: host => host.HasUi = true); harness.Login(); return harness; } [Fact] public void The_plugin_bar_gets_one_button_MM_with_the_original_icon() { using PluginHarness harness = WithUi(); RegisteredPanel shelf = Assert.Single(harness.Host.Ui.Panels, panel => panel.Descriptor.ShowInSidePanel); Assert.Equal("main", shelf.Descriptor.WindowId); Assert.Equal("MM", shelf.Descriptor.IconText); Assert.Equal(OriginalIcon, shelf.Descriptor.IconSurfaceId); Assert.EndsWith(MainWindow.MarkupFile, shelf.MarkupPath, StringComparison.Ordinal); Assert.IsType(shelf.Binding); // The only other window is the tracker, opened from its tab. RegisteredPanel other = Assert.Single(harness.Host.Ui.Panels, panel => !panel.Descriptor.ShowInSidePanel); Assert.Equal("flagtracker", other.Descriptor.WindowId); Assert.Equal(2, harness.Host.Ui.Panels.Count); } [Fact] public void The_main_window_has_the_original_tabs_plus_metas() { XElement root = LoadMain(); string[] tabs = [.. root.Elements("tab").Select(tab => (string?)tab.Attribute("text") ?? string.Empty)]; Assert.Equal(["Main", "Settings", "Statistics", "Mossy Tracker", "Metas", "Tools"], tabs); foreach (string tab in tabs) Assert.NotNull(PageOf(root, tab)); } [Fact] public void The_mossy_tracker_tab_opens_the_tracker_window() { using PluginHarness harness = WithUi(); MainWindow window = harness.Feature(); FlagTrackerViewModel tracker = harness.Feature().Panel!; XElement page = PageOf(LoadMain(), "Mossy Tracker")!; XElement tab = LoadMain().Elements("tab").Single(t => (string?)t.Attribute("text") == "Mossy Tracker"); Invoke(window, (string?)tab.Attribute("onclick")); Assert.True(Read(window, (string?)page.Attribute("visible"))); XElement button = Assert.Single(page.Elements("button"), b => (string?)b.Attribute("text") == "Open Mossy Tracker"); Assert.False(tracker.IsVisible); Invoke(window, (string?)button.Attribute("onclick")); Assert.True(tracker.IsVisible); Assert.Equal(["flagtracker"], harness.Host.Ui.ShowPanelCalls); } [Fact] public void The_metas_tab_is_bound_to_the_meta_sync_and_names_the_real_folders() { using PluginHarness harness = WithUi(); MainWindow window = harness.Feature(); MetasViewModel metas = harness.Feature().Panel!; XElement page = PageOf(LoadMain(), "Metas")!; // The file list, its search and the download buttons are all here. XElement list = Assert.Single(page.Elements("list")); string[] columnItems = [.. list.Elements("column").Select(column => (string?)column.Attribute("items") ?? string.Empty)]; Assert.Equal(3, columnItems.Length); Assert.Equal(["No files loaded"], Read>(window, columnItems[1])); Assert.Single(page.Elements("field"), field => (string?)field.Attribute("onchange") is not null); string[] buttons = [.. page.Elements("button").Select(b => (string?)b.Attribute("text") ?? string.Empty)]; Assert.Contains("Refresh List", buttons); Assert.Contains("Check Updates", buttons); Assert.Contains("Download Outdated", buttons); Assert.Contains("Download", buttons); // The labels name the folders the automation plugin loads from. string vtankRoot = harness.Host.VtankProfiles.RootPath!; string[] labels = [.. page.Elements("label").Select(label => Text(window, (string?)label.Attribute("text")))]; Assert.Contains(labels, text => text.EndsWith(Path.Combine(vtankRoot, "mosstank", "metas"), StringComparison.Ordinal)); Assert.Contains(labels, text => text.EndsWith(Path.Combine(vtankRoot, "mosstank", "navs"), StringComparison.Ordinal)); // Typing in the tab's search reaches the meta sync and redraws the list. XElement search = page.Elements("field").Single(f => (string?)f.Attribute("onchange") is not null); Invoke(window, (string?)search.Attribute("onchange"), "zzz"); Assert.Equal("zzz", metas.SearchText); Assert.Equal(["No matches"], Read>(window, columnItems[1])); Assert.Equal("zzz", Text(window, (string?)search.Attribute("text"))); } [Fact] public void The_metas_tab_changes_the_repository_address() { using PluginHarness harness = WithUi(); MainWindow window = harness.Feature(); XElement page = PageOf(LoadMain(), "Metas")!; XElement field = Assert.Single(page.Elements("field"), f => (string?)f.Attribute("onsubmit") is not null); Assert.Equal(MetaSyncManager.DefaultRepositoryUrl, Text(window, (string?)field.Attribute("text"))); Invoke(window, (string?)field.Attribute("onsubmit"), " https://example.org/club/metas "); Assert.Equal("https://example.org/club/metas", harness.Context.Settings.Current.MetasRepositoryUrl); Assert.Equal("https://example.org/club/metas", Text(window, (string?)field.Attribute("text"))); } // ── markup helpers ── private static XElement LoadMain() => XDocument.Load(Path.Combine(AppContext.BaseDirectory, MainWindow.MarkupFile)).Root!; /// The group a tab shows: the one whose visibility is the tab's selected binding. private static XElement? PageOf(XElement root, string tabText) { XElement? tab = root.Elements("tab").SingleOrDefault(t => (string?)t.Attribute("text") == tabText); string? selected = (string?)tab?.Attribute("selected"); return selected is null ? null : root.Elements("group").SingleOrDefault(group => (string?)group.Attribute("visible") == selected); } private static PropertyInfo Property(object binding, string? expression) { Assert.NotNull(expression); Assert.Matches("^\\{[^{}]+\\}$", expression); PropertyInfo? property = binding.GetType().GetProperty(expression[1..^1]); Assert.True(property is not null, $"{expression} names no public property on {binding.GetType().Name}."); return property; } private static T Read(object binding, string? expression) => Assert.IsAssignableFrom(Property(binding, expression).GetValue(binding)); /// A label's text: the literal, or the bound value. private static string Text(object binding, string? text) => text is not null && text.StartsWith('{') ? Read(binding, text).ToString() ?? string.Empty : text ?? string.Empty; private static void Invoke(object binding, string? expression) => Read(binding, expression)(); private static void Invoke(object binding, string? expression, string argument) => Read>(binding, expression)(argument); }