using AcDream.App.UI; using AcDream.App.UI.Layout; using AcDream.Core.Net.Messages; using AcDream.Core.Social; using AcDream.Runtime; namespace AcDream.App.Tests.UI.Layout; /// /// Controller-level tests for against the /// committed social_panel_2100006E_1000018F.json fixture (Campaign FA /// slice FA3) — the SAME LayoutImporter.ImportInfos(dats, 0x2100006Eu, /// 0x1000018Fu) catalog import /// performs against real DATs. No DAT access, no live runtime — dat-free per /// the established pattern. /// public sealed class SocialPanelControllerTests { private static SocialPanelController.Callbacks MakeCallbacks( List? calls = null, RuntimeFellowshipSnapshot fellowship = default, RuntimeAllegianceSnapshot allegiance = default, FriendsState? friends = null, SquelchState? squelch = null) { calls ??= new List(); return new SocialPanelController.Callbacks( Toggle: () => calls.Add("toggle"), FellowshipSnapshot: () => fellowship, AllegianceSnapshot: () => allegiance, Friends: friends ?? new FriendsState(), Squelch: squelch ?? new SquelchState(), TemplateResolver: FakeRowTemplateResolver); } /// Mirrors the real Friends/Squelch row templates' own two-deep /// nested-text shape (FA3 live-mount probe) so /// has something realistic /// to resolve, without needing the templates' own (uncommitted, separate /// LayoutDesc) fixtures. private static UiElement? FakeRowTemplateResolver(uint layoutId, uint elementId) { var outer = new UiText(); var inner = new UiText(); outer.AddChild(inner); return outer; } // ── Mount conformance ──────────────────────────────────────────────────── [Fact] public void Bind_RootBuildsAsUiTabPanel() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); Assert.IsType(layout.Root); } /// /// Pins the authored 0x2E tab table exactly as read from the live /// DATs (FA3 live-mount probe) — CORRECTING the coordinator addendum's /// x-order guess (see 's own class /// doc). Four entries; Allegiance is the sole default. /// [Fact] public void TabTable_MatchesLiveDatPairing_AllegianceIsDefault() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); var tabs = Assert.IsType(layout.Root); Assert.Equal(4, tabs.Tabs.Count); Assert.Contains(tabs.Tabs, e => e.ButtonElementId == 0x1000028Cu && e.PageElementId == 0x10000291u && e.IsDefault); Assert.Contains(tabs.Tabs, e => e.ButtonElementId == 0x1000028Eu && e.PageElementId == 0x10000292u && !e.IsDefault); Assert.Contains(tabs.Tabs, e => e.ButtonElementId == 0x10000512u && e.PageElementId == 0x10000513u && !e.IsDefault); Assert.Contains(tabs.Tabs, e => e.ButtonElementId == 0x1000053Bu && e.PageElementId == 0x1000054Au && !e.IsDefault); Assert.Single(tabs.Tabs, e => e.IsDefault); } [Fact] public void Bind_Succeeds_AndActivateTabs_SelectsTheAuthoredDefault() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind(layout, MakeCallbacks()); Assert.NotNull(controller); controller!.ActivateTabs(); Assert.Empty(controller.TabPanel.UnresolvedEntries); Assert.True(controller.IsShowingAllegiance); Assert.False(controller.IsShowingFellowship); } [Fact] public void ShowFellowship_SwitchesTheActiveTab() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind(layout, MakeCallbacks()); Assert.NotNull(controller); controller!.ActivateTabs(); controller.ShowFellowship(); Assert.True(controller.IsShowingFellowship); Assert.False(controller.IsShowingAllegiance); } [Fact] public void CloseButton_InvokesToggleCallback() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); var calls = new List(); SocialPanelController? controller = SocialPanelController.Bind(layout, MakeCallbacks(calls)); Assert.NotNull(controller); var close = Assert.IsType(layout.FindElement(0x10000290u)); close.OnEvent(new UiEvent(0, close, UiEventType.Click)); Assert.Contains("toggle", calls); } // ── Fellowship empty state (item 4) ───────────────────────────────────── [Fact] public void Fellowship_NoFellowship_ShowsNotInFellowshipFrame() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(fellowship: new RuntimeFellowshipSnapshot { IsInFellowship = false })); Assert.NotNull(controller); UiElement notIn = UiElement.FindDescendant(controller!.TabPanel, 0x1000026Bu)!; UiElement inFellowship = UiElement.FindDescendant(controller.TabPanel, 0x10000275u)!; Assert.True(notIn.Visible); Assert.False(inFellowship.Visible); } [Fact] public void Fellowship_HasFellowship_ShowsInFellowshipFrame() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(fellowship: new RuntimeFellowshipSnapshot { IsInFellowship = true })); Assert.NotNull(controller); UiElement notIn = UiElement.FindDescendant(controller!.TabPanel, 0x1000026Bu)!; UiElement inFellowship = UiElement.FindDescendant(controller.TabPanel, 0x10000275u)!; Assert.False(notIn.Visible); Assert.True(inFellowship.Visible); } // ── Allegiance empty state (item 4) ───────────────────────────────────── [Fact] public void Allegiance_NoProfile_HidesBlocksAndBlanksNames() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(allegiance: new RuntimeAllegianceSnapshot { HasProfile = false })); Assert.NotNull(controller); UiElement monarchField = UiElement.FindDescendant(controller!.TabPanel, 0x10000255u)!; UiElement patronField = UiElement.FindDescendant(controller.TabPanel, 0x1000025Au)!; Assert.False(monarchField.Visible); Assert.False(patronField.Visible); var monarchName = Assert.IsType( UiElement.FindDescendant(monarchField, 0x10000257u)); var patronName = Assert.IsType( UiElement.FindDescendant(patronField, 0x1000025Cu)); Assert.Equal(" ", Assert.Single(monarchName.LinesProvider()).Text); Assert.Equal(" ", Assert.Single(patronName.LinesProvider()).Text); } [Fact] public void Allegiance_HasProfile_ShowsBothBlocks() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(allegiance: new RuntimeAllegianceSnapshot { HasProfile = true })); Assert.NotNull(controller); UiElement monarchField = UiElement.FindDescendant(controller!.TabPanel, 0x10000255u)!; UiElement patronField = UiElement.FindDescendant(controller.TabPanel, 0x1000025Au)!; Assert.True(monarchField.Visible); Assert.True(patronField.Visible); } // ── Friends/Squelch read-only lists (item 5) ──────────────────────────── [Fact] public void Friends_PopulatesOneRowPerEntry() { var friends = new FriendsState(); friends.Apply(new FriendsUpdate( FriendsUpdateType.Full, new List { new(1u, "Alice", true, false, System.Array.Empty(), System.Array.Empty()), new(2u, "Bob", false, false, System.Array.Empty(), System.Array.Empty()), })); ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(friends: friends)); Assert.NotNull(controller); var listBox = Assert.IsType( UiElement.FindDescendant(controller!.TabPanel, 0x10000513u) is { } friendsPage ? UiElement.FindDescendant(friendsPage, 0x10000517u) : null); Assert.Equal(2, listBox.ViewportForTest!.Children.Count); } [Fact] public void Friends_ReactsToRevisionChange_OnTick() { var friends = new FriendsState(); ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(friends: friends)); Assert.NotNull(controller); UiElement friendsPage = UiElement.FindDescendant(controller!.TabPanel, 0x10000513u)!; var listBox = Assert.IsType( UiElement.FindDescendant(friendsPage, 0x10000517u)); Assert.Equal(0, listBox.ViewportForTest?.Children.Count ?? 0); friends.Apply(new FriendsUpdate( FriendsUpdateType.Add, new List { new(1u, "Alice", true, false, System.Array.Empty(), System.Array.Empty()), })); controller.Tick(); Assert.Single(listBox.ViewportForTest!.Children); } [Fact] public void Squelch_PopulatesCharacterAndAccountRows() { var squelch = new SquelchState(); squelch.Replace(new SquelchDatabase( new Dictionary(System.StringComparer.OrdinalIgnoreCase) { ["BadAccount"] = 1u }, new Dictionary { [7u] = new SquelchInfo("Grief", false, new HashSet()), }, new SquelchInfo(string.Empty, false, new HashSet()))); ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind( layout, MakeCallbacks(squelch: squelch)); Assert.NotNull(controller); UiElement squelchPage = UiElement.FindDescendant(controller!.TabPanel, 0x1000054Au)!; var listBox = Assert.IsType( UiElement.FindDescendant(squelchPage, 0x1000053Eu)); Assert.Equal(2, listBox.ViewportForTest!.Children.Count); } // ── D1: Friends/Squelch action buttons are honest INERT ──────────────── [Fact] public void FriendsAndSquelchActionButtons_AreClickable_ButHaveNoHandler() { ImportedLayout layout = FixtureLoader.LoadSocialPanelHost(); SocialPanelController? controller = SocialPanelController.Bind(layout, MakeCallbacks()); Assert.NotNull(controller); foreach (uint buttonId in new[] { 0x10000514u, 0x10000515u, 0x10000516u }) { var button = Assert.IsType( UiElement.FindDescendant(controller!.TabPanel, buttonId)); Assert.Null(button.OnClick); } foreach (uint buttonId in new[] { 0x10000547u, 0x1000054Bu, 0x1000054Cu }) { var button = Assert.IsType( UiElement.FindDescendant(controller!.TabPanel, buttonId)); Assert.Null(button.OnClick); } } }