Files
HellionChat/HellionChat/SelfTests/TellResetOnActivateStep.cs
T
JonKazama-Hellion 5b738e6885 chore: comments say what the code does, not which task produced it
A comment that reads "MUST stay in lockstep with TryGetActiveCrossfade (K8)"
helps nobody outside the plan that used to have a K8 in it, and the plans are
not in this repo. Same for "Spec FR-4", "plan §B.2", "Sub-Task 4.4" and the
F/R/M/A/S round codes scattered through the style engine and the self-tests.

Personal names go too. "tester feedback from Jin (v1.4.7)" and "Flo decision
2026-06-15" carry the reason fine without naming anyone -- the version and the
reason are the parts a reader can act on, and a public repo should not need a
cast list to be read.

The rule applied throughout: keep the why, drop the reference. Version numbers
stay, since those resolve through the changelog. 77 files.

ChunkUtil also carried 281 lines of commented-out code -- an older ToChunks
variant and two helpers with no callers, inherited and never removed. Deleted;
git remembers them.
2026-08-19 21:50:31 +02:00

150 lines
6.1 KiB
C#

using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.SelfTest;
using HellionChat.Code;
using HellionChat.GameFunctions.Types;
using HellionChat.Util;
namespace HellionChat.SelfTests;
// The activation strip. Drives the REAL OnTabActivated -- the entry the
// Sidebar/TopTabBar click handlers, the pop-out path and the Draw-seed all call
// — with local probe tabs (Plugin.Config.Tabs is never touched). Asserts the
// five contracts: strip-on-switch, no-strip-on-reclick (TR-4), leg1 preserve,
// derive, and non-tell untouched.
internal sealed class TellResetOnActivateStep : ISelfTestStep
{
public string Name => "Hellion Chat - Tell reset on tab activate";
public SelfTestStepResult RunStep()
{
var other = MakeSayTab();
// (a) switching ONTO a stale-tell tab with no Tab-level binding strips the
// runtime tell state (target + partner label) and re-derives the channel.
var stale = MakeStaleTellTab(boundTellTarget: false, withLabel: true);
TabLifecycleHelpers.OnTabActivated(stale, other);
if (stale.CurrentChannel.TellTarget is not null)
{
ImGui.Text("(a) stale tell target not cleared on switch");
return SelfTestStepResult.Fail;
}
if (stale.CurrentChannel.Channel != InputChannel.Say)
{
ImGui.Text($"(a) channel not re-derived to Say, got {stale.CurrentChannel.Channel}");
return SelfTestStepResult.Fail;
}
if (stale.CurrentChannel.Name.Count != 0)
{
ImGui.Text("(a) stale partner label not cleared");
return SelfTestStepResult.Fail;
}
// (b) re-clicking the already-active tab (previous == tab) must NOT strip
// a live game-tell conversation (TR-4 regression guard).
var reclick = MakeStaleTellTab(boundTellTarget: false, withLabel: false);
TabLifecycleHelpers.OnTabActivated(reclick, reclick);
if (reclick.CurrentChannel.TellTarget is null)
{
ImGui.Text("(b) re-click wrongly stripped the active tell tab");
return SelfTestStepResult.Fail;
}
if (reclick.CurrentChannel.Channel != InputChannel.Tell)
{
ImGui.Text("(b) re-click wrongly changed the active tab's channel");
return SelfTestStepResult.Fail;
}
// (c) a tab whose own Tab.TellTarget is set is a real binding (leg1):
// channel + runtime target survive a switch.
var bound = MakeStaleTellTab(boundTellTarget: true, withLabel: false);
TabLifecycleHelpers.OnTabActivated(bound, other);
if (bound.CurrentChannel.TellTarget is null)
{
ImGui.Text("(c) bound tell tab wrongly stripped");
return SelfTestStepResult.Fail;
}
if (bound.CurrentChannel.Channel != InputChannel.Tell)
{
ImGui.Text("(c) bound tell tab channel wrongly changed");
return SelfTestStepResult.Fail;
}
// (d) an Invalid-channel tab just derives (pre-existing semantics).
var invalid = MakeSayTab();
TabLifecycleHelpers.OnTabActivated(invalid, other);
if (invalid.CurrentChannel.Channel != InputChannel.Say)
{
ImGui.Text(
$"(d) invalid-channel tab not derived, got {invalid.CurrentChannel.Channel}"
);
return SelfTestStepResult.Fail;
}
// (e) a non-tell tab is left untouched. Seed it with runtime tell state
// AND a label so a guard that wrongly fired on non-tell tabs would null
// them — the channel re-derive alone could not mask that regression.
var say = MakeSayTab();
say.CurrentChannel.SetChannel(InputChannel.Say);
say.CurrentChannel.TellTarget = new TellTarget("Untouched", 21, 0, TellReason.Direct);
var sayLabel = new SeStringBuilder().AddText("Untouched@World").Build();
say.CurrentChannel.Name = ChunkUtil
.ToChunks(sayLabel, ChunkSource.Content, ChatType.Say)
.ToList();
TabLifecycleHelpers.OnTabActivated(say, other);
if (say.CurrentChannel.Channel != InputChannel.Say)
{
ImGui.Text("(e) non-tell tab channel wrongly changed");
return SelfTestStepResult.Fail;
}
if (say.CurrentChannel.TellTarget is null || say.CurrentChannel.Name.Count == 0)
{
ImGui.Text("(e) non-tell tab runtime state wrongly stripped");
return SelfTestStepResult.Fail;
}
return SelfTestStepResult.Pass;
}
// A tab carrying runtime tell state the way the game-side detour leaves it:
// CurrentChannel.Channel == Tell with a resolvable CurrentChannel.TellTarget,
// optionally with the partner-name label chunks. boundTellTarget controls
// whether the Tab-level TellTarget marks it a real binding (leg1).
private static Tab MakeStaleTellTab(bool boundTellTarget, bool withLabel)
{
var tab = new Tab
{
Name = "selftest-activate-tell",
TellTarget = boundTellTarget
? new TellTarget("Bound", 21, 0, TellReason.Direct)
: TellTarget.Empty(),
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{
[ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All),
},
};
tab.CurrentChannel.SetChannel(InputChannel.Tell);
tab.CurrentChannel.TellTarget = new TellTarget("Stale", 21, 0, TellReason.Direct);
if (withLabel)
{
var ss = new SeStringBuilder().AddText("Stale@World").Build();
tab.CurrentChannel.Name = ChunkUtil
.ToChunks(ss, ChunkSource.Content, ChatType.Say)
.ToList();
}
return tab;
}
private static Tab MakeSayTab() =>
new()
{
Name = "selftest-activate-say",
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{
[ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All),
},
};
public void CleanUp() { }
}