Files
benilla-octo/crates/benilla-app/src/ui_script/quest_share_tests.rs
T
SamandClaude Fable 5.1 c5df478c51 the buff bar goes stock, and gossip quests show their level
- the buff bar is the reference's own `BuffFrame.xml`, and the swap found an engine defect: an empty `<OnLoad>` in 1.12's XML is a BLANK that takes an inherited handler away again (seven such sites across four stock files), which our loader read as "none given" — so the two enchant slots would have painted the player's first buff in the corner and cancelled it on right-click; the first fix then had to be corrected at the bytes (`SetScript 0x7025c0` tests only NULL and empty, and nothing trims, so `<OnLoad>↵</OnLoad>` compiles to a valid empty function rather than storing nil, which any addon calling `GetScript` can observe); `SHOW_BUFF_DURATIONS` is applied once post-load because 1.12 leaves that to `UIOptionsFrame.lua`, which benilla does not build — without it the bar ships a 10px pitch error on every buff
- gossip quests come through the reference's four verbs — `GetGossipAvailableQuests`/`GetGossipActiveQuests` returning (title, level) PAIRS, with the two `Select*` verbs indexing into that list — replacing three invented names that sat on the beyond-1.12 exception list; the quest LEVEL was on the wire, parsed and dropped because the invented verb had nowhere to put it, and the reference's order (available before active, whatever the packet interleaved) comes with them
- eight more files off the chain as groundwork: `LocaleProperties.lua` (the gendered/plural `GetText` selector that eleven stock call sites read their words through — fifty lines of FrameXML that looked like an engine binding), `CinematicFrame.xml`, the UIMenu kit, the `UnitFrame.lua`/`CombatFeedback.lua` kits (the only two names blocking the stock player, party, target and pet frames), the money-input kit, and `ItemButtonTemplate.xml` — the base every bag, bank, merchant, loot, trade and mail slot inherits; `GetMerchantItemMaxStack` (on the wire the whole time, dropped) and the two trade item-link verbs join the engine
- two instruments for the migration: a gap report that reads a stock window's call sites against a LOADED VM and says which engine verbs it still needs (28 windows need none, the rest name 1–5), and the readiness probe's documented false positive — loading a stock window on top of our same-named one leaves `getglobal(name)` pointing at ours while the stock handlers run against theirs, so a failure inside a colliding name is a question, not an answer

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 16:58:47 +07:00

102 lines
3.9 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, "Fonts.xml");
load_xml(&s, "MoneyFrame.xml");
load_xml(&s, "UiPanels.xml");
load_xml(&s, "QuestShareFrame.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"
);
}