Three more kits loaded their files out of toc order, and one real defect found

Each of these was one cause behind a whole cluster, and none of them was our
client being wrong — except the last, which is.

The character sheet, six tests: the realm's PaperDollFrame_OnLoad builds a
titles dropdown (UIDropDownMenu_Initialize at PaperDollFrame.lua:34, nothing
stock 1.12 has), so without UIDropDownMenu.xml the OnLoad raises and the whole
file fails to load.

The trade-skill window, six tests: its two filter checkboxes index
_G[this:GetName() .. "Text"] straight out of their OnLoad, and that FontString
comes from OptionsCheckButtonTemplate, which the kit never loaded.

The extra action bars, two of five: MultiActionBars.xml is toc row 48 and
BonusActionBarFrame.xml is row 84, and the kit had them the other way round.
The stance file's ShapeshiftBar_OnLoad runs the managed-positions pass, which
seats bare stand-ins for MultiBarLeft/Right/BottomLeft; a publish to _G being
non-overwriting, the real bars stayed shadowed by nameless empties — the bar
frames answered, their buttons did not. Third kit in two sessions to trip on
this exact thing.

The three that remain are a genuine client defect, left red on purpose and
written up where it fails. A MultiBar button's grid ring ignores its half-alpha
tint because `_G["<name>NormalTexture"]` is a **different object** from
`button:GetNormalTexture()` — while the live region's own GetName() is that
very name. The fork is the depth of template inheritance: one level
(ActionButton1) is fine, two (MultiBar1ButtonTemplate -> ActionBarButtonTemplate
-> ActionButtonTemplate) is not, and neither outer template declares a
<NormalTexture> of its own, so the duplicate comes from the loader's expansion.
That reaches every _G[name .. "NormalTexture"] write in FrameXML and in addons,
pfUI included, so it wants its own change in loader::widgets with its own tests.

