Main window: Main, Settings, Statistics and Tools tabs as a markup panel
The original's notebook without the parts OpenAC or another plugin now owns (updater, VTank path, route visualisation, chest looter). Labels read live from the trackers each frame. Settings gains the backend URL and shared-secret fields, since the secret is no longer compiled in. Tools opens the Mossy Tracker and Metas windows through openers their features set. The WebSocket label now shows the real connection state rather than the setting. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1ff0a40aeb
commit
5799ea956e
5 changed files with 379 additions and 0 deletions
|
|
@ -0,0 +1,72 @@
|
|||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using OpenAC.MosswartMassacre.Views;
|
||||
|
||||
namespace OpenAC.MosswartMassacre.Tests.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Every <c>{Binding}</c> in a markup file names a public property of the
|
||||
/// shape the host expects, so a typo fails here instead of as a panel that
|
||||
/// refuses to build in the client.
|
||||
/// </summary>
|
||||
public sealed class MarkupContractTests
|
||||
{
|
||||
public static TheoryData<string, Type> MarkupFiles => new()
|
||||
{
|
||||
{ MainWindow.MarkupFile, typeof(MainWindow) },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(MarkupFiles))]
|
||||
public void EveryBindingResolvesWithTheRightShape(string file, Type bindingType)
|
||||
{
|
||||
XElement root = XDocument.Load(MarkupPath(file)).Root!;
|
||||
Dictionary<string, PropertyInfo> properties = bindingType
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.ToDictionary(property => property.Name, StringComparer.Ordinal);
|
||||
|
||||
foreach (XAttribute attribute in root.DescendantsAndSelf().Attributes())
|
||||
{
|
||||
string value = attribute.Value;
|
||||
if (!value.StartsWith('{'))
|
||||
continue;
|
||||
Assert.Matches("^\\{[^{}]+\\}$", value);
|
||||
string name = value[1..^1];
|
||||
Assert.True(properties.TryGetValue(name, out PropertyInfo? property),
|
||||
$"{file}: {value} on <{attribute.Parent!.Name}> names no public property.");
|
||||
|
||||
string element = attribute.Parent!.Name.LocalName;
|
||||
Type expected = (element, attribute.Name.LocalName) switch
|
||||
{
|
||||
(_, "onclick") => typeof(Action),
|
||||
("field", "onchange" or "onsubmit") => typeof(Action<string>),
|
||||
("menu", "onchange") => typeof(Action<string>),
|
||||
("list", "onchange") => typeof(Action<int>),
|
||||
("slider", "onchange") => typeof(Action<float>),
|
||||
(_, "checked" or "selected" or "visible" or "enabled") when element != "list" && element != "menu" => typeof(bool),
|
||||
_ => typeof(object),
|
||||
};
|
||||
if (expected != typeof(object))
|
||||
Assert.True(property!.PropertyType == expected,
|
||||
$"{file}: {value} on <{element} {attribute.Name}> is {property.PropertyType.Name}, expected {expected.Name}.");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_main_window_builds_its_bindings_without_a_host_ui()
|
||||
{
|
||||
using var harness = new PluginHarness();
|
||||
harness.Login();
|
||||
MainWindow window = harness.Feature<MainWindow>();
|
||||
|
||||
Assert.Equal("Total Kills: 0", window.TotalKillsText);
|
||||
Assert.Equal("Meta State: Unknown", window.MetaStateText);
|
||||
Assert.Equal("WebSocket: [DISCONNECTED]", window.WebSocketText);
|
||||
Assert.Equal("Never", window.LastKillValue);
|
||||
window.ShowStatistics();
|
||||
Assert.True(window.StatisticsSelected);
|
||||
Assert.False(window.MainSelected);
|
||||
}
|
||||
|
||||
private static string MarkupPath(string file) => Path.Combine(AppContext.BaseDirectory, file);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue