Files
benilla-octo/crates/benilla-app/src/ui_script/quest_share_tests.rs
T
SamandClaude Fable 5.1 e8db9ef14c 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

108 lines
4.3 KiB
Rust

//! The escort-quest confirm (decision 1733, `QuestShareFrame.xml`): the Lua wiring between the
//! `QUEST_ACCEPT_CONFIRM` event `ui_quest_share`'s feed fires and the shared StaticPopup engine,
//! driven exactly as that feed drives it.
//!
//! The asymmetry these tests pin is the reference's: **Yes sends, No does not**. There is no
//! decline packet for an escort confirm, so a test that only checked "the popup closes" would pass
//! against a version that silently answered No on the wire.
use benilla_ui::script::{ScriptValue, UiScript};
use super::test_ui::load_ui as load_xml;
fn setup() -> UiScript {
let mut s = UiScript::new().unwrap();
s.set_screen_size(1024.0, 768.0);
load_xml(&s, "Interface\\FrameXML\\Fonts.xml");
load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua");
load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml");
load_xml(&s, r"Interface\FrameXML\UIParent.xml");
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua");
load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml");
load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua");
load_xml(&s, r"Interface\FrameXML\BasicControls.xml");
load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua");
load_xml(&s, r"Interface\FrameXML\StaticPopup.xml");
s
}
fn confirm(s: &mut UiScript, who: &str, quest: &str) {
s.fire_event(
"QUEST_ACCEPT_CONFIRM",
vec![
ScriptValue::Str(who.to_string()),
ScriptValue::Str(quest.to_string()),
],
);
}
/// The event raises the popup with BOTH args filled, in the reference's order: the player first,
/// the quest title second (`QUEST_ACCEPT = "%s is starting %s\nWould you like to as well?"`). A
/// swap would read "Escort Duty is starting Thrall", which is exactly the kind of wrong that looks
/// right at a glance.
#[test]
fn the_confirm_names_the_player_first_and_the_quest_second() {
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
assert!(
s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
"QUEST_ACCEPT_CONFIRM shows the confirm popup"
);
assert_eq!(
s.eval::<String>("return StaticPopup1Text:GetText()")
.unwrap(),
"Thrall is starting Escort Duty\nWould you like to as well?"
);
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
}
/// Yes queues exactly one `ConfirmAcceptQuest()` and closes the popup.
#[test]
fn yes_answers_once() {
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
s.run("StaticPopup_OnClick(StaticPopup1, 1)").unwrap();
assert_eq!(s.take_quest_confirms(), 1);
assert_eq!(s.take_quest_confirms(), 0, "drained");
assert!(
!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap(),
"answering closes it"
);
}
/// **No sends nothing.** The reference's `QUEST_ACCEPT` entry has an `OnAccept` and no `OnCancel`
/// at all (`StaticPopup.lua:727-737`), because there is no decline packet — the server's pending
/// latch is cleared by the next thing that touches it. Same for ESC.
#[test]
fn no_and_escape_send_nothing() {
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
s.run("StaticPopup_OnClick(StaticPopup1, 2)").unwrap();
assert_eq!(s.take_quest_confirms(), 0, "No must not answer the server");
assert!(!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
confirm(&mut s, "Thrall", "Escort Duty");
s.run("StaticPopup_EscapePressed()").unwrap();
assert_eq!(s.take_quest_confirms(), 0, "ESC must not answer the server");
assert!(!s.eval::<bool>("return StaticPopup1:IsVisible()").unwrap());
}
/// A second confirm replaces the first rather than stacking — the server keeps ONE share latch per
/// player, so an older question is already dead by the time a newer one arrives, and answering the
/// stale popup would send the wrong quest id.
#[test]
fn a_second_confirm_replaces_the_first() {
let mut s = setup();
confirm(&mut s, "Thrall", "Escort Duty");
confirm(&mut s, "Jaina", "Deeper Still");
assert_eq!(
s.eval::<String>("return StaticPopup1Text:GetText()")
.unwrap(),
"Jaina is starting Deeper Still\nWould you like to as well?"
);
assert!(
!s.eval::<bool>("return StaticPopup2:IsVisible()").unwrap(),
"the confirm is exclusive — it reuses the one slot"
);
}