diff --git a/crates/benilla-app/src/ui_script/instance_tests.rs b/crates/benilla-app/src/ui_script/instance_tests.rs index daa6f204..659dc7ba 100644 --- a/crates/benilla-app/src/ui_script/instance_tests.rs +++ b/crates/benilla-app/src/ui_script/instance_tests.rs @@ -55,8 +55,13 @@ fn the_three_bindings_have_the_reference_shapes() { } /// Through the real hit test: right-click the player portrait, click the row, answer the confirm. +/// +/// The row is **not** gated on `CanShowResetInstances()`. Stock 1.12's `UnitPopup_HideButtons` +/// hid it unless that verb answered; the realm's rewrite of `UnitPopup.lua` dropped the branch +/// altogether — nothing in its 51 interface Lua rows so much as names the verb (scanned) — so +/// `RESET_INSTANCES` is an always-shown SELF row, solo included. The verb stays for addons. #[test] -fn the_self_menu_row_gates_on_the_binding_and_confirms_before_sending() { +fn the_self_menu_reset_row_always_shows_and_confirms_before_sending() { let _data = benilla_formats::wow_data_or_skip!(); let mut s = UiScript::new().unwrap(); s.set_screen_size(1024.0, 768.0); @@ -123,20 +128,26 @@ fn the_self_menu_row_gates_on_the_binding_and_confirms_before_sending() { .collect() }; - // Off, the row is hidden, not greyed (`UnitPopup.lua:379`). Solo it is the SELF menu's only - // candidate row, so with it gone the menu does not open at all. + // Solo and with the verb answering nil, the row is there all the same, and so is the menu: + // the realm's SELF list is `LOOT_METHOD, LOOT_THRESHOLD, LOOT_PROMOTE, LEAVE, + // RESET_INSTANCES, RAID_TARGET_ICON, XP, MOVE, MOVE_RESET, CANCEL` (`UnitPopup.lua:75`), and + // of those only the four group rows and the raid marks are gated off while solo. open_self_menu(&mut s); assert!( - s.eval::("return not DropDownList1:IsVisible()") - .unwrap(), - "solo with no lockout to reset, the SELF menu has nothing to show" + s.eval::("return DropDownList1:IsVisible()").unwrap(), + "the ungated rows open the SELF menu even solo" + ); + assert!( + row_labels(&mut s).contains(&"Reset all instances".to_string()), + "the row shows with no lockout and no party: {:?}", + row_labels(&mut s) ); s.set_can_reset_instances(true); open_self_menu(&mut s); assert!( s.eval::("return DropDownList1:IsVisible()").unwrap(), - "the one showable row is enough to open the SELF menu" + "and the verb answering changes nothing about the menu" ); let labels = row_labels(&mut s); let row = labels diff --git a/crates/benilla-app/src/ui_script/unit_frame_tests.rs b/crates/benilla-app/src/ui_script/unit_frame_tests.rs index 6da5c7b6..7014e4a8 100644 --- a/crates/benilla-app/src/ui_script/unit_frame_tests.rs +++ b/crates/benilla-app/src/ui_script/unit_frame_tests.rs @@ -373,12 +373,34 @@ fn left_clicking_the_player_frame_targets_self() { s.take_selection_requests().is_empty(), "right-click queues no target" ); - // Solo, every SELF row but Cancel is gated off (`UnitPopup.lua:69`, no PvP row): no menu. + // Solo the menu still opens: the realm's SELF list (`UnitPopup.lua:75`) gates off only its + // four group rows and the raid marks, leaving `RESET_INSTANCES` (whose stock + // `CanShowResetInstances` gate the realm dropped), its own `XP` row and the two + // movable-frame rows. Stock 1.12 left nothing but Cancel here, so no menu opened. assert!( - s.eval::("return not DropDownList1:IsVisible()") - .unwrap(), - "no SELF menu while solo" + s.eval::("return DropDownList1:IsVisible()").unwrap(), + "the realm's ungated SELF rows open the menu solo" ); + let solo: Vec = (1..=s.eval::("return DropDownList1.numButtons").unwrap()) + .map(|i| { + s.eval::(&format!("return DropDownList1Button{i}:GetText()")) + .unwrap() + }) + .collect(); + assert_eq!( + solo, + [ + // This fixture's player carries no name, and the title row falls back to `UNKNOWN` + // the way the reference does (`UnitPopup_ShowMenu`: `info.text = name or UNKNOWN`). + "Unknown", + "Reset all instances", + "Experience: Enabled", + "Unlock Frame", + "Reset Position", + "Cancel", + ] + ); + s.run("CloseDropDownMenus()").unwrap(); s.mouse_button(cx as f32, cy as f32, "LeftButton", true); s.mouse_button(cx as f32, cy as f32, "LeftButton", false); @@ -409,10 +431,27 @@ fn left_clicking_the_player_frame_targets_self() { s.eval::("return DropDownList1:IsVisible()").unwrap(), "the SELF menu opens for a party leader" ); + // The whole realm SELF list with nothing gated off but the two leader-only loot rows a + // party of two has no use for: `LOOT_PROMOTE` needs master loot, and that is the only + // absence. The last three are the realm's own additions (`UnitPopup.lua:75`). assert_eq!( - s.eval::("return DropDownList1.numButtons").unwrap(), - 6, - "title + Loot Method + Loot Threshold + Leave Party + Raid Target Icon + Cancel" + (1..=s.eval::("return DropDownList1.numButtons").unwrap()) + .map(|i| s + .eval::(&format!("return DropDownList1Button{i}:GetText()")) + .unwrap()) + .collect::>(), + [ + "Unknown", + "Loot: Group Loot", + "Uncommon", + "Leave party", + "Reset all instances", + "Raid Target Icon", + "Experience: Enabled", + "Unlock Frame", + "Reset Position", + "Cancel", + ] ); // `PARTY_LEAVE` is "Leave party", lower-case (`GlobalStrings.lua:2991`). assert_eq!( @@ -490,13 +529,18 @@ fn raid_mark_clicks_through_the_nested_level() { ); s.resolve(); - assert_eq!( - s.eval::("return DropDownList1Button5:GetText()") - .unwrap(), - "Raid Target Icon" - ); + // Found by label, not by index: the realm's SELF list has grown three rows since 1.12 + // (`XP`, `MOVE`, `MOVE_RESET`) and dropped the `RESET_INSTANCES` gate, so the marks row no + // longer sits at a fixed seat. + let marks = (1..=s.eval::("return DropDownList1.numButtons").unwrap()) + .find(|i| { + s.eval::(&format!("return DropDownList1Button{i}:GetText()")) + .unwrap() + == "Raid Target Icon" + }) + .expect("the SELF menu has a Raid Target Icon row for a leader in a group"); let (rx, ry) = s - .eval::<(f64, f64)>("return DropDownList1Button5:GetCenter()") + .eval::<(f64, f64)>(&format!("return DropDownList1Button{marks}:GetCenter()")) .unwrap(); s.mouse_move(rx as f32, ry as f32); assert!( @@ -1593,12 +1637,22 @@ fn status_bar_text_paints_the_player_numerals_but_not_the_targets() { .contains("Somebody"), "and it is the unit's own plate" ); + // …and moving off the bar leaves it up. The realm overrode this bar's own `` and + // `` (`TargetFrame.xml:280-298`), replacing the `TextStatusBar` template's pair: the + // enter shows the two HP strings and then borrows the parent's plate by hand + // (`TargetFrame:GetScript("OnEnter")()`, which it has to, because a child that takes the + // mouse means `TargetFrame` itself never gets an enter), while the leave hides only those two + // strings. Nothing in it hides the tooltip and the parent has no leave pending to do it, so + // the plate stands — the realm's own asymmetry, reproduced rather than papered over. unhover(&mut s); assert!( - s.eval::("return not GameTooltip:IsShown()").unwrap(), - "the template's OnLeave hides it (ref TextStatusBar.xml l.28-31)" + s.eval::("return GameTooltip:IsShown()").unwrap(), + "the realm's own bar OnLeave hides its HP strings and nothing else" ); + // It is cleared by whatever the cursor touches next, not by leaving this bar. + s.run("GameTooltip:Hide()").unwrap(); + // Switched off, a hover still reveals the player's numerals (`TextStatusBar.lua:77-102`). s.run("SetCVar(\"statusBarText\", \"0\", \"STATUS_BAR_TEXT\")") .unwrap(); diff --git a/crates/benilla-app/src/ui_script/unit_popup_tests.rs b/crates/benilla-app/src/ui_script/unit_popup_tests.rs index f1200800..8a5d639b 100644 --- a/crates/benilla-app/src/ui_script/unit_popup_tests.rs +++ b/crates/benilla-app/src/ui_script/unit_popup_tests.rs @@ -82,6 +82,17 @@ fn bake_strings(s: &UiScript) { .unwrap(); } +/// Every row of an open `DropDownList1`, in order, as the menu drew them. +fn rows(s: &UiScript) -> Vec { + let n = s.eval::("return DropDownList1.numButtons").unwrap(); + (1..=n) + .map(|i| { + s.eval::(&format!("return DropDownList1Button{i}:GetText()")) + .unwrap() + }) + .collect() +} + /// Right-clicking a chat player name opens the FRIEND dropdown (`FriendsFrame_ShowDropdown`), /// whose Invite invites by name; a left-click on the name whispers instead. #[test] @@ -109,23 +120,21 @@ fn chat_name_right_click_opens_the_invite_menu() { s.eval::("return DropDownList1:IsVisible()").unwrap(), "the chat-name right-click opens the FRIEND dropdown" ); - // Title, Whisper, Invite, Target, Cancel: the FRIEND menu (`UnitPopup.lua:74`) with its two - // guild rows hidden (no guild, no guild frame). + // The realm's FRIEND menu (`UnitPopup.lua:80`), with its two guild rows hidden (no guild, no + // guild frame). `REPORT` and `IGNORE` are the realm's own rows — stock 1.12's table has + // neither, which is what this list used to assert. assert_eq!( - s.eval::("return DropDownList1.numButtons").unwrap(), - 5, - "title + Whisper + Invite + Target + Cancel" - ); - assert_eq!( - s.eval::("return DropDownList1Button1:GetText()") - .unwrap(), - "Bob", - "the clicked name titles the menu" - ); - assert_eq!( - s.eval::("return DropDownList1Button3:GetText()") - .unwrap(), - "Invite" + rows(&s), + [ + "Bob", + "Whisper", + "Invite", + "Target", + "Report", + "Ignore Player", + "Cancel", + ], + "the clicked name titles the menu, then the realm's FRIEND rows in its own order" ); // Click Invite through the real hit path → UnitPopup_OnClick → InviteByName("Bob"). @@ -206,37 +215,25 @@ fn solo_target_right_click_invites_a_player() { s.eval::("return DropDownList1:IsVisible()").unwrap(), "a friendly player target opens the PLAYER menu solo" ); - // Title, Whisper, Inspect, Invite, Trade, Follow, Duel, Cancel: the PLAYER menu in - // `UnitPopup.lua:72`'s order, the raid-target row hidden without a party. + // The realm's PLAYER menu (`UnitPopup.lua:78`) in its own order, with the raid-target row + // hidden without a party and the three group rows hidden solo. The last four are all the + // realm's: `REPORT` and `IGNORE`, then `MOVE`/`MOVE_RESET`, which are its movable unit frames. assert_eq!( - s.eval::("return DropDownList1.numButtons").unwrap(), - 8, - "title + Whisper + Inspect + Invite + Trade + Follow + Duel + Cancel" - ); - assert_eq!( - s.eval::("return DropDownList1Button3:GetText()") - .unwrap(), - "Inspect" - ); - assert_eq!( - s.eval::("return DropDownList1Button4:GetText()") - .unwrap(), - "Invite" - ); - assert_eq!( - s.eval::("return DropDownList1Button5:GetText()") - .unwrap(), - "Trade" - ); - assert_eq!( - s.eval::("return DropDownList1Button6:GetText()") - .unwrap(), - "Follow" - ); - assert_eq!( - s.eval::("return DropDownList1Button7:GetText()") - .unwrap(), - "Duel" + rows(&s), + [ + "Ally", + "Whisper", + "Inspect", + "Invite", + "Trade", + "Follow", + "Duel", + "Report", + "Ignore Player", + "Unlock Frame", + "Reset Position", + "Cancel", + ] ); // Click Invite → UnitPopup_OnClick → InviteToParty("target") → InviteUnit.