Skip to content

Commit

Permalink
Merge pull request #123 from nowsprinting/chore/obsolete_interactivec…
Browse files Browse the repository at this point in the history
…omponent

Obsolete InteractiveComponent class
  • Loading branch information
nowsprinting authored Apr 20, 2024
2 parents b875d29 + e6951a3 commit 0fae1b3
Show file tree
Hide file tree
Showing 15 changed files with 371 additions and 136 deletions.
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Specify the screen position offset on world space where Monkey operates.
Specify the world position where Monkey operates.


### Find and operate interactive uGUI elements API
### Find and operate interactable uGUI components

#### GameObjectFinder.FindByNameAsync

Expand Down Expand Up @@ -154,18 +154,17 @@ public class MyIntegrationTest
[Test]
public void MyTestMethod()
{
var finder = new GameObjectFinder(5d); // 5 seconds timeout
var finder = new GameObjectFinder();
var button = await finder.FindByPathAsync("/**/Confirm/**/Cancel", reachable: true, interactable: true);
}
}
```

#### InteractiveComponent and Operators
#### Get interactable components and operators

##### InteractiveComponent
Returns new `InteractableComponent` instance from GameObject. If GameObject is not interactable so, return null.
`GetInteractableComponents` are extensions of `GameObject` that return interactable components.

##### Operators
`SelectOperators` and `SelectOperatorsOfType` are extensions of `Component` that return available operators.
Operators implements `IOperator` interface. It has `OperateAsync` method that operates on the component.

Usage:
Expand All @@ -178,14 +177,14 @@ using TestHelper.Monkey;
public class MyIntegrationTest
{
[Test]
public void MyTestMethod()
public void ClickStartButton()
{
var finder = new GameObjectFinder();
var button = await finder.FindByNameAsync("StartButton", interactable: true);

var interactableComponent = InteractiveComponent.CreateInteractableComponent(button);
var clickOperator = interactableComponent.GetOperatorsByType(OperatorType.Click).First();
clickOperator.OperateAsync(interactableComponent.component);
var buttonComponent = button.GetInteractableComponents().First();
var clickOperator = buttonComponent.SelectOperatorsOfType(_operators, OperatorType.Click).First();
clickOperator.OperateAsync(buttonComponent);
}
}
```
Expand All @@ -211,8 +210,8 @@ public class MyIntegrationTest
var components = InteractiveComponentCollector.FindInteractableComponents();

