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