Add more UI helper methods for tests (#15463)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.IntegrationTests.Tests.Interaction;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
@@ -22,7 +23,7 @@ public sealed class DispenserTest : InteractionTest
|
||||
Assert.IsNull(Hands.ActiveHandEntity);
|
||||
|
||||
// Open BUI
|
||||
await Interact("");
|
||||
await Interact();
|
||||
|
||||
// Eject beaker via BUI.
|
||||
var ev = new ItemSlotButtonPressedEvent(SharedChemMaster.InputSlotName);
|
||||
@@ -31,5 +32,16 @@ public sealed class DispenserTest : InteractionTest
|
||||
// Beaker is back in the player's hands
|
||||
Assert.IsNotNull(Hands.ActiveHandEntity);
|
||||
AssertPrototype("Beaker", Hands.ActiveHandEntity);
|
||||
|
||||
// Re-insert the beaker
|
||||
await Interact();
|
||||
Assert.IsNull(Hands.ActiveHandEntity);
|
||||
|
||||
// Re-eject using the button directly instead of sending a BUI event. This test is really just a test of the
|
||||
// bui/window helper methods.
|
||||
await ClickControl<ReagentDispenserWindow>(nameof(ReagentDispenserWindow.EjectButton));
|
||||
await RunTicks(5);
|
||||
Assert.IsNotNull(Hands.ActiveHandEntity);
|
||||
AssertPrototype("Beaker", Hands.ActiveHandEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Construction;
|
||||
using Content.Server.Construction.Components;
|
||||
using Content.Server.Power.Components;
|
||||
@@ -11,8 +14,13 @@ using Content.Server.Tools.Components;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Item;
|
||||
using NUnit.Framework;
|
||||
using OpenToolkit.GraphicsLibraryFramework;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
@@ -221,6 +229,8 @@ public abstract partial class InteractionTest
|
||||
Assert.IsNull(Hands.ActiveHandEntity);
|
||||
}
|
||||
|
||||
#region Interact
|
||||
|
||||
/// <summary>
|
||||
/// Use the currently held entity.
|
||||
/// </summary>
|
||||
@@ -263,7 +273,14 @@ public abstract partial class InteractionTest
|
||||
}
|
||||
|
||||
await PlaceInHands(entity);
|
||||
await Interact(shouldSucceed, awaitDoAfters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interact with an entity using the currently held entity.
|
||||
/// </summary>
|
||||
protected async Task Interact(bool shouldSucceed = true, bool awaitDoAfters = true)
|
||||
{
|
||||
if (Target == null || !Target.Value.IsClientSide())
|
||||
{
|
||||
await Server.WaitPost(() => InteractSys.UserInteraction(Player, TargetCoords, Target));
|
||||
@@ -283,6 +300,22 @@ public abstract partial class InteractionTest
|
||||
await CheckTargetChange(shouldSucceed && awaitDoAfters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="InteractUsing"/> that performs several interactions using different entities.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Empty strings imply empty hands.
|
||||
/// </remarks>
|
||||
protected async Task Interact(params EntitySpecifier[] specifiers)
|
||||
{
|
||||
foreach (var spec in specifiers)
|
||||
{
|
||||
await Interact(spec);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Wait for any currently active DoAfters to finish.
|
||||
/// </summary>
|
||||
@@ -382,20 +415,6 @@ public abstract partial class InteractionTest
|
||||
await CheckTargetChange(shouldSucceed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="InteractUsing"/> that performs several interactions using different entities.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Empty strings imply empty hands.
|
||||
/// </remarks>
|
||||
protected async Task Interact(params EntitySpecifier[] specifiers)
|
||||
{
|
||||
foreach (var spec in specifiers)
|
||||
{
|
||||
await Interact(spec);
|
||||
}
|
||||
}
|
||||
|
||||
#region Asserts
|
||||
|
||||
protected void AssertPrototype(string? prototype, EntityUid? target = null)
|
||||
@@ -721,6 +740,135 @@ public abstract partial class InteractionTest
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI
|
||||
|
||||
/// <summary>
|
||||
/// Presses and releases a button on some client-side window. Will fail if the button cannot be found.
|
||||
/// </summary>
|
||||
protected async Task ClickControl<TWindow>(string name) where TWindow : BaseWindow
|
||||
{
|
||||
await ClickControl(GetControl<TWindow, Control>(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates a click and release at the center of some UI Constrol.
|
||||
/// </summary>
|
||||
protected async Task ClickControl(Control control)
|
||||
{
|
||||
var screenCoords = new ScreenCoordinates(
|
||||
control.GlobalPixelPosition + control.PixelSize/2,
|
||||
control.Window?.Id ?? default);
|
||||
|
||||
var relativePos = screenCoords.Position / control.UIScale - control.GlobalPosition;
|
||||
var relativePixelPos = screenCoords.Position - control.GlobalPixelPosition;
|
||||
|
||||
var args = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.UIClick,
|
||||
BoundKeyState.Down,
|
||||
screenCoords,
|
||||
default,
|
||||
relativePos,
|
||||
relativePixelPos);
|
||||
|
||||
await Client.DoGuiEvent(control, args);
|
||||
await RunTicks(1);
|
||||
|
||||
args = new GUIBoundKeyEventArgs(
|
||||
EngineKeyFunctions.UIClick,
|
||||
BoundKeyState.Up,
|
||||
screenCoords,
|
||||
default,
|
||||
relativePos,
|
||||
relativePixelPos);
|
||||
|
||||
await Client.DoGuiEvent(control, args);
|
||||
await RunTicks(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find a control on some client-side window. Will fail if the control cannot be found.
|
||||
/// </summary>
|
||||
protected TControl GetControl<TWindow, TControl>(string name)
|
||||
where TWindow : BaseWindow
|
||||
where TControl : Control
|
||||
{
|
||||
var control = GetControl<TWindow>(name);
|
||||
Assert.That(control.GetType().IsAssignableTo(typeof(TControl)));
|
||||
return (TControl) control;
|
||||
}
|
||||
|
||||
protected Control GetControl<TWindow>(string name) where TWindow : BaseWindow
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
var field = typeof(TWindow).GetField(name, flags);
|
||||
var prop = typeof(TWindow).GetProperty(name, flags);
|
||||
|
||||
if (field == null && prop == null)
|
||||
{
|
||||
Assert.Fail($"Window {typeof(TWindow).Name} does not have a field or property named {name}");
|
||||
return default!;
|
||||
}
|
||||
|
||||
var window = GetWindow<TWindow>();
|
||||
var control = (field?.GetValue(window) ?? prop?.GetValue(window)) as Control;
|
||||
|
||||
if (control == null)
|
||||
{
|
||||
Assert.Fail($"{name} was null or was not a control.");
|
||||
return default!;
|
||||
}
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find a currently open client-side window. Will fail if the window cannot be found.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this just returns the very first open window of this type that is found.
|
||||
/// </remarks>
|
||||
protected TWindow GetWindow<TWindow>() where TWindow : BaseWindow
|
||||
{
|
||||
if (TryFindWindow(out TWindow? window))
|
||||
return window;
|
||||
|
||||
Assert.Fail($"Could not find a window assignable to {nameof(TWindow)}");
|
||||
return default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find a currently open client-side window.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this just returns the very first open window of this type that is found.
|
||||
/// </remarks>
|
||||
protected bool TryFindWindow<TWindow>([NotNullWhen(true)] out TWindow? window) where TWindow : BaseWindow
|
||||
{
|
||||
TryFindWindow(typeof(TWindow), out var control);
|
||||
window = control as TWindow;
|
||||
return window != null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find a currently open client-side window.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this just returns the very first open window of this type that is found.
|
||||
/// </remarks>
|
||||
protected bool TryFindWindow(Type type, [NotNullWhen(true)] out BaseWindow? window)
|
||||
{
|
||||
Assert.That(type.IsAssignableTo(typeof(BaseWindow)));
|
||||
window = UiMan.WindowRoot.Children
|
||||
.OfType<BaseWindow>()
|
||||
.Where(x => x.IsOpen)
|
||||
.FirstOrDefault(x => x.GetType().IsAssignableTo(type));
|
||||
|
||||
return window != null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Power
|
||||
|
||||
protected void ToggleNeedPower(EntityUid? target = null)
|
||||
|
||||
@@ -14,6 +14,7 @@ using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -91,6 +92,7 @@ public abstract partial class InteractionTest
|
||||
|
||||
// CLIENT dependencies
|
||||
protected IEntityManager CEntMan = default!;
|
||||
protected IUserInterfaceManager UiMan = default!;
|
||||
protected ConstructionSystem CConSys = default!;
|
||||
protected ExamineSystem ExamineSys = default!;
|
||||
protected InteractionTestSystem CTestSystem = default!;
|
||||
@@ -98,7 +100,6 @@ public abstract partial class InteractionTest
|
||||
// player components
|
||||
protected HandsComponent Hands = default!;
|
||||
protected DoAfterComponent DoAfters = default!;
|
||||
protected UserInterfaceSystem CUISystem = default!;
|
||||
|
||||
public float TickPeriod => (float)Timing.TickPeriod.TotalSeconds;
|
||||
|
||||
@@ -125,10 +126,10 @@ public abstract partial class InteractionTest
|
||||
|
||||
// client dependencies
|
||||
CEntMan = Client.ResolveDependency<IEntityManager>();
|
||||
UiMan = Client.ResolveDependency<IUserInterfaceManager>();
|
||||
CTestSystem = CEntMan.System<InteractionTestSystem>();
|
||||
CConSys = CEntMan.System<ConstructionSystem>();
|
||||
ExamineSys = CEntMan.System<ExamineSystem>();
|
||||
CUISystem = CEntMan.System<UserInterfaceSystem>();
|
||||
|
||||
// Setup map.
|
||||
MapData = await PoolManager.CreateTestMap(PairTracker);
|
||||
|
||||
Reference in New Issue
Block a user