using System.Collections.Generic; using System.Linq; using AcDream.App.UI; using AcDream.UI.Abstractions; namespace AcDream.App.Tests.UI; public class UiMenuTests { // PopupH = RowsPerColumn(7) * RowHeight(17) = 119; popup opens upward so top = -119. // Item idx -> col = idx/7, row = idx%7; row band y in [top+row*17, top+(row+1)*17). // Right column needs lx >= ColumnWidth(191) + Border(5) = lx >= 196 after bevel offset, // but the original tests used lx=200 which maps ix=195 -> col=(int)(195/191)=1. OK. // The 14 channel items verbatim (matches ChannelItems in ChatWindowController). private static readonly UiMenu.MenuItem[] ChannelItems = { new("Squelch (ignore)", (object?)null), new("Tell to Selected", (object?)null), new("Chat to All", (object?)ChatChannelKind.Say), new("Tell to Fellows", (object?)ChatChannelKind.Fellowship), new("Tell to General Chat", (object?)ChatChannelKind.General), new("Tell to LFG Chat", (object?)ChatChannelKind.Lfg), new("Tell to Society Chat", (object?)ChatChannelKind.Society), new("Tell to Monarch", (object?)ChatChannelKind.Monarch), new("Tell to Patron", (object?)ChatChannelKind.Patron), new("Tell to Vassals", (object?)ChatChannelKind.Vassals), new("Tell to Allegiance", (object?)ChatChannelKind.Allegiance), new("Tell to Trade Chat", (object?)ChatChannelKind.Trade), new("Tell to Roleplay Chat", (object?)ChatChannelKind.Roleplay), new("Tell to Olthoi Chat", (object?)ChatChannelKind.Olthoi), }; // Availability gate identical to ChatWindowController.ChannelAvailable. private static bool ChannelAvailable(object? p) => p is ChatChannelKind ch ? ch is ChatChannelKind.Say or ChatChannelKind.General or ChatChannelKind.Trade or ChatChannelKind.Lfg : false; // null-payload (Squelch/Tell-to-Selected) = inert private UiMenu MakeMenu() => new UiMenu { Width = 80f, Height = 18f, Items = ChannelItems, Selected = (object?)ChatChannelKind.Say, EnabledProvider = ChannelAvailable, }; [Fact] public void Items_HasExpected14Entries() { Assert.Equal(14, ChannelItems.Length); } [Fact] public void Items_FirstEntry_IsSquelch_Special() { Assert.Equal("Squelch (ignore)", ChannelItems[0].Label); Assert.Null(ChannelItems[0].Payload); } [Fact] public void Items_LastEntry_IsOlthoi() { var last = ChannelItems[^1]; Assert.Equal("Tell to Olthoi Chat", last.Label); Assert.Equal(ChatChannelKind.Olthoi, last.Payload); } [Fact] public void Items_ContainAll12ChannelKinds() { var kinds = new HashSet( ChannelItems.Where(i => i.Payload is ChatChannelKind).Select(i => (ChatChannelKind)i.Payload!)); foreach (var k in new[] { ChatChannelKind.Say, ChatChannelKind.General, ChatChannelKind.Trade, ChatChannelKind.Lfg, ChatChannelKind.Fellowship, ChatChannelKind.Allegiance, ChatChannelKind.Patron, ChatChannelKind.Vassals, ChatChannelKind.Monarch, ChatChannelKind.Roleplay, ChatChannelKind.Society, ChatChannelKind.Olthoi, }) Assert.Contains(k, kinds); } [Fact] public void DefaultSelected_IsNull_OnBlankMenu() { // A freshly constructed UiMenu has no Selected by default (controller sets it). Assert.Null(new UiMenu().Selected); } [Fact] public void Select_AvailableLeftColumnItem_FiresOnSelect() { var menu = MakeMenu(); Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open object? fired = null; menu.OnSelect = p => fired = p; // "Chat to All" (Say) is index 2 = left col, row 2: y in [-85,-68). Say is available. Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -76))); Assert.Equal(ChatChannelKind.Say, fired); Assert.Equal(ChatChannelKind.Say, menu.Selected); } [Fact] public void Select_AvailableRightColumnItem_FiresOnSelect() { var menu = MakeMenu(); Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open object? fired = null; menu.OnSelect = p => fired = p; // "Tell to Trade Chat" (Trade) is index 11 = right col (lx>=191), row 4: y in [-51,-34). Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 200, -42))); Assert.Equal(ChatChannelKind.Trade, fired); Assert.Equal(ChatChannelKind.Trade, menu.Selected); } [Fact] public void Select_SpecialItem_DoesNotFire() { var menu = MakeMenu(); Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open int fired = 0; menu.OnSelect = _ => fired++; // "Squelch (ignore)" is index 0 = left col, row 0 (null payload): y in [-119,-102). // null payload → ChannelAvailable returns false → inert. Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -110))); Assert.Equal(0, fired); } [Fact] public void Select_UnavailableChannel_DoesNotFire() { var menu = MakeMenu(); Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open int fired = 0; menu.OnSelect = _ => fired++; // "Tell to Fellows" (Fellowship) is index 3 = left col, row 3: y in [-68,-51). // Fellowship is unavailable by the default static gate, so the click is inert. Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -60))); Assert.Equal(0, fired); } [Fact] public void EnabledProvider_Overrides_DefaultGate() { // Override: all items enabled (even Fellowship which is normally greyed). var menu = new UiMenu { Width = 80f, Height = 18f, Items = ChannelItems, Selected = (object?)ChatChannelKind.Say, EnabledProvider = _ => true, }; Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open object? fired = null; menu.OnSelect = p => fired = p; // With every item enabled, "Tell to Fellows" (idx 3, row 3) now fires. Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -60))); Assert.Equal(ChatChannelKind.Fellowship, fired); } }