a clone without the install runs the suite green

465 tests loaded the stock interface off the install without declaring
it, and 25 addon tests assumed the install beside the corpus; each now
opens with `wow_data_or_skip!`. The gates run the workspace tests a
second time with `WOW_DATA=`, so a test that reads the install
undeclared fails at land instead of on a contributor's clone. The skip
log writes each line in one call so parallel tests do not interleave.

Verified: the workspace tests with `WOW_DATA=` in a checkout that has
the addon corpus pass 6972, fail 0, and log 1555 skips; on a fresh
clone of main before this change they failed 465.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sam
2026-09-23 19:10:41 +07:00
co-authored by Claude Opus 5.5
parent 9baf42d900
commit 7cd95aabb0
64 changed files with 503 additions and 2 deletions
@@ -4406,6 +4406,7 @@ mod dependency_tests {
/// was an `inherits=` in their own XML, none of which appeared in the `CreateFrame` ranking.
#[test]
fn the_inherits_census_counts_templates_and_not_fonts() {
benilla_formats::wow_data_or_skip!();
let tmp =
std::env::temp_dir().join(format!("benilla-harness-inherits-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&tmp);
@@ -4573,6 +4574,7 @@ mod dependency_tests {
/// which is exactly why the old table could not see any of it.
#[test]
fn the_per_kind_census_finds_a_verb_wired_to_the_wrong_kind() {
benilla_formats::wow_data_or_skip!();
let tmp =
std::env::temp_dir().join(format!("benilla-harness-perkind-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&tmp);
@@ -4970,6 +4972,7 @@ mod dependency_tests {
/// rather than a guess.
#[test]
fn a_published_name_carries_its_kind() {
benilla_formats::wow_data_or_skip!();
let script = seated_vm();
assert_eq!(script.widget_kind("UIErrorsFrame"), Some("MessageFrame"));
assert_eq!(script.widget_kind("UIParent"), Some("Frame"));
@@ -5094,6 +5097,7 @@ mod dependency_tests {
/// (see `drive_ui_probe`'s own comment), and a probe that cannot fail is not a probe.
#[test]
fn the_ui_probe_hovers_and_records_what_a_tooltip_hook_raises() {
benilla_formats::wow_data_or_skip!();
let mut script = UiScript::new().unwrap();
script.set_screen_size(1024.0, 768.0);
seat_a_session(&mut script);
@@ -65,6 +65,7 @@ impl Drop for Fixtures {
/// not — and "creates a frame" is not "paints".
#[test]
fn the_render_column_can_fail() {
benilla_formats::wow_data_or_skip!();
let fx = Fixtures::new("cannotfail");
// Paints: a texture on a window of its own.
fx.addon(
@@ -171,6 +172,7 @@ fn the_render_column_can_fail() {
/// If this test ever disagrees with the director's eyes, **the test is wrong**.
#[test]
fn the_directors_two_verified_addons_come_out_on_opposite_sides() {
benilla_formats::wow_data_or_skip!();
// The one resolver, and a skip the gate can refuse (`benilla_formats::install`).
let corpus = benilla_formats::addon_corpus_or_skip!();
// A root holding just these four, SYMLINKED rather than copied. Surveying the whole corpus
@@ -262,6 +262,7 @@ fn the_use_column_can_fail() {
/// silently stops touching them is exactly how this instrument has been wrong four times.
#[test]
fn the_directors_two_verified_addons_are_reachable_and_omnicc_is_not_broken() {
benilla_formats::wow_data_or_skip!();
// The one resolver, and a skip the gate can refuse (`benilla_formats::install`).
let corpus = benilla_formats::addon_corpus_or_skip!();
let fx = Fixtures::new("oracle");
+1
View File
@@ -1312,6 +1312,7 @@ mod tests {
/// tests carry no page — between them the join was never asserted.
#[test]
fn a_wheel_notch_binds_through_the_real_page_and_then_dispatches() {
benilla_formats::wow_data_or_skip!();
let by_name =
|n: &str| Cmd(SPECS.iter().position(|s| s.name == n).expect("registered") as u16);
let mut s = crate::ui_script::keybindings_tests::harness();
@@ -2219,6 +2219,7 @@ mod tests {
/// here as the witness for it.
#[test]
fn the_global_scanner_sees_both_kinds_of_definition() {
benilla_formats::wow_data_or_skip!();
let defined = lua_globals_defined();
for host in ["TargetUnit", "UseAction", "SetBinding"] {
assert!(
+1
View File
@@ -352,6 +352,7 @@ mod tests {
/// it drives stays the shipped one.
#[test]
fn the_readout_tells_the_dev_pill_how_much_of_the_top_it_uses() {
benilla_formats::wow_data_or_skip!();
// NOT 768. The layout answers in WoW UI units — a screen that is always 768 units tall
// whatever the window is (decision 0582) — and the pill draws in window px, so a probe that
// subtracts one from the other is right only when the two happen to coincide. Feeding a
@@ -417,6 +417,7 @@ fn a_macro_slot_shows_the_macros_own_icon_and_follows_an_edit() {
/// notice belongs here, red — and its `SMSG_AREA_TRIGGER_MESSAGE` sibling here, yellow.
#[test]
fn pre_resolved_lines_land_on_the_errors_frame_in_the_arms_colour() {
benilla_formats::wow_data_or_skip!();
let (mut app, _rx) = app_with_food_on_the_bar();
{
let mut script = app.world_mut().non_send_resource_mut::<UiScript>();
@@ -92,6 +92,7 @@ fn gate_open(s: &UiScript) -> bool {
/// end to end — not a hand-rolled `fire_event`.
#[test]
fn a_you_joined_notice_opens_ace2s_initialisation_gate() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = ace_vm(&root);
let mut windows = ChatWindows::default();
@@ -150,6 +151,7 @@ fn a_you_joined_notice_opens_ace2s_initialisation_gate() {
/// gate and the headline is measuring the wrong thing.
#[test]
fn without_the_notice_the_gate_stays_shut() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = ace_vm(&root);
for _ in 0..40 {
@@ -427,6 +427,7 @@ mod tests {
/// divergence.
#[test]
fn a_defense_line_renders_with_the_references_bare_colon() {
benilla_formats::wow_data_or_skip!();
let event = ChatEvent {
kind: Some(ChatEventKind::Channel),
text: "|cffffff00Sentinel Hill is under attack!|r".into(),
+10
View File
@@ -1314,6 +1314,7 @@ mod bind_confirm_tests {
/// that fed it, where the reference defers inside the call that consumed the cursor.
#[test]
fn placing_a_boe_on_the_doll_asks_and_survives_its_own_cursor_update() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = place_the_axe_on_the_doll();
assert!(
rx.try_iter().next().is_none(),
@@ -1379,6 +1380,7 @@ mod bind_confirm_tests {
/// would be unanswerable.
#[test]
fn accepting_re_issues_the_place_and_does_not_ask_again() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = place_the_axe_on_the_doll();
app.world_mut()
.non_send_resource_mut::<UiScript>()
@@ -1427,6 +1429,7 @@ mod bind_confirm_tests {
/// the second call finds the element already free.
#[test]
fn cancelling_sends_nothing_and_the_doubled_cancel_is_harmless() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = place_the_axe_on_the_doll();
app.world_mut()
.non_send_resource_mut::<UiScript>()
@@ -1455,6 +1458,7 @@ mod bind_confirm_tests {
/// `quality >= 2` across). Every other bonding value places straight through.
#[test]
fn only_bind_on_equip_defers_the_place() {
benilla_formats::wow_data_or_skip!();
for (bonding, why) in [
(0u32, "no bind"),
(1, "bind on PICKUP is the loot arm's value, not this one"),
@@ -1480,6 +1484,7 @@ mod bind_confirm_tests {
/// (The loot arm's `quality >= 2` would have silenced exactly this case.)
#[test]
fn a_white_bind_on_equip_item_still_asks() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = place_the_axe_with(2, 1, false);
app.world_mut()
.non_send_resource_mut::<UiScript>()
@@ -1495,6 +1500,7 @@ mod bind_confirm_tests {
/// cursor and the tooltip's Soulbound override use (decisions 0928, 1562).
#[test]
fn an_already_bound_item_places_without_asking() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = place_the_axe_with(2, 4, true);
app.world_mut()
.non_send_resource_mut::<UiScript>()
@@ -1512,6 +1518,7 @@ mod bind_confirm_tests {
/// this gate could exist in a single place.
#[test]
fn auto_equipping_a_boe_asks_and_the_accept_re_issues() {
benilla_formats::wow_data_or_skip!();
let (tx, rx) = crossbeam_channel::unbounded();
let mut app = App::new();
app.init_resource::<PendingItemOps>()
@@ -1600,6 +1607,7 @@ mod bind_confirm_tests {
/// level above the player's, the gate's first leg.
#[test]
fn an_unusable_item_is_never_asked_about() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = place_the_axe_with(2, 4, false);
// Re-push the player's requirement state and a template the level leg refuses. (The
// fixture's own push leaves `level == 0`, the "decline to judge" state, so this test has
@@ -1735,6 +1743,7 @@ mod bind_confirm_tests {
#[test]
fn right_clicking_a_bind_on_use_item_asks_before_using_it() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = right_click_a_bind_on_use_item(false);
assert!(
rx.try_iter().next().is_none(),
@@ -1773,6 +1782,7 @@ mod bind_confirm_tests {
/// question. Under the first placement (inside the plain-cast route only) this asked nothing.
#[test]
fn a_bind_on_use_item_with_no_on_use_spell_still_asks() {
benilla_formats::wow_data_or_skip!();
let (mut app, rx) = right_click_a_bind_on_use_item(true);
app.world_mut()
.non_send_resource_mut::<UiScript>()
+4
View File
@@ -101,6 +101,7 @@ fn ui() -> UiScript {
/// queue, a roll in the roll queue — without the runner knowing any of them.
#[test]
fn running_a_macro_runs_its_lines_through_the_references_edit_box() {
benilla_formats::wow_data_or_skip!();
let mut s = ui();
s.set_macros(MacroState {
account: vec![macro_view("Greet", "/wave\n\n /say pew \n/roll 1 100")],
@@ -142,6 +143,7 @@ fn running_a_macro_runs_its_lines_through_the_references_edit_box() {
/// case anywhere in the runner.
#[test]
fn a_character_macro_runs_by_its_own_index() {
benilla_formats::wow_data_or_skip!();
let mut s = ui();
s.set_macros(MacroState {
account: Vec::new(),
@@ -202,6 +204,7 @@ fn the_generation_moves_on_every_write_and_is_not_drained() {
/// UI's use of it.
#[test]
fn a_registered_frame_sees_every_macro_line_as_an_event() {
benilla_formats::wow_data_or_skip!();
let mut s = ui();
s.run(
r#"MacroSpy = CreateFrame("Frame")
@@ -243,6 +246,7 @@ fn a_registered_frame_sees_every_macro_line_as_an_event() {
/// file round-tripped through one) is therefore three lines, not one long one.
#[test]
fn either_line_ending_splits_a_body() {
benilla_formats::wow_data_or_skip!();
let mut s = ui();
s.set_macros(MacroState {
account: vec![macro_view("Mixed", "/say Charge\r/say a\r\n/say b")],
@@ -12,6 +12,7 @@ fn action_ids(s: &mut UiScript) -> Vec<u32> {
/// slots drawing no icon, and a physical click queuing the right UseAction id.
#[test]
fn shipped_action_bar_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Cooldown.xml");
@@ -226,6 +227,7 @@ fn load_action_bar(s: &UiScript) {
/// `ACTIONBAR_UPDATE_STATE` checks the ring, and `IsUsableAction`'s OOM pair blue-tints the icon.
#[test]
fn state_feedback_drives_cooldown_checked_and_usable_through_the_xml() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -355,6 +357,7 @@ fn state_feedback_drives_cooldown_checked_and_usable_through_the_xml() {
/// start behind the new epoch.
#[test]
fn a_start_behind_the_clocks_epoch_hides_the_stock_sweep() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -424,6 +427,7 @@ fn a_start_behind_the_clocks_epoch_hides_the_stock_sweep() {
/// under the icon), which is exactly why the order is pinned end-to-end through the real XML.
#[test]
fn the_cooldown_sweep_paints_over_the_buttons_icon_and_ring() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -496,6 +500,7 @@ fn the_cooldown_sweep_paints_over_the_buttons_icon_and_ring() {
/// `ui_models`' own tests.)
#[test]
fn a_cooldown_count_addons_hook_leaves_the_sweep_running() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -612,6 +617,7 @@ const COOLDOWN_COUNT_HOOK: &str = r#"
/// cooldown came out at `button + 3` — over the count text the hook hangs at `button + 2`.
#[test]
fn a_cooldown_count_draws_over_the_bonus_bars_sweep() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -688,6 +694,7 @@ fn a_cooldown_count_draws_over_the_bonus_bars_sweep() {
/// registration set is consulted.
#[test]
fn a_right_click_on_an_action_button_uses_the_action() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -740,6 +747,7 @@ fn a_right_click_on_an_action_button_uses_the_action() {
/// unit tests directly — the modifier-key mirror gating `PickupAction` vs `UseAction`, end to end.
#[test]
fn shift_click_picks_up_not_uses() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -799,6 +807,7 @@ fn shift_click_picks_up_not_uses() {
/// way to rearrange it at all.
#[test]
fn the_action_bar_lock_stops_the_drag_and_leaves_shift_click_alone() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -873,6 +882,7 @@ fn the_action_bar_lock_stops_the_drag_and_leaves_shift_click_alone() {
/// the one gesture (0218 §4: "a drag-swap is two sends, never atomic").
#[test]
fn drag_drop_onto_another_button_hops_the_displaced_action() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -945,6 +955,7 @@ fn drag_drop_onto_another_button_hops_the_displaced_action() {
/// the only order the runtime can produce.
#[test]
fn count_fontstring_follows_is_consumable_action_not_the_bag_count() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -1046,6 +1057,7 @@ fn count_fontstring_follows_is_consumable_action_not_the_bag_count() {
/// macro on the bar was nameless.
#[test]
fn macro_name_line_follows_get_action_text_through_the_xml() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::{MacroState, MacroView};
let mut s = UiScript::new().unwrap();
@@ -1319,6 +1331,7 @@ fn shipped_bag_frame_drives_end_to_end() {
/// a usable/cooldown event with empties on the bar — and asserts no empty well gains a solid.
#[test]
fn state_events_leave_empty_wells_untinted() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -1389,6 +1402,7 @@ fn state_events_leave_empty_wells_untinted() {
/// never the colour).
#[test]
fn an_occupied_slot_going_empty_leaves_no_white_plate() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ActionState;
let mut s = UiScript::new().unwrap();
@@ -1461,6 +1475,7 @@ fn an_occupied_slot_going_empty_leaves_no_white_plate() {
/// the visible bar shows.
#[test]
fn the_bonus_action_bar_exists_hidden_and_takes_layout_calls() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -1523,6 +1538,7 @@ fn the_bonus_action_bar_exists_hidden_and_takes_layout_calls() {
/// forever, where nil fails loudly. Pinned so a later "completeness" pass cannot quietly add it.
#[test]
fn the_reference_action_bar_constants_are_defined() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_action_bar(&s);
@@ -1578,6 +1594,7 @@ fn the_reference_action_bar_constants_are_defined() {
/// full thing, or 48 `inherits=` sites across four files silently lose their handlers.
#[test]
fn both_reference_action_button_templates_are_inheritable() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_action_bar(&s);
@@ -1662,6 +1679,7 @@ fn both_reference_action_button_templates_are_inheritable() {
/// every extra bar ships off and nothing has claimed a page yet.
#[test]
fn the_main_bar_pages_and_a_bonus_page_still_outranks_it() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for file in [
@@ -1837,6 +1855,7 @@ fn the_main_bar_pages_and_a_bonus_page_still_outranks_it() {
/// pressed the instant you enter stealth already drives the stealth page.
#[test]
fn bonus_bar_slides_up_with_sound_and_down_without() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Cooldown.xml");
@@ -2059,6 +2078,7 @@ fn bonus_bar_slides_up_with_sound_and_down_without() {
/// quirk (1524).
#[test]
fn bonus_bar_turnaround_continues_from_position() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Cooldown.xml");
@@ -2141,6 +2161,7 @@ fn bonus_bar_turnaround_continues_from_position() {
/// actually draws, which separates them — the insets are behaviour here, not decoration.
#[test]
fn the_page_arrows_do_not_steal_each_other_s_clicks() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Cooldown.xml");
@@ -596,6 +596,7 @@ fn hovered_bag_tooltip_fills_itself_when_the_stats_land() {
/// leaving resets it.
#[test]
fn vendor_bag_hover_shows_sell_price_and_arms_the_pouch_cursor() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::{MerchantState, ScriptValue, UiCursorMode};
let mut s = UiScript::new().unwrap();
@@ -734,6 +735,7 @@ fn vendor_bag_hover_shows_sell_price_and_arms_the_pouch_cursor() {
/// `this.readable → ShowInspectCursor()`); a plain item leaves the base cursor; leaving resets.
#[test]
fn readable_letter_hover_shows_the_inspect_magnifier() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::UiCursorMode;
let mut s = UiScript::new().unwrap();
@@ -361,6 +361,7 @@ fn click(s: &mut UiScript, name: &str, button: &str) {
/// director was on.
#[test]
fn bagnon_draws_a_slot_for_every_bag_slot() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = seat(&root, Seat::BeforeAddons);
@@ -410,6 +411,7 @@ fn bagnon_draws_a_slot_for_every_bag_slot() {
/// drawing slots, the headline above has stopped testing the thing it was written for.
#[test]
fn without_a_player_at_addon_load_bagnon_draws_an_empty_window() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = seat(&root, Seat::AfterAddons);
s.run("ToggleBackpack()").expect("ToggleBackpack");
@@ -449,6 +451,7 @@ fn without_a_player_at_addon_load_bagnon_draws_an_empty_window() {
/// on screen at all — and it is what the identity fix alone would have produced.
#[test]
fn the_item_button_helpers_paint_a_slots_icon_and_count() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().expect("VM");
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -735,6 +738,7 @@ fn dragging_a_bagnon_slot_picks_the_item_up() {
/// ```
#[test]
fn the_backpack_button_still_hovers_with_bagnon_holding_its_script() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon(&root);
@@ -767,6 +771,7 @@ fn the_backpack_button_still_hovers_with_bagnon_holding_its_script() {
/// `widget-api-batch-benilla.md` Q8 lists it present, and `GetStringWidth` absent, on Button).
#[test]
fn a_button_reports_its_own_label_width() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon(&root);
@@ -829,6 +834,7 @@ fn a_button_reports_its_own_label_width() {
/// we had never declared, so every one of the 36 buttons warned and the child came out bare.
#[test]
fn the_reference_cooldown_template_resolves() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let s = open_bagnon(&root);
let occupied = bagnon_button_for_slot(&s, 0, 1);
@@ -857,6 +863,7 @@ fn the_reference_cooldown_template_resolves() {
/// does.
#[test]
fn the_whole_bagnon_window_survives_being_used() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon(&root);
@@ -1047,6 +1054,7 @@ fn text_quad(s: &mut UiScript, owner: &str) -> QuadContent {
/// asserted as such — a future "fix" that adds one would be a divergence, not an improvement.
#[test]
fn a_stack_count_wears_the_font_object_its_font_attr_names() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon_stacked(&root);
@@ -1101,6 +1109,7 @@ fn a_stack_count_wears_the_font_object_its_font_attr_names() {
/// wow-re `texture-color-composition.md` §1-2).
#[test]
fn a_texture_gradient_tints_the_art_it_sits_on() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon_stacked(&root);
s.run("BagnonDBUI_ShowCharacterList(Bagnon)")
@@ -1151,6 +1160,7 @@ fn a_texture_gradient_tints_the_art_it_sits_on() {
/// The assertion is the FIRST open, which is the half that was broken.
#[test]
fn a_money_frame_is_the_right_width_on_the_first_open() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = seat(&root, Seat::BeforeAddons);
@@ -1215,6 +1225,7 @@ fn resolved_font(s: &UiScript, lua_expr: &str) -> (String, String) {
/// zero against nine controls each returning one); it is a later-client idiom.
#[test]
fn a_button_state_font_takes_the_font_attribute_not_just_inherits() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon_stacked(&root);
s.run("BagnonDBUI_ShowCharacterList(Bagnon)")
@@ -1248,6 +1259,7 @@ fn a_button_state_font_takes_the_font_attribute_not_just_inherits() {
/// reference does not.
#[test]
fn a_fontheight_with_no_font_attr_beside_it_is_never_read() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let s = open_bagnon_stacked(&root);
assert_eq!(
@@ -1288,6 +1300,7 @@ impl benilla_ui::script::TextMeasure for BlockFont {
/// name plus the addon's own 40px of checkbox and padding.
#[test]
fn the_character_dropdown_is_as_wide_as_the_names_in_it() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon(&root);
s.set_text_measurer(Box::new(BlockFont));
@@ -1342,6 +1355,7 @@ fn the_character_dropdown_is_as_wide_as_the_names_in_it() {
/// (`ui_items::feed::tests::an_absent_self_player_is_no_source_never_an_empty_bag_burst`).
#[test]
fn bagnon_forevers_records_survive_the_logout_boundary() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = seat(&root, Seat::BeforeAddons);
@@ -1547,6 +1561,7 @@ fn a_bag_slot_click_toggles_bagnon_not_the_native_window() {
/// a shim in `ContainerFrameAdapters.xml` carries the why.
#[test]
fn the_backpack_button_lit_state_with_bagnon_holding_the_bags() {
benilla_formats::wow_data_or_skip!();
let root = corpus_or_skip!();
let mut s = open_bagnon(&root);
@@ -23,6 +23,7 @@ fn basic_controls() -> UiScript {
/// reason the frame has to exist rather than the function being a stub that prints somewhere.
#[test]
fn message_shows_the_script_errors_dialog_carrying_its_text() {
benilla_formats::wow_data_or_skip!();
let s = basic_controls();
assert!(
!s.eval::<bool>("return ScriptErrors:IsVisible()").unwrap(),
@@ -57,6 +58,7 @@ fn message_shows_the_script_errors_dialog_carrying_its_text() {
/// is replaceable — which is the entire point for the two corpus addons that replace it.
#[test]
fn error_message_returns_its_argument_and_can_be_replaced() {
benilla_formats::wow_data_or_skip!();
let s = basic_controls();
assert_eq!(
s.eval::<String>(r#"return _ERRORMESSAGE("boom")"#).unwrap(),
@@ -82,6 +84,7 @@ fn error_message_returns_its_argument_and_can_be_replaced() {
/// test used to be).
#[test]
fn error_message_is_the_installed_handler_and_the_host_channel_stays_sighted() {
benilla_formats::wow_data_or_skip!();
let mut s = basic_controls();
assert!(
s.eval::<bool>("return geterrorhandler() == _ERRORMESSAGE")
@@ -120,6 +123,7 @@ fn error_message_is_the_installed_handler_and_the_host_channel_stays_sighted() {
/// `TEXT` is the identity function, and 26 corpus addons call it because shipped FrameXML does.
#[test]
fn text_is_the_identity_function_the_corpus_expects() {
benilla_formats::wow_data_or_skip!();
let s = basic_controls();
assert_eq!(
s.eval::<String>(r#"return TEXT("Level")"#).unwrap(),
@@ -133,6 +137,7 @@ fn text_is_the_identity_function_the_corpus_expects() {
/// name — `MyDialogButton`, never `DialogBoxFrameButton`.
#[test]
fn a_dialog_box_from_the_template_names_and_closes_the_callers_frame() {
benilla_formats::wow_data_or_skip!();
let s = basic_controls();
s.run(
r#"MyDialog = CreateFrame("Frame", "MyDialog", UIParent, "DialogBoxFrame")
@@ -30,6 +30,7 @@ fn setup() -> UiScript {
/// this wiring the click produced no dialog and no packet at all (B249).
#[test]
fn the_confirm_shows_the_area_and_accept_queues_the_bind() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event(
"CONFIRM_BINDER",
@@ -58,6 +59,7 @@ fn the_confirm_shows_the_area_and_accept_queues_the_bind() {
/// no decline opcode), so the only observable is that no confirm was queued.
#[test]
fn declining_sends_nothing() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_BINDER", vec![ScriptValue::Str("Goldshire".into())]);
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
@@ -77,6 +79,7 @@ fn declining_sends_nothing() {
/// nothing; the frame it goes false, the dialog hides itself — with no packet either way.
#[test]
fn leaving_the_innkeepers_range_hides_the_confirm() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_BINDER", vec![ScriptValue::Str("Kharanos".into())]);
s.run("StaticPopup_OnUpdate(StaticPopup1, 0.1)").unwrap();
@@ -309,6 +309,7 @@ const RAISABLE_BARS: &[&str] = &[
/// eventually wired), and re-runs the pass between combinations exactly as a live bar change does.
#[test]
fn no_bottom_band_frame_overlaps_a_raised_bar() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1600.0, 900.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -457,6 +458,7 @@ fn overlaps(a: (f32, f32, f32, f32), b: (f32, f32, f32, f32)) -> bool {
/// is the test that says so out loud rather than the change slipping through green.
#[test]
fn the_item_push_card_shares_the_band_with_a_raised_bar_exactly_as_the_reference_does() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1600.0, 900.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -50,6 +50,7 @@ fn tex_quad<'a>(quads: &'a [ExtractedQuad], leaf: &str) -> Option<&'a ExtractedQ
#[test]
fn cast_fills_then_completes_green_and_fades() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
assert!(
!s.eval::<bool>("return CastingBarFrame:IsShown()").unwrap(),
@@ -99,6 +100,7 @@ fn cast_fills_then_completes_green_and_fades() {
#[test]
fn a_hit_pushes_the_bar_back_it_does_not_cancel() {
benilla_formats::wow_data_or_skip!();
// Pushback (`SMSG_SPELL_DELAYED` → `SPELLCAST_DELAYED`): a hit while casting must slide the
// bar's window out (the spark jumps back and it keeps running), never hide or fail it —
// decision 0256's open item, the "disappears on a hit" report.
@@ -145,6 +147,7 @@ fn a_hit_pushes_the_bar_back_it_does_not_cancel() {
#[test]
fn failed_cast_turns_red_holds_then_fades() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event(
"SPELLCAST_START",
@@ -179,6 +182,7 @@ fn failed_cast_turns_red_holds_then_fades() {
#[test]
fn channel_counts_down_not_up() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
// SPELLCAST_CHANNEL_START(ms, name) — args reversed vs START, per the reference contract.
// The name is whatever `ui_cast::channel_start_args` composed; for all but nine of the 323
@@ -261,6 +265,7 @@ fn channel_counts_down_not_up() {
/// `SPELLCAST_CHANNEL_STOP` by hand, which is the *interrupt* timing, not this one.
#[test]
fn a_finished_channel_fades_on_its_own_clock_and_the_late_stop_is_inert() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event(
"SPELLCAST_CHANNEL_START",
@@ -316,6 +321,7 @@ const FILL: &str = "UI-StatusBar";
/// smear over the bar from the first frame — invisible to every state assertion above.
#[test]
fn the_flash_stays_hidden_for_the_whole_cast() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event(
"SPELLCAST_START",
@@ -343,6 +349,7 @@ fn the_flash_stays_hidden_for_the_whole_cast() {
/// finished.
#[test]
fn the_flash_blooms_from_zero_only_on_completion() {
benilla_formats::wow_data_or_skip!();
const REF_TICK: f32 = 1.0 / 30.0; // one reference tick of wall clock
let mut s = harness();
s.fire_event(
@@ -373,6 +380,7 @@ fn the_flash_blooms_from_zero_only_on_completion() {
/// A failed cast never flashes — the reference only arms the ramp on STOP/CHANNEL_STOP.
#[test]
fn a_failed_cast_never_flashes() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event(
"SPELLCAST_START",
@@ -395,6 +403,7 @@ fn a_failed_cast_never_flashes() {
/// border art's own layer, where declaration order put the fill on top.)
#[test]
fn the_fill_draws_beneath_the_border_art() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event(
"SPELLCAST_START",
@@ -416,6 +425,7 @@ fn the_fill_draws_beneath_the_border_art() {
/// ramp keeps its true gradient at every fill level.
#[test]
fn the_fill_crops_its_texture_rather_than_stretching_it() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event(
"SPELLCAST_START",
@@ -460,6 +470,7 @@ fn bottom(s: &UiScript, name: &str) -> f64 {
/// (base + bottomEither/bottomLeft + pet, and chat's bottomLeft-and-pet +23 extra).
#[test]
fn managed_positions_track_the_bottom_bar_stack() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -553,6 +564,7 @@ fn managed_positions_track_the_bottom_bar_stack() {
/// "exists" and still break every addon that drives the bar through it.
#[test]
fn the_casting_bar_publishes_its_status_bar_alias() {
benilla_formats::wow_data_or_skip!();
let s = harness();
assert!(
s.eval::<bool>("return CastingBarFrameStatusBar ~= nil")
@@ -67,6 +67,7 @@ fn close(a: f32, b: f32) -> bool {
#[test]
fn injected_lines_render_in_the_pinned_colors() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
// The app's feed passes 0..1 floats from the pinned type→color table; the seam quantizes them.
// SAY white (FFFFFF), SYSTEM yellow (FFFF00 — the GM-feedback color), LOOT green (00AA00).
@@ -104,6 +105,7 @@ fn injected_lines_render_in_the_pinned_colors() {
#[test]
fn newest_line_sits_at_the_bottom() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.add_chat_message("ChatFrame1", "older", 1.0, 1.0, 1.0);
s.add_chat_message("ChatFrame1", "newer", 1.0, 1.0, 1.0);
@@ -129,6 +131,7 @@ fn newest_line_sits_at_the_bottom() {
/// any input the client offers (director-reported, 2026-08-29).
#[test]
fn wheel_scroll_re_arms_the_fade_then_freezes_it() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.run("ChatFrame1:SetTimeVisible(0); ChatFrame1:SetFadeDuration(4)")
.unwrap();
@@ -160,6 +163,7 @@ fn wheel_scroll_re_arms_the_fade_then_freezes_it() {
#[test]
fn input_editbox_enter_drains_the_typed_line() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
assert!(s.focus_editbox("ChatFrameEditBox"), "the edit box focuses");
assert!(s.has_keyboard_focus(), "focus gates the world's keys");
@@ -184,6 +188,7 @@ fn input_editbox_enter_drains_the_typed_line() {
#[test]
fn input_escape_closes_without_submitting() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
assert!(s.focus_editbox("ChatFrameEditBox"));
s.char_input("hello");
@@ -198,6 +203,7 @@ fn input_escape_closes_without_submitting() {
/// leave LEFT/RIGHT consumed-but-dead here, and an unpushed history would leave Up/Down empty.
#[test]
fn chat_box_arrows_edit_and_history_recalls() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::{EditAction, EditUnit};
let mut s = chat_frame();
assert!(s.focus_editbox("ChatFrameEditBox"));
@@ -265,6 +271,7 @@ fn chat_box_arrows_edit_and_history_recalls() {
/// untouched (a silent item dismissal would be a destroy — only the world-drop popup offers that).
#[test]
fn chat_click_dismisses_a_stuck_spell_but_not_an_item() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::{
ContainerSlot, ContainerState, SpellBookState, SpellSlotView, SpellTabView,
};
@@ -385,6 +392,7 @@ fn chat_click_dismisses_a_stuck_spell_but_not_an_item() {
/// that makes the duplication safe: every kind we model must agree to the byte, and the table's shape (`sticky`/`id`) must be the reference's.
#[test]
fn chat_type_info_matches_the_host_color_table() {
benilla_formats::wow_data_or_skip!();
use crate::ui_chat::{default_color, ChatEventKind as K};
/// Each modeled kind and its `ChatTypeInfo` key — the reference's own spellings.
@@ -453,6 +461,7 @@ fn chat_type_info_matches_the_host_color_table() {
/// extras. An addon reads `.sticky` and `.id` as often as it reads the color.
#[test]
fn chat_type_info_has_the_references_shape() {
benilla_formats::wow_data_or_skip!();
let s = chat_frame();
let count: i64 = s
@@ -518,6 +527,7 @@ fn chat_type_info_has_the_references_shape() {
/// a matching table key fails here instead of shipping a name nothing can resolve.
#[test]
fn fired_event_names_are_all_chat_type_info_keys() {
benilla_formats::wow_data_or_skip!();
use crate::ui_chat::{event_name, ChatEventKind as K};
let s = chat_frame();
@@ -544,6 +554,7 @@ fn fired_event_names_are_all_chat_type_info_keys() {
/// guard (`LazyPig.lua:1992`), so the constant without the frames is worse than neither.
#[test]
fn every_window_num_chat_windows_promises_is_a_real_frame() {
benilla_formats::wow_data_or_skip!();
let s = chat_frame();
assert_eq!(s.eval::<i64>("return NUM_CHAT_WINDOWS").unwrap(), 7);
for i in 1..=7 {
@@ -561,6 +572,7 @@ fn every_window_num_chat_windows_promises_is_a_real_frame() {
/// `getglobal(...):IsVisible()` that used to die at i=3.
#[test]
fn the_lazypig_window_walk_survives_all_seven_indices() {
benilla_formats::wow_data_or_skip!();
let s = chat_frame();
let visible: i64 = s
.eval(
@@ -587,6 +599,7 @@ fn the_lazypig_window_walk_survives_all_seven_indices() {
/// tabs (1575), so the Outfitter walk below is still safe.
#[test]
fn the_undocked_windows_are_hidden_and_carry_no_is_docked() {
benilla_formats::wow_data_or_skip!();
let s = chat_frame();
for i in 1..=2 {
let docked: bool = s
@@ -626,6 +639,7 @@ fn the_undocked_windows_are_hidden_and_carry_no_is_docked() {
/// frame will disagree about the same window. The drift guard between the Rust table and the XML.
#[test]
fn get_chat_window_info_shown_matches_the_shipped_frames() {
benilla_formats::wow_data_or_skip!();
let s = chat_frame();
for i in 1..=7 {
let agrees: bool = s
@@ -650,6 +664,7 @@ fn get_chat_window_info_shown_matches_the_shipped_frames() {
/// in ChatFrame3 and nowhere near the window the player is reading.
#[test]
fn a_line_added_to_chat_frame3_lands_in_chat_frame3_only() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.add_chat_message("ChatFrame1", "a real line", 1.0, 1.0, 1.0);
s.run("ChatFrame3:AddMessage('Radar: debug', 1, 1, 0)")
@@ -683,6 +698,7 @@ fn a_line_added_to_chat_frame3_lands_in_chat_frame3_only() {
/// reaches it, since both pass `DEFAULT_CHAT_FRAME`.
#[test]
fn fcf_select_dock_frame_selects_by_frame_and_leaves_an_undocked_one_alone() {
benilla_formats::wow_data_or_skip!();
let s = chat_frame();
s.run("FCF_SelectDockFrame(ChatFrame2)").unwrap();
let (one, two): (bool, bool) = (
@@ -733,6 +749,7 @@ fn fcf_select_dock_frame_selects_by_frame_and_leaves_an_undocked_one_alone() {
/// next tick overwrites 0.42 with 0 and this goes red.
#[test]
fn an_idle_dock_stops_rewriting_the_tab_alpha_every_frame() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.mouse_move(1500.0, 850.0); // far from the dock: no hover, so `reveal` stays 0
for _ in 0..8 {
@@ -758,6 +775,7 @@ fn an_idle_dock_stops_rewriting_the_tab_alpha_every_frame() {
/// cursor over the dock for longer than `CHAT_TAB_SHOW_DELAY` fades the selected tab to full.
#[test]
fn hovering_the_dock_still_reveals_the_tabs() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.mouse_move(1500.0, 850.0);
for _ in 0..4 {
@@ -798,6 +816,7 @@ fn hovering_the_dock_still_reveals_the_tabs() {
/// 0.5s toggle; and scrolling back while the flash is LIT still hides it and zeroes the timer.
#[test]
fn a_chat_view_at_the_bottom_stops_rewriting_the_flash() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
for t in ["L0", "L1", "L2"] {
s.add_chat_message("ChatFrame1", t, 1.0, 1.0, 1.0);
@@ -866,6 +885,7 @@ fn a_chat_view_at_the_bottom_stops_rewriting_the_flash() {
/// dead driver. So this drives the clock and nothing else.
#[test]
fn selecting_the_combat_log_keeps_the_dock_driver_running() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
let (x, y): (f32, f32) = s
.eval(
@@ -912,6 +932,7 @@ fn selecting_the_combat_log_keeps_the_dock_driver_running() {
/// `reveal` never decayed either, so the chat box and both tabs stayed lit after the cursor left.
#[test]
fn the_dock_still_fades_out_with_the_combat_log_selected() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
let (x, y): (f32, f32) = s
.eval(
@@ -955,6 +976,7 @@ fn the_dock_still_fades_out_with_the_combat_log_selected() {
/// button, which only happens if window 2 both HAS the button and runs `ChatFrame_OnUpdate`.
#[test]
fn the_combat_log_window_runs_its_own_bottom_button_blink() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.run("FCF_SelectDockFrame(ChatFrame2)").unwrap();
for i in 0..40 {
@@ -1064,6 +1086,7 @@ fn the_chat_menu_builds_its_rows_on_the_references_kit() {
/// the chat window shows the tabs and the buttons but no plate.
#[test]
fn a_glass_windows_plate_fades_in_on_every_hover() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_frame();
s.mouse_move(1500.0, 850.0);
for _ in 0..4 {
@@ -1109,6 +1132,7 @@ fn a_glass_windows_plate_fades_in_on_every_hover() {
/// The same law under the WHOLE shipped manifest, driven the way the app drives it.
#[test]
fn a_glass_windows_plate_fades_in_on_every_hover_under_the_full_manifest() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1600.0, 900.0);
super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua");
@@ -1207,6 +1231,7 @@ fn a_glass_windows_plate_fades_in_on_every_hover_under_the_full_manifest() {
/// then a repair of nothing.
#[test]
fn a_quick_exit_and_reentry_keeps_the_plates_hover_fade() {
benilla_formats::wow_data_or_skip!();
fn drive(s: &mut UiScript) -> (f64, f64, f64) {
let (x, y): (f32, f32) = s
.eval(
@@ -1312,6 +1337,7 @@ fn a_quick_exit_and_reentry_keeps_the_plates_hover_fade() {
/// broken — which is exactly how this shipped.
#[test]
fn the_stock_afk_and_dnd_commands_reach_the_wire() {
benilla_formats::wow_data_or_skip!();
use crate::net::ChatKind;
use crate::ui_chat::edit::SendType;
@@ -51,6 +51,7 @@ fn start_cinematic(s: &mut UiScript) {
/// cannot walk during a cinematic.
#[test]
fn a_playing_cinematic_swallows_the_movement_keys() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_with_the_cinematic_frame();
// Before it starts, the frame is hidden and declines: the world keeps its keys.
@@ -84,6 +85,7 @@ fn a_playing_cinematic_swallows_the_movement_keys() {
/// never that they fall through.
#[test]
fn escape_is_consumed_and_acted_on_rather_than_falling_through() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_with_the_cinematic_frame();
start_cinematic(&mut s);
@@ -132,6 +134,7 @@ fn the_host_has_a_reference_name_for_the_keys_it_now_delivers() {
/// ever asked about keys the frame *swallows*.
#[test]
fn the_screenshot_key_is_handed_back_to_its_binding() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_with_the_cinematic_frame();
// The binding table the passthrough reads: `GetBindingKey("SCREENSHOT")` has to answer, or the
// arm is skipped and the test proves nothing.
@@ -183,6 +186,7 @@ fn the_screenshot_key_is_handed_back_to_its_binding() {
/// `IsVisible` the honest question again, so the check is an assertion instead of a log.
#[test]
fn a_cinematic_hides_the_hud_through_uiparent_and_spares_the_cinematic_frame() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_with_the_cinematic_frame();
let visible = |s: &UiScript, f: &str| {
s.eval::<i64>(&format!("return {f}:IsVisible() and 1 or 0"))
@@ -235,6 +239,7 @@ fn a_cinematic_hides_the_hud_through_uiparent_and_spares_the_cinematic_frame() {
/// silent about the hide.
#[test]
fn the_screenshot_confirmation_shows_during_a_cinematic() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -28,6 +28,7 @@ fn setup() -> UiScript {
/// the Repop intent, and PLAYER_ALIVE (the release landing) hides it.
#[test]
fn death_popup_counts_down_and_release_queues_repop() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
release_remaining: Some(300.0),
@@ -80,6 +81,7 @@ fn death_popup_counts_down_and_release_queues_repop() {
/// "Reincarnate" default, and clicking it queues `UseSoulstone` — while button1 still releases.
#[test]
fn death_popup_offers_the_self_resurrect_and_spends_it() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
release_remaining: Some(300.0),
@@ -119,6 +121,7 @@ fn death_popup_offers_the_self_resurrect_and_spends_it() {
/// `DisplayButton2` nor `OnShow` per tick).
#[test]
fn death_popup_button2_is_decided_at_show_time() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
release_remaining: Some(300.0),
@@ -159,6 +162,7 @@ fn death_popup_button2_is_decided_at_show_time() {
/// DEATH_RELEASE_NOTIMER text and never counts down.
#[test]
fn death_popup_no_timer_shows_the_static_text() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
release_remaining: None,
@@ -180,6 +184,7 @@ fn death_popup_no_timer_shows_the_static_text() {
/// name in, gates Accept behind the recovery delay, and Accept/Decline queue their intents.
#[test]
fn resurrect_request_picks_variant_and_answers() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
// No sickness + timer, delay already elapsed: RESURRECT_NO_SICKNESS with an armed Accept.
s.set_death(DeathUiState {
@@ -251,6 +256,7 @@ fn resurrect_request_picks_variant_and_answers() {
/// and keeps the dialog, the second queues AcceptXPLoss; walking out of range auto-hides.
#[test]
fn xp_loss_two_step_confirm_then_range_hide() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
sickness_duration: Some("8 Minutes".into()),
@@ -320,6 +326,7 @@ fn xp_loss_two_step_confirm_then_range_hide() {
/// instance.
#[test]
fn xp_loss_cancel_then_reconfirm_reshows_with_the_alert_dress() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
sickness_duration: Some("8 Minutes".into()),
@@ -373,6 +380,7 @@ fn xp_loss_cancel_then_reconfirm_reshows_with_the_alert_dress() {
/// false for it; decision 0308 §1).
#[test]
fn the_ghost_predicates() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_unit(
"player",
@@ -405,6 +413,7 @@ fn the_ghost_predicates() {
/// variant is the buttonless notice.
#[test]
fn corpse_range_events_drive_recover_corpse() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_death(DeathUiState {
recovery_delay: 2.0,
@@ -465,6 +474,7 @@ fn corpse_range_events_drive_recover_corpse() {
/// WorldMapFrame.lua:443-452 law the map's update block branches on.
#[test]
fn corpse_map_position_binding() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
let (x, y) = s
.eval::<(f64, f64)>("return GetCorpseMapPosition()")
@@ -135,6 +135,7 @@ fn drop_in_world(s: &mut UiScript, item_id: u32, name: &str, quality: u32) {
/// `DeleteCursorItem()` — the item leaves the cursor and queues its wire destroy.
#[test]
fn delete_item_confirm_shows_the_real_strings_and_yes_deletes() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
pick_up_and_drop_in_world(&mut s);
@@ -261,6 +262,7 @@ fn desaturated(s: &mut UiScript, button: &str) -> bool {
/// this mainly proves the popup itself closes.)
#[test]
fn escape_closes_the_delete_confirm_popup() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
pick_up_and_drop_in_world(&mut s);
@@ -280,6 +282,7 @@ fn escape_closes_the_delete_confirm_popup() {
/// DIFFERENT visible dialog is untouched by cursor traffic.
#[test]
fn the_delete_entry_polls_itself_hidden_and_other_dialogs_are_untouched() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
pick_up_and_drop_in_world(&mut s);
assert!(s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
@@ -316,6 +319,7 @@ fn the_delete_entry_polls_itself_hidden_and_other_dialogs_are_untouched() {
/// focused, and OKAY **disabled** until the confirm word is typed.
#[test]
fn a_rare_payload_raises_the_typed_confirm_with_okay_disabled() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
drop_in_world(&mut s, 871, "Flurry Axe", 4);
@@ -354,6 +358,7 @@ fn a_rare_payload_raises_the_typed_confirm_with_okay_disabled() {
/// passes, and backing away from the word disables it again. Then OKAY destroys.
#[test]
fn typing_the_confirm_word_enables_okay_and_untyping_it_disables_again() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
drop_in_world(&mut s, 871, "Flurry Axe", 4);
@@ -408,6 +413,7 @@ fn typing_the_confirm_word_enables_okay_and_untyping_it_disables_again() {
/// only while OKAY is enabled, so a reflexive Enter over a half-typed word does nothing at all.
#[test]
fn enter_in_the_box_destroys_only_once_okay_is_enabled() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
drop_in_world(&mut s, 871, "Flurry Axe", 4);
@@ -437,6 +443,7 @@ fn enter_in_the_box_destroys_only_once_okay_is_enabled() {
/// left over from last time would be armed before the player read the dialog).
#[test]
fn no_on_the_typed_confirm_clears_and_leaves_the_box_empty_for_next_time() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
drop_in_world(&mut s, 871, "Flurry Axe", 4);
s.run(r#"StaticPopup1EditBox:SetText("DELETE")"#).unwrap();
@@ -476,6 +483,7 @@ fn no_on_the_typed_confirm_clears_and_leaves_the_box_empty_for_next_time() {
/// cancel here, which the earlier form of this test pinned.
#[test]
fn escape_in_the_typed_confirms_box_is_swallowed_as_the_reference_leaves_it() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
drop_in_world(&mut s, 871, "Flurry Axe", 4);
assert_eq!(
@@ -502,6 +510,7 @@ fn escape_in_the_typed_confirms_box_is_swallowed_as_the_reference_leaves_it() {
/// destroy must not grow a field to type into. The control for every test above.
#[test]
fn the_plain_arm_shows_no_edit_box() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
pick_up_and_drop_in_world(&mut s); // Tough Jerky, quality 1
@@ -278,6 +278,7 @@ fn shift_click_posts_the_link_with_chat_open_and_splits_with_it_closed() {
/// is what the message frame's `OnHyperlinkClick` calls with the full markup.
#[test]
fn ctrl_clicking_a_chat_link_previews_it() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_room(&s);
@@ -412,6 +413,7 @@ fn shift_clicking_an_unresolved_slot_posts_nothing_and_never_raises() {
/// BOTH mouse edges, so one tap is 0.06 (decision 0638 §3).
#[test]
fn reset_re_dresses_close_empties_and_the_arrows_spin_the_pane() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_room(&s);
@@ -41,6 +41,7 @@ fn load_dropdown_kit(s: &UiScript) {
/// `DropDownList1:Hide()` on its own.
#[test]
fn hiding_the_parent_list_closes_an_open_submenu() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_dropdown_kit(&s);
@@ -70,6 +71,7 @@ fn hiding_the_parent_list_closes_an_open_submenu() {
/// The divergence from the reference is in this file, so it is fixed and falsified in this file.
#[test]
fn hiding_a_list_clears_its_open_menu_registry_entry() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_dropdown_kit(&s);
@@ -110,6 +112,7 @@ fn hiding_a_list_clears_its_open_menu_registry_entry() {
/// equals some number: a literal would pass a number check and still be wrong.
#[test]
fn the_default_text_height_is_derived_from_button1_not_a_literal() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_dropdown_kit(&s);
@@ -141,6 +144,7 @@ fn the_default_text_height_is_derived_from_button1_not_a_literal() {
/// `GameFontHighlightSmall` by name because that is what `UIDropDownMenuButtonTemplate` declares.
#[test]
fn a_button_label_reports_the_font_object_its_button_set() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_dropdown_kit(&s);
@@ -27,6 +27,7 @@ fn setup() -> UiScript {
/// Accept queues `AcceptDuel()`.
#[test]
fn the_challenge_popup_shows_the_name_and_accept_queues_the_accept() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event(
"DUEL_REQUESTED",
@@ -54,6 +55,7 @@ fn the_challenge_popup_shows_the_name_and_accept_queues_the_accept() {
/// engine routes an escape through OnCancel.
#[test]
fn decline_and_escape_both_cancel() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("DUEL_REQUESTED", vec![ScriptValue::Str("Twomage".into())]);
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
@@ -69,6 +71,7 @@ fn decline_and_escape_both_cancel() {
/// StaticPopup engine's per-tick branch, and `DUEL_INBOUNDS` takes it away.
#[test]
fn out_of_bounds_counts_down_and_inbounds_dismisses_it() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("DUEL_OUTOFBOUNDS", vec![]);
assert!(s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
@@ -100,6 +103,7 @@ fn out_of_bounds_counts_down_and_inbounds_dismisses_it() {
/// leave a stale popup or fire a second cancel.
#[test]
fn finishing_sweeps_both_dialogs_silently() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("DUEL_REQUESTED", vec![ScriptValue::Str("Twomage".into())]);
assert!(s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
@@ -123,6 +127,7 @@ fn finishing_sweeps_both_dialogs_silently() {
/// their argument through so the app can resolve it.
#[test]
fn the_era_globals_queue_their_intents() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.run("AcceptDuel(); CancelDuel(); StartDuel('Onerogue'); StartDuelUnit('target')")
.unwrap();
@@ -32,6 +32,7 @@ fn setup() -> UiScript {
/// — which on the app side re-enters `0x495d60` with the confirmed flag, not a send.
#[test]
fn the_bind_confirm_shows_and_its_okay_queues_bind_enchant() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("BIND_ENCHANT", vec![]);
assert!(
@@ -52,6 +53,7 @@ fn the_bind_confirm_shows_and_its_okay_queues_bind_enchant() {
/// — the fire site pushes the enchant already on the item first, then the one about to land.
#[test]
fn the_replace_confirm_names_the_old_enchant_first() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event(
"REPLACE_ENCHANT",
@@ -73,6 +75,7 @@ fn the_replace_confirm_names_the_old_enchant_first() {
/// returned before `BindTarget` and the targeting word is still standing.
#[test]
fn declining_either_confirm_queues_nothing() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("BIND_ENCHANT", vec![]);
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
@@ -92,6 +95,7 @@ fn declining_either_confirm_queues_nothing() {
/// anything, so a cancelled or replaced cast must not leave a live Yes button behind.
#[test]
fn a_changed_pending_cast_dismisses_both() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("BIND_ENCHANT", vec![]);
s.fire_event("CURRENT_SPELL_CAST_CHANGED", vec![]);
@@ -203,6 +203,7 @@ fn escape_is_consumed_by_a_focused_editbox_and_leaves_windows_open() {
/// to eat, opens the menu (one eater per press, the 0449 law).
#[test]
fn escape_closes_the_options_window_before_opening_the_menu() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -526,6 +527,7 @@ fn escape_ladder_targeting_rung_after_cast_before_windows() {
/// `Hide`, never `HideUIPanel`, because these frames hold no panel slot.
#[test]
fn an_addon_frame_registered_in_uispecialframes_closes_on_escape() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -53,6 +53,7 @@ fn exp_bar_harness() -> UiScript {
/// transparent and the hover silently never fires, which no tooltip assertion alone would catch.
#[test]
fn the_xp_bar_takes_the_mouse_and_explains_itself() {
benilla_formats::wow_data_or_skip!();
let mut s = exp_bar_harness();
s.resolve();
@@ -106,6 +107,7 @@ fn the_xp_bar_takes_the_mouse_and_explains_itself() {
/// pool whose span runs past the level's end hides the tick but keeps the rested blue.
#[test]
fn the_exhaustion_tick_marks_where_rested_runs_out() {
benilla_formats::wow_data_or_skip!();
let mut s = exp_bar_harness();
s.set_player_xp(1000, 10000);
@@ -220,6 +222,7 @@ fn the_exhaustion_tick_marks_where_rested_runs_out() {
/// hides it — the ref's own load order, ending hidden.
#[test]
fn the_max_level_rail_replaces_the_xp_bar_at_60() {
benilla_formats::wow_data_or_skip!();
let mut s = exp_bar_harness();
s.set_player_xp(1000, 10000);
s.set_rest_state(1, 700, true);
@@ -271,6 +274,7 @@ fn the_max_level_rail_replaces_the_xp_bar_at_60() {
/// re-stamp can lift a lower layer over them.
#[test]
fn the_gryphons_outrank_the_bars_across_hide_show_cycles() {
benilla_formats::wow_data_or_skip!();
let mut s = exp_bar_harness();
s.set_player_xp(300, 400);
s.fire_event("PLAYER_ENTERING_WORLD", vec![]);
@@ -307,6 +311,7 @@ fn the_gryphons_outrank_the_bars_across_hide_show_cycles() {
/// 0↔1 edge — the `statusBarText` cvar that pins it on permanently reads OFF here).
#[test]
fn the_xp_bar_numerals_show_on_hover() {
benilla_formats::wow_data_or_skip!();
let mut s = exp_bar_harness();
s.set_player_xp(1234, 5678);
s.fire_event("PLAYER_ENTERING_WORLD", vec![]);
@@ -342,6 +347,7 @@ fn the_xp_bar_numerals_show_on_hover() {
/// held-open newbie tip, once (the handshake is consumed).
#[test]
fn the_rest_state_line_joins_the_held_open_tooltip() {
benilla_formats::wow_data_or_skip!();
let mut s = exp_bar_harness();
s.set_player_xp(1000, 10000);
s.set_rest_state(1, 700, true);
@@ -11,6 +11,7 @@ use super::test_ui::load_ui as load_xml;
#[test]
fn a_started_fade_ramps_off_uiparents_tick_and_completes() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
@@ -67,6 +68,7 @@ fn a_started_fade_ramps_off_uiparents_tick_and_completes() {
/// twelve faux lists shipped unable to scroll (1868), so this runs a real flash to completion.
#[test]
fn a_flash_alternates_then_stops() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
@@ -78,6 +78,7 @@ fn harness() -> UiScript {
/// wear instead of a `Benilla*` one.)
#[test]
fn a_fresh_faux_frame_loads_at_the_top_with_no_range() {
benilla_formats::wow_data_or_skip!();
let s = harness();
assert_eq!(s.eval::<i64>("return TestScroll.offset").unwrap(), 0);
let (lo, hi) = s
@@ -98,6 +99,7 @@ fn a_fresh_faux_frame_loads_at_the_top_with_no_range() {
/// row, and `FauxScrollFrame_Update` returns the reference's `showScrollBar`.
#[test]
fn more_rows_than_fit_raise_the_bar_over_the_overflow_range() {
benilla_formats::wow_data_or_skip!();
let s = harness();
let shown = s
.eval::<i64>("return FauxScrollFrame_Update(TestScroll, 12, 5, 16)")
@@ -139,6 +141,7 @@ fn more_rows_than_fit_raise_the_bar_over_the_overflow_range() {
/// in five slots shows nothing, and there is no off-by-one that leaves a dead bar up.
#[test]
fn a_list_that_exactly_fits_shows_no_bar() {
benilla_formats::wow_data_or_skip!();
let s = harness();
let shown = s
.eval::<Option<i64>>("return FauxScrollFrame_Update(TestScroll, 5, 5, 16)")
@@ -176,6 +179,7 @@ fn a_list_that_exactly_fits_shows_no_bar() {
/// this test used to read a clamped offset immediately.
#[test]
fn a_shrinking_list_clamps_the_offset_back_into_range() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run("FauxScrollFrame_Update(TestScroll, 20, 5, 16)")
.unwrap();
@@ -238,6 +242,7 @@ fn a_shrinking_list_clamps_the_offset_back_into_range() {
/// it leaves (ref UIPanelTemplates.lua l.205-223).
#[test]
fn the_shrink_widen_tail_resizes_the_rows_and_the_highlight() {
benilla_formats::wow_data_or_skip!();
let s = harness();
s.run(
"FauxScrollFrame_Update(TestScroll, 12, 5, 16, \"TestRow\", 280, 300, TestHighlight, 276, 296)",
@@ -280,6 +285,7 @@ fn the_shrink_widen_tail_resizes_the_rows_and_the_highlight() {
/// value model in one test (pixels on the bar, rows in the offset, `floor(v/step + 0.5)` between).
#[test]
fn dragging_the_bar_steps_the_offset_by_rows_and_repaints() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run("TestRepaints = 0 function TestUpdate() TestRepaints = TestRepaints + 1 end")
.unwrap();
@@ -354,6 +360,7 @@ fn dragging_the_bar_steps_the_offset_by_rows_and_repaints() {
/// → `FauxScrollFrame_OnVerticalScroll`'s `floor(v / step + 0.5)`, the reference's path end to end.
#[test]
fn the_reference_on_vertical_scroll_path_runs_once_a_scroll_child_exists() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
load_inline(
&s,
@@ -416,6 +423,7 @@ fn the_reference_on_vertical_scroll_path_runs_once_a_scroll_child_exists() {
/// on that fallback, so the no-argument path is what this test drives.
#[test]
fn scrolling_edit_helpers_answer_bare_calls_from_a_handler() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
load_xml(&s, "ScrollTemplates.xml");
@@ -96,6 +96,7 @@ const ROW: &str = "BenillaOptionsFrameContainerBodyKeybindingsRow";
#[test]
fn the_page_is_an_options_category_with_the_collapsed_honest_tree() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
on_page(&mut s);
assert!(s
@@ -191,6 +192,7 @@ fn the_page_is_an_options_category_with_the_collapsed_honest_tree() {
#[test]
fn the_capture_flow_binds_steals_and_refuses_like_112() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
on_page(&mut s);
s.run(&format!("{ROW}1Header:Click()")).unwrap(); // expand Movement
@@ -300,6 +302,7 @@ fn the_capture_flow_binds_steals_and_refuses_like_112() {
#[test]
fn unbind_reset_and_the_live_commit_replace_okay_cancel() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
on_page(&mut s);
s.run(&format!("{ROW}1Header:Click()")).unwrap(); // expand Movement
@@ -348,6 +351,7 @@ fn unbind_reset_and_the_live_commit_replace_okay_cancel() {
#[test]
fn the_esc_ladder_closes_the_window_and_the_checkbox_switches_sets() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
on_page(&mut s);
// The ladder (the ESC binding's own body): the options rung hides the window — since 1008
@@ -400,6 +404,7 @@ fn the_esc_ladder_closes_the_window_and_the_checkbox_switches_sets() {
#[test]
fn search_surfaces_bindings_as_live_rows_under_the_redirect_head() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run(r#"BINDING_NAME_JUMP = "Jump""#).unwrap();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -463,6 +468,7 @@ fn search_surfaces_bindings_as_live_rows_under_the_redirect_head() {
#[test]
fn the_action_bar_abbreviation_is_the_refs_own_getbindingtext() {
benilla_formats::wow_data_or_skip!();
let s = harness();
// ref UIParent.lua:1819 transcribed (UIParent.xml): one modifier abbreviates…
assert_eq!(
@@ -519,6 +525,7 @@ fn the_action_bar_abbreviation_is_the_refs_own_getbindingtext() {
/// own -32 — anchoring it to body-right hung the bar on the window border.
#[test]
fn the_wheel_bubbles_from_the_rows_and_the_bar_rides_the_gutter() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
on_page(&mut s);
// Every section open: 100+ flat rows — the list overflows its 19 slots and the bar shows.
@@ -694,6 +701,7 @@ fn the_wheel_bubbles_from_the_rows_and_the_bar_rides_the_gutter() {
/// path a player actually finds a binding by — paints the row live with that key on its capsule.
#[test]
fn the_pet_lane_is_registered_under_the_action_bar_header() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
on_page(&mut s);
@@ -90,6 +90,7 @@ fn coin_and_two_items() -> LootState {
/// close button releases through OnHide → CloseLoot.
#[test]
fn shipped_loot_frame_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -283,6 +284,7 @@ fn shipped_loot_frame_drives_end_to_end() {
/// rare — but the branch must exist, and this proves it fires the right kit and only then.
#[test]
fn loot_empty_roll_plays_the_empty_open_kit() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -324,6 +326,7 @@ fn loot_empty_roll_plays_the_empty_open_kit() {
/// the next ordinary open resets the skull (the ref re-stamps TargetDead at every show, l.133).
#[test]
fn fishing_loot_open_plays_the_reel_and_swaps_the_portrait() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -375,6 +378,7 @@ fn fishing_loot_open_plays_the_reel_and_swaps_the_portrait() {
/// pager on page 2 — the real shipped XML driving the render, not just the arithmetic.
#[test]
fn shipped_loot_frame_pages_five_items() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -472,6 +476,7 @@ fn shipped_loot_frame_pages_five_items() {
/// the synthetic stand-in the merchant panel test uses.
#[test]
fn shipped_loot_pushed_to_center_by_merchant() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -549,6 +554,7 @@ fn shipped_loot_pushed_to_center_by_merchant() {
/// frames (`SetFrameStrata 0x76a470` is a whole-subtree cascade).
#[test]
fn the_loot_window_draws_over_the_party_frames() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -602,6 +608,7 @@ fn the_loot_window_draws_over_the_party_frames() {
/// unmodified click still loots — the regression that would otherwise ship silently.
#[test]
fn ctrl_and_shift_on_a_loot_row_preview_and_post_without_looting() {
benilla_formats::wow_data_or_skip!();
const WOOL_LINK: &str = "|cffffffff|Hitem:2589:0:0:0|h[Wool Cloth]|h|r";
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
@@ -750,6 +757,7 @@ fn ctrl_and_shift_on_a_loot_row_preview_and_post_without_looting() {
/// out and an epic asks. The misspelling is the reference's own (`LootFrame.lua:3`).
#[test]
fn shipped_loot_frame_hands_a_master_row_to_a_candidate() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -890,6 +898,7 @@ fn shipped_loot_frame_hands_a_master_row_to_a_candidate() {
/// the menu that comes out, which a densely-packed list could not produce.
#[test]
fn the_master_loot_menu_groups_raid_candidates_by_subgroup() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::{PartyState, RaidMemberInfo};
let mut s = UiScript::new().unwrap();
@@ -973,6 +982,7 @@ fn the_master_loot_menu_groups_raid_candidates_by_subgroup() {
/// take. Cancel sends nothing at all, and `LOOT_CLOSED` takes the dialog down with the window.
#[test]
fn the_loot_bind_confirm_raises_the_dialog_and_okay_calls_loot_slot() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -1060,6 +1070,7 @@ fn the_loot_bind_confirm_raises_the_dialog_and_okay_calls_loot_slot() {
/// would sit there doing nothing (decision 1744).
#[test]
fn a_row_click_takes_rather_than_continues() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -1098,6 +1109,7 @@ fn a_row_click_takes_rather_than_continues() {
/// `ITEM_QUALITY_COLORS[-1]`, the row `UIParent.lua`'s `for i = -1, 6` exists to provide.
#[test]
fn loot_row_awaiting_its_template_opens_clean() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -1216,6 +1228,7 @@ fn loot_row_awaiting_its_template_opens_clean() {
/// emitted quad, and the quad is what was missing.
#[test]
fn stock_loot_rows_wear_the_item_button_art_and_light_under_the_cursor() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::LOOT_UI {
@@ -48,6 +48,7 @@ fn frame_rect(quads: &[ExtractedQuad], w: f32, h: f32) -> benilla_ui::layout::Re
/// intent, and MERCHANT_CLOSED hiding it through HideUIPanel, vacating the left slot.
#[test]
fn shipped_merchant_frame_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The named virtual Font objects the re-skinned rows/title inherit through — loaded first at
@@ -338,6 +339,7 @@ fn shipped_merchant_frame_drives_end_to_end() {
/// → Hide() fires OnHide. Nothing queues at load (the frame is authored hidden="true").
#[test]
fn merchant_show_hide_plays_open_and_close_kits() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::MERCHANT_UI {
@@ -528,6 +530,7 @@ fn vendor_opens_and_closes_all_equipped_bags() {
/// doesn't clear the vendor it just re-opened to.
#[test]
fn merchant_switch_plays_close_then_open_and_queues_the_consumable_close() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::MERCHANT_UI {
@@ -571,6 +574,7 @@ fn merchant_switch_plays_close_then_open_and_queues_the_consumable_close() {
/// with no buy-price line (the price is on the row). Leaving the row hides it all.
#[test]
fn shipped_merchant_hover_scopes_highlight_and_anchors_item_tooltip() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The stock tooltip declares no size: it sizes from its lines through the font engine, as
@@ -824,6 +828,7 @@ fn shipped_merchant_hover_scopes_highlight_and_anchors_item_tooltip() {
/// queue the BuybackItem/RepairAllItems intents the app drains.
#[test]
fn merchant_tabs_drive_buyback_page_and_repair_pair() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::MERCHANT_UI {
@@ -954,6 +959,7 @@ fn merchant_tabs_drive_buyback_page_and_repair_pair() {
/// configuration the app does not have.
#[test]
fn merchant_tabs_fit_their_labels() {
benilla_formats::wow_data_or_skip!();
/// `2 * $parentLeft:GetWidth()` — the template's two 20-unit end slices.
const SIDES: f64 = 40.0;
let mut s = UiScript::new().unwrap();
@@ -992,6 +998,7 @@ fn merchant_tabs_fit_their_labels() {
/// the frame's `OnUpdate` (a `tick`) exactly as the app does, since the coin is re-armed per frame.
#[test]
fn shipped_merchant_frame_arms_the_buy_cursor_on_hover() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::UiCursorMode;
let mut s = UiScript::new().unwrap();
@@ -1088,6 +1095,7 @@ fn shipped_merchant_frame_arms_the_buy_cursor_on_hover() {
/// ellipsis. If this FAILS, the bug is structural and reproduced here; if it PASSES, it is live-only.
#[test]
fn trade_recipient_money_renders_the_digit_not_ellipsis() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in super::test_ui::MERCHANT_UI {
@@ -1146,6 +1154,7 @@ fn trade_recipient_money_renders_the_digit_not_ellipsis() {
/// right-button guard, l.332-333) a CTRL-held right-click buys nothing at all.
#[test]
fn ctrl_and_shift_on_a_vendor_row_preview_and_post_without_buying() {
benilla_formats::wow_data_or_skip!();
const WATER_LINK: &str = "|cffffffff|Hitem:159:0:0:0|h[Refreshing Spring Water]|h|r";
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
@@ -74,6 +74,7 @@ fn shown(s: &UiScript, frame: &str) -> (bool, bool, bool) {
/// `MoneyFrame_SetType("PLAYER")`.
#[test]
fn a_money_frame_loads_as_the_player_purse_and_splits_the_denominations() {
benilla_formats::wow_data_or_skip!();
let s = harness(12_345); // 1g 23s 45c
assert_eq!(
s.eval::<String>("return TestPurse.moneyType").unwrap(),
@@ -103,6 +104,7 @@ fn a_money_frame_loads_as_the_player_purse_and_splits_the_denominations() {
/// take the mouse (`canPickup`), and how they collapse.
#[test]
fn set_type_rewires_the_source_the_mouse_and_the_collapse() {
benilla_formats::wow_data_or_skip!();
let s = harness(12_345);
// PLAYER: canPickup, so all three coins are mouse-enabled.
@@ -151,6 +153,7 @@ fn set_type_rewires_the_source_the_mouse_and_the_collapse() {
/// STATIC does not, so the same amount is gold alone.
#[test]
fn showsmallercoins_is_what_keeps_the_zero_coins_visible() {
benilla_formats::wow_data_or_skip!();
let s = harness(50_000); // exactly 5g
assert_eq!(coins(&s, "TestPurse"), ("5".into(), "0".into(), "0".into()));
@@ -182,6 +185,7 @@ fn showsmallercoins_is_what_keeps_the_zero_coins_visible() {
/// frame's own width is recomputed from the coins that survived the collapse.
#[test]
fn update_paints_a_static_frame_by_name_and_resizes_it() {
benilla_formats::wow_data_or_skip!();
let s = harness(0);
s.run("this = TestPurse MoneyFrame_SetType(\"STATIC\") this = nil")
.unwrap();
@@ -222,6 +226,7 @@ fn update_paints_a_static_frame_by_name_and_resizes_it() {
/// constants (`MONEY_ICON_WIDTH` / `MONEY_BUTTON_SPACING`), selected purely by `this.small`.
#[test]
fn the_large_template_measures_with_the_nineteen_pixel_icon() {
benilla_formats::wow_data_or_skip!();
let s = harness(20_304);
// One 8px digit + the 19px icon — the same digit sum as the small template, over the other
// icon constant. That the two differ ONLY by the icon is what this test is for.
@@ -249,6 +254,7 @@ fn the_large_template_measures_with_the_nineteen_pixel_icon() {
/// it had.
#[test]
fn an_unknown_money_type_leaves_the_frame_on_the_one_it_had() {
benilla_formats::wow_data_or_skip!();
let s = harness(12_345);
s.run("this = TestPurse MoneyFrame_SetType(\"NOT_A_TYPE\") this = nil")
.unwrap();
@@ -269,6 +275,7 @@ fn an_unknown_money_type_leaves_the_frame_on_the_one_it_had() {
/// and not the icons" is actually observable.
#[test]
fn set_money_frame_color_recolours_the_digits_and_not_the_icons() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(12_345); // 1g 23s 45c — three distinguishable digit strings
s.run("SetMoneyFrameColor(\"TestPurse\", 1.0, 0.1, 0.1)")
.unwrap();
@@ -327,6 +334,7 @@ fn set_money_frame_color_recolours_the_digits_and_not_the_icons() {
/// their engine getters hold (`GetSendMailMoney`/`GetSendMailCOD`, built by 1962).
#[test]
fn the_money_type_table_carries_all_seven_reference_types() {
benilla_formats::wow_data_or_skip!();
let s = harness(12_345);
for t in [
"PLAYER",
@@ -84,6 +84,7 @@ fn show_bars(s: &UiScript, bars: &[u32]) {
/// about what a bar looks like once the player has asked for it.
#[test]
fn shipped_multibars_drive_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -267,6 +268,7 @@ fn shipped_multibars_drive_end_to_end() {
/// the whole frame again — the wow-re shapeshift-bar-api mechanism driven end to end.
#[test]
fn shipped_stance_bar_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ShapeshiftFormView;
let mut s = UiScript::new().unwrap();
@@ -461,6 +463,7 @@ fn shipped_stance_bar_drives_end_to_end() {
/// past it, so nothing else showed the bug.
#[test]
fn multibar_hover_renders_the_buttons_own_action() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for file in [
@@ -581,6 +584,7 @@ fn multibar_hover_renders_the_buttons_own_action() {
/// Since 1500 "hidden" is where they START rather than where they stay — see the toggle tests.
#[test]
fn the_vertical_multibars_exist_hidden_on_the_reference_pages() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_action_bar(&s);
load_xml(&s, "Interface\\FrameXML\\Cooldown.xml");
@@ -642,6 +646,7 @@ fn the_vertical_multibars_exist_hidden_on_the_reference_pages() {
/// hanging off a bar that is not on screen.
#[test]
fn every_extra_bar_stays_down_until_its_own_toggle_is_set() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -729,6 +734,7 @@ fn every_extra_bar_stays_down_until_its_own_toggle_is_set() {
/// frames drawing straight through a bar the player just asked for (decision 1499's screenshot).
#[test]
fn raising_a_bottom_bar_moves_the_managed_bottom_stack() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -793,6 +799,7 @@ fn raising_a_bottom_bar_moves_the_managed_bottom_stack() {
/// which was only ever right because visibility was static.
#[test]
fn viewable_action_bar_pages_follow_the_bar_toggles() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -859,6 +866,7 @@ fn viewable_action_bar_pages_follow_the_bar_toggles() {
/// can ever nest" stopped being true the moment this switch existed.
#[test]
fn the_grid_option_holds_the_extra_bars_empty_wells_open() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -964,6 +972,7 @@ fn the_grid_option_holds_the_extra_bars_empty_wells_open() {
/// payload, which is what the last two assertions here pin.
#[test]
fn a_held_payload_ghosts_the_empty_wells_it_opens() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -1107,6 +1116,7 @@ fn a_held_payload_ghosts_the_empty_wells_it_opens() {
/// list and each step below checks the packet it just caused.
#[test]
fn a_bar_toggle_sends_the_byte_its_globals_pack_to() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -1194,6 +1204,7 @@ fn a_bar_toggle_sends_the_byte_its_globals_pack_to() {
/// this file drives the real binding.
#[test]
fn the_shipped_setter_passes_exactly_four_arguments() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_action_bar(&s);
@@ -1245,6 +1256,7 @@ fn the_shipped_setter_passes_exactly_four_arguments() {
/// here rather than assumed.
#[test]
fn the_stance_bar_sits_where_the_pass_puts_it() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ShapeshiftFormView;
let mut s = UiScript::new().unwrap();
@@ -1317,6 +1329,7 @@ fn the_stance_bar_sits_where_the_pass_puts_it() {
/// set once at load.
#[test]
fn the_stance_shelf_follows_the_bottom_left_bar() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ShapeshiftFormView;
let mut s = UiScript::new().unwrap();
@@ -1438,6 +1451,7 @@ fn the_stance_shelf_follows_the_bottom_left_bar() {
/// exercised 2 and 3 forms and only ever read `IsShown()`, which is exactly why it shipped.
#[test]
fn the_stance_shelf_is_as_long_as_the_form_count() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ShapeshiftFormView;
let mut s = UiScript::new().unwrap();
@@ -1554,6 +1568,7 @@ fn the_stance_shelf_is_as_long_as_the_form_count() {
/// slot has an action.
#[test]
fn an_extra_bars_empty_well_keeps_its_bound_hotkey_label() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The real command set, so MULTIACTIONBAR1BUTTONn is bindable at all (it ships unbound —
@@ -1663,6 +1678,7 @@ fn an_extra_bars_empty_well_keeps_its_bound_hotkey_label() {
/// the end caps (atlas crops of `ShapeshiftBarEnds`).
#[test]
fn the_middle_strip_tiles_along_its_length_only() {
benilla_formats::wow_data_or_skip!();
use super::extract::tiling_axes;
use crate::ui_pass::UvRect;
use benilla_ui::script::{ShapeshiftFormView, TexCoords};
@@ -1745,6 +1761,7 @@ fn the_middle_strip_tiles_along_its_length_only() {
/// one the switch really carries and reading the checked ring.
#[test]
fn a_forms_state_change_leaves_the_shelf_down_over_the_raised_bar() {
benilla_formats::wow_data_or_skip!();
use crate::ui_shapeshift::{push_forms, FormsEdge, StanceMemory};
use benilla_ui::script::ShapeshiftFormView;
@@ -104,6 +104,7 @@ fn harness_on(mut s: UiScript) -> UiScript {
/// igMainMenuOption kit.
#[test]
fn the_menu_options_button_swaps_the_menu_for_the_options_window() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run("ShowUIPanel(GameMenuFrame)").unwrap();
let _ = s.take_sounds();
@@ -133,6 +134,7 @@ fn the_menu_options_button_swaps_the_menu_for_the_options_window() {
/// label — including the one key whose label differs from it (ActionBars → "Action Bars").
#[test]
fn controls_is_the_default_category_and_the_title_reads_it() {
benilla_formats::wow_data_or_skip!();
let s = harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -159,6 +161,7 @@ fn controls_is_the_default_category_and_the_title_reads_it() {
/// survives a close/reopen (the OnShow re-applies the last seat, not the default).
#[test]
fn clicking_a_row_moves_the_selection_and_the_page_title() {
benilla_formats::wow_data_or_skip!();
let s = harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -195,6 +198,7 @@ fn clicking_a_row_moves_the_selection_and_the_page_title() {
/// the guard itself is pinned by `the_defaults_button_is_armed_by_rows_not_by_a_category` below.
#[test]
fn the_close_button_hides_the_window() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -223,6 +227,7 @@ fn the_close_button_hides_the_window() {
/// (.196,.388,.8) while the locked gold stands — 1.14's LockHighlight guard.
#[test]
fn the_selected_row_wears_the_gold_wash_and_hover_runs_blue() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
// Pin the window at scale 1 so row rects and the pointer share coordinates (the fit
// clamp stays out of the way at 1024x768: both ratios sit above 1).
@@ -336,6 +341,7 @@ fn the_selected_row_wears_the_gold_wash_and_hover_runs_blue() {
/// would smear across the rope's inner half, which is exactly the mistake this pins.
#[test]
fn the_ground_dim_draws_over_the_tile_and_clear_of_the_rope() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run("ERA_WINDOW_SCALE = 1").unwrap();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -404,6 +410,7 @@ fn the_ground_dim_draws_over_the_tile_and_clear_of_the_rope() {
/// (the one-layout-law pin) with the page view restored.
#[test]
fn search_reflows_live_rows_and_restores_the_page() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowAudio:Click()")
@@ -499,6 +506,7 @@ fn search_reflows_live_rows_and_restores_the_page() {
/// first within the group — "master volume" seats Master above the token-matched children.
#[test]
fn the_phrase_match_outranks_its_words() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameSearchBox:SetText(\"master volume\")")
@@ -524,6 +532,7 @@ fn the_phrase_match_outranks_its_words() {
/// the box) and a no-match query (the section-header "no results" line, nothing chained).
#[test]
fn a_head_click_ends_the_search_and_a_miss_shows_no_results() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -584,6 +593,7 @@ fn a_head_click_ends_the_search_and_a_miss_shows_no_results() {
/// dragging (0250 §5's capture began), and the stepper buttons no longer exist.
#[test]
fn a_track_press_seats_the_thumb_and_keeps_dragging() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.set_cvar_host("MasterVolume", "0.1");
s.run("ERA_WINDOW_SCALE = 1").unwrap(); // pointer and rects share coordinates
@@ -660,6 +670,7 @@ fn a_track_press_seats_the_thumb_and_keeps_dragging() {
/// window has: a label/value line, and the groove still riding 3 units high of the row.
#[test]
fn a_slider_rows_readout_sits_on_its_labels_line() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
for (page, rows) in [
@@ -918,6 +929,7 @@ fn actionbars_harness() -> UiScript {
/// Leaving the page hides it and puts Defaults back to sleep.
#[test]
fn the_audio_page_reads_the_cvar_table_on_select() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("MusicVolume", "0.7");
s.set_cvar_host("EnableMusic", "0");
@@ -973,6 +985,7 @@ fn the_audio_page_reads_the_cvar_table_on_select() {
/// (the page reading the table) queues nothing.
#[test]
fn a_slider_move_snaps_and_writes_the_cvar() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowAudio:Click()")
@@ -1009,6 +1022,7 @@ fn a_slider_move_snaps_and_writes_the_cvar() {
/// 1.12 dependency holds: Enable All Sound off greys exactly the Enable Ambience row.
#[test]
fn the_checkbox_rows_write_flags_and_the_master_greys_ambience() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowAudio:Click()")
@@ -1059,6 +1073,7 @@ fn the_checkbox_rows_write_flags_and_the_master_greys_ambience() {
/// exactly two rows in `SoundOptionsFrame_UpdateDependencies` and never grew a third.
#[test]
fn the_background_sound_row_boots_off_and_writes_the_era_cvar() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowAudio:Click()")
@@ -1098,6 +1113,7 @@ fn the_background_sound_row_boots_off_and_writes_the_era_cvar() {
/// the era per-page reset, on the one page with rows.
#[test]
fn defaults_resets_the_audio_page_to_registered_defaults() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("MusicVolume", "0.9");
s.set_cvar_host("EnableMusic", "0");
@@ -1141,6 +1157,7 @@ fn defaults_resets_the_audio_page_to_registered_defaults() {
/// Audio's body takes over when clicked.
#[test]
fn the_graphics_page_reads_the_cvar_table_on_select() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("uiScale", "0.8");
s.set_cvar_host("farclip", "297");
@@ -1218,6 +1235,7 @@ fn the_graphics_page_reads_the_cvar_table_on_select() {
/// inverted by a later edit, and until this test there was no coverage of the row at all.
#[test]
fn the_display_mode_dropdown_maps_its_entries_to_the_gx_window_polarity() {
benilla_formats::wow_data_or_skip!();
const ROW: &str = "BenillaOptionsFrameContainerBodyGraphicsRowDisplayMode";
let mut s = audio_harness();
s.set_cvar_host("gxWindow", "0");
@@ -1279,6 +1297,7 @@ fn the_display_mode_dropdown_maps_its_entries_to_the_gx_window_polarity() {
/// and wgpu can — so the click commits, like every other checkbox here.
#[test]
fn the_vertical_sync_row_reads_and_writes_the_present_mode_cvar() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("gxVSync", "0");
let mut s = harness_on(s);
@@ -1342,6 +1361,7 @@ fn the_vertical_sync_row_reads_and_writes_the_present_mode_cvar() {
/// never did was move the slider, which is the only thing a player does. So this one drags.
#[test]
fn the_brightness_slider_writes_through_its_engine_pair_and_survives_a_reopen() {
benilla_formats::wow_data_or_skip!();
const ROW: &str = "BenillaOptionsFrameContainerBodyGraphicsRowBrightness";
let s = audio_harness();
let mut s = harness_on(s);
@@ -1407,6 +1427,7 @@ fn the_brightness_slider_writes_through_its_engine_pair_and_survives_a_reopen()
/// and writes nothing back (0959's out-of-range law).
#[test]
fn the_world_detail_slider_writes_the_cvar_and_the_readout_names_its_stop() {
benilla_formats::wow_data_or_skip!();
const ROW: &str = "BenillaOptionsFrameContainerBodyGraphicsRowWorldDetail";
let mut s = audio_harness();
s.set_cvar_host("WorldDetail", "0");
@@ -1572,6 +1593,7 @@ fn a_world_entry_leaves_the_saved_nameplate_setting_alone() {
#[test]
fn the_nameplates_page_toggles_the_unit_name_cvars() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowNameplates:Click()")
@@ -1660,6 +1682,7 @@ fn the_nameplates_page_toggles_the_unit_name_cvars() {
/// committed value clears the pending without a commit (era's IsModified).
#[test]
fn the_ui_scale_slider_defers_to_the_apply_button() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowGraphics:Click()")
@@ -1749,6 +1772,7 @@ fn the_ui_scale_slider_defers_to_the_apply_button() {
/// could not tell what it did.
#[test]
fn the_render_scale_row_shows_a_percentage_and_defers_to_apply() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("renderScale", "1");
let mut s = harness_on(s);
@@ -1820,6 +1844,7 @@ fn the_render_scale_row_shows_a_percentage_and_defers_to_apply() {
/// only when the window hides — the reopened window reads the committed truth.
#[test]
fn a_pending_ui_scale_survives_the_page_switch_and_dies_on_hide() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowGraphics:Click()")
@@ -1873,6 +1898,7 @@ fn a_pending_ui_scale_survives_the_page_switch_and_dies_on_hide() {
/// write is what moves the far-clip wall and the residency window together.
#[test]
fn the_terrain_distance_slider_snaps_to_the_1_12_grid_and_writes_live() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowGraphics:Click()")
@@ -1913,6 +1939,7 @@ fn the_terrain_distance_slider_snaps_to_the_1_12_grid_and_writes_live() {
/// dies with it (the default write supersedes what Apply would have committed).
#[test]
fn defaults_resets_the_graphics_page_to_registered_defaults() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("uiScale", "0.8");
s.set_cvar_host("farclip", "297");
@@ -1973,6 +2000,7 @@ fn defaults_resets_the_graphics_page_to_registered_defaults() {
/// `deselectOnClick` is "0", the 1.12 interface panel's own arm), the plain flags direct.
#[test]
fn the_controls_page_reads_flags_with_the_sticky_inversion() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("deselectOnClick", "0");
s.set_cvar_host("autoLootDefault", "1");
@@ -2017,6 +2045,7 @@ fn the_controls_page_reads_flags_with_the_sticky_inversion() {
/// Sticky Targeting writes the CVar inverted both ways.
#[test]
fn the_controls_checkboxes_write_flags_with_the_interface_panel_kit() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
assert!(
@@ -2061,6 +2090,7 @@ fn the_controls_checkboxes_write_flags_with_the_interface_panel_kit() {
/// autoLootDefault "0"), the rows follow, and only the moved values queue.
#[test]
fn defaults_resets_the_controls_page_to_registered_defaults() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("deselectOnClick", "0");
s.set_cvar_host("autoLootDefault", "1");
@@ -2113,6 +2143,7 @@ fn settle(s: &mut UiScript) {
/// rect until scrolled to.
#[test]
fn a_broad_search_scrolls_the_page_instead_of_overflowing_it() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
// On NAMEPLATES, not the default Controls page: since 2180 seated the four camera toggles and
@@ -2259,6 +2290,7 @@ fn a_broad_search_scrolls_the_page_instead_of_overflowing_it() {
/// keybindings_tests, where the harness has a real binding registry to overflow the list with.)
#[test]
fn the_page_scroll_bar_wears_the_trough_with_its_arrows_in_the_sockets() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameSearchBox:SetText(\"e\")")
@@ -2390,6 +2422,7 @@ fn scroll_into_view(s: &mut UiScript, frame: &str) {
/// property the 1.12 panel's own `getglobal("OPTION_TOOLTIP_"..key)` lookup buys.
#[test]
fn a_hovered_row_raises_its_1_12_description_on_the_era_seat() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
// The stock tooltip declares no size: it sizes from its lines through the font engine, as
// the client's does (1968) — a harness that reads its rect needs one; the fixed-width
@@ -2466,6 +2499,7 @@ fn a_hovered_row_raises_its_1_12_description_on_the_era_seat() {
/// hover driven without the outgoing row's OnLeave (1054) must still put the neighbour away.
#[test]
fn a_row_with_no_1_12_string_raises_no_plate() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("OPTION_TOOLTIP_GAMEFIELD_DESELECT = \"Sticky's own description.\"")
.unwrap();
@@ -2650,6 +2684,7 @@ fn every_row_tooltip_key_resolves_in_the_real_global_strings() {
/// line instead of a plate, which no key-mapping check would ever see.
#[test]
fn every_flavor_of_row_raises_its_plate_from_the_page_it_lives_on() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
// Stand-in strings for every key the rows name (the real texts are the data test's job).
s.run(
@@ -2749,6 +2784,7 @@ fn every_flavor_of_row_raises_its_plate_from_the_page_it_lives_on() {
/// effect is the per-type `show` flag and the scroll function.
#[test]
fn the_combat_page_writes_saved_variable_globals_and_applies_them() {
benilla_formats::wow_data_or_skip!();
let mut s = combat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowCombat:Click()")
@@ -2846,6 +2882,7 @@ fn the_combat_page_writes_saved_variable_globals_and_applies_them() {
/// wake is the master's own click rather than the load: the same rule, entered from the other end.
#[test]
fn the_combat_master_greys_the_family_and_combo_points_is_class_gated() {
benilla_formats::wow_data_or_skip!();
let mut s = combat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowCombat:Click()")
@@ -2903,6 +2940,7 @@ fn the_combat_master_greys_the_family_and_combo_points_is_class_gated() {
/// Defaults lands a player back on a stock 1.12 combat-text family.
#[test]
fn defaults_resets_the_combat_page_to_the_shipped_assignments() {
benilla_formats::wow_data_or_skip!();
let s = combat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowCombat:Click()")
@@ -2969,6 +3007,7 @@ fn defaults_resets_the_combat_page_to_the_shipped_assignments() {
/// text comes up with the player's choice rather than CombatText.xml's shipped default.
#[test]
fn what_the_combat_page_writes_survives_a_restart() {
benilla_formats::wow_data_or_skip!();
let s = combat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowCombat:Click()")
@@ -3043,6 +3082,7 @@ fn what_the_combat_page_writes_survives_a_restart() {
/// (including the shift-click that deliberately still works); this owns the wire between them.
#[test]
fn the_action_bars_page_locks_the_real_bar() {
benilla_formats::wow_data_or_skip!();
let mut s = actionbars_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowActionBars:Click()")
@@ -3107,6 +3147,7 @@ fn the_action_bars_page_locks_the_real_bar() {
/// questgiver's instant-text arm, live on the very next show.
#[test]
fn the_interface_page_writes_the_three_stock_globals() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3180,6 +3221,7 @@ fn the_interface_page_writes_the_three_stock_globals() {
/// * the picker's five values are the reference's own strings, in its own order.
#[test]
fn the_target_of_target_rows_gate_each_other_and_write_their_globals() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3287,6 +3329,7 @@ fn the_target_of_target_rows_gate_each_other_and_write_their_globals() {
/// a second click inside that round trip would otherwise compute the wrong direction.
#[test]
fn the_equipment_display_rows_read_and_write_through_the_api_not_a_store() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3365,6 +3408,7 @@ fn the_equipment_display_rows_read_and_write_through_the_api_not_a_store() {
/// tutorial.
#[test]
fn show_tutorials_reads_the_bank_and_writes_through_clear_and_reset() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
// A bank with unacknowledged bits: tutorials are enabled.
s.set_tutorial_bank(Some(vec![0x00; 32]));
@@ -3423,6 +3467,7 @@ fn show_tutorials_reads_the_bank_and_writes_through_clear_and_reset() {
/// has dismissed — an option that silently undoes hours of play.
#[test]
fn a_no_op_write_does_not_re_arm_the_tutorials() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
s.set_tutorial_bank(Some(vec![0x00; 32])); // enabled
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -3447,6 +3492,7 @@ fn a_no_op_write_does_not_re_arm_the_tutorials() {
/// no-op default must not queue a packet that would turn the preference on its head.
#[test]
fn defaults_sends_a_flip_only_for_the_row_that_moved() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3486,6 +3532,7 @@ fn defaults_sends_a_flip_only_for_the_row_that_moved() {
/// property worth holding: the capture follows the definer wherever the definer stands.
#[test]
fn defaults_on_the_interface_page_restores_the_definers_own_assignment() {
benilla_formats::wow_data_or_skip!();
let s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3524,6 +3571,7 @@ fn defaults_on_the_interface_page_restores_the_definers_own_assignment() {
/// player's choice instead of QuestLogFrame.xml's shipped one.
#[test]
fn what_the_interface_page_writes_survives_a_restart() {
benilla_formats::wow_data_or_skip!();
let s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3581,6 +3629,7 @@ fn what_the_interface_page_writes_survives_a_restart() {
/// other one: the registrations were never armed at all, and only the walk can arm them.
#[test]
fn a_saved_switch_with_a_side_effect_is_applied_when_the_variables_land() {
benilla_formats::wow_data_or_skip!();
let mut s = combat_harness();
// What the saved chunk does, verbatim: assign over the file-scope default, then the event.
s.run("SHOW_COMBAT_TEXT = \"1\"").unwrap();
@@ -3618,6 +3667,7 @@ fn a_saved_switch_with_a_side_effect_is_applied_when_the_variables_land() {
/// only which of them a fresh install opens on.
#[test]
fn the_buff_durations_row_repitches_the_bar_and_the_pitch_survives_a_restart() {
benilla_formats::wow_data_or_skip!();
let gap = |s: &mut UiScript| -> f64 {
s.resolve();
s.eval::<f64>("return BuffButton0:GetBottom() - BuffButton8:GetTop()")
@@ -3689,6 +3739,7 @@ fn the_buff_durations_row_repitches_the_bar_and_the_pitch_survives_a_restart() {
/// rather than through a stand-in page — there is no longer one to borrow.
#[test]
fn the_defaults_button_is_armed_by_rows_not_by_a_category() {
benilla_formats::wow_data_or_skip!();
let s = harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -3747,6 +3798,7 @@ fn the_defaults_button_is_armed_by_rows_not_by_a_category() {
/// (l.337). So a fresh client shows the box **unchecked**, and ticking it turns the filter off.
#[test]
fn the_disable_spam_filter_row_is_inverted() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowChat:Click()")
@@ -3788,6 +3840,7 @@ fn the_disable_spam_filter_row_is_inverted() {
/// column-C chain rather than joining the Chat page.
#[test]
fn the_profanity_filter_row_sits_on_the_interface_page_and_writes_its_cvar() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowInterface:Click()")
@@ -3814,6 +3867,7 @@ fn the_profanity_filter_row_sits_on_the_interface_page_and_writes_its_cvar() {
#[test]
fn the_chat_page_toggles_the_chat_bubble_cvars() {
benilla_formats::wow_data_or_skip!();
// No host override: since 1804 the registered pair IS bubbles-on / party-off, so the page's
// read below is of the shipped table rather than of a value this test planted.
let mut s = harness_on(audio_harness());
@@ -3887,6 +3941,7 @@ fn the_chat_page_toggles_the_chat_bubble_cvars() {
/// 1136's rule for when a row needs one, seen from both sides on one page).
#[test]
fn the_chat_page_writes_the_hover_delay_global_and_the_loot_spam_cvar() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowChat:Click()")
@@ -3970,6 +4025,7 @@ fn the_chat_page_writes_the_hover_delay_global_and_the_loot_spam_cvar() {
/// fresh VM has re-run `ChatFrame.xml`'s file-scope `"0"`.
#[test]
fn a_saved_hover_delay_is_applied_when_the_variables_land() {
benilla_formats::wow_data_or_skip!();
let mut s = chat_harness();
// What the saved-variables chunk does: assign the global, then the window's VARIABLES_LOADED.
s.run("REMOVE_CHAT_DELAY = \"1\"").unwrap();
@@ -4003,6 +4059,7 @@ fn a_saved_hover_delay_is_applied_when_the_variables_land() {
/// here is the uppercase display name and not the CVar's own spelling.
#[test]
fn the_status_bar_text_row_pins_the_numerals_the_moment_it_is_clicked() {
benilla_formats::wow_data_or_skip!();
let mut s = interface_harness();
// The bar needs a real span before it decides anything about its numerals (its update bails
// on valueMax == 0 and hides the strip instead).
@@ -4060,6 +4117,7 @@ fn the_status_bar_text_row_pins_the_numerals_the_moment_it_is_clicked() {
/// `LookConfig::sensitivity`.
#[test]
fn the_mouse_sensitivity_slider_snaps_to_the_reference_step() {
benilla_formats::wow_data_or_skip!();
let mut s = audio_harness();
s.set_cvar_host("mousespeed", "1.25");
let mut s = harness_on(s);
@@ -4118,6 +4176,7 @@ fn the_mouse_sensitivity_slider_snaps_to_the_reference_step() {
/// rather than down it.
#[test]
fn the_max_camera_distance_slider_stores_a_factor_and_reads_out_yards() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowControls:Click()")
@@ -4196,6 +4255,7 @@ fn the_max_camera_distance_slider_stores_a_factor_and_reads_out_yards() {
/// nearest "Always", and the plate follows the SELECTION the way that dropdown's own does.
#[test]
fn the_camera_following_style_dropdown_carries_the_engine_enum_and_plate() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowControls:Click()")
@@ -4325,6 +4385,7 @@ fn the_camera_following_style_dropdown_carries_the_engine_enum_and_plate() {
/// `MultiActionBar_Update`, which reaches `UIParent_ManageFramePositions`.
#[test]
fn the_action_bars_page_toggles_the_real_bars() {
benilla_formats::wow_data_or_skip!();
let mut s = actionbars_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowActionBars:Click()")
@@ -4503,6 +4564,7 @@ impl benilla_ui::script::TextMeasure for SteppedFont {
/// (64 and 76 here); on the show, behind the window's own OnShow, they measure at the drawn 0.78.
#[test]
fn the_two_option_tabs_fit_their_labels_at_the_drawn_scale() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_text_measurer(Box::new(SteppedFont(6.0)));
let mut s = harness_on(s);
@@ -4573,6 +4635,7 @@ fn the_two_option_tabs_fit_their_labels_at_the_drawn_scale() {
/// seated `AtlasMeasurer` for certain, whatever order the boot took.
#[test]
fn without_a_seated_measurer_the_same_fit_reads_zero() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(UiScript::new().unwrap());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.resolve();
@@ -4649,6 +4712,7 @@ const UNBACKED_REFERENCE_CVARS: &[(&str, &str)] = &[
/// us and shipping one was somebody re-reading a decision record from months ago.
#[test]
fn every_cvar_the_reference_table_names_is_registered_or_listed_with_its_blocker() {
benilla_formats::wow_data_or_skip!();
let s = harness();
// The table as the file declares it: `name` (the display key) -> its `cvar`, if it has one.
// **BOTH of the reference's option tables** (2115). The checkbuttons were the whole census
@@ -4733,6 +4797,7 @@ fn every_cvar_the_reference_table_names_is_registered_or_listed_with_its_blocker
/// player change and benilla does not, and it costs a stated reason at the row.
#[test]
fn every_registered_reference_cvar_has_a_row_on_our_own_window() {
benilla_formats::wow_data_or_skip!();
let s = harness();
// Both of the reference's own option tables, exactly as the registration census reads them.
@@ -4839,6 +4904,7 @@ const UNREACHABLE_REFERENCE_CVARS: &[(&str, &str)] = &[];
/// `cameraWaterCollision` a player who wants them OFF had no way to say so at all.
#[test]
fn the_four_camera_toggles_read_their_shipped_defaults_and_write_on_the_click() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowControls:Click()")
@@ -4887,6 +4953,7 @@ fn the_four_camera_toggles_read_their_shipped_defaults_and_write_on_the_click()
/// kept floating.
#[test]
fn the_pet_damage_box_and_the_look_slider_each_write_their_unnamed_twin() {
benilla_formats::wow_data_or_skip!();
let mut s = combat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowCombat:Click()")
@@ -4939,6 +5006,7 @@ fn the_pet_damage_box_and_the_look_slider_each_write_their_unnamed_twin() {
/// combat text also turns off damage numbers, which is the opposite of what `CombatDamage` does.
#[test]
fn show_target_damage_greys_its_own_pair_and_the_floating_text_master_leaves_it_alone() {
benilla_formats::wow_data_or_skip!();
let mut s = combat_harness();
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
s.run("BenillaOptionsFrameCategoryListRowCombat:Click()")
@@ -4990,6 +5058,7 @@ fn show_target_damage_greys_its_own_pair_and_the_floating_text_master_leaves_it_
/// (l.715-719 — there is no follow to set a speed for).
#[test]
fn the_guild_line_greys_with_player_names_and_the_follow_speed_with_the_style() {
benilla_formats::wow_data_or_skip!();
let mut s = harness_on(audio_harness());
s.run("ShowUIPanel(BenillaOptionsFrame)").unwrap();
@@ -61,6 +61,7 @@ fn bar_quad(s: &mut UiScript) -> (usize, [f32; 4], Option<[f32; 4]>) {
/// grey column into the slot rather than floating it above.
#[test]
fn the_meter_sits_in_the_bar_recess_the_reference_leaves_for_it() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
s.resolve();
@@ -90,6 +91,7 @@ fn the_meter_sits_in_the_bar_recess_the_reference_leaves_for_it() {
/// would paint over the metal surround that frames the 10-wide slot.
#[test]
fn the_meter_paints_under_the_bar_art_it_shows_through() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
let (meter, _, _) = bar_quad(&mut s);
@@ -128,6 +130,7 @@ fn the_meter_paints_under_the_bar_art_it_shows_through() {
/// — color is the entire readout, which is why this test asserts the tint and the rect separately.
#[test]
fn the_meter_tints_by_latency_on_the_reference_thresholds() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
// The first tick polls immediately (updateInterval starts at 0), so an unmeasured connection
@@ -197,6 +200,7 @@ fn poll_beat(s: &mut UiScript) {
/// arithmetic on them can't error.
#[test]
fn get_net_stats_reports_the_pushed_latency() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
assert_eq!(
s.eval::<(f64, f64, f64)>("return GetNetStats()").unwrap(),
@@ -229,6 +233,7 @@ fn get_net_stats_reports_the_pushed_latency() {
/// tooltip that is quietly one line short.
#[test]
fn hovering_the_meter_shows_the_live_latency() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[r"Interface\FrameXML\UIParent.xml"]);
s.set_latency_ms(Some(42));
// 1.12 ships detailed tips ON — `SHOW_NEWBIE_TIPS = "1"` is UIOptionsFrame_Init's (ref
@@ -309,6 +314,7 @@ fn hovering_the_meter_shows_the_live_latency() {
/// the bar's end-to-end test pins, kept here so the two move together.
#[test]
fn the_meter_adds_its_two_frames_to_the_bar() {
benilla_formats::wow_data_or_skip!();
let s = harness(&[]);
for name in [
"MainMenuBarPerformanceBarFrame",
@@ -128,6 +128,7 @@ fn texture_rect(
/// painting what they should, and hidden again when the pet goes.
#[test]
fn the_shipped_pet_bar_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -249,6 +250,7 @@ fn the_shipped_pet_bar_drives_end_to_end() {
/// `IsPetAttackActive` fork — the one branch that makes the Attack button a toggle).
#[test]
fn clicks_route_through_the_attack_toggle_fork() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -310,6 +312,7 @@ fn clicks_route_through_the_attack_toggle_fork() {
/// director; decision 1027 diverged from it and 1030 put it back.
#[test]
fn a_click_drops_the_ring_and_the_repaint_restores_it() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -353,6 +356,7 @@ fn a_click_drops_the_ring_and_the_repaint_restores_it() {
/// looks like, and collapsing it to "hide the bar" would lose the state entirely.
#[test]
fn a_disabled_bar_greys_rather_than_hides() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -429,6 +433,7 @@ fn pet_bar_row(with_multibar: bool) -> (usize, f32) {
/// is the base position, so a pass that never fired would leave it exactly where the bug was.
#[test]
fn the_pet_bar_rises_and_sheds_its_shelf_over_the_bottom_left_bar() {
benilla_formats::wow_data_or_skip!();
let (low_shelf, low_top) = pet_bar_row(false);
let (high_shelf, high_top) = pet_bar_row(true);
@@ -454,6 +459,7 @@ fn the_pet_bar_rises_and_sheds_its_shelf_over_the_bottom_left_bar() {
/// exists, and the reason an unnamed button's `Hide()` is conditional.
#[test]
fn dragging_a_pet_spell_between_slots_moves_it_through_the_shipped_handlers() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -507,6 +513,7 @@ fn dragging_a_pet_spell_between_slots_moves_it_through_the_shipped_handlers() {
/// reference leaves it (l.253-255).
#[test]
fn the_lock_stops_the_pet_bar_drag_but_not_its_shift_click() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -556,6 +563,7 @@ fn the_lock_stops_the_pet_bar_drag_but_not_its_shift_click() {
/// puts shift above the left/right split, so it never toggles autocast by accident.
#[test]
fn shift_clicking_a_pet_button_picks_it_up_rather_than_casting() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -598,6 +606,7 @@ fn shift_clicking_a_pet_button_picks_it_up_rather_than_casting() {
/// one worth pinning. Read as "ordinary pets differ from clicks", this test would be lying.
#[test]
fn the_keybind_pair_pushes_and_casts_without_the_clicks_forks() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_pet_bar(&s);
@@ -705,6 +714,7 @@ fn tooltip_line1(s: &UiScript) -> String {
/// which is what the stock `MicroButtonTooltipText` — the same idea, the other shape — produces).
#[test]
fn token_tooltips_name_their_binding_and_pet_spells_do_not() {
benilla_formats::wow_data_or_skip!();
let mut s = hovered_pet_bar();
s.mouse_move(ATTACK_BUTTON.0, ATTACK_BUTTON.1);
@@ -742,6 +752,7 @@ fn token_tooltips_name_their_binding_and_pet_spells_do_not() {
/// deliberate unbind to see, which is exactly what this does.
#[test]
fn an_unbound_token_row_renders_the_references_empty_parentheses() {
benilla_formats::wow_data_or_skip!();
let mut s = hovered_pet_bar();
s.run(r#"SetBinding("CTRL-1")"#).unwrap();
assert!(
@@ -770,6 +781,7 @@ fn an_unbound_token_row_renders_the_references_empty_parentheses() {
/// at all because these Lua sites read it (the honest-tree rule's live-consumer half).
#[test]
fn ubertooltips_off_takes_the_spells_through_the_token_branch_and_moves_the_plate() {
benilla_formats::wow_data_or_skip!();
let mut s = hovered_pet_bar();
s.set_cvar_engine("UberTooltips", "0");
@@ -132,6 +132,7 @@ fn pet_buff(spell_id: u32, name: &str, count: u8) -> AuraState {
/// `UNIT_PET` is the only wire either edge has, which is why it carries them both.
#[test]
fn the_pet_frame_appears_on_a_summon_and_leaves_on_a_dismiss() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
assert!(
@@ -169,6 +170,7 @@ fn the_pet_frame_appears_on_a_summon_and_leaves_on_a_dismiss() {
/// this drives each event with the WRONG token and asserts nothing moved.
#[test]
fn the_frame_answers_only_the_events_that_name_its_own_unit() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 0)));
s.fire_event("UNIT_PET", vec![ScriptValue::Str("player".into())]);
@@ -226,6 +228,7 @@ fn the_frame_answers_only_the_events_that_name_its_own_unit() {
/// because it means the bar comes back only through a value change, never through `PetFrame.lua`.
#[test]
fn a_powerless_pet_wears_the_no_mana_plate() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
const PLAIN: &str = "Interface\\TargetingFrame\\UI-SmallTargetingFrame";
@@ -262,6 +265,7 @@ fn a_powerless_pet_wears_the_no_mana_plate() {
/// repaints the rest of the frame (they are the ref's own early returns).
#[test]
fn the_attack_overlay_follows_its_own_two_events() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 0)));
s.fire_event("UNIT_PET", vec![ScriptValue::Str("player".into())]);
@@ -318,6 +322,7 @@ fn the_attack_overlay_follows_its_own_two_events() {
/// house at all; the old assertion was reading a FontString our transcription invented.
#[test]
fn the_debuff_row_fills_from_the_pets_own_auras() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 0)));
s.fire_event("UNIT_PET", vec![ScriptValue::Str("player".into())]);
@@ -366,6 +371,7 @@ fn the_debuff_row_fills_from_the_pets_own_auras() {
/// the app answers in `target::click::target_unit_requests`' `"pet"` arm.
#[test]
fn left_clicking_the_pet_frame_targets_it() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 0)));
s.fire_event("UNIT_PET", vec![ScriptValue::Str("player".into())]);
@@ -395,6 +401,7 @@ fn left_clicking_the_pet_frame_targets_it() {
/// a queued token and no error.
#[test]
fn every_leg_of_the_pet_frame_click_reaches_a_live_binding() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 0)));
s.fire_event("UNIT_PET", vec![ScriptValue::Str("player".into())]);
@@ -489,6 +496,7 @@ fn stats(
/// branch ran** — `PET_HAPPINESS2` can only come from the `happiness == 2` arm.
#[test]
fn the_happiness_icon_shows_per_bucket_and_hides_for_a_non_hunter_pet() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
declare_happiness_strings(&s);
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 2)));
@@ -536,6 +544,7 @@ fn the_happiness_icon_shows_per_bucket_and_hides_for_a_non_hunter_pet() {
/// the trap wow-re calls out by name, tested where it would actually bite: in the frame.
#[test]
fn happiness_bucket_zero_keeps_the_icon_showing() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
declare_happiness_strings(&s);
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 2)));
@@ -560,6 +569,7 @@ fn happiness_bucket_zero_keeps_the_icon_showing() {
/// frame-wide update, so a happiness tick must not be a full repaint.
#[test]
fn unit_happiness_repaints_only_the_icon() {
benilla_formats::wow_data_or_skip!();
let mut s = load_pet_frame();
declare_happiness_strings(&s);
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 2)));
@@ -600,6 +610,7 @@ fn unit_happiness_repaints_only_the_icon() {
/// the mana bar and simply covers the rail.
#[test]
fn the_pet_art_paints_over_the_bars() {
benilla_formats::wow_data_or_skip!();
let s = load_pet_frame();
let level: (i64, i64, i64) = s
.eval(
@@ -42,6 +42,7 @@ fn confirm(s: &mut UiScript, who: &str, quest: &str) {
/// right at a glance.
#[test]
fn the_confirm_names_the_player_first_and_the_quest_second() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
assert!(
@@ -59,6 +60,7 @@ fn the_confirm_names_the_player_first_and_the_quest_second() {
/// Yes queues exactly one `ConfirmAcceptQuest()` and closes the popup.
#[test]
fn yes_answers_once() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
s.run("StaticPopup_OnClick(StaticPopup1, 1)").unwrap();
@@ -75,6 +77,7 @@ fn yes_answers_once() {
/// latch is cleared by the next thing that touches it. Same for ESC.
#[test]
fn no_and_escape_send_nothing() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
@@ -92,6 +95,7 @@ fn no_and_escape_send_nothing() {
/// stale popup would send the wrong quest id.
#[test]
fn a_second_confirm_replaces_the_first() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
confirm(&mut s, "Jaina", "Deeper Still");
@@ -15,6 +15,7 @@ use super::test_ui::load_ui as load_xml;
/// Same pair as gossip — questgiver and gossip are the same "list" surface to the client.
#[test]
fn questgiver_show_hide_plays_open_and_close_kits() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -86,6 +87,7 @@ fn questgiver_show_hide_plays_open_and_close_kits() {
/// stays open is silent).
#[test]
fn panel_events_show_exactly_one_child_panel_and_hide_the_others() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -185,6 +187,7 @@ fn panel_events_show_exactly_one_child_panel_and_hide_the_others() {
/// have and a split the ref's own function cannot be written against.
#[test]
fn detail_panel_reward_grid_follows_the_refs_two_per_row_layout() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -288,6 +291,7 @@ fn detail_panel_reward_grid_follows_the_refs_two_per_row_layout() {
/// the highlight and arms `GetQuestReward`'s 1-based→0-based conversion.
#[test]
fn reward_panel_choice_click_selects_and_completes_with_zero_based_index() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -368,6 +372,7 @@ fn reward_panel_choice_click_selects_and_completes_with_zero_based_index() {
/// v1 layout never carried) — a plain client-side close, no `DeclineQuest()` call (ref l.748-752).
#[test]
fn greeting_goodbye_button_closes_the_window() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -426,6 +431,7 @@ fn greeting_goodbye_button_closes_the_window() {
/// The write-on arm is `write_on_still_fades_when_instant_text_is_off`'s subject.
#[test]
fn detail_panel_action_buttons_resolve_to_real_onscreen_rects() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -555,6 +561,7 @@ fn detail_panel_action_buttons_resolve_to_real_onscreen_rects() {
/// objectives/rewards block FADES in over QUESTINFO_FADE_IN rather than snapping.
#[test]
fn write_on_still_fades_when_instant_text_is_off() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -633,6 +640,7 @@ fn write_on_still_fades_when_instant_text_is_off() {
/// update it too (the 0112-era capture showed a permanently blank bar).
#[test]
fn npc_name_reaches_the_title_bar_on_open_and_on_refresh() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -713,6 +721,7 @@ fn npc_name_reaches_the_title_bar_on_open_and_on_refresh() {
/// 6 px/char × 14 px/line measure fake as the gossip row test.
#[test]
fn greeting_panel_title_rows_grow_to_their_wrapped_titles() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// Stock sizes each row at show time — `SetHeight(GetTextHeight() + 2)` right after SetText
@@ -818,6 +827,7 @@ fn greeting_panel_title_rows_grow_to_their_wrapped_titles() {
/// it closes the giver window, exactly as it does in play.
#[test]
fn reward_rows_preview_and_post_without_selecting_the_choice() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -202,6 +202,7 @@ fn seat_a_hunter(root: &Path) -> UiScript {
/// again even if it left a different error behind.
#[test]
fn quiver_publishes_its_global_functions_for_a_hunter() {
benilla_formats::wow_data_or_skip!();
let root = quiver_or_skip!();
let mut s = seat_a_hunter(&root);
@@ -249,6 +250,7 @@ fn quiver_publishes_its_global_functions_for_a_hunter() {
/// the test that names a *new* wall the moment one appears, instead of reporting a nil field.
#[test]
fn quiver_survives_a_hunter_session_start_without_raising() {
benilla_formats::wow_data_or_skip!();
let root = quiver_or_skip!();
let mut s = seat_a_hunter(&root);
assert!(load_addon_files(&s, &root, "Quiver").is_empty());
@@ -322,6 +324,7 @@ fn shot_state(s: &mut UiScript) -> (bool, bool, f64, f64) {
/// forever for a shot it is never told about — which is a bar pinned at 100%.
#[test]
fn auto_shot_bar_saturates_when_no_ammo_lock_event_ever_arrives() {
benilla_formats::wow_data_or_skip!();
let root = quiver_or_skip!();
let mut s = seat_a_hunter(&root);
seat_a_bow(&mut s);
@@ -353,6 +356,7 @@ fn auto_shot_bar_saturates_when_no_ammo_lock_event_ever_arrives() {
/// the addon, on `SetWidth`, or on the standing-still check.
#[test]
fn auto_shot_bar_drains_the_moment_an_ammo_lock_event_arrives() {
benilla_formats::wow_data_or_skip!();
let root = quiver_or_skip!();
let mut s = seat_a_hunter(&root);
seat_a_bow(&mut s);
@@ -436,6 +440,7 @@ fn draws(s: &mut UiScript, leaf: &str) -> bool {
/// dependency of ours, and a break in any of them shows up here as a missing quad.
#[test]
fn aspect_tracker_draws_the_icon_for_an_active_aspect() {
benilla_formats::wow_data_or_skip!();
let root = quiver_or_skip!();
let mut s = seat_a_hunter(&root);
assert!(load_addon_files(&s, &root, "Quiver").is_empty());
@@ -477,6 +482,7 @@ fn aspect_tracker_draws_the_icon_for_an_active_aspect() {
/// Hawk is up" and failed — the addon was right and the test was wrong.
#[test]
fn the_hawk_reminder_is_suppressed_only_once_frames_are_locked() {
benilla_formats::wow_data_or_skip!();
let root = quiver_or_skip!();
let mut s = seat_a_hunter(&root);
assert!(load_addon_files(&s, &root, "Quiver").is_empty());
@@ -516,6 +522,7 @@ fn the_hawk_reminder_is_suppressed_only_once_frames_are_locked() {
/// spent**, which is the half that was missing (decision 1509).
#[test]
fn spending_an_arrow_starts_quivers_reload_through_the_real_item_feed() {
benilla_formats::wow_data_or_skip!();
use crate::ui_items::feed::{apply_container_source, FeedMemory};
use benilla_ui::script::{ContainerSlot, ContainerState};
use std::collections::HashMap;
@@ -156,6 +156,7 @@ fn tooltip_change_costs_a_whole_ui_solve() {
/// about the second solve is tooltip-sized, and nothing about it is cheap.
#[test]
fn a_tooltip_content_change_costs_exactly_one_layout_solve() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
install_changing_tooltip(&s, "GateOwner", "gate_change");
@@ -215,6 +216,7 @@ fn a_tooltip_content_change_costs_exactly_one_layout_solve() {
/// backtracing the touch sites on a live run.
#[test]
fn the_settled_shipped_ui_costs_no_gate_walk_on_a_quiet_frame() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
// Ticked frames, so the shipped UI's own OnUpdate handlers run — the whole point (see
// `app_frame_ticked`). A couple of frames of grace first: `settled_default_ui` stops as soon
@@ -272,6 +274,7 @@ fn the_settled_shipped_ui_costs_no_gate_walk_on_a_quiet_frame() {
/// walks was exactly that, so the old counter under-reported the bug by a third.
#[test]
fn a_region_moving_every_frame_costs_one_gate_walk_on_the_shipped_ui() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
s.run(
r#"
@@ -356,6 +359,7 @@ fn a_region_moving_every_frame_costs_one_gate_walk_on_the_shipped_ui() {
/// everything else, and only a ticked frame can see that.
#[test]
fn a_region_moving_every_frame_costs_no_graph_derivation_on_the_shipped_ui() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
s.run(
r#"
@@ -424,6 +428,7 @@ fn a_region_moving_every_frame_costs_no_graph_derivation_on_the_shipped_ui() {
/// skipped as well as the ones it resolved.
#[test]
fn a_tooltip_content_change_solves_a_tooltip_sized_scope() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
install_changing_tooltip(&s, "ScopeSizeOwner", "scope_size_change");
@@ -457,6 +462,7 @@ fn a_tooltip_content_change_solves_a_tooltip_sized_scope() {
/// bug, and a stale rect that one does carry is the bug in the form the screen would show it.
#[test]
fn a_scoped_resolve_reproduces_the_whole_graph_solve() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
install_changing_tooltip(&s, "ScopeOwner", "scope_change");
@@ -530,6 +536,7 @@ fn measure_sweep_steady_state_cost() {
/// structural) and the counter is proved able to move.
#[test]
fn a_tooltip_line_flipping_wrapped_to_plain_costs_no_graph_derivation() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
s.run(
r#"
@@ -601,6 +608,7 @@ fn a_tooltip_line_flipping_wrapped_to_plain_costs_no_graph_derivation() {
/// is big enough for the derivation to matter.
#[test]
fn a_hover_sweep_across_owners_costs_no_graph_derivation() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
s.run(
r#"
@@ -676,6 +684,7 @@ fn a_hover_sweep_across_owners_costs_no_graph_derivation() {
/// idiom, not the gesture.
#[test]
fn an_action_bar_hover_sweep_costs_no_graph_derivation() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
s.run(
r#"
@@ -752,6 +761,7 @@ fn an_action_bar_hover_sweep_costs_no_graph_derivation() {
/// addons. `derives/frame` read 0.88 with Bagnon and 0.00 without.
#[test]
fn a_bag_addon_hover_sweep_costs_no_graph_derivation() {
benilla_formats::wow_data_or_skip!();
let mut s = settled_default_ui();
s.run(
r#"
@@ -50,6 +50,7 @@ fn alpha(s: &UiScript) -> f64 {
/// captured cannot contain it.
#[test]
fn the_capture_is_asked_for_silently_and_only_the_answer_speaks() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
assert!(!shown(&s), "the status line starts hidden");
@@ -76,6 +77,7 @@ fn the_capture_is_asked_for_silently_and_only_the_answer_speaks() {
/// engine is asked — otherwise that shot would have "Screen Captured" printed across it.
#[test]
fn a_second_press_inside_the_fade_clears_the_line_before_capturing() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("SCREENSHOT_SUCCEEDED", Vec::new());
s.tick(0.5);
@@ -97,6 +99,7 @@ fn a_second_press_inside_the_fade_clears_the_line_before_capturing() {
/// The 1.5 s fade: alpha falls with elapsed time and the frame takes itself off screen at the end.
#[test]
fn the_line_fades_out_over_the_reference_s_second_and_a_half() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("SCREENSHOT_SUCCEEDED", Vec::new());
@@ -114,6 +117,7 @@ fn the_line_fades_out_over_the_reference_s_second_and_a_half() {
/// The failure path says so, in the reference's own words.
#[test]
fn a_failed_capture_shows_the_failure_string() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("SCREENSHOT_FAILED", Vec::new());
assert!(shown(&s));
@@ -49,6 +49,7 @@ fn every_shipped_ui_xml_parses() {
/// window's entry is covered the moment it is added rather than when someone remembers to test it.
#[test]
fn the_whole_shipped_manifest_loads_without_errors() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -88,6 +89,7 @@ fn the_whole_shipped_manifest_loads_without_errors() {
/// every single launch.
#[test]
fn loading_the_shipped_ui_queues_no_sounds() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -154,6 +156,7 @@ fn pet_bar_vm() -> benilla_ui::script::UiScript {
/// catch a third template added with a borrowed number.
#[test]
fn the_autocast_brackets_reach_each_buttons_corners() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::framexml::{Element, TopLevel};
/// The fraction of `UI-AutoCastableOverlay.blp` the bracket art actually covers, measured off
@@ -252,6 +255,7 @@ fn the_autocast_brackets_reach_each_buttons_corners() {
/// drifting, or being "corrected" back to 1.22 by someone who only read the ref.
#[test]
fn the_shine_panes_ask_for_the_rims_we_meant() {
benilla_formats::wow_data_or_skip!();
let mut found: Vec<(String, f32, f32)> = Vec::new();
let mut s = super::spellbook_tests::spellbook_ui(1024.0, 768.0);
s.run("ToggleSpellBook(BOOKTYPE_SPELL)").unwrap();
@@ -779,6 +783,7 @@ fn no_shipped_script_sets_a_global_string_key_as_display_text() {
/// churn on every action-bar edit, and a noisy gate is a gate nobody reads.
#[test]
fn every_texture_frame_outranks_its_status_bars() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::order::unpack;
let mut s = benilla_ui::script::UiScript::new().unwrap();
@@ -894,6 +899,7 @@ fn every_texture_frame_outranks_its_status_bars() {
/// screen; this is the structural half of that fix.)
#[test]
fn the_boot_phase_materializes_no_frames() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The font registry is manifest entry 0 and comes off the chain since 1888, so this reads
@@ -922,6 +928,7 @@ fn the_boot_phase_materializes_no_frames() {
/// rebakeable.
#[test]
fn the_font_registry_alone_covers_the_whole_bake_plan() {
benilla_formats::wow_data_or_skip!();
let plan = |whole: bool| -> std::collections::BTreeSet<(String, String, String)> {
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
@@ -978,6 +985,7 @@ fn the_font_registry_alone_covers_the_whole_bake_plan() {
/// handler that errors on the one event that runs before any window has been shown.
#[test]
fn the_shipped_ui_takes_variables_loaded_without_a_script_error() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -1018,6 +1026,7 @@ fn the_shipped_ui_takes_variables_loaded_without_a_script_error() {
/// `Tablet-2.0.lua:289` header-size probe, 268 read sites).
#[test]
fn every_shipped_font_object_is_published_as_a_lua_global() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -1107,6 +1116,7 @@ fn every_shipped_font_object_is_published_as_a_lua_global() {
/// geometry and the script the reference confers.
#[test]
fn the_inheritable_reference_templates_confer_their_shape() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -1171,6 +1181,7 @@ fn the_inheritable_reference_templates_confer_their_shape() {
/// it from its keyring fork.
#[test]
fn the_inspect_cursor_pair_takes_both_arms() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -1247,6 +1258,7 @@ fn the_inspect_cursor_pair_takes_both_arms() {
/// stops exactly as the reference's does.)
#[test]
fn a_cinematic_leaves_nothing_of_the_interface_on_screen() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -1352,6 +1364,7 @@ fn a_cinematic_leaves_nothing_of_the_interface_on_screen() {
/// because of it.
#[test]
fn every_declared_parent_really_attaches() {
benilla_formats::wow_data_or_skip!();
let mut s = benilla_ui::script::UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The in-game UI materializes on world entry (1051), so a player always exists by the time the
@@ -127,6 +127,7 @@ pub(super) fn spellbook_ui(w: f32, h: f32) -> UiScript {
/// AutoCast Model child) + 8 skill-line tabs + the 3 Spell/Pet toggle tabs + the tab flash frame.
#[test]
fn shipped_spellbook_loads_clean() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
@@ -166,6 +167,7 @@ fn shipped_spellbook_loads_clean() {
/// bar-to-bar drag uses — packing kind 0x00 (SPELL) with the spell id.
#[test]
fn shipped_spellbook_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = spellbook_ui(1024.0, 768.0);
s.fire_event("PLAYER_ENTERING_WORLD", vec![]);
@@ -256,6 +258,7 @@ fn shipped_spellbook_drives_end_to_end() {
/// parked until SMSG_COOLDOWN_EVENT) keeps the widget hidden and dims the icon to 40% (l.361-365).
#[test]
fn shipped_spellbook_shows_the_cooldown_pie() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::QuadContent;
let mut s = spellbook_ui(1024.0, 768.0);
@@ -331,6 +334,7 @@ fn shipped_spellbook_shows_the_cooldown_pie() {
/// finding, which stands). That is the half this still pins.
#[test]
fn shipped_spellbook_empty_slot_draws_its_background_and_socket_ring() {
benilla_formats::wow_data_or_skip!();
let mut s = spellbook_ui(640.0, 700.0);
// A one-spell book: slot 5 takes the reference's `id > offset + numSpells` disable path. (A
// book with NO spells is a state no character is ever in — the reference's own
@@ -371,6 +375,7 @@ fn shipped_spellbook_empty_slot_draws_its_background_and_socket_ring() {
/// l.132/134/268/296-303/336): 1/"true"/true check; 0/"false"/nil/non-numeric strings uncheck.
#[test]
fn set_checked_uses_blizzard_bool_coercion() {
benilla_formats::wow_data_or_skip!();
let s = spellbook_ui(1024.0, 768.0);
for (arg, want) in [
("1", true),
@@ -438,6 +443,7 @@ fn pet_book() -> benilla_ui::script::PetBookState {
/// the class token's label, and a right-click flips autocast instead of casting.
#[test]
fn the_pet_tab_switches_books_and_renders_the_pets_spells() {
benilla_formats::wow_data_or_skip!();
let mut s = spellbook_ui(1024.0, 768.0);
s.fire_event("PLAYER_ENTERING_WORLD", vec![]);
s.set_spellbook(book());
@@ -659,6 +665,7 @@ fn the_pet_tab_switches_books_and_renders_the_pets_spells() {
/// wrong, and the install says it is what 1.12.1 does.
#[test]
fn the_macro_editor_takes_a_shift_click_and_only_a_shift_click() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The macro window's own strings (the app runs the real `GlobalStrings.lua`; `macro_tests`'
@@ -27,6 +27,7 @@ fn setup() -> UiScript {
/// (ref StaticPopup_OnUpdate l.1713-1726).
#[test]
fn timeout_expires_into_a_timeout_cancel() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.run(
r#"reason = "unset"
@@ -62,6 +63,7 @@ fn timeout_expires_into_a_timeout_cancel() {
/// itself is exercised too.
#[test]
fn start_delay_gates_button1_then_swaps_the_text_in() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.run(
r#"StaticPopupDialogs["RECOVER_CORPSE"] = {
@@ -105,6 +107,7 @@ fn start_delay_gates_button1_then_swaps_the_text_in() {
/// the death arc's RESURRECT-cancels-DEATH chain.
#[test]
fn showing_a_dialog_cancels_its_named_victim_with_override() {
benilla_formats::wow_data_or_skip!();
let s = setup();
s.run(
r#"victim_reason = "unset"
@@ -137,6 +140,7 @@ fn showing_a_dialog_cancels_its_named_victim_with_override() {
/// non-escapable entry — the DEATH release popup's law — survives ToggleGameMenu.
#[test]
fn escape_skips_dialogs_without_hide_on_escape() {
benilla_formats::wow_data_or_skip!();
let s = setup();
s.run(
r#"StaticPopupDialogs["TEST_STICKY"] = {
@@ -169,6 +173,7 @@ fn escape_skips_dialogs_without_hide_on_escape() {
/// `timeleft` re-renders "%d %s until release" every tick, minutes above 60 s.
#[test]
fn the_death_countdown_text_rerenders_each_tick() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.run(
r#"StaticPopupDialogs["DEATH"] = {
@@ -212,6 +217,7 @@ fn the_death_countdown_text_rerenders_each_tick() {
/// kind has at most one, so its data is never consulted.
#[test]
fn hide_and_find_address_one_instance_by_data_only_for_a_multiple_dialog() {
benilla_formats::wow_data_or_skip!();
let s = setup();
s.run(
r#"
@@ -269,6 +275,7 @@ fn hide_and_find_address_one_instance_by_data_only_for_a_multiple_dialog() {
/// binding this engine answers.
#[test]
fn the_verb_dialogs_open_from_their_events_and_call_their_verbs() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::ScriptValue;
let mut s = setup();
load_xml(&s, r"Interface\FrameXML\UIParent.xml"); // the arms
@@ -53,6 +53,7 @@ fn setup() -> UiScript {
/// per-tick branch at all rather than `StaticPopup_Show`'s arguments.
#[test]
fn the_confirm_names_the_summoner_and_accept_queues_the_response() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_SUMMON", Vec::new());
assert!(
@@ -86,6 +87,7 @@ fn the_confirm_names_the_summoner_and_accept_queues_the_response() {
/// engine's shared `StaticPopupTimeUnit`, reached through this dialog's own four-argument format.
#[test]
fn the_countdown_line_switches_to_seconds_and_singularises() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_summon_confirm(SummonConfirmUiState {
summoner: "Twomage".into(),
@@ -117,6 +119,7 @@ fn the_countdown_line_switches_to_seconds_and_singularises() {
/// countdown branch re-reads the getters rather than caching `StaticPopup_Show`'s arguments.
#[test]
fn a_summoner_whose_name_is_still_resolving_fills_in_on_a_later_tick() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_summon_confirm(SummonConfirmUiState {
summoner: String::new(),
@@ -151,6 +154,7 @@ fn a_summoner_whose_name_is_still_resolving_fills_in_on_a_later_tick() {
/// confirm in the folder. Cancel is never touched.
#[test]
fn combat_locks_accept_without_taking_the_dialog_down() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.set_unit("player", Some(player(false)));
s.fire_event("CONFIRM_SUMMON", Vec::new());
@@ -198,6 +202,7 @@ fn combat_locks_accept_without_taking_the_dialog_down() {
/// expiry.
#[test]
fn declining_and_expiring_both_send_nothing() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_SUMMON", Vec::new());
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
@@ -32,6 +32,7 @@ fn setup() -> UiScript {
/// parsed as an unknown opcode and dropped.
#[test]
fn the_confirm_shows_and_accept_queues_the_wipe() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_TALENT_WIPE", vec![ScriptValue::Int(15_000)]);
assert!(
@@ -58,6 +59,7 @@ fn the_confirm_shows_and_accept_queues_the_wipe() {
/// raise one, and the engine's `hasMoneyFrame` leg is what shows it.
#[test]
fn the_cost_lands_in_the_money_frame() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
assert!(
!s.eval::<bool>("return StaticPopup1MoneyFrame:IsVisible()")
@@ -100,6 +102,7 @@ fn the_cost_lands_in_the_money_frame() {
/// other), so the only observable is that no confirm was queued.
#[test]
fn declining_sends_nothing() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_TALENT_WIPE", vec![ScriptValue::Int(15_000)]);
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
@@ -120,6 +123,7 @@ fn declining_sends_nothing() {
/// holds, ticking changes nothing; the frame it goes false, the dialog hides itself.
#[test]
fn leaving_the_trainers_range_hides_the_confirm() {
benilla_formats::wow_data_or_skip!();
let mut s = setup();
s.fire_event("CONFIRM_TALENT_WIPE", vec![ScriptValue::Int(15_000)]);
s.run("StaticPopup_OnUpdate(StaticPopup1, 0.1)").unwrap();
@@ -134,6 +134,7 @@ fn size(s: &UiScript, name: &str) -> (f64, f64) {
#[test]
fn a_hostile_target_draws_debuffs_first_with_tint_and_count() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
target(
&mut s,
@@ -214,6 +215,7 @@ fn a_hostile_target_draws_debuffs_first_with_tint_and_count() {
#[test]
fn a_friendly_target_puts_the_buff_row_first() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
target(
&mut s,
@@ -241,6 +243,7 @@ fn a_friendly_target_puts_the_buff_row_first() {
#[test]
fn reaching_the_wrap_shrinks_the_first_row_to_17px() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
let debuffs: Vec<AuraState> = (0..6)
.map(|i| debuff(1000 + i, &format!("D{i}"), 1, None))
@@ -262,6 +265,7 @@ fn reaching_the_wrap_shrinks_the_first_row_to_17px() {
#[test]
fn clearing_the_list_or_the_target_hides_the_buttons() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
target(&mut s, 2, vec![debuff(589, "Pain", 1, Some("Magic"))]);
assert!(shown(&s, "TargetFrameDebuff1"));
@@ -78,6 +78,7 @@ fn menu() -> TaxiUiState {
/// hides it again.
#[test]
fn shipped_taxi_frame_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = taxi_script();
assert!(!s.eval::<bool>("return TaxiFrame:IsVisible()").unwrap());
@@ -133,6 +134,7 @@ fn shipped_taxi_frame_drives_end_to_end() {
/// refusal is visible through.
#[test]
fn no_single_hop_destination_posts_the_error_and_closes() {
benilla_formats::wow_data_or_skip!();
let mut s = taxi_script();
s.set_taxi(Some(TaxiUiState {
art: "Interface\\TaxiFrame\\TAXIMAP0".into(),
@@ -172,6 +174,7 @@ fn no_single_hop_destination_posts_the_error_and_closes() {
/// the window hides immediately (no one-frame lag) — the Gossip/Trainer close-button precedent.
#[test]
fn close_button_queues_the_intent_and_hides() {
benilla_formats::wow_data_or_skip!();
let mut s = taxi_script();
s.set_taxi(Some(menu()));
seat_flight_master(&mut s, "Dungar Longdrink");
@@ -57,6 +57,7 @@ fn wolf() -> UnitState {
/// resolves, and its rect IS the screen.
#[test]
fn uiparent_is_a_real_full_screen_frame() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
s.resolve();
let ok: bool = s
@@ -77,6 +78,7 @@ fn uiparent_is_a_real_full_screen_frame() {
/// kept its load-time position instead.
#[test]
fn world_hover_seats_the_default_corner() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
s.set_unit("mouseover", Some(wolf()));
assert!(s.world_tooltip_unit("mouseover"), "the hover shows");
@@ -99,6 +101,7 @@ fn world_hover_seats_the_default_corner() {
/// world mouseover keeps the ramp; this is the unit *frame*.
#[test]
fn unit_frame_hover_takes_the_default_corner_and_drops_on_leave() {
benilla_formats::wow_data_or_skip!();
// The kit + popups precede the unit frames (their DropDown children's OnLoad), app order.
let mut s = harness(&[
// The stock unit frames resolve GlobalStrings at LOAD (`CombatFeedback.lua` l.7-17,
@@ -214,6 +217,7 @@ fn a_pending_name_hover_titles_the_chains_unknownobject() {
/// (the sibling test above).
#[test]
fn your_own_portrait_explains_the_menu_instead_of_showing_your_health() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[
// The stock unit frames resolve GlobalStrings at LOAD (`CombatFeedback.lua` l.7-17,
// `UnitFrame.lua` l.1-6) and `UnitFrame_OnEnter` passes `PARTY_OPTIONS_LABEL` /
@@ -287,6 +291,7 @@ fn your_own_portrait_explains_the_menu_instead_of_showing_your_health() {
/// table so the pass means what it says.
#[test]
fn action_button_hover_takes_the_default_corner() {
benilla_formats::wow_data_or_skip!();
let s = harness(&[
"Interface\\FrameXML\\Cooldown.xml",
"Interface\\FrameXML\\ActionButtonTemplate.xml",
@@ -328,6 +333,7 @@ fn action_button_hover_takes_the_default_corner() {
/// (`script/tooltip/verbs.rs`).
#[test]
fn ubertooltips_off_seats_action_bar_plates_beside_the_button() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[
"Interface\\FrameXML\\Cooldown.xml",
"Interface\\FrameXML\\ActionButtonTemplate.xml",
@@ -408,6 +414,7 @@ fn ubertooltips_off_seats_action_bar_plates_beside_the_button() {
/// un-collapsed with it.
#[test]
fn ubertooltips_off_seats_stance_plates_beside_the_button() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[
"Interface\\FrameXML\\Cooldown.xml",
"Interface\\FrameXML\\ActionButtonTemplate.xml",
@@ -461,6 +468,7 @@ fn ubertooltips_off_seats_stance_plates_beside_the_button() {
/// BOTTOMLEFT.
#[test]
fn buff_hover_hangs_below_left_of_the_button() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[
"Interface\\FrameXML\\Cooldown.xml",
"Interface\\FrameXML\\ActionButtonTemplate.xml",
@@ -527,6 +535,7 @@ fn buff_hover_hangs_below_left_of_the_button() {
/// cost.
#[test]
fn a_cursor_seated_gameobject_plate_is_owned() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
assert!(s.world_tooltip_gameobject("Brill", &[], Some((512.0, 384.0))));
let owned: bool = s.eval("return GameTooltip:IsOwned(UIParent)").unwrap();
@@ -551,6 +560,7 @@ fn a_cursor_seated_gameobject_plate_is_owned() {
/// plate is ever shown. With the owner written, so do we.
#[test]
fn a_cursor_seated_gameobject_plate_survives_an_addons_on_show_hook() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
// Questie's hook, in one line: the plate's own show event calls Show() again.
s.run(r#"GameTooltip:SetScript("OnShow", function() GameTooltip:Show() end)"#)
@@ -579,6 +589,7 @@ fn a_cursor_seated_gameobject_plate_survives_an_addons_on_show_hook() {
/// the failure 2255 had just fixed on the cursor arm.
#[test]
fn a_corner_seated_gameobject_plate_is_owned_and_shown() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
assert!(s.world_tooltip_gameobject("Ironforge Main Gate", &[], None));
let owned: bool = s.eval("return GameTooltip:IsOwned(UIParent)").unwrap();
@@ -593,6 +604,7 @@ fn a_corner_seated_gameobject_plate_is_owned_and_shown() {
/// `GameTooltip:Show()`, and `:Show()` is the existence gate `0x530a80`.
#[test]
fn a_corner_seated_gameobject_plate_survives_an_addons_on_show_hook() {
benilla_formats::wow_data_or_skip!();
let mut s = harness(&[]);
s.run(r#"GameTooltip:SetScript("OnShow", function() GameTooltip:Show() end)"#)
.unwrap();
@@ -99,6 +99,7 @@ fn drawn_textures(s: &mut UiScript) -> Vec<String> {
/// itself never publishes a line region.
#[test]
fn an_addon_tooltip_from_the_template_names_its_lines_after_the_caller() {
benilla_formats::wow_data_or_skip!();
let s = harness();
// AtlasLoot/Core/AtlasLoot.xml l.576's declaration, renamed.
let report = load_addon_xml(
@@ -168,6 +169,7 @@ fn an_addon_tooltip_from_the_template_names_its_lines_after_the_caller() {
/// Strip `<Backdrop>` out of `GameTooltipTemplate` and this is the assertion that goes red.
#[test]
fn an_addon_tooltip_from_the_template_gets_the_plate() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
load_addon_xml(
&s,
@@ -214,6 +216,7 @@ fn an_addon_tooltip_from_the_template_gets_the_plate() {
/// Asserted from both sides: no error, and the tint actually landed.
#[test]
fn an_addon_may_call_gametooltip_onload_the_reference_way_with_no_argument() {
benilla_formats::wow_data_or_skip!();
let s = harness();
let report = load_addon_xml(
&s,
@@ -266,6 +269,7 @@ fn an_addon_may_call_gametooltip_onload_the_reference_way_with_no_argument() {
/// owns the children, and the plate came along.
#[test]
fn createframe_with_the_template_is_the_same_tooltip() {
benilla_formats::wow_data_or_skip!();
let s = harness();
// BetterCharacterStats/helper.lua l.3, verbatim in shape (its `getglobal(...) or` guard and
// the WorldFrame owner both collapse to this in a fresh VM).
@@ -305,6 +309,7 @@ fn createframe_with_the_template_is_the_same_tooltip() {
/// an explicit `Hide()` between passes (l.1832). Both read `TextRight` as well as `TextLeft`.
#[test]
fn the_scanner_shape_reads_both_columns_and_hides_again() {
benilla_formats::wow_data_or_skip!();
let s = harness();
load_addon_xml(
&s,
@@ -368,6 +373,7 @@ fn the_scanner_shape_reads_both_columns_and_hides_again() {
/// instance, which is where it lived while it was inlined.
#[test]
fn the_status_bar_is_the_templates_because_tipbuddy_anchors_to_it() {
benilla_formats::wow_data_or_skip!();
let s = harness();
let report = load_addon_xml(
&s,
@@ -423,6 +429,7 @@ fn the_status_bar_is_the_templates_because_tipbuddy_anchors_to_it() {
/// would notice.
#[test]
fn an_instance_attribute_beats_the_templates() {
benilla_formats::wow_data_or_skip!();
let s = harness();
load_addon_xml(
&s,
@@ -461,6 +468,7 @@ fn an_instance_attribute_beats_the_templates() {
/// so 30 hidden pairs add no height and no width.
#[test]
fn the_tooltip_line_globals_exist_cold_and_do_not_count_as_lines() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
for name in [
@@ -170,6 +170,7 @@ fn raid(n: usize) -> PartyState {
/// frame, so this is the switch alone.
#[test]
fn the_frame_ships_off_and_the_switch_is_what_shows_it() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
assert_eq!(
@@ -191,6 +192,7 @@ fn the_frame_ships_off_and_the_switch_is_what_shows_it() {
/// target, a target OF it, a target that is not you, and a target that is alive.
#[test]
fn the_four_unit_gates_each_take_the_frame_down() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
assert!(shown(&mut s));
@@ -240,6 +242,7 @@ fn the_four_unit_gates_each_take_the_frame_down() {
/// [`the_solo_mode_keeps_the_frame_when_a_raid_forms`], which shows the frame first.
#[test]
fn the_five_modes_answer_solo_party_and_raid() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
@@ -290,6 +293,7 @@ fn the_five_modes_answer_solo_party_and_raid() {
/// sixty times a second and still never takes the frame down.
#[test]
fn the_solo_mode_keeps_the_frame_when_a_raid_forms() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
s.run(r#"SHOW_TARGET_OF_TARGET_STATE = "3" this = TargetofTargetFrame TargetofTarget_Update() this = nil"#)
@@ -325,6 +329,7 @@ fn the_solo_mode_keeps_the_frame_when_a_raid_forms() {
/// answer one with if it did (ref TargetFrame.xml l.612-625).
#[test]
fn the_frame_paints_its_unit() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
s.tick(0.016);
@@ -379,6 +384,7 @@ fn the_frame_paints_its_unit() {
/// only thing that ever calls `TargetofTarget_CheckDead`.
#[test]
fn the_dead_word_needs_a_connected_corpse() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
s.tick(0.016);
@@ -420,6 +426,7 @@ fn the_dead_word_needs_a_connected_corpse() {
/// reached from the frame's OnUpdate and nowhere else.
#[test]
fn the_portrait_tints_with_a_players_state_and_never_a_creatures() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
s.tick(0.016);
@@ -456,6 +463,7 @@ fn the_portrait_tints_with_a_players_state_and_never_a_creatures() {
/// reason the frame is clickable.
#[test]
fn the_left_click_targets_the_unit() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
s.run(r#"TargetofTarget_OnClick("LeftButton")"#).unwrap();
@@ -481,6 +489,7 @@ fn the_left_click_targets_the_unit() {
/// it appear.
#[test]
fn the_debuff_row_draws_what_the_unit_carries() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
s.set_auras(
@@ -520,6 +529,7 @@ fn the_debuff_row_draws_what_the_unit_carries() {
/// wrapped short behind it.
#[test]
fn the_target_rows_wrap_short_while_the_frame_stands_beside_them() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
// A hostile target, so the debuffs lead and the buffs hang off them.
s.set_auras("target", Some(vec![debuff(2000, "Sunder", None)]));
@@ -571,6 +581,7 @@ fn the_target_rows_wrap_short_while_the_frame_stands_beside_them() {
/// runs only while you have a target.
#[test]
fn the_reconcile_takes_the_frame_down_when_the_token_goes_silent() {
benilla_formats::wow_data_or_skip!();
let mut s = load_tot();
switch_on(&mut s);
assert!(shown(&mut s));
@@ -244,6 +244,7 @@ fn menu() -> TrainerState {
/// and hides on TRAINER_CLOSED.
#[test]
fn shipped_trainer_frame_drives_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
// Hidden by default.
@@ -351,6 +352,7 @@ fn shipped_trainer_frame_drives_end_to_end() {
/// the row button's OnClick → Collapse/ExpandTrainerSkillLine(headerIndex).
#[test]
fn clicking_a_header_row_collapses_its_group() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(menu()));
@@ -388,6 +390,7 @@ fn clicking_a_header_row_collapses_its_group() {
/// its header stays, and the row count falls.
#[test]
fn filter_hides_a_state_keeping_headers() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(menu()));
@@ -424,6 +427,7 @@ fn filter_hides_a_state_keeping_headers() {
/// have to agree.
#[test]
fn filter_rows_toggle_through_the_dropdown_kit() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(menu()));
@@ -513,6 +517,7 @@ fn long_menu() -> TrainerState {
#[test]
fn wheel_over_a_row_scrolls_the_list() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(long_menu()));
@@ -594,6 +599,7 @@ fn wheel_over_a_row_scrolls_the_list() {
/// (decision 1605).
#[test]
fn a_selected_or_hovered_service_row_paints_its_name_white() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(menu()));
@@ -657,6 +663,7 @@ fn a_selected_or_hovered_service_row_paints_its_name_white() {
/// while the arrow BUTTONS keep the ref's `UChatScrollButton` click.
#[test]
fn wheel_scroll_is_silent_but_the_arrows_click() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(long_menu())); // 16 rows > 11 visible → the bar + arrows show
@@ -707,6 +714,7 @@ fn wheel_scroll_is_silent_but_the_arrows_click() {
/// a number someone has to re-derive.
#[test]
fn the_scrollbar_arrows_step_the_list_the_way_they_point() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(long_menu())); // 16 rows > 11 visible → the bar shows
@@ -841,6 +849,7 @@ fn the_state_filter_survives_a_restart_through_the_saved_variables_file() {
/// the window re-pushes its saved globals on every show.
#[test]
fn a_new_list_packet_resets_the_filter_mask_and_the_collapse_set() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_trainer(Some(menu()));
s.fire_event(
@@ -930,6 +939,7 @@ fn recipe_menu() -> TrainerState {
/// carries a wrap width, the long name measures two lines, and its height passes the row height.
#[test]
fn a_long_row_name_stays_on_one_line_and_carries_its_rank_along() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(5000);
s.set_trainer(Some(recipe_menu()));
@@ -1032,6 +1042,7 @@ fn a_long_row_name_stays_on_one_line_and_carries_its_rank_along() {
/// collapsed groups, which 1128 recorded as "a collapse does not survive a purchase" — with it.
#[test]
fn learning_a_spell_keeps_the_filter_and_the_collapse_a_re_open_still_resets() {
benilla_formats::wow_data_or_skip!();
use crate::ui_trainer::TrainerOpen;
const DAZALAR: u64 = 0xabc;
@@ -1160,6 +1171,7 @@ fn learning_a_spell_keeps_the_filter_and_the_collapse_a_re_open_still_resets() {
/// reason: the packet-vs-repaint decision under test is the app's own.
#[test]
fn learning_a_spell_takes_the_detail_pane_with_it_instead_of_stranding_the_last_one() {
benilla_formats::wow_data_or_skip!();
use crate::ui_trainer::TrainerOpen;
const DAZALAR: u64 = 0xabc;
@@ -1263,6 +1275,7 @@ fn learning_a_spell_takes_the_detail_pane_with_it_instead_of_stranding_the_last_
/// (`mov ecx,0x136; jmp 0x703e50`). Ours did not, so the checkbox moved and the list did not.
#[test]
fn a_filter_click_repaints_the_rows_the_player_is_looking_at() {
benilla_formats::wow_data_or_skip!();
let mut s = trainer_script();
s.set_money(50);
s.set_trainer(Some(menu()));
@@ -37,6 +37,7 @@ fn ui_parent() -> UiScript {
/// bounds are strict, so a cursor exactly on an edge is outside.
#[test]
fn mouse_is_over_is_the_references_own_box_test() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_parent();
// The box is x 200..300, y 300..350.
s.mouse_move(250.0, 325.0);
@@ -100,6 +101,7 @@ fn mouse_is_over_is_the_references_own_box_test() {
/// so repeated calls accumulate.
#[test]
fn raise_and_lower_frame_level_step_the_frames_own_level() {
benilla_formats::wow_data_or_skip!();
let s = ui_parent();
let base: i64 = s.eval("return Box:GetFrameLevel()").unwrap();
@@ -121,6 +123,7 @@ fn raise_and_lower_frame_level_step_the_frames_own_level() {
/// `IgniteStatus` calls it at file scope and dies on `attempt to call global`.
#[test]
fn randomseed_is_a_bare_global_like_random() {
benilla_formats::wow_data_or_skip!();
let s = ui_parent();
// Seeding twice with the same value must produce the same first draw; that is the whole
// contract an addon wants from it.
@@ -138,6 +141,7 @@ fn randomseed_is_a_bare_global_like_random() {
/// laid out answers nil from `GetLeft()`, and `MouseIsOver` must return nil rather than raise.
#[test]
fn mouse_is_over_survives_a_frame_with_no_resolved_rect() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_parent();
s.mouse_move(250.0, 325.0);
s.run(r#"Floating = CreateFrame("Frame", "Floating", UIParent)"#)
@@ -162,6 +166,7 @@ fn mouse_is_over_survives_a_frame_with_no_resolved_rect() {
/// that rather than reading one key.
#[test]
fn raid_class_colors_is_the_references_own_nine() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
// RAID_CLASS_COLORS comes off the chain with the font registry since 1888.
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -216,6 +221,7 @@ fn raid_class_colors_is_the_references_own_nine() {
/// found rather than the block those four came out of. It now enumerates the block.
#[test]
fn the_font_path_globals_are_the_references_own_four() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
// The four font-path globals come off the chain with the registry since 1888.
super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml");
@@ -294,6 +300,7 @@ fn the_font_path_globals_are_the_references_own_four() {
/// answered nil under the very cursor that had just entered it (decision 1985).
#[test]
fn mouse_is_over_reads_a_scaled_frame_in_its_own_units() {
benilla_formats::wow_data_or_skip!();
let mut s = ui_parent();
s.run(
r#"Scaled = CreateFrame("Frame", "Scaled", UIParent) Scaled:SetWidth(200) Scaled:SetHeight(100)
@@ -48,6 +48,7 @@ fn load_unit_frames(s: &UiScript) {
/// name arriving via UNIT_NAME_UPDATE.
#[test]
fn shipped_unit_frames_drive_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -306,6 +307,7 @@ fn extracted_text_height(s: &mut UiScript, text: &str) -> Option<f32> {
/// touches it (only the player frame registers UNIT_COMBAT in 1.12).
#[test]
fn unit_combat_drives_the_player_hit_indicator() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -408,6 +410,7 @@ fn unit_combat_drives_the_player_hit_indicator() {
/// menu), the full leader set once a party is pushed.
#[test]
fn left_clicking_the_player_frame_targets_self() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The SELF-menu strings the popup rows bake at UnitPopup.xml load arrive with
@@ -528,6 +531,7 @@ fn left_clicking_the_player_frame_targets_self() {
/// unit. The level-1 click was pinned above; this pins the level the marks actually live on.
#[test]
fn raid_mark_clicks_through_the_nested_level() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The menu strings arrive with `load_unit_frames`' own `GlobalStrings.lua`
@@ -644,6 +648,7 @@ fn raid_mark_clicks_through_the_nested_level() {
/// GetQuestGreenRange binding.
#[test]
fn shipped_target_frame_runs_the_level_law() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::PlayerReqState;
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
@@ -835,6 +840,7 @@ fn shipped_target_frame_runs_the_level_law() {
/// UNIT_FACTION edge, not on every repaint.
#[test]
fn pvp_icon_follows_the_three_branch_law() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -944,6 +950,7 @@ fn pvp_icon_follows_the_three_branch_law() {
/// both collapsed into blue.
#[test]
fn flagged_friendly_player_plate_is_green() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1005,6 +1012,7 @@ fn flagged_friendly_player_plate_is_green() {
/// UNIT_CLASSIFICATION_CHANGED alone, with no re-target.
#[test]
fn target_frame_border_follows_the_classification_law() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1135,6 +1143,7 @@ fn target_frame_border_follows_the_classification_law() {
/// level *below* the texture frame, where no layer can lift them back over it.
#[test]
fn the_ring_art_paints_over_the_bars() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1206,6 +1215,7 @@ fn the_ring_art_paints_over_the_bars() {
/// silence — nobody was in a party when the director reported the player frame.
#[test]
fn the_party_art_paints_over_the_bars() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
// The loot test's prefix (`loot_tests.rs`): PartyFrame's inline <Script> reads
@@ -1344,6 +1354,7 @@ fn the_party_art_paints_over_the_bars() {
/// lights on the same `UnitHealth(unit) <= 0` test a corpse trips.
#[test]
fn a_feigning_target_paints_empty_bars_and_the_dead_text() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1444,6 +1455,7 @@ fn a_feigning_target_paints_empty_bars_and_the_dead_text() {
/// ring/swords/disc — resting still wins when both hold — and leaving both states clears the lot.
#[test]
fn the_player_frame_flashes_zzz_while_resting() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1546,6 +1558,7 @@ fn the_player_frame_flashes_zzz_while_resting() {
/// the number rode on the badge (director report, 2026-08-07).
#[test]
fn the_rest_badge_covers_the_level_number() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1616,6 +1629,7 @@ fn the_rest_badge_covers_the_level_number() {
/// no such cut.
#[test]
fn status_bar_text_paints_the_player_numerals_but_not_the_targets() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1785,6 +1799,7 @@ fn status_bar_text_paints_the_player_numerals_but_not_the_targets() {
/// back to its owner's rect and every string would appear to sit in the same box.
#[test]
fn no_two_numeral_strings_overlap_on_any_frame() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -1871,6 +1886,7 @@ fn no_two_numeral_strings_overlap_on_any_frame() {
/// addons and fires events, but never hovers anything.
#[test]
fn the_unit_frame_hover_hooks_carry_the_references_names() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_unit_frames(&s);
@@ -1922,6 +1938,7 @@ fn the_unit_frame_hover_hooks_carry_the_references_names() {
/// sheet's mark order says it should.
#[test]
fn the_raid_mark_helper_maps_each_index_to_its_cell() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_unit_frames(&s);
s.run(r#"RTMark = UIParent:CreateTexture("RTMark", "OVERLAY")"#)
@@ -1963,6 +1980,7 @@ fn the_raid_mark_helper_maps_each_index_to_its_cell() {
/// index is `0` — the player's own seat on that scale — *and* that we are grouped at all.
#[test]
fn the_player_frame_wears_the_leader_and_master_looter_icons() {
benilla_formats::wow_data_or_skip!();
use benilla_ui::script::{PartyMemberInfo, PartyState};
let mut s = UiScript::new().unwrap();
@@ -2057,6 +2075,7 @@ fn the_player_frame_wears_the_leader_and_master_looter_icons() {
/// not asserted against; what is asserted is that nothing the reference declares is absent.
#[test]
fn the_unit_frames_publish_every_name_the_reference_declares() {
benilla_formats::wow_data_or_skip!();
let s = UiScript::new().unwrap();
load_unit_frames(&s);
@@ -2189,6 +2208,7 @@ fn the_unit_frames_publish_every_name_the_reference_declares() {
/// This drives the real stock file, so it is the symptom itself and not a proxy for it.
#[test]
fn the_target_leader_crown_follows_player_flags_changed() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_unit_frames(&s);
@@ -449,6 +449,7 @@ fn logging_out_leaves_no_in_game_frames_behind() {
/// the world.
#[test]
fn reload_ui_is_a_fresh_login_in_place() {
benilla_formats::wow_data_or_skip!();
let _l = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
@@ -615,6 +616,7 @@ fn reload_outside_the_world_is_dropped() {
/// entirely off terminal WARN lines because the client showed nothing.
#[test]
fn an_addon_error_while_entering_world_reports_on_screen_and_the_sibling_loads() {
benilla_formats::wow_data_or_skip!();
let _l = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
@@ -664,6 +666,7 @@ fn an_addon_error_while_entering_world_reports_on_screen_and_the_sibling_loads()
/// loads, and this test FINISHING is the claim — before 1306 it would hang here forever.
#[test]
fn a_looping_addon_cannot_freeze_world_entry() {
benilla_formats::wow_data_or_skip!();
let _l = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
@@ -943,6 +946,7 @@ fn the_login_one_shots_wait_for_the_in_game_ui() {
/// and the player is **told to look**.
#[test]
fn an_addon_that_fails_to_load_without_raising_is_readable_in_the_error_log() {
benilla_formats::wow_data_or_skip!();
let _l = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
@@ -1331,6 +1335,7 @@ fn a_placed_window_comes_back_after_a_reload() {
/// file reddens this instead of scrolling past, and closing one means deleting a line here.
#[test]
fn a_clean_world_entry_raises_only_the_warnings_we_have_named() {
benilla_formats::wow_data_or_skip!();
let _l = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
@@ -81,6 +81,7 @@ pub(crate) fn push(s: &mut UiScript, rows: Vec<WorldStateUiView>) {
/// The Eastern Plaguelands readout: two labelled tower counters, each with its faction icon.
#[test]
fn the_tower_counters_draw_with_their_icons() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("PLAYER_ENTERING_WORLD", vec![ScriptValue::Str("".into())]);
assert!(
@@ -122,6 +123,7 @@ fn the_tower_counters_draw_with_their_icons() {
/// are hidden rather than left painting a stale count.
#[test]
fn leaving_the_zone_clears_the_readout() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("PLAYER_ENTERING_WORLD", vec![ScriptValue::Str("".into())]);
push(
@@ -154,6 +156,7 @@ fn leaving_the_zone_clears_the_readout() {
/// never have moved; both halves of that are pinned here.
#[test]
fn the_dynamic_icon_is_a_second_slot_lit_only_by_the_taken_state() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("PLAYER_ENTERING_WORLD", vec![ScriptValue::Str("".into())]);
@@ -245,6 +248,7 @@ fn the_dynamic_icon_is_a_second_slot_lit_only_by_the_taken_state() {
/// icon is optional, and the row must not collapse when the DBC column is empty.
#[test]
fn a_row_without_an_icon_still_shows_its_text() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("PLAYER_ENTERING_WORLD", vec![ScriptValue::Str("".into())]);
push(
@@ -263,6 +267,7 @@ fn a_row_without_an_icon_still_shows_its_text() {
/// Our transcription had shown the text; the reference's file draws the bar (1972).
#[test]
fn a_capture_point_row_is_a_bar_not_a_line_of_text() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.fire_event("PLAYER_ENTERING_WORLD", vec![ScriptValue::Str("".into())]);
push(
@@ -289,6 +294,7 @@ fn a_capture_point_row_is_a_bar_not_a_line_of_text() {
/// value — the number `0`, not nil and not ten nils — and a non-number argument raises.
#[test]
fn the_bindings_answer_the_reference_shape() {
benilla_formats::wow_data_or_skip!();
let mut s = harness();
s.set_world_state_ui(vec![WorldStateUiView {
ui_state: 7,
@@ -117,6 +117,7 @@ fn pump(script: &mut UiScript) {
#[test]
fn collapse_all_tab_and_filter_dropdowns_work_end_to_end() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
load_ui(&s);
@@ -208,6 +209,7 @@ fn collapse_all_tab_and_filter_dropdowns_work_end_to_end() {
/// deviation note); this pins the load + the text attribute + the hidden-in-practice state.
#[test]
fn craft_collapse_tab_loads_with_text_and_stays_hidden_for_a_flat_list() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
load_ui(&s);
@@ -243,6 +245,7 @@ fn craft_collapse_tab_loads_with_text_and_stays_hidden_for_a_flat_list() {
/// test going red.
#[test]
fn reagent_slots_carry_the_questitemtemplate_shape_in_both_windows() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
load_ui(&s);
@@ -386,6 +389,7 @@ fn reagent_slots_carry_the_questitemtemplate_shape_in_both_windows() {
/// the tooltip it opens happens to be empty in a headless VM.
#[test]
fn a_row_click_shows_the_selection_glow_and_a_row_hover_shows_nothing() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
load_ui(&s);
s.set_trade_skill(Some(state()));
@@ -481,6 +485,7 @@ fn a_row_click_shows_the_selection_glow_and_a_row_hover_shows_nothing() {
/// colour off the extracted text quad, not off any Lua state, so it fails if either half regresses.
#[test]
fn a_hovered_or_selected_recipe_row_paints_its_label_white() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
load_ui(&s);
s.set_screen_size(1024.0, 768.0);
@@ -593,6 +598,7 @@ fn a_hovered_or_selected_recipe_row_paints_its_label_white() {
/// (decision 1605).
#[test]
fn a_hovered_or_selected_craft_row_paints_its_label_white() {
benilla_formats::wow_data_or_skip!();
let mut s = UiScript::new().unwrap();
load_ui(&s);
s.set_screen_size(1024.0, 768.0);
+3 -1
View File
@@ -172,7 +172,9 @@ pub fn skipped(what: &str, looked_in: &[PathBuf]) {
.append(true)
.open(log)
{
let _ = writeln!(f, "{what}");
// One write per line: parallel test threads append to the same file, and a
// formatted write is several syscalls that interleave.
let _ = f.write_all(format!("{what}\n").as_bytes());
}
}
}
+2 -1
View File
@@ -23,7 +23,8 @@ fork, and forks are welcome.
## How a change is judged
1. `scripts/gates.sh` is green: fmt, clippy with warnings denied, the workspace tests, the
1. `scripts/gates.sh` is green: fmt, clippy with warnings denied, the workspace tests (once
with the client data, once without, so a test that reads the install has to declare it), the
doc-link and render-pass lints, the player build with its own tests, and the engine boot
checks.
2. The reference fact is stated: what 1.12.1 does, and where that is known from (the client's
+2
View File
@@ -74,6 +74,8 @@ comment naming the reference fact and why we differ. Anything else is a fork.
`scripts/gates.sh`. It runs on the tree that lands, before it lands; it is memoized on the
tree. The tests that read the install or the addon corpus skip where the data is absent, and
the gate prints the count; where the data is, a skip is a failure (`BENILLA_REQUIRE_DATA=1`).
The workspace tests then run a second time with `WOW_DATA=` (no install), so a test that
reads the install without opening with `wow_data_or_skip!` fails here and not on a clone.
- **Per round**: `scripts/check.sh`, scoped to the changed crates and their dependents. Run each
gate once per round; `tee` the output to a file and grep the file.
- **A clean run** is the fourth gate: `scripts/smoke.sh`, a live login and logout against the
+6
View File
@@ -145,6 +145,12 @@ fi
run test env ${require_data:+BENILLA_REQUIRE_DATA=1} BENILLA_SKIP_LOG="$skips" cargo test --workspace
report_skips test
# test-no-install: the same suite as a clone without client data runs it. A test that reads the
# install without opening with `wow_data_or_skip!` passes where the data is and panics everywhere
# else; `WOW_DATA=` (set, empty) is the resolver's "no install", so this run is that machine. No
# build variable reads it, so the binaries are reused and only the run time is paid.
run test-no-install env -u BENILLA_REQUIRE_DATA WOW_DATA= cargo test --workspace
# **doc-links** (decision 1925) — the docs are the knowledge base, so a doc link pointing at a
# DELETED item is rot, and nothing else here runs rustdoc. Deliberately narrow: it fails only on a
# `crate::`/`super::`/`self::`/`Self::` path whose leaf exists nowhere in the workspace. The broad