deleting a good item asks in the reference's words
- deleting a rare-or-better item forks to the reference's typed confirm — "Type \"DELETE\" into the field to confirm": Yes starts disabled, the box takes focus, the button enables only while the field spells DELETE, and a reflexive Enter over a half-typed word is inert; the missing engine piece was one hook, the edit box's on-text-changed; beside it, the loot bind-on-pickup confirm — bind-on-pickup AND uncommon-or-better, two conjuncts and no others, so a white BoP drop is taken with no dialog and a bind-on-equip blue never asks here — with the reference's one-pending-slot latch, and the take split into two verbs the way 1.12 actually has it: the row click stays C-side, and LootSlot is only the confirmation's continuation - the message frames get their justify quartet, read out of the binary's method tables (with the Button table re-derived as the control that lacks it); Frame:EnableDrawLayer/DisableDrawLayer and Region:GetDrawLayer land — the disabled layers are a five-bit mask on the frame, so re-enabling a layer restores exactly what was showing underneath; and the Skills tab's children take the reference's names, with SKILLS_TO_DISPLAY published — pfUI loops over it and got "'for' limit must be a number" - the README's roadmap line now names what is genuinely big — battlegrounds and meeting stones, both verified still missing — plus the long tail, because the old line named four features that have since landed and small features go stale on a roadmap within a week Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -758,7 +758,7 @@ fn tab_round_trip_with_a_selected_skill_by_point() {
|
||||
s.eval::<i64>("return GetSelectedSkill()").unwrap() > 0,
|
||||
"the row click selects"
|
||||
);
|
||||
assert!(shown(&mut s, "SkillDetailBar"), "detail bar arms");
|
||||
assert!(shown(&mut s, "SkillDetailStatusBar"), "detail bar arms");
|
||||
// The description body renders through the SKILL_DESCRIPTION format (skillType is "" in every
|
||||
// reachable 1.12 branch) — the nil-global half of the report, pinned.
|
||||
assert_eq!(
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
//! world-drop's `DELETE_ITEM_CONFIRM` showing the ref's `DELETE_ITEM` StaticPopup entry (decision
|
||||
//! 0308 §3's engine) with the real `DELETE_ITEM`/`YES`/`NO` GlobalStrings, its Yes/No/ESC routing
|
||||
//! to `DeleteCursorItem`/`ClearCursor`, and the entry's own `OnUpdate` auto-hide poll.
|
||||
//!
|
||||
//! Plus the `arg2 >= 3` fork the driver grew in 1743 (ref `UIParent.lua:344-352`): a RARE-or-better
|
||||
//! payload raises `DELETE_GOOD_ITEM` instead, whose OKAY stays disabled until the player types
|
||||
//! `DELETE_ITEM_CONFIRM_STRING` into its edit box. The two fixtures are real 1.12 rows (vmangos
|
||||
//! `item_template`): **Tough Jerky** 117, quality 1 — the plain arm; **Flurry Axe** 871, quality 4 —
|
||||
//! the typed arm.
|
||||
|
||||
use benilla_ui::script::{ContainerSlot, ContainerState, UiScript};
|
||||
|
||||
@@ -25,9 +31,10 @@ fn load_xml(s: &UiScript, file: &str) -> usize {
|
||||
report.frames
|
||||
}
|
||||
|
||||
/// A one-item, one-slot backpack: a quality-3 item (`Tough Jerky`) so the confirm text and the
|
||||
/// wire's destroy count are exercisable end to end.
|
||||
fn one_item_backpack() -> ContainerState {
|
||||
/// A one-item, one-slot backpack holding `item_id`/`name` at `quality`, so the confirm text and
|
||||
/// the wire's destroy count are exercisable end to end. Quality is what forks the driver, so it is
|
||||
/// the fixture's only real parameter.
|
||||
fn one_item_backpack(item_id: u32, name: &str, quality: u32) -> ContainerState {
|
||||
let mut slots = std::collections::HashMap::new();
|
||||
slots.insert(
|
||||
1,
|
||||
@@ -38,9 +45,9 @@ fn one_item_backpack() -> ContainerState {
|
||||
durability: None,
|
||||
texture: Some("Interface\\Icons\\INV_Misc_Food_16".into()),
|
||||
count: 5,
|
||||
quality: Some(3),
|
||||
item_id: 117,
|
||||
link: Some("|cffffffff|Hitem:117|h[Tough Jerky]|h|r".into()),
|
||||
quality: Some(quality),
|
||||
item_id,
|
||||
link: Some(format!("|cffffffff|Hitem:{item_id}|h[{name}]|h|r")),
|
||||
locked: false,
|
||||
equip_slots: Vec::new(),
|
||||
cooldown: None,
|
||||
@@ -74,7 +81,13 @@ fn setup() -> UiScript {
|
||||
/// release, both over nothing; 0218's byte-verified trigger) fires the world-drop
|
||||
/// `DELETE_ITEM_CONFIRM(name, quality)` the driver listens for.
|
||||
fn pick_up_and_drop_in_world(s: &mut UiScript) {
|
||||
s.set_container(0, Some(one_item_backpack()));
|
||||
drop_in_world(s, 117, "Tough Jerky", 1);
|
||||
}
|
||||
|
||||
/// The same world drop for an arbitrary fixture item — 1743's fork reads `arg2` (the quality), so
|
||||
/// every test below picks the arm it wants by choosing the item.
|
||||
fn drop_in_world(s: &mut UiScript, item_id: u32, name: &str, quality: u32) {
|
||||
s.set_container(0, Some(one_item_backpack(item_id, name, quality)));
|
||||
s.run("PickupContainerItem(0, 1)").unwrap();
|
||||
assert!(s.cursor_item().is_some(), "fixture: the item is held");
|
||||
// Off past every frame (the bag sits bottom-right; off-screen negative is always clear).
|
||||
@@ -218,3 +231,202 @@ fn the_delete_entry_polls_itself_hidden_and_other_dialogs_are_untouched() {
|
||||
);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// The RARE-or-better fork (ref `UIParent.lua:346-350`, `arg2 >= 3`): a quality-4 payload raises
|
||||
/// `DELETE_GOOD_ITEM`, not `DELETE_ITEM` — with the second GlobalString, the edit box up and
|
||||
/// focused, and OKAY **disabled** until the confirm word is typed.
|
||||
#[test]
|
||||
fn a_rare_payload_raises_the_typed_confirm_with_okay_disabled() {
|
||||
let mut s = setup();
|
||||
drop_in_world(&mut s, 871, "Flurry Axe", 4);
|
||||
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1.which").unwrap(),
|
||||
"DELETE_GOOD_ITEM",
|
||||
"quality 4 forks to the typed-confirmation variant"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1Text:GetText()")
|
||||
.unwrap(),
|
||||
"Do you want to destroy Flurry Axe?\n\nType \"DELETE\" into the field to confirm.",
|
||||
"the real GlobalStrings DELETE_GOOD_ITEM text, formatted with the item name"
|
||||
);
|
||||
assert!(
|
||||
s.eval::<bool>("return StaticPopup1EditBox:IsShown()")
|
||||
.unwrap(),
|
||||
"hasEditBox raises the narrow box"
|
||||
);
|
||||
assert!(
|
||||
s.eval::<bool>("return StaticPopup1EditBox:HasFocus()")
|
||||
.unwrap(),
|
||||
"the entry's OnShow focuses the box, so the player can type straight away"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return StaticPopup1Button1:IsEnabled()")
|
||||
.unwrap(),
|
||||
0,
|
||||
"OKAY starts disabled — nothing has been typed yet"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// The typed gate itself (ref `EditBoxOnTextChanged`, `StaticPopup.lua:718-723`): only the exact
|
||||
/// `DELETE_ITEM_CONFIRM_STRING` enables OKAY, it is compared through `strupper` so lower case
|
||||
/// 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() {
|
||||
let mut s = setup();
|
||||
drop_in_world(&mut s, 871, "Flurry Axe", 4);
|
||||
|
||||
let enabled = |s: &mut UiScript| {
|
||||
s.eval::<i64>("return StaticPopup1Button1:IsEnabled()")
|
||||
.unwrap()
|
||||
== 1
|
||||
};
|
||||
|
||||
s.run(r#"StaticPopup1EditBox:SetText("DELET")"#).unwrap();
|
||||
assert!(!enabled(&mut s), "a prefix of the word is not the word");
|
||||
|
||||
// Typed character by character through the engine's own input path, lower case: the ref
|
||||
// compares through strupper, so this arms OKAY exactly as shouting it would.
|
||||
s.run(r#"StaticPopup1EditBox:SetText("")"#).unwrap();
|
||||
for c in ["d", "e", "l", "e", "t", "e"] {
|
||||
assert!(s.char_input(c), "the focused box takes the keystroke");
|
||||
}
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1EditBox:GetText()")
|
||||
.unwrap(),
|
||||
"delete"
|
||||
);
|
||||
assert!(
|
||||
enabled(&mut s),
|
||||
"the ref compares through strupper, so lower case passes"
|
||||
);
|
||||
|
||||
s.run(r#"StaticPopup1EditBox:SetText("deletex")"#).unwrap();
|
||||
assert!(!enabled(&mut s), "one char past the word disables again");
|
||||
|
||||
s.run(r#"StaticPopup1EditBox:SetText("DELETE")"#).unwrap();
|
||||
assert!(enabled(&mut s));
|
||||
|
||||
// OKAY now does what the plain arm's does: DeleteCursorItem().
|
||||
s.run("StaticPopup_OnClick(StaticPopup1, 1)").unwrap();
|
||||
assert!(
|
||||
!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
|
||||
"OKAY hides the popup"
|
||||
);
|
||||
assert!(s.cursor_item().is_none(), "DeleteCursorItem cleared it");
|
||||
assert_eq!(s.take_container_destroys(), vec![(0, 1, 0)]);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// Enter in the box is the ref's `EditBoxOnEnterPressed` (`StaticPopup.lua:712-717`): it destroys
|
||||
/// 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() {
|
||||
let mut s = setup();
|
||||
drop_in_world(&mut s, 871, "Flurry Axe", 4);
|
||||
|
||||
s.run(r#"StaticPopup1EditBox:SetText("DEL")"#).unwrap();
|
||||
assert!(s.key_input("ENTER"), "the focused box consumes ENTER");
|
||||
assert!(
|
||||
s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
|
||||
"Enter with OKAY disabled is inert — the dialog stays up"
|
||||
);
|
||||
assert!(s.cursor_item().is_some(), "and the item is still held");
|
||||
assert!(s.take_container_destroys().is_empty());
|
||||
|
||||
s.run(r#"StaticPopup1EditBox:SetText("DELETE")"#).unwrap();
|
||||
assert!(s.key_input("ENTER"));
|
||||
assert!(
|
||||
!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
|
||||
"Enter with OKAY enabled destroys and hides"
|
||||
);
|
||||
assert!(s.cursor_item().is_none());
|
||||
assert_eq!(s.take_container_destroys(), vec![(0, 1, 0)]);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// NO on the typed variant is the plain one's cancel — `ClearCursor()`, no wire send — and the
|
||||
/// entry's `OnHide` empties the box so the next raise opens blank (the ref's own reason: a word
|
||||
/// 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() {
|
||||
let mut s = setup();
|
||||
drop_in_world(&mut s, 871, "Flurry Axe", 4);
|
||||
s.run(r#"StaticPopup1EditBox:SetText("DELETE")"#).unwrap();
|
||||
|
||||
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
|
||||
assert!(!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
|
||||
assert!(s.cursor_item().is_none(), "ClearCursor cleared it");
|
||||
assert!(s.take_container_destroys().is_empty(), "No never destroys");
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1EditBox:GetText()")
|
||||
.unwrap(),
|
||||
"",
|
||||
"OnHide empties the box"
|
||||
);
|
||||
|
||||
// Raise it again: blank box, OKAY disabled — the armed state did not survive.
|
||||
drop_in_world(&mut s, 871, "Flurry Axe", 4);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1EditBox:GetText()")
|
||||
.unwrap(),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return StaticPopup1Button1:IsEnabled()")
|
||||
.unwrap(),
|
||||
0,
|
||||
"and OKAY is disabled again"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// ESC out of the focused box: 1743's named divergence. The reference's `DELETE_GOOD_ITEM` names
|
||||
/// no `EditBoxOnEscapePressed`, so in 1.12 the focused box swallows the key and the destroy
|
||||
/// confirm cannot be dismissed with ESC at all. benilla's engine falls back to the ordinary
|
||||
/// hideOnEscape leg — the entry's `OnCancel` (`ClearCursor()`), then hide — so the item is
|
||||
/// released rather than left held under a dialog that will not close.
|
||||
#[test]
|
||||
fn escape_out_of_the_typed_confirm_cancels_the_way_every_other_popup_does() {
|
||||
let mut s = setup();
|
||||
drop_in_world(&mut s, 871, "Flurry Axe", 4);
|
||||
assert!(s
|
||||
.eval::<bool>("return StaticPopup1EditBox:HasFocus()")
|
||||
.unwrap());
|
||||
|
||||
assert!(s.key_input("ESCAPE"), "the focused box consumes ESCAPE");
|
||||
assert!(
|
||||
!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
|
||||
"ESC closes it"
|
||||
);
|
||||
assert!(s.cursor_item().is_none(), "and runs OnCancel's ClearCursor");
|
||||
assert!(s.take_container_destroys().is_empty(), "ESC never destroys");
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// The plain arm keeps its own edit box HIDDEN — the fork is not cosmetic, and a common-quality
|
||||
/// destroy must not grow a field to type into. The control for every test above.
|
||||
#[test]
|
||||
fn the_plain_arm_shows_no_edit_box() {
|
||||
let mut s = setup();
|
||||
pick_up_and_drop_in_world(&mut s); // Tough Jerky, quality 1
|
||||
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1.which").unwrap(),
|
||||
"DELETE_ITEM"
|
||||
);
|
||||
assert!(
|
||||
!s.eval::<bool>("return StaticPopup1EditBox:IsShown()")
|
||||
.unwrap(),
|
||||
"no hasEditBox on the plain entry"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return StaticPopup1Button1:IsEnabled()")
|
||||
.unwrap(),
|
||||
1,
|
||||
"and YES is live immediately"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
@@ -979,3 +979,122 @@ fn the_master_loot_menu_groups_raid_candidates_by_subgroup() {
|
||||
"title + two occupied groups, out of eight blocks"
|
||||
);
|
||||
}
|
||||
|
||||
/// **The soulbind confirm's Lua half** (decision 1744, ref `StaticPopup.lua:601-611` +
|
||||
/// `UIParent.lua:317-323` + `LootFrame.lua:53-54`). `LOOT_BIND_CONFIRM` raises `LOOT_BIND` with the
|
||||
/// real `LOOT_NO_DROP` text and Okay/Cancel; the row rides on `dialog.data`, and Okay hands it back
|
||||
/// through `LootSlot` — the continuation verb, which is the only thing that can complete a deferred
|
||||
/// 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() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "UIDropDownMenu.xml");
|
||||
load_xml(&s, "LootFrame.xml");
|
||||
s.set_loot(Some(coin_and_two_items()));
|
||||
s.fire_event("LOOT_OPENED", vec![]);
|
||||
let _ = s.take_loot_picks();
|
||||
|
||||
// The app defers row 2 and says so.
|
||||
s.fire_event(
|
||||
"LOOT_BIND_CONFIRM",
|
||||
vec![benilla_ui::script::ScriptValue::Int(2)],
|
||||
);
|
||||
assert!(
|
||||
s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
|
||||
"LOOT_BIND_CONFIRM raises the dialog"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1.which").unwrap(),
|
||||
"LOOT_BIND"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1Text:GetText()")
|
||||
.unwrap(),
|
||||
"Looting this item will bind it to you.",
|
||||
"the real GlobalStrings LOOT_NO_DROP, which carries no format specifier"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1Button1:GetText()")
|
||||
.unwrap(),
|
||||
"Okay"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<String>("return StaticPopup1Button2:GetText()")
|
||||
.unwrap(),
|
||||
"Cancel"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return StaticPopup1.data").unwrap(),
|
||||
2,
|
||||
"the row rides on dialog.data, not in the text"
|
||||
);
|
||||
|
||||
// Cancel: no continuation, and nothing reaches either queue.
|
||||
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
|
||||
assert!(!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
|
||||
assert!(
|
||||
s.take_loot_confirms().is_empty(),
|
||||
"Cancel completes nothing"
|
||||
);
|
||||
assert!(s.take_loot_picks().is_empty(), "and takes nothing either");
|
||||
|
||||
// Okay: the row goes back out through LootSlot, the continuation verb.
|
||||
s.fire_event(
|
||||
"LOOT_BIND_CONFIRM",
|
||||
vec![benilla_ui::script::ScriptValue::Int(2)],
|
||||
);
|
||||
s.run("StaticPopup_OnClick(StaticPopup1, 1)").unwrap();
|
||||
assert!(!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
|
||||
assert_eq!(s.take_loot_confirms(), vec![2]);
|
||||
assert!(
|
||||
s.take_loot_picks().is_empty(),
|
||||
"the accept is a continuation, never a fresh take"
|
||||
);
|
||||
|
||||
// The window closing takes an open dialog with it (ref LootFrame.lua l.53-54) — its row number
|
||||
// would name a slot in the next corpse.
|
||||
s.fire_event(
|
||||
"LOOT_BIND_CONFIRM",
|
||||
vec![benilla_ui::script::ScriptValue::Int(2)],
|
||||
);
|
||||
assert!(s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
|
||||
s.fire_event("LOOT_CLOSED", vec![]);
|
||||
assert!(
|
||||
!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
|
||||
"LOOT_CLOSED hides the confirm"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// The row click is `BenillaTakeLootSlot`, not `LootSlot` — a real mouse click on a row must land
|
||||
/// in the TAKE queue, or every loot would be a continuation nothing is pending for and the window
|
||||
/// would sit there doing nothing (decision 1744).
|
||||
#[test]
|
||||
fn a_row_click_takes_rather_than_continues() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(1024.0, 768.0);
|
||||
load_xml(&s, "Fonts.xml");
|
||||
load_xml(&s, "MoneyFrame.xml");
|
||||
load_xml(&s, "UiPanels.xml");
|
||||
load_xml(&s, "GameTooltip.xml");
|
||||
load_xml(&s, "UIDropDownMenu.xml");
|
||||
load_xml(&s, "LootFrame.xml");
|
||||
s.set_loot(Some(coin_and_two_items()));
|
||||
s.fire_event("LOOT_OPENED", vec![]);
|
||||
let quads = s.extract();
|
||||
|
||||
let (ix, iy) = icon_center(&quads, "INV_Fabric_Wool_01");
|
||||
s.mouse_button(ix, iy, "LeftButton", true);
|
||||
s.mouse_button(ix, iy, "LeftButton", false);
|
||||
assert_eq!(s.take_loot_picks(), vec![2], "the click is a TAKE");
|
||||
assert!(
|
||||
s.take_loot_confirms().is_empty(),
|
||||
"and never a continuation"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
@@ -182,13 +182,13 @@ fn a_single_rank_line_paints_gray_with_no_rank_text() {
|
||||
s.run("SetSelectedSkill(2) BenillaSkillFrame_Update()")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
s.eval::<String>("return SkillDetailBarSkillRank:GetText() or \"\"")
|
||||
s.eval::<String>("return SkillDetailStatusBarSkillRank:GetText() or \"\"")
|
||||
.unwrap(),
|
||||
"",
|
||||
"the detail pane's own bar is a proficiency too"
|
||||
);
|
||||
assert_eq!(
|
||||
s.eval::<(f32, f32, f32, f32)>("return SkillDetailBarBackground:GetVertexColor()")
|
||||
s.eval::<(f32, f32, f32, f32)>("return SkillDetailStatusBarBackground:GetVertexColor()")
|
||||
.unwrap(),
|
||||
(1.0, 1.0, 1.0, 0.5),
|
||||
"including its trough (the shared PaintBar; ref SkillFrame.lua:376)"
|
||||
@@ -401,11 +401,13 @@ fn the_collapse_all_fold_wears_the_row_font_and_the_references_seat() {
|
||||
);
|
||||
|
||||
let (tab_top, tab_bottom) = s
|
||||
.eval::<(f64, f64)>("return SkillExpandTabLeft:GetTop(), SkillExpandTabLeft:GetBottom()")
|
||||
.eval::<(f64, f64)>(
|
||||
"return SkillFrameExpandTabLeft:GetTop(), SkillFrameExpandTabLeft:GetBottom()",
|
||||
)
|
||||
.unwrap();
|
||||
let (btn_top, btn_bottom, btn_left) = s
|
||||
.eval::<(f64, f64, f64)>(
|
||||
"local b = SkillCollapseAllButton \
|
||||
"local b = SkillFrameCollapseAllButton \
|
||||
return b:GetTop(), b:GetBottom(), b:GetLeft()",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -415,7 +417,7 @@ fn the_collapse_all_fold_wears_the_row_font_and_the_references_seat() {
|
||||
"the fold sits 3px BELOW the cap's centre (ref's -3); +3 above was the bug"
|
||||
);
|
||||
let cap_right = s
|
||||
.eval::<f64>("return SkillExpandTabLeft:GetRight()")
|
||||
.eval::<f64>("return SkillFrameExpandTabLeft:GetRight()")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
btn_left - cap_right,
|
||||
@@ -434,7 +436,7 @@ fn the_collapse_all_fold_wears_the_row_font_and_the_references_seat() {
|
||||
fn the_expand_tab_fits_its_label_once_a_measure_answers() {
|
||||
let mut s = shown_skills_page();
|
||||
assert_eq!(
|
||||
s.eval::<f64>("return SkillExpandButtonFrame:GetWidth()")
|
||||
s.eval::<f64>("return SkillFrameExpandButtonFrame:GetWidth()")
|
||||
.unwrap(),
|
||||
54.0,
|
||||
"unmeasured, the declared width stands — a 0 measure must not squash the tab to 45"
|
||||
@@ -444,7 +446,7 @@ fn the_expand_tab_fits_its_label_once_a_measure_answers() {
|
||||
s.run("BenillaSkillFrame_Update()").unwrap();
|
||||
s.resolve();
|
||||
assert_eq!(
|
||||
s.eval::<f64>("return SkillExpandButtonFrame:GetWidth()")
|
||||
s.eval::<f64>("return SkillFrameExpandButtonFrame:GetWidth()")
|
||||
.unwrap(),
|
||||
7.0 * 3.0 + 45.0,
|
||||
"then the ref's own law: the label's width + 45"
|
||||
@@ -452,8 +454,8 @@ fn the_expand_tab_fits_its_label_once_a_measure_answers() {
|
||||
// The middle slab is the span between the two caps, so the fit reaches the art for free.
|
||||
let (mid_l, mid_r, cap_r_l) = s
|
||||
.eval::<(f64, f64, f64)>(
|
||||
"return SkillExpandTabMiddle:GetLeft(), SkillExpandTabMiddle:GetRight(), \
|
||||
SkillExpandTabRight:GetLeft()",
|
||||
"return SkillFrameExpandTabMiddle:GetLeft(), SkillFrameExpandTabMiddle:GetRight(), \
|
||||
SkillFrameExpandTabRight:GetLeft()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(mid_r, cap_r_l, "the middle stretches to the right cap");
|
||||
|
||||
Reference in New Issue
Block a user