test(ui): pin every Journal-panel button as enabled and reachable
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>
This commit is contained in:
parent
73a04244e7
commit
0b27c5d0fe
1 changed files with 129 additions and 0 deletions
|
|
@ -0,0 +1,129 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue