the paper doll steps its feet round, and the Raid Info button behaves
- rotating the paper doll plays the reference's turn-in-place shuffle instead of spinning it like a turntable — ShuffleLeft/Right picked by direction off the same scalar `SetRotation` is handed, a 100 ms expiry, a frequency-weighted idle variation on the way back; wow-re's re-derivation caught the pair inverted in the prose (the doll was stepping into its turn), and a display with no shuffle stays standing rather than freezing mid-turn - the doll's item effects run at full speed: the half-rate booth render (our pacing, not a cull) was skipping the emitter tick with the frame; measured off the reporter's side-by-side clip the skeletal clock was never slow, only the 30 fps cadence, so `boothHalfRate` now defaults off - the Raid Info button follows the server's answer: `UPDATE_INSTANCE_INFO` fires per packet as the client does (the reference discards the first), so a character bound to nothing ends with a grey button over an empty panel instead of a live button over nothing; the panel's scroll bar sits centred on the trough drawn behind it - an inspected player's gear keeps the template's bind line, stated as deliberate — there is no item object to read a bound flag from, which is what the reference prints for the same reason Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -755,6 +755,119 @@ fn the_raid_info_panel_lists_the_saved_lockouts() {
|
||||
assert!(!visible(&s, "RaidInfoFrame"), "and closes it again");
|
||||
}
|
||||
|
||||
/// **The player with no lockouts at all** — the case that shipped broken (1561). Two answers, both
|
||||
/// empty, is the whole ordinary session: one for `PLAYER_ENTERING_WORLD`'s `RequestRaidInfo` and
|
||||
/// one for the pane's own on show. The first arms `RaidFrame.hasRaidInfo` and returns; the second
|
||||
/// is the one that has to put the button away and empty the panel behind it.
|
||||
///
|
||||
/// Its sibling above proves the same arithmetic from a list that had something in it. This one
|
||||
/// proves it from a list that never did, which is the case a *diff* cannot reach — and the button
|
||||
/// left live over an empty panel is exactly what the director saw.
|
||||
#[test]
|
||||
fn a_player_with_no_lockouts_loses_the_raid_info_button_on_the_second_answer() {
|
||||
let mut s = setup();
|
||||
open_raid_tab(&mut s);
|
||||
|
||||
// Answer one: the latch, and nothing else. The button is still whatever it loaded as — the
|
||||
// reference does not disable it in `RaidFrame_OnLoad` either, so this window is its own.
|
||||
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return RaidFrame.hasRaidInfo or 0").unwrap(),
|
||||
1,
|
||||
"the first answer only arms the latch"
|
||||
);
|
||||
|
||||
// Answer two, saying the same nothing.
|
||||
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled() and 1 or 0")
|
||||
.unwrap(),
|
||||
0,
|
||||
"an empty lockout list is a dead button"
|
||||
);
|
||||
|
||||
// And the panel behind it is emptied, not left in the state the XML loaded: every row away,
|
||||
// and no scroll bar standing over a list of nothing.
|
||||
s.run("RaidInfoFrame:Show()").unwrap();
|
||||
assert!(!visible(&s, "RaidInfoInstance1"));
|
||||
assert!(!visible(&s, "RaidInfoInstance10"));
|
||||
assert!(!visible(&s, "RaidInfoScrollFrameScrollBar"));
|
||||
s.run("RaidInfoFrame:Hide()").unwrap();
|
||||
|
||||
// The button is dead to a real click, not merely drawn grey — the half the director asked for
|
||||
// by name. Driven through the pointer, because that is the path a press actually takes.
|
||||
let (bx, by) = {
|
||||
let l: f32 = s.eval("return RaidFrameRaidInfoButton:GetLeft()").unwrap();
|
||||
let r: f32 = s.eval("return RaidFrameRaidInfoButton:GetRight()").unwrap();
|
||||
let t: f32 = s.eval("return RaidFrameRaidInfoButton:GetTop()").unwrap();
|
||||
let b: f32 = s
|
||||
.eval("return RaidFrameRaidInfoButton:GetBottom()")
|
||||
.unwrap();
|
||||
((l + r) / 2.0, (t + b) / 2.0)
|
||||
};
|
||||
s.mouse_button(bx, by, "LeftButton", true);
|
||||
s.mouse_button(bx, by, "LeftButton", false);
|
||||
s.tick(0.016);
|
||||
assert!(
|
||||
!visible(&s, "RaidInfoFrame"),
|
||||
"a disabled button does not open the panel"
|
||||
);
|
||||
}
|
||||
|
||||
/// The scroll bar the fifth lockout brings on has to land on the trough drawn behind it, not where
|
||||
/// `UIPanelScrollFrameTemplate` seats a bare ScrollFrame's bar (1561).
|
||||
///
|
||||
/// Geometry, asserted as the relationship rather than as four numbers: the bar is CENTRED in the
|
||||
/// trough art on X, and its top is the panel's own -3 rather than the template's -16. Both were
|
||||
/// wrong before — 2 px left, 13 px low — and neither is visible to any test that only asks whether
|
||||
/// the bar is shown.
|
||||
#[test]
|
||||
fn the_scroll_bar_is_seated_on_the_trough_the_panel_draws_behind_it() {
|
||||
let mut s = setup();
|
||||
open_raid_tab(&mut s);
|
||||
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
|
||||
s.set_saved_instances(
|
||||
(1..=6)
|
||||
.map(|i| SavedInstanceInfo {
|
||||
name: format!("Instance {i}"),
|
||||
instance: 1000 + i,
|
||||
reset: i * 3_600,
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
|
||||
s.run("RaidInfoFrame:Show()").unwrap();
|
||||
assert!(
|
||||
visible(&s, "RaidInfoScrollFrameScrollBar"),
|
||||
"six lockouts do not fit in four rows"
|
||||
);
|
||||
|
||||
let mid_x = |s: &UiScript, f: &str| -> f32 {
|
||||
let l: f32 = s.eval(&format!("return {f}:GetLeft()")).unwrap();
|
||||
let r: f32 = s.eval(&format!("return {f}:GetRight()")).unwrap();
|
||||
(l + r) / 2.0
|
||||
};
|
||||
let top = |s: &UiScript, f: &str| -> f32 { s.eval(&format!("return {f}:GetTop()")).unwrap() };
|
||||
|
||||
let bar = mid_x(&s, "RaidInfoScrollFrameScrollBar");
|
||||
let trough = mid_x(&s, "RaidInfoScrollFrameTop");
|
||||
assert!(
|
||||
(bar - trough).abs() < 0.01,
|
||||
"the bar rides the middle of its trough: bar {bar} vs trough {trough}"
|
||||
);
|
||||
assert!(
|
||||
(top(&s, "RaidInfoScrollFrameScrollBar") - (top(&s, "RaidInfoScrollFrame") - 3.0)).abs()
|
||||
< 0.01,
|
||||
"and hangs 3 px under the frame's top, not the template's 16"
|
||||
);
|
||||
// The up arrow rides above the bar, so re-seating the bar is what lifts it into the trough's
|
||||
// own cap — the piece of this that is actually visible.
|
||||
assert!(
|
||||
top(&s, "RaidInfoScrollFrameScrollBarScrollUpButton") > top(&s, "RaidInfoScrollFrame"),
|
||||
"the up arrow clears the scroll frame, where the template left it 13 px inside"
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────────────────────
|
||||
// The ready check
|
||||
// ─────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user