using System.Collections.Generic; using System.IO; using AcDream.App.UI; using AcDream.App.UI.Testing; using AcDream.Core.Items; namespace AcDream.App.Tests.UI; public sealed class RetailUiAutomationProbeTests { private sealed class SpyHandler : IItemListDragHandler { public (UiItemList list, UiItemSlot cell, ItemDragPayload payload)? LastDrop; public (UiItemList list, UiItemSlot cell, ItemDragPayload payload)? LastLift; public void OnDragLift(UiItemList sourceList, UiItemSlot sourceCell, ItemDragPayload payload) => LastLift = (sourceList, sourceCell, payload); public bool OnDragOver(UiItemList targetList, UiItemSlot targetCell, ItemDragPayload payload) => true; public void HandleDropRelease(UiItemList targetList, UiItemSlot targetCell, ItemDragPayload payload) => LastDrop = (targetList, targetCell, payload); } private static (UiRoot root, UiItemList source, UiItemList target, SpyHandler handler, ClientObjectTable objects) RootWithTwoItemLists() { var root = new UiRoot { Width = 240, Height = 120 }; var objects = new ClientObjectTable(); var source = new UiItemList(_ => (1u, 1, 1)) { DatElementId = 0x10000010u, Left = 10, Top = 10, Width = 32, Height = 32, }; source.Cell.SlotIndex = 0; source.Cell.SourceKind = ItemDragSource.Inventory; source.Cell.SetItem(0x5001u, 0x99u); var target = new UiItemList(_ => (1u, 1, 1)) { DatElementId = 0x10000020u, Left = 70, Top = 10, Width = 32, Height = 32, }; target.Cell.SlotIndex = 1; var handler = new SpyHandler(); target.RegisterDragHandler(handler); root.AddChild(source); root.AddChild(target); return (root, source, target, handler, objects); } [Fact] public void Snapshot_listsDatElementsAndItemSlots() { var (root, _, _, _, objects) = RootWithTwoItemLists(); var probe = new RetailUiAutomationProbe(root, objects); var rows = probe.Snapshot(); Assert.Contains(rows, r => r.DatElementId == 0x10000010u && r.TypeName == nameof(UiItemList)); var itemRow = Assert.Single(rows, r => r.ItemId == 0x5001u); Assert.Equal(ItemDragSource.Inventory, itemRow.SourceKind); Assert.Equal(0, itemRow.SlotIndex); Assert.True(itemRow.Width > 0f); Assert.True(itemRow.Height > 0f); } [Fact] public void DoubleClickItem_routesThroughUiRootDoubleClick() { var (root, source, _, _, objects) = RootWithTwoItemLists(); bool doubleClicked = false; source.Cell.DoubleClicked = () => doubleClicked = true; var probe = new RetailUiAutomationProbe(root, objects); Assert.True(probe.DoubleClickItem(0x5001u, ItemDragSource.Inventory)); Assert.True(doubleClicked); } [Fact] public void DragItemToElement_deliversDropReleaseToTargetCell() { var (root, _, target, handler, objects) = RootWithTwoItemLists(); var probe = new RetailUiAutomationProbe(root, objects); Assert.True(probe.DragItemToElement(0x5001u, 0x10000020u, ItemDragSource.Inventory)); Assert.NotNull(handler.LastDrop); Assert.Same(target, handler.LastDrop!.Value.list); Assert.Same(target.Cell, handler.LastDrop.Value.cell); Assert.Equal(0x5001u, handler.LastDrop.Value.payload.ObjId); } [Fact] public void DragItemOutside_raisesRootOutsideUiEvent() { var (root, _, _, _, objects) = RootWithTwoItemLists(); object? payload = null; (int x, int y) release = default; root.DragReleasedOutsideUi += (p, x, y) => { payload = p; release = (x, y); }; var probe = new RetailUiAutomationProbe(root, objects); Assert.True(probe.DragItemOutside(0x5001u, 220, 100, ItemDragSource.Inventory)); var itemPayload = Assert.IsType(payload); Assert.Equal(0x5001u, itemPayload.ObjId); Assert.Equal((220, 100), release); } [Fact] public void AssertItem_checksObjectTableState() { var objects = new ClientObjectTable(); objects.AddOrUpdate(new ClientObject { ObjectId = 0x5001u, ContainerId = 0x7001u, ContainerSlot = 3, CurrentlyEquippedLocation = EquipMask.ChestArmor | EquipMask.UpperArmArmor, }); var probe = new RetailUiAutomationProbe(new UiRoot(), objects); var ok = probe.AssertItem( 0x5001u, equippedLocation: EquipMask.ChestArmor | EquipMask.UpperArmArmor, containerId: 0x7001u, slot: 3); var bad = probe.AssertItem(0x5001u, slot: 4); Assert.True(ok.Success); Assert.False(bad.Success); } [Fact] public void ScriptRunner_waitItemThenDoubleClick_executesThroughProbe() { var (root, source, _, _, objects) = RootWithTwoItemLists(); bool doubleClicked = false; source.Cell.DoubleClicked = () => doubleClicked = true; var probe = new RetailUiAutomationProbe(root, objects); var logs = new List(); string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".ui-probe.txt"); File.WriteAllLines(path, new[] { "wait item 0x5001 inventory 100", "doubleclick item 0x5001 inventory", }); try { var runner = new RetailUiAutomationScriptRunner(probe, path, dumpOnStart: false, logs.Add); runner.Tick(0.016); Assert.True(doubleClicked); Assert.True(runner.Completed); Assert.Contains(logs, line => line.Contains("UI probe script complete")); } finally { File.Delete(path); } } }