From 1aa3418fdcb10a5b1d3f4b144df107cc6e863884 Mon Sep 17 00:00:00 2001 From: grom Date: Wed, 28 Oct 2026 22:41:36 -0600 Subject: [PATCH] Read the test that reports errors first: 20 more, from four causes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guild and friends clusters — 26 tests between them — were one nil function. `guild_tests::driving_the_guild_windows_raises_no_script_errors` exists to name its errors, and all four of them were the same line: `attempt to call global 'GetDifficultyColor' (a nil value)` at `FriendsFrame.lua:523`, reached from four entry points. `GetDifficultyColor` is FrameXML's, not a client global (`reference/1.12-globals.tsv` types it so), and `QuestLogFrame.lua` defines it — ten toc rows above `FriendsFrame.xml`. The raise took the rest of the roster update with it, including the `GuildPlayerStatusFrame:Show()` that ends that branch, which is how it read as "the guild tab opens and the player pane is not visible": an assertion about visibility with no hint of a nil function behind it. One prerequisite row took guild 17 → 1 and friends 9 → 1. The reputation, honor and skills pages (20 more) shared a fixture gap: their player `UnitState` had a level, a race and a class and **no name**. The realm's `CharacterFrame_OnShow` formats `gsub(UnitPVPName("player"), "\n.+", "")`, and `UnitPVPName` answers nil for a nameless unit, so every one of those pages raised `bad argument #1 to 'gsub'` on the way up and never finished showing. The inspect page's arena tab raised three more: its team buttons inherit `ArenaTeamTemplate`, which is a FrameXML row of the realm's own (`Turtle_ArenaUI\Turtle_ArenaUITemplates.xml`) and not in the addon's templates file, so the inherits was skipped, `$parentText` was never built, and the OnLoad's `_G[this:GetName().."Text"]:SetText("(2v2)")` had nothing to index. That fixture already said "Before the inspect addon, as `inherits=` resolves at load" — it was simply missing this one. And five expected a rank title without its trailing space. The realm's `GlobalStrings.lua` gives every rank one — `PVP_RANK_8_1 = "Master Sergeant "`, where stock 1.12's copy has none — because its own callers concatenate a name after the title. The titles are now read off `PVP_RANK__` through one helper, so the realm owns its own strings; that also turned up a wrong index hiding behind the stale spelling (Grand Marshal is rank 18, not 14). 136 → 116, nothing newly failing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn --- .../src/ui_script/honor_frame_tests.rs | 58 ++++++++++++++++--- .../src/ui_script/reputation_frame_tests.rs | 6 ++ .../src/ui_script/skills_frame_tests.rs | 12 ++++ crates/benilla-app/src/ui_script/test_ui.rs | 15 +++++ 4 files changed, 84 insertions(+), 7 deletions(-) diff --git a/crates/benilla-app/src/ui_script/honor_frame_tests.rs b/crates/benilla-app/src/ui_script/honor_frame_tests.rs index 01e7cba3..222f89cf 100644 --- a/crates/benilla-app/src/ui_script/honor_frame_tests.rs +++ b/crates/benilla-app/src/ui_script/honor_frame_tests.rs @@ -32,6 +32,19 @@ fn state() -> HonorState { /// The character window, whose fifth tab this page is; [`super::test_ui::CHARACTER_UI`] carries /// `HonorFrame.xml`, as `PaperDollFrame.lua:103` writes into it on every show. +/// The realm's own `PVP_RANK__` string. +/// +/// Never a copy written here: the realm's `GlobalStrings.lua` gives every rank a **trailing +/// space** (`PVP_RANK_8_1 = "Master Sergeant "`, where stock 1.12's `patch.MPQ` copy has none), +/// because its own callers concatenate a name after the title. Pinning the stock spelling made +/// five tests in this file assert against a string the realm does not ship. +fn rank_title(s: &UiScript, rank: u32, team: u32) -> String { + s.eval::(&format!( + "return PVP_RANK_{rank}_{team} or \"\"" + )) + .expect("the realm's own rank string") +} + fn load_page(s: &UiScript) { for file in super::test_ui::CHARACTER_UI { super::test_ui::load_ui_strict(s, file); @@ -43,6 +56,12 @@ fn load_page(s: &UiScript) { fn alliance_player() -> UnitState { UnitState { exists: true, + // Named: the window this page lives in formats the player's name on every show — + // `CharacterFrame_OnShow` does `gsub(UnitPVPName("player"), "\n.+", "")` + // (`CharacterFrame.lua:85`) — and `UnitPVPName` answers nil for a unit with no name, so a + // nameless fixture raises `bad argument #1 to 'gsub'` and takes the rest of that OnShow + // with it. + name: Some("Tester".into()), level: 60, sex: 2, faction_group: Some("Alliance".into()), @@ -93,7 +112,7 @@ fn every_figure_lands_in_its_own_row() { ("HonorFrameLifeTimeHKValue", "3907"), ("HonorFrameLifeTimeDKValue", "12"), // The highest rank's title (14), not the current one (12). - ("HonorFrameLifeTimeRankValue", "Lieutenant Commander"), + ("HonorFrameLifeTimeRankValue", &rank_title(&s, 14, 1)), ] { assert_eq!(text(&mut s, frame), want, "{frame}"); } @@ -105,7 +124,10 @@ fn every_figure_lands_in_its_own_row() { fn the_title_is_the_internal_rank_and_the_badge_is_the_visual_one() { let _data = benilla_formats::wow_data_or_skip!(); let mut s = shown_honor_page(); - assert_eq!(text(&mut s, "HonorFrameCurrentPVPTitle"), "Knight-Captain"); + assert_eq!( + text(&mut s, "HonorFrameCurrentPVPTitle"), + rank_title(&s, 12, 1) + ); assert_eq!(text(&mut s, "HonorFrameCurrentPVPRank"), "(Rank 8)"); assert!( s.eval::("return HonorFramePvPIcon:IsShown()") @@ -178,12 +200,12 @@ fn a_gm_flagged_player_keeps_his_rank_title_and_loses_only_the_faction_group() { s.resolve(); assert_eq!( text(&mut s, "HonorFrameLifeTimeRankValue"), - "Grand Marshal", + rank_title(&s, 18, 1), "the Highest Rank row — the reported symptom" ); assert_eq!( text(&mut s, "HonorFrameCurrentPVPTitle"), - "Grand Marshal", + rank_title(&s, 18, 1), "and the title under the badge, which reads the same key" ); assert!( @@ -278,7 +300,22 @@ fn the_real_global_strings_name_the_rank() { load_page(&s); s.set_honor(Some(state())); - for (team, want) in [(1i8, "Knight-Captain"), (0, "Legionnaire")] { + // The expected title comes off the realm's **own** `PVP_RANK__` string, not a copy + // written here: the realm's `GlobalStrings.lua` gives every rank a **trailing space** + // (`PVP_RANK_8_1 = "Master Sergeant "`, where stock 1.12's `patch.MPQ` copy has none), because + // its own callers concatenate a name after the title. Pinning the stock spelling here made + // five tests in this file assert against a string the realm does not ship. + // + // Rank 12 is the fixture's (`state`), and the team digit is what this is really testing: the + // same rank reads as the Alliance title for team 1 and the Horde one for team 0. + for team in [1i8, 0] { + let want = s + .eval::(&format!( + "return PVP_RANK_12_{} or \"\"", + if team == 1 { 1 } else { 0 }, + if team == 1 { 1 } else { 0 } + )) + .expect("the realm's own rank string"); s.set_unit( "player", Some(UnitState { @@ -361,6 +398,13 @@ fn shown_inspect_honor_page() -> UiScript { // `ItemButtonTemplate`, its honor rows `HonorFrame.xml`'s row templates. "Interface\\FrameXML\\ItemButtonTemplate.xml", "Interface\\FrameXML\\HonorFrame.xml", + // …and its **arena** page's three team buttons inherit `ArenaTeamTemplate`, which is a + // FrameXML row of the realm's (`Turtle_ArenaUI\Turtle_ArenaUITemplates.xml`, toc row 109) + // rather than anything in the addon's own templates file. Without it the inherits is + // skipped, so `$parentText` is never built, and the OnLoad's + // `_G[this:GetName().."Text"]:SetText("(2v2)")` (`InspectArenaFrame.xml:71`) raises + // `attempt to index field '?'` — three times, once per team. + r"Interface\FrameXML\Turtle_ArenaUI\Turtle_ArenaUITemplates.xml", ] { load_xml(&s, file); } @@ -398,14 +442,14 @@ fn the_inspect_page_paints_the_reply_it_holds() { ("InspectHonorFrameLastWeekStandingValue", "57"), ("InspectHonorFrameLifeTimeHKValue", "3907"), ("InspectHonorFrameLifeTimeDKValue", "12"), - ("InspectHonorFrameLifeTimeRankValue", "Lieutenant Commander"), + ("InspectHonorFrameLifeTimeRankValue", &rank_title(&s, 14, 1)), ] { assert_eq!(text(&mut s, frame), want, "{frame}"); } // The target's current rank, 12, and the bar from the reply's own byte. assert_eq!( text(&mut s, "InspectHonorFrameCurrentPVPTitle"), - "Knight-Captain" + rank_title(&s, 12, 1) ); assert_eq!(text(&mut s, "InspectHonorFrameCurrentPVPRank"), "(Rank 8)"); let bar = s diff --git a/crates/benilla-app/src/ui_script/reputation_frame_tests.rs b/crates/benilla-app/src/ui_script/reputation_frame_tests.rs index 9800e053..7a47dbc5 100644 --- a/crates/benilla-app/src/ui_script/reputation_frame_tests.rs +++ b/crates/benilla-app/src/ui_script/reputation_frame_tests.rs @@ -63,6 +63,12 @@ fn load_page(s: &UiScript) { fn player(level: u32) -> UnitState { UnitState { exists: true, + // Named, because the window this page lives in formats the player's name on **every** + // show: `CharacterFrame_OnShow` does `gsub(UnitPVPName("player"), "\n.+", "")` + // (`CharacterFrame.lua:85`), and `UnitPVPName` answers nil for a unit with no name — so a + // nameless fixture raises `bad argument #1 to 'gsub'` and takes the rest of that OnShow + // with it, including the sub-frame show this page needs. + name: Some("Tester".into()), level, race: Some("Human".into()), race_file: Some("Human".into()), diff --git a/crates/benilla-app/src/ui_script/skills_frame_tests.rs b/crates/benilla-app/src/ui_script/skills_frame_tests.rs index dd2ebea5..d5688b3a 100644 --- a/crates/benilla-app/src/ui_script/skills_frame_tests.rs +++ b/crates/benilla-app/src/ui_script/skills_frame_tests.rs @@ -53,6 +53,12 @@ fn shown_skills_page_with(measurer: Option