diff --git a/crates/benilla-app/src/ui_script/battlefield_minimap_tests.rs b/crates/benilla-app/src/ui_script/battlefield_minimap_tests.rs index cd03be6c..65911a0f 100644 --- a/crates/benilla-app/src/ui_script/battlefield_minimap_tests.rs +++ b/crates/benilla-app/src/ui_script/battlefield_minimap_tests.rs @@ -8,9 +8,16 @@ use benilla_ui::script::{ WorldStateUiView, ARROW_MODEL, }; -/// The manifest's one warning. Deviation: `gxRefresh`, read at `OptionsFrame.lua:300`, is not -/// registered, because nothing here can set a refresh rate. -const MANIFEST_WARNINGS: [&str; 1] = ["unknown CVar 'gxRefresh' (not host-registered) — ignored"]; +/// The whole manifest, loaded, warns about **nothing**. +/// +/// It held one row until now: `gxRefresh`, which this client does not register because no target +/// offers the exclusive mode-set a refresh rate needs. The reader was stock `OptionsFrame.lua`'s +/// `OptionsFrameRefreshDropDown_OnLoad` (l.300) — and the realm has replaced that file with +/// Turtle's rewrite, which has no refresh-rate dropdown and reads the CVar nowhere: 3 mentions in +/// `patch.MPQ`'s stock copy, **0** in `patch-4.mpq`'s. So the deviation is still a deviation and +/// nothing reaches it, which is why this list is empty rather than deleted — a realm that puts the +/// stock window back brings the row back with it, and this is where that shows. +const MANIFEST_WARNINGS: [&str; 0] = []; fn quiet(s: &UiScript) { assert!(s.errors().is_empty(), "script errors: {:#?}", s.errors()); diff --git a/crates/benilla-app/src/ui_script/questlog_tests.rs b/crates/benilla-app/src/ui_script/questlog_tests.rs index c3a34684..b52bbfce 100644 --- a/crates/benilla-app/src/ui_script/questlog_tests.rs +++ b/crates/benilla-app/src/ui_script/questlog_tests.rs @@ -3,23 +3,32 @@ //! the list and detail scroll frames inherit (`QuestLogFrame.xml:589`, `:606`). use benilla_ui::script::{ - ExtractedQuad, PartyMemberInfo, PartyState, QuadContent, QuestItemView, QuestLogDetail, - QuestLogEntryView, QuestLogObjectiveView, QuestLogState, SoundRequest, UiScript, + PartyMemberInfo, PartyState, QuadContent, QuestItemView, QuestLogDetail, QuestLogEntryView, + QuestLogObjectiveView, QuestLogState, SoundRequest, UiScript, }; use super::test_ui::load_ui as load_xml; /// The rect of the frame sized `w` by `h`, from the `QuadContent::Frame` quad every frame emits. -fn frame_rect(quads: &[ExtractedQuad], w: f32, h: f32) -> benilla_ui::layout::Rect { - quads +/// A named frame's own resolved rect, asked of the frame instead of hunted for among the quads by +/// size. +/// +/// This replaces a `frame_rect(quads, w, h)` that hunted the quad list for a bare frame of a +/// given size, because a size written in a test is a number the realm can change: the detail +/// scroll frame was 300x261 and the window 384x512 when these tests were written, and the realm's +/// `QuestLogFrame` is a different shape now — it shows `QUESTS_DISPLAYED = 24` rows where 1.12 +/// shows six. Five tests died looking for quads that no longer exist. Nothing any of them asserts +/// is about the size: they clip reward plates and text against the pane and compare the window's +/// own edges, so the frame is asked where it is. +fn named_rect(s: &UiScript, name: &str) -> benilla_ui::layout::Rect { + let edges: Vec = ["GetBottom", "GetLeft", "GetTop", "GetRight"] .iter() - .find_map(|q| match &q.content { - QuadContent::Frame => q - .rect - .filter(|r| (r.width() - w).abs() < 0.5 && (r.height() - h).abs() < 0.5), - _ => None, + .map(|getter| { + s.eval::(&format!("return {name}:{getter}()")) + .unwrap_or_else(|e| panic!("{name}:{getter}(): {e}")) }) - .unwrap_or_else(|| panic!("no bare-frame quad sized {w}x{h}")) + .collect(); + benilla_ui::layout::Rect::new(edges[0], edges[1], edges[2], edges[3]) } thread_local! { @@ -44,10 +53,21 @@ thread_local! { }; } -/// Eight quests, two more than the list shows (`QUESTS_DISPLAYED`, `QuestLogFrame.lua:1`); only -/// quest 1 has an objective, and each has its own `quest_id`, the watch set's key. +/// Eight quests; only quest 1 has an objective, and each has its own `quest_id`, the watch set's +/// key. +/// +/// Eight **no longer overflows the list**: the realm shows `QUESTS_DISPLAYED = 24` rows at once +/// where 1.12 shows 6, so a fixture this size fits with room to spare and nothing about it can +/// scroll. Anything testing the scroll seats [`entries`] off the live global instead — see +/// `the_wheel_scrolls_a_list_longer_than_the_realm_displays`. fn eight_entries() -> QuestLogState { - let entries = (1..=8) + entries(8) +} + +/// `n` quests in the shape [`eight_entries`] describes, so a test that needs to overflow the list +/// can size itself off `QUESTS_DISPLAYED` rather than off 1.12's row count. +fn entries(n: u32) -> QuestLogState { + let entries = (1..=n) .map(|i| QuestLogEntryView { quest_id: i, title: format!("Quest {i}"), @@ -70,7 +90,7 @@ fn eight_entries() -> QuestLogState { }) .collect(); QuestLogState { - num_quests: 8, + num_quests: n, entries, hidden_quest_ids: Vec::new(), } @@ -153,10 +173,16 @@ fn shipped_questlog_frame_drives_end_to_end() { .eval::("return QuestLogTitle1NormalText:GetText()") .unwrap() .contains("Quest 1")); + // The cap off the global, for the reason `the_quest_count_reads_the_realms_own_cap` gives + // below: OctoWoW's `QuestLogFrame.lua` sets `MAX_QUESTLOG_QUESTS = 25`, and pinning 1.12's 20 + // here was the same mistake made twice in one file. + let cap = s + .eval::("return MAX_QUESTLOG_QUESTS") + .expect("the realm's quest-log cap"); assert_eq!( s.eval::("return QuestLogQuestCount:GetText()") .unwrap(), - "Quests: |cffffffff8/20|r" + format!("Quests: |cffffffff8/{cap}|r") ); assert_eq!(s.eval::("return GetQuestLogSelection()").unwrap(), 1); @@ -201,7 +227,21 @@ fn shipped_questlog_frame_drives_end_to_end() { // Choices and rewards share one pool: item 1 is the reward, item 2 unused. assert!(!s.eval::("return QuestLogItem2:IsShown()").unwrap()); - // Wheel down over row 3, a list row (the detail pane shows quest 1). + // Wheel down over a list row — on a log long enough to scroll. + // + // Eight quests used to be "two more than the list shows", which was 1.12's six. The realm's + // `QuestLogFrame.lua` shows **`QUESTS_DISPLAYED = 24`**, so eight fit with room to spare and + // the notch had nothing to move: this `assert_ne!` was passing on a stale row count and went + // red the moment the count was read off the realm. So the log is re-seated past the window + // for this leg, sized off the live global rather than off any number written here, and put + // back to the eight the rest of the test reads. + let displayed = s + .eval::("return QUESTS_DISPLAYED") + .expect("the realm's visible row count"); + #[allow(clippy::cast_sign_loss)] + let seated = u32::try_from(displayed).expect("a positive row count") + 4; + s.set_quest_log(entries(seated)); + s.run("QuestLog_Update()").unwrap(); s.resolve(); let quads = s.extract(); let (wx, wy) = quads @@ -223,8 +263,16 @@ fn shipped_questlog_frame_drives_end_to_end() { s.eval::("return QuestLogTitle1NormalText:GetText()") .unwrap(), before, - "wheel-down scrolled the list" + "wheel-down scrolled {seated} quests in a {displayed}-row window" ); + // Back to the eight, scrolled to the top and quest 1 selected, which is the state the rest of + // this test was written against. + s.set_quest_log(eight_entries()); + s.run( + "QuestLogListScrollFrameScrollBar:SetValue(0) QuestLog_Update() QuestLog_SetSelection(1)", + ) + .unwrap(); + s.resolve(); assert_eq!(s.eval::("return GetQuestLogSelection()").unwrap(), 1); // Abandon, then No: the ABANDON_QUEST popup closes and queues nothing. @@ -338,7 +386,7 @@ fn shift_click_toggles_the_watch_checkbox_and_the_tracker_hud() { // Row 1, quest 1, the one with an objective: 300x16 at the window's TOPLEFT +19,-75 // (`QuestLogFrame.xml:4-7`, `:535-539`). s.resolve(); - let win = frame_rect(&s.extract(), 384.0, 512.0); + let win = named_rect(&s, "QuestLogFrame"); let (rx, ry) = (win.left + 19.0 + 150.0, win.top - 75.0 - 8.0); s.set_modifiers(true, false, false); s.mouse_button(rx, ry, "LeftButton", true); @@ -451,7 +499,7 @@ fn watch_guards_no_op_without_erroring() { // Rows are 300x16 from the window's TOPLEFT +19,-75, each overlapping the last by 1, a 15 px // pitch (`QuestLogFrame.xml:535-548`). s.resolve(); - let win = frame_rect(&s.extract(), 384.0, 512.0); + let win = named_rect(&s, "QuestLogFrame"); let row_center = |n: u32| win.top - 75.0 - 15.0 * (n - 1) as f32 - 8.0; let x = win.left + 19.0 + 150.0; @@ -1010,7 +1058,7 @@ fn overflowing_detail_content_clips_to_the_scrollframe_rect() { s.resolve(); let quads = s.extract(); - let scroll_rect = frame_rect(&quads, 300.0, 261.0); + let scroll_rect = named_rect(&s, "QuestLogDetailScrollFrame"); let reward_plate_clips: Vec> = quads .iter() @@ -1082,8 +1130,7 @@ fn wheel_over_the_detail_pane_changes_vertical_scroll() { s.resolve(); // the measures reach the scroll range and the bar s.resolve(); - let quads = s.extract(); - let scroll_rect = frame_rect(&quads, 300.0, 261.0); + let scroll_rect = named_rect(&s, "QuestLogDetailScrollFrame"); // Near the pane's top-left, clear of the reward row. let (x, y) = (scroll_rect.left + 10.0, scroll_rect.top - 10.0); @@ -1441,7 +1488,7 @@ fn shift_click_on_a_title_posts_the_quest_name_with_chat_open_and_watches_with_i // Row 1's centre: 300x16 at the window's TOPLEFT +19,-75. s.resolve(); - let win = frame_rect(&s.extract(), 384.0, 512.0); + let win = named_rect(&s, "QuestLogFrame"); let (rx, ry) = (win.left + 19.0 + 150.0, win.top - 75.0 - 8.0); let shift_click = |s: &mut UiScript| { s.set_modifiers(true, false, false);