Files
benilla-octo/crates/benilla-app/src/ui_script/screenshot_tests.rs
T
SamandClaude Fable 5.1 7a825111ab the world map's arrow shows on zone sheets, and UIParent goes stock
- the world map's arrow shows on every zone sheet, and alt-clicking a town jumps there: the stock file creates the player arrow as a `Model` child of `WorldMapFrame` and lays a zone sheet's explored-area overlays as ARTWORK textures on `WorldMapDetailFrame` — one `(strata, level)` bucket, where the draw layer outranks the frame — and benilla drew a model's scene at the frame's own layer-0 slot, so every overlay painted over it (the continent sheet has no overlays, which is why it was the only sheet with an arrow); wow-re's carve: a layer batch carries three sub-arrays — quads, the string batch, a render-callback list — that `0x76fb00` drains in that order, and `0x76d160` registers a model's callback only for layer 2 (`0x76d17f cmp ebx,2`), so a model's scene is ARTWORK content, neither a separate pass nor a layer-0 slot; a `ModelPane` quad now keys at the ARTWORK content rank, which re-orders the pet page's happiness icon (a sibling's BACKGROUND quad) under the pane's scene — the reference's own order, harmless because the bake is transparent where the model is not; a model against a LATER-linked ARTWORK font string in the same bucket is still ordered by link stamp, named not built; and the alt-click jump, which required the topmost frame under the cursor to be `WorldMapButton` itself while the stock file parents every POI icon and blip to that button, now walks up from whatever was hit to the button and takes its rect — the map's own chrome still answers nothing (1992)
- `UIParent` is the reference's own: `UIParent.xml` loads off the chain at its toc's slot, right after `WorldFrame.xml`, sourcing `UIParent.lua` and `LocaleProperties.lua` itself, and our `UIParent.xml` + `UiPanels.xml` — 3086 lines of transcribed `UIParent.lua` — are deleted; the one unbound verb the stock file ticks, `CheckReadyCheckTime`, was already 1989's; back from the reference: all sixty-six registered events and their arms (`PLAYER_CONTROL_LOST`/`_GAINED`, `RAID_ROSTER_UPDATE`, the trade-request pair, `PLAYER_SKINNED`, `GOSSIP_ENTER_CODE`), the ESC ladder as written — StaticPopup, the options window's Cancel, the game menu, `CloseMenus`, `SpellStopCasting`, `SpellStopTargeting`, `CloseAllWindows`, `ClearTarget`, else open — so ESC no longer clears a held cursor item or cancels the colour picker, two arms ours had grown that the reference does not have; the fade/flash/pulse kits walked from `UIParent`'s own OnUpdate in place of three 1×1 driver frames of ours, so a cinematic's `UIParent:Hide()` stops the walks as it does there; the managed bottom-stack positions without our `UIParent_RegisterManagedPositionListener` extension, which no shipped file registered any more; `LowHealthFrame`/`OutOfControlFrame`; and the `firstTimeLoaded` close on re-show; what stayed ours moved out — the window tab template and its OnUpdate settle (1004's async-measure adapter) and `BENILLA_FALLBACK_ICON` to `ScrollTemplates.xml`, 0561's bag verbs to `ContainerFrameAdapters`, the script log's slot row to its own file, `OptionsFrameCancel = OptionsFrameCloseButton` beside the two options aliases — and the Auction/Inspect/Macro/Talent panel rows, verbatim copies of the LoadOnDemand addons' own registrations, go; the reference also decides that the area spirit healer's dialog accepts on show with one CANCEL button, that losing control closes every window bar the reference's exceptions, and that the bottom-bar clearance reads the saved globals `SHOW_MULTI_ACTIONBAR_1`/`_2` rather than the bars' shown state; three integration binaries that kept private copies of the manifest loader use the shared one now (1988)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:30:54 +07:00

122 lines
4.2 KiB
Rust

//! Print screen's UI half — the stock `WorldFrame.xml`'s `ScreenshotStatus` (decision 1983) — and above
//! all **B261's third clause: the "Screen Captured" line must not be in the file it announces.**
//!
//! That contract is an ORDERING, so it is tested as one. Two paths could put text in a picture and
//! both are pinned here: the ordinary press (nothing is shown until the engine reports back, which
//! it can only do after the readback) and the double press inside the 1.5 s fade (the previous
//! shot's line is still up, and `TakeScreenshot` takes it down *before* asking).
use benilla_ui::script::UiScript;
use super::test_ui::load_ui as load_xml;
fn harness() -> UiScript {
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
for f in [
"Interface\\FrameXML\\Fonts.xml",
r"Interface\FrameXML\MoneyFrame.lua",
r"Interface\FrameXML\MoneyFrame.xml",
r"Interface\FrameXML\UIParent.xml",
r"Interface\FrameXML\UIPanelTemplates.lua",
r"Interface\FrameXML\UIPanelTemplates.xml",
"Interface\\FrameXML\\GlobalStrings.lua",
"Interface\\FrameXML\\BasicControls.xml",
"Interface\\FrameXML\\LocaleProperties.lua",
"Interface\\FrameXML\\StaticPopup.xml",
r"Interface\FrameXML\WorldFrame.xml",
] {
load_xml(&s, f);
}
s
}
fn shown(s: &UiScript) -> bool {
s.eval::<bool>("return ScreenshotStatus:IsVisible()")
.unwrap()
}
fn text(s: &UiScript) -> String {
s.eval::<String>("return ScreenshotStatusText:GetText() or \"\"")
.unwrap()
}
fn alpha(s: &UiScript) -> f64 {
s.eval::<f64>("return ScreenshotStatus:GetAlpha()").unwrap()
}
/// **The bug, as a test.** A press asks the engine and shows NOTHING; the line appears only when
/// the engine reports back, which in the real client is frames later — so the frame that was
/// captured cannot contain it.
#[test]
fn the_capture_is_asked_for_silently_and_only_the_answer_speaks() {
let mut s = harness();
assert!(!shown(&s), "the status line starts hidden");
s.run("TakeScreenshot()").unwrap();
assert_eq!(
s.take_screenshot_asks(),
1,
"the binding body reaches the engine verb"
);
assert!(
!shown(&s),
"NOTHING is on screen at the moment of capture — this is B261's whole contract"
);
// The engine's answer, one or more frames later.
s.fire_event("SCREENSHOT_SUCCEEDED", Vec::new());
assert!(shown(&s));
assert_eq!(text(&s), "Screen Captured");
assert_eq!(alpha(&s), 1.0);
}
/// **The second leak path, and the reason `TakeScreenshot` hides rather than waiting for the
/// fade.** Press again while the last confirmation is still on screen and it comes off BEFORE the
/// 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() {
let mut s = harness();
s.fire_event("SCREENSHOT_SUCCEEDED", Vec::new());
s.tick(0.5);
assert!(shown(&s), "half a second in, the line is still up");
assert!(alpha(&s) < 1.0, "and already fading");
s.run("TakeScreenshot()").unwrap();
assert!(
!shown(&s),
"hidden BEFORE the engine is asked, not after it answers"
);
assert_eq!(s.take_screenshot_asks(), 1);
// The new answer restarts the fade at full rather than inheriting the old alpha.
s.fire_event("SCREENSHOT_SUCCEEDED", Vec::new());
assert_eq!(alpha(&s), 1.0);
}
/// 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() {
let mut s = harness();
s.fire_event("SCREENSHOT_SUCCEEDED", Vec::new());
s.tick(0.75);
let half = alpha(&s);
assert!(
(half - 0.5).abs() < 1e-3,
"halfway through 1.5 s the line is at half alpha, got {half}"
);
s.tick(0.75);
assert!(!shown(&s), "and it is gone at the end of the fade");
}
/// The failure path says so, in the reference's own words.
#[test]
fn a_failed_capture_shows_the_failure_string() {
let mut s = harness();
s.fire_event("SCREENSHOT_FAILED", Vec::new());
assert!(shown(&s));
assert_eq!(text(&s), "Screen Capture Failed");
}