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>
72 lines
3 KiB
C#
72 lines
3 KiB
C#
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);
|
|
}
|