acdream/tests/AcDream.App.Tests/UI/Layout/IndicatorDetailPanelControllerTests.cs
Erik 16c21e299c fix(ui): match retail vitae and character info
Restore Vitae's omitted penalty paragraph, replace the invented character summary with gmCharacterInfoUI's ordered report and property meanings, preserve authored translucent body surfaces, and initialize the end-session button in its visible Normal DAT state.

Release build and all 5,830 tests pass with five intentional skips. Connected visual gate pending.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-17 12:06:14 +02:00

181 lines
6.1 KiB
C#

using AcDream.App.UI;
using AcDream.App.UI.Layout;
using AcDream.Core.Items;
using AcDream.Core.Net;
using AcDream.Core.Spells;
namespace AcDream.App.Tests.UI.Layout;
public sealed class IndicatorDetailPanelControllerTests
{
private const uint Player = 0x50000001u;
[Fact]
public void SharedPanelCenter_IsNotPaintedTwiceForAuthoredDetailBodies()
{
Assert.True(RetailUiRuntime.AuthorsFullPanelCenter(
FixtureLoader.LoadVitaeInfos()));
Assert.True(RetailUiRuntime.AuthorsFullPanelCenter(
FixtureLoader.LoadCharacterInformationInfos()));
Assert.False(RetailUiRuntime.AuthorsFullPanelCenter(
FixtureLoader.LoadPositiveEffectsInfos()));
}
[Fact]
public void AuthoredFixtures_AreRetailMainPanelPages()
{
AssertPage(
FixtureLoader.LoadLinkStatusInfos(),
LinkStatusUiController.RootId,
0x1000001Du,
LinkStatusUiController.MainTextId,
LinkStatusUiController.CloseId);
AssertPage(
FixtureLoader.LoadVitaeInfos(),
VitaeUiController.RootId,
0x10000020u,
VitaeUiController.MainTextId,
VitaeUiController.CloseId);
AssertPage(
FixtureLoader.LoadMiniGameInfos(),
MiniGameUiController.RootId,
0x1000001Eu,
0x1000016Eu,
MiniGameUiController.CloseId);
Assert.Equal(8u, RetailPanelCatalog.LinkStatus);
Assert.Equal(9u, RetailPanelCatalog.MiniGame);
Assert.Equal(15u, RetailPanelCatalog.Vitae);
}
[Fact]
public void LinkStatus_RequestsPingOnShow_RefreshesText_AndRepeatsAfter120Seconds()
{
ImportedLayout layout = FixtureLoader.LoadLinkStatus();
double now = 0d;
int pings = 0;
int closes = 0;
LinkStatusSnapshot snapshot = new(
Connected: true,
SecondsSinceLastPacket: 0.25d,
PacketLossPercentage: 1.25d,
RoundTripSeconds: 0.123d);
using LinkStatusUiController controller = LinkStatusUiController.Bind(
layout,
() => snapshot,
() => now,
() => pings++,
LinkStatusStrings.English,
() => closes++)!;
controller.OnShown();
Assert.Equal(1, pings);
string text = VisibleText(layout, LinkStatusUiController.MainTextId);
Assert.Contains("Link Indicator", text, StringComparison.Ordinal);
Assert.Contains("1.25", text, StringComparison.Ordinal);
Assert.Contains("123", text, StringComparison.Ordinal);
controller.Tick();
now = 119d;
controller.Tick();
Assert.Equal(1, pings);
now = 124d;
controller.Tick();
Assert.Equal(2, pings);
UiButton close = Assert.IsType<UiButton>(layout.FindElement(LinkStatusUiController.CloseId));
close.OnEvent(new UiEvent(0, close, UiEventType.Click));
Assert.Equal(1, closes);
controller.OnHidden();
now = 300d;
controller.Tick();
Assert.Equal(2, pings);
}
[Fact]
public void Vitae_UsesDeathLevelPoolAndRetailThresholdFormula()
{
ImportedLayout layout = FixtureLoader.LoadVitae();
var spellbook = new Spellbook();
var objects = new ClientObjectTable();
objects.AddOrUpdate(new ClientObject { ObjectId = Player });
objects.UpdateIntProperty(Player, VitaeUiController.DeathLevelProperty, 10);
objects.UpdateIntProperty(Player, VitaeUiController.VitaeCpPoolProperty, 100);
using VitaeUiController controller = VitaeUiController.Bind(
layout,
spellbook,
objects,
() => Player,
VitaeStrings.English)!;
Assert.Contains(
"full strength",
VisibleText(layout, VitaeUiController.MainTextId),
StringComparison.Ordinal);
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
SpellId: 666u,
LayerId: 1u,
Duration: -1f,
CasterGuid: Player,
StatModValue: 0.9f,
Bucket: 4u));
string text = VisibleText(layout, VitaeUiController.MainTextId);
Assert.Contains("lost 10%", text, StringComparison.Ordinal);
Assert.Contains(
"health, stamina, mana, and skills are temporarily reduced by 10%",
text,
StringComparison.Ordinal);
Assert.Contains("earn 379 more experience", text, StringComparison.Ordinal);
Assert.Equal(479, VitaeUiController.VitaeCpPoolThreshold(0.9f, 10));
}
[Fact]
public void MiniGameFixture_BindsItsAuthoredCloseButton()
{
ImportedLayout layout = FixtureLoader.LoadMiniGame();
int closes = 0;
using MiniGameUiController controller = MiniGameUiController.Bind(
layout, () => closes++)!;
UiButton close = Assert.IsType<UiButton>(layout.FindElement(MiniGameUiController.CloseId));
close.OnEvent(new UiEvent(0, close, UiEventType.Click));
Assert.Equal(1, closes);
}
private static void AssertPage(
ElementInfo info,
uint rootId,
uint type,
uint contentId,
uint closeId)
{
Assert.Equal(rootId, info.Id);
Assert.Equal(type, info.Type);
Assert.Equal((300f, 362f), (info.Width, info.Height));
Assert.NotNull(Find(info, contentId));
Assert.NotNull(Find(info, closeId));
Assert.True(info.TryGetEffectiveBool(
RetailPanelUiController.RestorePreviousPropertyId,
out bool restorePrevious));
Assert.True(restorePrevious);
}
private static string VisibleText(ImportedLayout layout, uint id)
{
UiText text = Assert.IsType<UiText>(layout.FindElement(id));
return string.Join(' ', text.LinesProvider().Select(line => line.Text));
}
private static ElementInfo? Find(ElementInfo root, uint id)
{
if (root.Id == id) return root;
foreach (ElementInfo child in root.Children)
if (Find(child, id) is { } found) return found;
return null;
}
}