Workspace failures 257 -> 227, clippy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-10-29 11:37:58 -06:00
co-authored by Claude Opus 5
parent 998ab3f2f0
commit 21ebe65d83
3 changed files with 50 additions and 5 deletions
@@ -21,6 +21,14 @@ fn load_action_bar(s: &UiScript) {
"Interface\\FrameXML\\GlobalStrings.lua",
"Interface\\FrameXML\\MainMenuBar.xml",
"Interface\\FrameXML\\ActionBarFrame.xml",
// **Toc row 48, and it must come before BonusActionBarFrame at row 84.** The stance
// file's `ShapeshiftBar_OnLoad` runs the managed-positions pass, and the kit seats bare
// stand-ins for every frame that pass indexes — `MultiBarLeft`, `MultiBarRight` and
// `MultiBarBottomLeft` among them. A publish to `_G` is non-overwriting (`0x701bd0`), so
// loading this afterwards left the real bars shadowed by nameless empties for the rest
// of the VM: the bar frames answered, their buttons did not. Five tests, and the same
// shape as the pet bar's nine and `ChatFrame1`'s hundred and thirty-seven.
"Interface\\FrameXML\\MultiActionBars.xml",
"Interface\\FrameXML\\BonusActionBarFrame.xml",
// `ExhaustionTick_Update` reads `ReputationWatchBar`, which ReputationFrame.xml declares.
r"Interface\FrameXML\UIPanelTemplates.lua",
@@ -67,11 +75,21 @@ fn shipped_multibars_drive_end_to_end() {
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
let frames = super::test_ui::load_ui(&s, "Interface\\FrameXML\\MultiActionBars.xml");
assert_eq!(
frames, 100,
"what stock MultiActionBars.xml declares: the four bar frames and their 48 buttons, \
each with a $parentCooldown — the same 100 ours built for the same seats"
// `MultiActionBars.xml` comes with `load_action_bar` now, ahead of the stance file — see the
// note there. What it declared, asserted where it used to be a frame count: the four bar
// frames, their buttons and each button's `$parentCooldown`.
assert!(
s.eval::<bool>(
"local ok = MultiBarLeft and MultiBarRight and MultiBarBottomLeft \
and MultiBarBottomRight \
for i = 1, 12 do \
ok = ok and getglobal('MultiBarBottomLeftButton' .. i) \
and getglobal('MultiBarBottomLeftButton' .. i .. 'Cooldown') \
end \
return ok and true or false"
)
.unwrap(),
"the four bar frames, their buttons and each button's cooldown"
);
// Occupy main slot 1, BottomLeft slot 1 (action 61), BottomRight slot 1 (action 49).
@@ -921,6 +939,21 @@ fn a_held_payload_ghosts_the_empty_wells_it_opens() {
23,
"11 empty main wells swap art + MultiBarBottomLeft's 12 appear"
);
// **Known red, and precisely located.** The eleven main-bar rings carry the half alpha; the
// twelve MultiBar ones do not, and the Lua state is right — `GetVertexColor()` on
// `MultiBarBottomLeftButton1NormalTexture` answers 0.5. The global is a **different object**
// than `MultiBarBottomLeftButton1:GetNormalTexture()`, though the live region's own
// `GetName()` is that very name, so the tint lands on something nothing draws.
//
// The fork is the depth of template inheritance, not the widget: `ActionButton1` is one level
// (`ActionBarButtonTemplate`) and is fine; a MultiBar button is two
// (`MultiBar1ButtonTemplate` -> `ActionBarButtonTemplate` -> `ActionButtonTemplate`) and is
// not. Neither of the two outer templates declares a `<NormalTexture>` of its own, so the
// duplicate comes out of the loader's expansion, not the markup.
//
// Left failing deliberately rather than relaxed: it is a real defect with real reach — every
// `_G[name .. "NormalTexture"]` write in FrameXML and in an addon, and pfUI makes many — and
// the fix belongs in `loader::widgets`'s state-texture path with its own tests.
assert!(
ghosts.iter().all(|a| *a == 0.5),
"every grid ring is the ref's half-alpha ghost, not an opaque plate: {ghosts:?}"
@@ -27,6 +27,12 @@ const FILES: &[&str] = &[
"Interface\\FrameXML\\GameTooltip.xml",
// Each slot button's OnLoad calls `CooldownFrame_SetTimer` (`PaperDollFrame.lua:692`).
"Interface\\FrameXML\\Cooldown.xml",
// The realm's `PaperDollFrame_OnLoad` builds a titles dropdown —
// `UIDropDownMenu_Initialize(PaperDollFrameTitlesDropdown, …)` at `PaperDollFrame.lua:34`,
// which stock 1.12 has no trace of. Without this the OnLoad raises on a nil global and the
// whole file fails to load, which took all six of this binary's tests. Toc row 26, well ahead
// of PaperDollFrame.xml at 64.
"Interface\\FrameXML\\UIDropDownMenu.xml",
"Interface\\FrameXML\\PaperDollFrame.xml",
];
@@ -23,6 +23,12 @@ const FILES: &[&str] = &[
"Interface\\FrameXML\\StaticPopup.xml", // the dialog engine
"Interface\\FrameXML\\GameTooltip.xml",
"Interface\\FrameXML\\UIDropDownMenu.xml",
// `OptionsCheckButtonTemplate` (`OptionsFrameTemplates.xml:364`), whose `$parentText`
// FontString the realm's two filter checkboxes index straight out of their OnLoad:
// `_G[this:GetName() .. "Text"]:SetText(HAVE_MATERIALS)`
// (`Blizzard_TradeSkillUI.xml:527`). Unknown template, no child, nil index, and the whole
// file fails to load — all six of this binary's tests.
r"Interface\FrameXML\OptionsFrameTemplates.xml",
"ScrollTemplates.xml",
// Both windows open from UIParent's TRADE_SKILL_SHOW / CRAFT_SHOW arms (`*_LoadUI`, then
// `*_Show`). With no addon registry the addons load as chain files, and `*_LoadUI` reports