Built from the REAL authored layout, because that is the only place property 0x0D exists — the hermetic UiButton tests construct their own ElementInfo and so could not have caught this class of bug at all. Asserts the two things the player actually experiences, separately: the button builds enabled, and a click at its centre reaches IT rather than falling through. Enabled alone would not have been enough to call the fix proven. Verified to have teeth rather than assumed: restoring the previous UiButton and re-running fails both tests, naming all ten buttons. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
4.2 KiB
C#
129 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
using AcDream.Content;
|
|
using DatReaderWriter;
|
|
using DatReaderWriter.Options;
|
|
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// The Journal panel's buttons must be clickable when built from the REAL
|
|
/// authored layout.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Every button on this panel authors property <c>0x0D</c>, which was read as
|
|
/// "starts disabled" — so all of them built visible and dead. The hermetic
|
|
/// UiButton tests could not catch it because they construct their own
|
|
/// ElementInfo; only the installed layout carries the property.
|
|
/// </para>
|
|
/// <para>
|
|
/// This asserts the two things the player actually experiences: the button is
|
|
/// enabled, and a click at its centre reaches IT rather than falling through to
|
|
/// whatever is behind.
|
|
/// </para>
|
|
/// </remarks>
|
|
[Trait("Lane", "InstalledDat")]
|
|
public sealed class JournalPanelButtonsAreClickableTests
|
|
{
|
|
private static readonly (uint Id, string Name)[] Buttons =
|
|
[
|
|
(0x100005DCu, "Abandon (contracts)"),
|
|
(0x10000567u, "New"),
|
|
(0x1000056Fu, "First"),
|
|
(0x10000571u, "Last"),
|
|
(0x10000565u, "Previous"),
|
|
(0x10000566u, "Next"),
|
|
(0x10000574u, "Record"),
|
|
(0x1000057Du, "Start"),
|
|
(0x10000585u, "Delete (page list)"),
|
|
(0x10000588u, "Reset (page list)"),
|
|
];
|
|
|
|
private static ImportedLayout BuildPanel()
|
|
{
|
|
string? datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR");
|
|
if (string.IsNullOrWhiteSpace(datDir) || !Directory.Exists(datDir))
|
|
{
|
|
datDir = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
"Documents", "Asheron's Call");
|
|
}
|
|
|
|
if (!Directory.Exists(datDir))
|
|
{
|
|
Assert.Fail(
|
|
"Lane=InstalledDat requires an installed retail DAT directory; "
|
|
+ "see docs/release-gate.md.");
|
|
}
|
|
|
|
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
|
using var adapter = new DatCollectionAdapter(dats);
|
|
|
|
ElementInfo? root = LayoutImporter.ImportInfos(
|
|
adapter,
|
|
JournalPanelController.HostLayoutId,
|
|
JournalPanelController.SlotElementId);
|
|
Assert.NotNull(root);
|
|
|
|
var strings = new DatStringResolver(adapter);
|
|
return LayoutImporter.Build(root!, _ => (0u, 0, 0), null, _ => null, strings.Resolve);
|
|
}
|
|
|
|
[Fact]
|
|
public void EveryJournalPanelButtonBuildsEnabled()
|
|
{
|
|
ImportedLayout layout = BuildPanel();
|
|
|
|
var dead = new List<string>();
|
|
foreach ((uint id, string name) in Buttons)
|
|
{
|
|
if (layout.FindElement(id) is not UiButton button)
|
|
{
|
|
dead.Add($"{name} (0x{id:X8}) did not build as a UiButton");
|
|
continue;
|
|
}
|
|
|
|
if (!button.Enabled)
|
|
dead.Add($"{name} (0x{id:X8}) built DISABLED");
|
|
}
|
|
|
|
Assert.Empty(dead);
|
|
}
|
|
|
|
[Fact]
|
|
public void EveryJournalPanelButtonIsReachableByAClickAtItsCentre()
|
|
{
|
|
// Enabled alone is not enough — the click has to actually land on it
|
|
// rather than pass through to something behind.
|
|
ImportedLayout layout = BuildPanel();
|
|
|
|
var unreachable = new List<string>();
|
|
foreach ((uint id, string name) in Buttons)
|
|
{
|
|
if (layout.FindElement(id) is not UiButton button)
|
|
continue;
|
|
|
|
UiElement? hit = HitTestSelf(button);
|
|
if (!ReferenceEquals(hit, button))
|
|
unreachable.Add($"{name} (0x{id:X8}) hit-tested to {hit?.GetType().Name ?? "nothing"}");
|
|
}
|
|
|
|
Assert.Empty(unreachable);
|
|
}
|
|
|
|
/// <summary>Hit-tests a button at its own centre, in its own local space.</summary>
|
|
private static UiElement? HitTestSelf(UiButton button)
|
|
{
|
|
var hitTest = typeof(UiElement).GetMethod(
|
|
"HitTest",
|
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!;
|
|
return hitTest.Invoke(
|
|
button,
|
|
[button.Width / 2f, button.Height / 2f]) as UiElement;
|
|
}
|
|
}
|