var firstComponent = components.First();
var clickAndHoldOperator = firstComponent.GetOperatorsByType(OperatorType.ClickAndHold).First();
await clickAndHoldOperator.OperateAsync(firstComponent.component);
var clickAndHoldOperator = firstComponent.SelectOperatorsOfType(_operators, OperatorType.ClickAndHold).First();
await clickAndHoldOperator.OperateAsync(firstComponent);
}
}
```
Expand All @@ -239,8 +238,8 @@ public class MyIntegrationTest
var components = InteractiveComponentCollector.FindReachableInteractableComponents();

var firstComponent = components.First();
var textInputOperator = firstComponent.GetOperatorsByType(OperatorType.TextInput).First();
textInputOperator.OperateAsync(firstComponent.component); // input random text
var textInputOperator = firstComponent.SelectOperatorsOfType(_operators, OperatorType.TextInput).First();
textInputOperator.OperateAsync(firstComponent); // input random text
}
}
```
Expand Down
27 changes: 27 additions & 0 deletions Runtime/Extensions/ComponentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// This software is released under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using TestHelper.Monkey.Operators;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
Expand All @@ -11,6 +13,31 @@ namespace TestHelper.Monkey.Extensions
{
public static class ComponentExtensions
{
/// <summary>
/// Returns the operators available to this component.
/// </summary>
/// <param name="component"></param>
/// <param name="operators">All available operators in autopilot/tests. Usually defined in <c>MonkeyConfig</c></param>
/// <returns>Available operators</returns>
public static IEnumerable<IOperator> SelectOperators(this Component component,
IEnumerable<IOperator> operators)
{
return operators.Where(iOperator => iOperator.CanOperate(component));
}

/// <summary>
/// Returns the operators that specify types and are available to this component.
/// </summary>
/// <param name="component"></param>
/// <param name="operators">All available operators in autopilot/tests. Usually defined in <c>MonkeyConfig</c></param>
/// <param name="type">Operator type</param>
/// <returns>Available operators</returns>
public static IEnumerable<IOperator> SelectOperatorsOfType(this Component component,
IEnumerable<IOperator> operators, OperatorType type)
{
return operators.Where(iOperator => iOperator.Type == type && iOperator.CanOperate(component));
}

/// <summary>
/// Make sure the <c>Component</c> is interactable.
/// If any of the following is true:
Expand Down
39 changes: 39 additions & 0 deletions Runtime/Extensions/GameObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.ScreenPointStrategies;
using UnityEngine;
using UnityEngine.Assertions;
Expand Down Expand Up @@ -58,6 +59,30 @@ public static Camera GetAssociatedCamera(this GameObject gameObject)
return canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera;
}

/// <summary>
/// Hit test using raycaster
/// </summary>
/// <param name="gameObject"></param>
/// <param name="isReachable">The function returns the <c>GameObject</c> is reachable from user or not.
/// Default is <c>DefaultReachableStrategy.IsReachable</c>.</param>
/// <param name="pointerEventData">Specify if avoid GC memory allocation</param>
/// <param name="raycastResults">Specify if avoid GC memory allocation</param>
/// <returns>true: this object can control by user</returns>
public static bool IsReachable(this GameObject gameObject,
Func<GameObject, PointerEventData, List<RaycastResult>, bool> isReachable = null,
PointerEventData pointerEventData = null,
List<RaycastResult> raycastResults = null)
{
Assert.IsNotNull(EventSystem.current);

isReachable = isReachable ?? DefaultReachableStrategy.IsReachable;
pointerEventData = pointerEventData ?? new PointerEventData(EventSystem.current);
raycastResults = raycastResults ?? new List<RaycastResult>();

raycastResults.Clear();
return isReachable.Invoke(gameObject, pointerEventData, raycastResults);
}

/// <summary>
/// Make sure the <c>GameObject</c> is reachable from user.
/// Hit test using raycaster
Expand Down Expand Up @@ -102,6 +127,20 @@ private static bool IsSameOrChildObject(GameObject target, Transform hitObjectTr
return false;
}

/// <summary>
///
/// </summary>
/// <param name="gameObject"></param>
/// <param name="isComponentInteractable"></param>
/// <returns></returns>
public static IEnumerable<Component> GetInteractableComponents(this GameObject gameObject,
Func<Component, bool> isComponentInteractable = null)
{
isComponentInteractable = isComponentInteractable ?? DefaultComponentInteractableStrategy.IsInteractable;

return gameObject.GetComponents<Component>().Where(x => isComponentInteractable.Invoke(x));
}

/// <summary>
/// Make sure the <c>GameObject</c> is interactable.
/// If any of the following is true:
Expand Down
3 changes: 2 additions & 1 deletion Runtime/Hints/InteractiveComponentHint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Text;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.Extensions;
using TestHelper.Monkey.ScreenPointStrategies;
using UnityEngine;
Expand Down Expand Up @@ -101,7 +102,7 @@ private void Refresh()
var interactiveComponentCollector = new InteractiveComponentCollector();
foreach (var component in interactiveComponentCollector.FindInteractableComponents())
{
var dst = component.IsReachable()
var dst = component.gameObject.IsReachable(isReachable: DefaultReachableStrategy.IsReachable)
? _tmpReallyInteractives
: _tmpNotReallyInteractives;

Expand Down
21 changes: 7 additions & 14 deletions Runtime/InteractiveComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace TestHelper.Monkey
/// <summary>
/// Wrapped component that provide interaction for user.
/// </summary>
// TODO: Rename to InteractableComponent
[Obsolete]
public class InteractiveComponent
{
/// <summary>
Expand Down Expand Up @@ -125,38 +125,31 @@ public bool IsReallyInteractiveFromUser(Func<GameObject, Vector2> screenPointStr
/// Hit test using raycaster
/// </summary>
/// <returns>true: this object can control by user</returns>
[Obsolete("Use GameObjectExtensions.IsReachable() instead")]
public bool IsReachable()
{
return _isReachable.Invoke(gameObject, _eventData, _results);
return gameObject.IsReachable(_isReachable, _eventData, _results);
}

/// <summary>
/// Returns the operators available to this component.
/// </summary>
/// <returns>Available operators</returns>
[Obsolete("Use ComponentExtensions.SelectOperators() instead.")]
public IEnumerable<IOperator> GetOperators()
{
if (_operators == null || !_operators.Any())
{
throw new NotSupportedException("Operators are not set.");
}

return _operators.Where(iOperator => iOperator.CanOperate(component));
return component.SelectOperators(_operators);
}

/// <summary>
/// Returns the operators that specify types and are available to this component.
/// </summary>
/// <param name="type">Operator type</param>
/// <returns>Available operators</returns>
[Obsolete("Use ComponentExtensions.SelectOperatorsOfType() instead.")]
public IEnumerable<IOperator> GetOperatorsByType(OperatorType type)
{
if (_operators == null || !_operators.Any())
{
throw new NotSupportedException("Operators are not set.");
}

return _operators.Where(iOperator => iOperator.Type == type && iOperator.CanOperate(component));
return component.SelectOperatorsOfType(_operators, type);
}

/// <summary>
Expand Down
37 changes: 22 additions & 15 deletions Runtime/InteractiveComponentCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using System;
using System.Collections.Generic;
using System.Linq;
using TestHelper.Monkey.DefaultStrategies;
using TestHelper.Monkey.Extensions;
using TestHelper.Monkey.Operators;
using UnityEngine;
using UnityEngine.EventSystems;
Expand Down Expand Up @@ -47,26 +49,22 @@ public InteractiveComponentCollector(
///
/// Note: If you only need UI elements, using UnityEngine.UI.Selectable.allSelectablesArray is faster.
/// </summary>
/// <returns>Interactive components</returns>
public IEnumerable<InteractiveComponent> FindInteractableComponents()
/// <returns>Interactable components</returns>
public IEnumerable<Component> FindInteractableComponents()
{
foreach (var component in FindMonoBehaviours())
{
if (_isInteractable.Invoke(component))
{
yield return InteractiveComponent.CreateInteractableComponent(component,
_isReachable,
_isInteractable,
_operators);
yield return component;
}
}
}

[Obsolete("Use FindInteractableComponents() instead")]
public static IEnumerable<InteractiveComponent> FindInteractiveComponents()
{
var instance = new InteractiveComponentCollector();
return instance.FindInteractableComponents();
throw new NotImplementedException("Use FindInteractableComponents() instead");
}

/// <summary>
Expand All @@ -75,24 +73,33 @@ public static IEnumerable<InteractiveComponent> FindInteractiveComponents()
///
/// Note: If you only need UI elements, using UnityEngine.UI.Selectable.allSelectablesArray is faster.
/// </summary>
/// <returns>Really interactive components</returns>
public IEnumerable<InteractiveComponent> FindReachableInteractableComponents()
/// <returns>Reachable and Interactable components</returns>
public IEnumerable<Component> FindReachableInteractableComponents()
{
foreach (var interactiveComponent in FindInteractableComponents())
foreach (var interactableComponent in FindInteractableComponents())
{
if (_isReachable.Invoke(interactiveComponent.gameObject, _eventData, _results))
if (_isReachable.Invoke(interactableComponent.gameObject, _eventData, _results))
{
yield return interactiveComponent;
yield return interactableComponent;
}
}
}

/// <summary>
/// Returns tuple of interactable component and operator.
/// Note: Not check reachable from user.
/// </summary>
/// <returns>Tuple of interactable component and operator</returns>
public IEnumerable<(Component, IOperator)> FindInteractableComponentsAndOperators()
{
return FindInteractableComponents().SelectMany(x => x.SelectOperators(_operators), (x, o) => (x, o));
}

[Obsolete("Use FindReachableInteractableComponents() instead")]
public static IEnumerable<InteractiveComponent> FindReallyInteractiveComponents(
Func<GameObject, Vector2> screenPointStrategy)
{
var instance = new InteractiveComponentCollector();
return instance.FindReachableInteractableComponents();
throw new NotImplementedException("Use FindReachableInteractableComponents() instead");
}

private static IEnumerable<MonoBehaviour> FindMonoBehaviours()
Expand Down
Loading

0 comments on commit 0fae1b3

Please sign in to comment.