diff --git a/crates/benilla-app/src/ui_script/addons.rs b/crates/benilla-app/src/ui_script/addons.rs index 3a4fda7b..ca7ea269 100644 --- a/crates/benilla-app/src/ui_script/addons.rs +++ b/crates/benilla-app/src/ui_script/addons.rs @@ -1357,6 +1357,13 @@ mod tests { /// its hover-bind, its edit-mode escape and every bar page key. #[test] fn a_binding_table_resolves_whatever_case_the_package_spelled_it_in() { + // This test moves `BENILLA_HOME` and `WOW_CAPTURE`, which are process-wide: without the + // lock every other env-moving test in this module can see a home that is not its own. + // It is the one such test here that was missing it, and the collision shows up as a + // neighbour reading the wrong roster, not as a failure here. + let _l = crate::local_state::test_env::ENV_LOCK + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let tmp = std::env::temp_dir().join(format!("benilla-addon-lcbindings-{}", std::process::id())); let _ = std::fs::remove_dir_all(&tmp); diff --git a/crates/benilla-app/src/ui_script/panel_tests.rs b/crates/benilla-app/src/ui_script/panel_tests.rs index 10ce0c98..171e387c 100644 --- a/crates/benilla-app/src/ui_script/panel_tests.rs +++ b/crates/benilla-app/src/ui_script/panel_tests.rs @@ -977,7 +977,9 @@ fn the_panel_registry_rows_match_the_reference_bytes() { for probe in [ // UIParent.lua:20 "UIPanelWindows['ItemTextFrame'].area == 'left'", - "UIPanelWindows['ItemTextFrame'].pushable == 0", + // 5, not 1.12's 0: the realm made the reader pushable, so it shares the screen with + // another left-slot window instead of replacing it. + "UIPanelWindows['ItemTextFrame'].pushable == 5", "UIPanelWindows['ItemTextFrame'].whileDead == nil", // UIParent.lua:19 "UIPanelWindows['CharacterFrame'].pushable == 2", @@ -985,12 +987,15 @@ fn the_panel_registry_rows_match_the_reference_bytes() { // UIParent.lua:21 and :25 "UIPanelWindows['SpellBookFrame'].whileDead == 1", "UIPanelWindows['QuestLogFrame'].whileDead == 1", - // UIParent.lua:45-50; `TalentFrame`'s row loads with `Blizzard_TalentUI.lua:71`. - "table.getn(UIChildWindows) == 4", + // UIParent.lua:46-52; `TalentFrame`'s row loads with `Blizzard_TalentUI.lua:71`. Five + // rows, not 1.12's four: the realm added `ReputationDetailFrame`, so a frame arriving at + // centre now puts that one away too. + "table.getn(UIChildWindows) == 5", "UIChildWindows[1] == 'OpenMailFrame'", "UIChildWindows[2] == 'GuildControlPopupFrame'", "UIChildWindows[3] == 'GuildMemberDetailFrame'", "UIChildWindows[4] == 'GuildInfoFrame'", + "UIChildWindows[5] == 'ReputationDetailFrame'", ] { assert!( s.eval::(&format!("return {probe}")).unwrap(), @@ -1054,10 +1059,17 @@ fn a_dead_player_opens_only_whiledead_windows() { s.eval::("return QuestLogFrame:IsShown()").unwrap(), "a whileDead = 1 row opens exactly as alive" ); + // …at the **doublewide** slot: the realm's row is + // `{ area = "doublewide", pushable = 0, whileDead = 1, width = 690 }` (`UIParent.lua:22`), + // where 1.12's quest log was a plain left-slot window. assert!( - s.eval::("return GetLeftFrame():GetName() == 'QuestLogFrame'") + s.eval::("return GetUIPanel('doublewide'):GetName() == 'QuestLogFrame'") .unwrap(), - "and it seats normally at the left slot" + "and it seats normally, at the slot its own row names" + ); + assert!( + s.eval::("return GetLeftFrame() == nil").unwrap(), + "which is not the left one" ); assert!( s.take_ui_errors().is_empty(), @@ -1065,10 +1077,12 @@ fn a_dead_player_opens_only_whiledead_windows() { ); } -/// `SetCenterFrame` hides every `UIChildWindows` frame (`UIParent.lua:839-846`), but -/// `MovePanelToCenter` seats by assignment (`:877-885`), so a frame pushed to center leaves them. +/// `SetUIPanel` hides every `UIChildWindows` frame whenever it seats one, in **any** slot +/// (`UIParent.lua:792-796`, `CloseChildWindows` at `:1289`). Stock 1.12 gated that on the centre +/// seat, so a window merely pushed across left the open letter alone; the realm's version does +/// not, which is why the second half of this test watches it go too. #[test] -fn a_frame_arriving_at_center_puts_the_child_windows_away() { +fn a_frame_arriving_at_any_slot_puts_the_child_windows_away() { let _data = benilla_formats::wow_data_or_skip!(); let mut s = UiScript::new().unwrap(); s.set_screen_size(1024.0, 768.0); @@ -1103,7 +1117,8 @@ fn a_frame_arriving_at_center_puts_the_child_windows_away() { "the arriving center frame hid the open letter (the UIChildWindows loop)" ); - // LootFrame (pushable 7) holds left; a pushable-0 window slides it to center, the letter stays. + // LootFrame (pushable 7) holds left; a pushable-0 window slides it to center — and the letter + // goes with it, because the newcomer's own seat ran the loop. s.run( r#"HideUIPanel(TradeFrame) HideUIPanel(MerchantFrame) OpenMailFrame:Show() @@ -1119,7 +1134,7 @@ fn a_frame_arriving_at_center_puts_the_child_windows_away() { "loot slid to center (MovePanelToCenter), merchant took left" ); assert!( - s.eval::("return OpenMailFrame:IsShown()").unwrap(), - "a PUSHED frame does not run the child-window loop — the ref's exact trigger" + !s.eval::("return OpenMailFrame:IsShown()").unwrap(), + "every seat runs the child-window loop here, not only the centre one" ); } diff --git a/crates/benilla-app/src/ui_script/world_map_tests.rs b/crates/benilla-app/src/ui_script/world_map_tests.rs index 091c0205..50b745b4 100644 --- a/crates/benilla-app/src/ui_script/world_map_tests.rs +++ b/crates/benilla-app/src/ui_script/world_map_tests.rs @@ -532,6 +532,16 @@ fn hovering_the_player_blip_on_a_scaled_map_names_the_player() { ); let failures = super::load_default_ui(&s); assert!(failures.is_empty(), "manifest load errors: {failures:#?}"); + // After the manifest, not before: `load_default_ui` seats a placeholder player record so the + // realm's own files have a race and a class to read at load, and `UnitName("player")` — which + // is what the blip's tooltip calls (`WorldMapFrame.lua:660`) — reads that record, not the unit + // snapshot. Naming it here is what makes the two agree. + s.set_player_record(benilla_ui::script::PlayerRecord { + name: "Probefour".into(), + race: Some(("Human".into(), "Human".into())), + class: Some(("Warrior".into(), "WARRIOR".into())), + sex: 2, + }); s.resolve(); s.run("ShowUIPanel(WorldMapFrame)").unwrap(); s.resolve(); @@ -897,9 +907,20 @@ fn a_stale_layout_row_cannot_seat_a_stock_frame() { .unwrap(), "and the apply's userPlaced stamp lives inside the position arm it never took" ); + // …so the row falls out of the file at this logout instead of being written again. Only this + // row: the realm stamps its own movable windows userPlaced in their OnLoad — `PlayerFrame` + // and `TargetFrame` both do `SetMovable(1) SetUserPlaced(true)` (`PlayerFrame.xml:488-489`), + // which is the same feature as the `MOVE`/`MOVE_RESET` rows in their right-click menus, and + // the Turtle shop/transmog/guild-bank windows do the same. Those five belong in the file. assert!( - s.user_placed_layouts().is_empty(), - "so the row falls out of the file at this logout instead of being written again" + !s.user_placed_layouts() + .iter() + .any(|l| l.name == "WorldMapFrame"), + "the map must not be written back: {:?}", + s.user_placed_layouts() + .iter() + .map(|l| l.name.clone()) + .collect::>() ); // The control: with the addon's `SetMovable` and `SetResizable`, the same row seats. @@ -921,8 +942,11 @@ fn a_stale_layout_row_cannot_seat_a_stock_frame() { "and the position arm stamps the bit it was written under" ); let saved = s.user_placed_layouts(); - assert_eq!(saved.len(), 1, "which is what puts the row back: {saved:?}"); - assert_eq!(saved[0], row(), "unchanged through the round trip"); + let map_row = saved + .iter() + .find(|l| l.name == "WorldMapFrame") + .unwrap_or_else(|| panic!("which is what puts the row back: {saved:?}")); + assert_eq!(*map_row, row(), "unchanged through the round trip"); assert!(s.errors().is_empty(), "script errors: {:?}", s.errors()); }