Skip to content

Commit

Permalink
Release 1.3.0 (#42)
Browse files Browse the repository at this point in the history
* New class (UIFind) to find the closest node(based on another node) that matches a specific with. 
* Changed so all kind of interaction with the device (tap, swipe, input text, keyevent, start intent) clear the UI cache.
  • Loading branch information
MilleBo authored Aug 10, 2017
1 parent e59d19d commit 676015a
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public void SetUp()
_adbServiceMock = new Mock<IAdbService>();
_androidMock = new Mock<IAndroidDevice>();
_androidMock.Setup(a => a.Adb).Returns(_adbServiceMock.Object);
_androidMock.Setup(a => a.Ui).Returns(new Mock<IUiService>().Object);

_activityService = new ActivityService();
_activityService.InitializeServiceOwner(_androidMock.Object);
Expand Down
1 change: 1 addition & 0 deletions src/Testura.Android.Tests/Testura.Android.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
<Compile Include="Integration\Device\UiAutomator\Server\UiAutomatorServerTests.cs" />
<Compile Include="TestHelper.cs" />
<Compile Include="Util\Exceptions\UiNodeNotFoundExceptionTests.cs" />
<Compile Include="Util\Helpers\UiFindTests.cs" />
<Compile Include="Util\Helpers\UiWaitTests.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
67 changes: 67 additions & 0 deletions src/Testura.Android.Tests/Util/Helpers/UiFindTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using NUnit.Framework;
using Testura.Android.Device.Ui.Nodes.Data;
using Testura.Android.Device.Ui.Search;
using Testura.Android.Util.Helpers;

namespace Testura.Android.Tests.Util.Helpers
{
[TestFixture]
class UiFindTests
{
[Test]
public void FindClosestNode_WhenSearchingForNodeThatExist_ShouldGetNode()
{
var root = CreateNode();
var fondNode = UiFind.ClosestNode(With.Text("second_node_1"), root.Children[0]);
Assert.IsNotNull(fondNode);
Assert.AreEqual(root.Children[0].Children[1], fondNode);
}

[Test]
public void FindClosestNode_WhenComplexSearchingForNodeThatExist_ShouldGetNode()
{
var root = CreateNode();
var fondNode = UiFind.ClosestNode(With.Text("third_node_1_1_1"), root.Children[0]);
Assert.IsNotNull(fondNode);
Assert.AreEqual(root.Children[1].Children[1].Children[1], fondNode);
}

[Test]
public void FindClosestNode_WhenSearchingForNodeThatDontExist_ShouldGetNull()
{
var root = CreateNode();
var fondNode = UiFind.ClosestNode(With.Text("second_node_23"), root.Children[0]);
Assert.IsNull(fondNode);
}

private Node CreateNode()
{
var rootNode = new Node(new XElement("node", new XAttribute("text", "root")), null);

for (int n = 0; n < 2; n++)
{
var secondNode = new Node(new XElement("node", new XAttribute("text", $"node_{n}")), rootNode);

for (int i = 0; i < 2; i++)
{
secondNode.Children.Add(new Node(new XElement("node", new XAttribute("text", $"second_node_{i}")), secondNode));

for (int d = 0; d < 2; d++)
{
secondNode.Children[i].Children.Add(new Node(new XElement("node", new XAttribute("text", $"third_node_{n}_{i}_{d}")), secondNode.Children[i]));
}
}

rootNode.Children.Add(secondNode);
}

return rootNode;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public void Start(string packageName, string activity, bool forceStopActivity, b
{
throw new AdbException(result);
}

Device.Ui.ClearCache();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class InteractionService : Service, IInteractionService
public void Swipe(int fromX, int fromY, int toX, int toY, int duration)
{
Device.Adb.Shell($"input swipe {fromX} {fromY} {toX} {toY} {duration}");
Device.Ui.ClearCache();
}

/// <summary>
Expand Down Expand Up @@ -83,6 +84,7 @@ public void Tap(Node node)
public void Tap(int x, int y)
{
Device.Adb.Shell($"input tap {x} {y}");
Device.Ui.ClearCache();
}

/// <summary>
Expand All @@ -97,6 +99,7 @@ public void InputText(string text)
}

Device.Adb.Shell($"input text {text.Replace(" ", "%s")}");
Device.Ui.ClearCache();
}

/// <summary>
Expand All @@ -106,6 +109,7 @@ public void InputText(string text)
public void InputKeyEvent(KeyEvents keyEvent)
{
Device.Adb.Shell($"input keyevent {(int)keyEvent}");
Device.Ui.ClearCache();
}

private void SetScreenHeightAndWidth()
Expand Down
17 changes: 12 additions & 5 deletions src/Testura.Android/Device/Services/Default/UiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ public UiService(IScreenDumper screenDumper, INodeParser nodeParser, INodeFinder
}

/// <summary>
/// Gets the latest cached nodes
/// Gets a list of ui extensions
/// </summary>
internal IList<Node> CachedNodes { get; private set; }
public IList<IUiExtension> Extensions { get; }

/// <summary>
/// Gets a list of ui extensions
/// Gets the latest cached nodes
/// </summary>
public IList<IUiExtension> Extensions { get; }
internal IList<Node> CachedNodes { get; private set; }

/// <summary>
/// Find a node on the screen
Expand Down Expand Up @@ -173,10 +173,17 @@ public void StopUiServer()
_screenDumper.StopUiServer();
}

/// <summary>
/// Clear the UI cache
/// </summary>
public void ClearCache()
{
CachedNodes = null;
}

/// <summary>
/// Get all nodes on the screen
/// </summary>
/// <returns>A list with all nodes on the screen</returns>
internal void UpdateCachedNodes()
{
var screenDump = _screenDumper.DumpUi();
Expand Down
5 changes: 5 additions & 0 deletions src/Testura.Android/Device/Services/IUiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,10 @@ public interface IUiService
/// Force stop the ui server.
/// </summary>
void StopUiServer();

/// <summary>
/// Clear the UI cache
/// </summary>
void ClearCache();
}
}
1 change: 0 additions & 1 deletion src/Testura.Android/Device/Ui/Objects/UiObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public IList<Node> Values(int timeout = 2)
/// <summary>
/// Get a list of nodes from the latest cached dump that contain all values.
/// </summary>
/// <param name="timeout">Timeout in seconds.</param>
/// <returns>A list of nodes that contain all values.</returns>
public IList<Node> ValuesFromCache()
{
Expand Down
4 changes: 2 additions & 2 deletions src/Testura.Android/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyVersion("1.3.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")]
1 change: 1 addition & 0 deletions src/Testura.Android/Testura.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
<Compile Include="Util\Exceptions\UiAutomatorServerException.cs" />
<Compile Include="Util\Exceptions\UiNodeNotFoundException.cs" />
<Compile Include="Util\Extensions\AssemblyExtensions.cs" />
<Compile Include="Util\Helpers\UiFind.cs" />
<Compile Include="Util\Helpers\UiWait.cs" />
<Compile Include="Util\Logging\ConsoleLogListener.cs" />
<Compile Include="Util\Logging\ILogListener.cs" />
Expand Down
80 changes: 80 additions & 0 deletions src/Testura.Android/Util/Helpers/UiFind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using Testura.Android.Device.Ui.Nodes.Data;
using Testura.Android.Device.Ui.Search;

namespace Testura.Android.Util.Helpers
{
/// <summary>
/// Provides functionality to find nodes
/// </summary>
public static class UiFind
{
/// <summary>
/// Find the closest node(based on another node) that matches a specific with
/// </summary>
/// <param name="with">With that the close node should match</param>
/// <param name="startNode">The start node and position where we should start to search</param>
/// <returns>The closest node, null if we can't find a matching node</returns>
/// <example>This example show how to call the method with another node </example>
/// <code>
/// var node = uiObject.Values()
/// UiFind.ClosestNode(With.Text("test"), node)
/// </code>
public static Node ClosestNode(With with, Node startNode)
{
return FindNode(with.NodeSearch, startNode, new List<Node>(), 0, null)?.Node;
}

private static FoundNode FindNode(Func<Node, bool> func, Node current, ICollection<Node> visitedNodes, int distance, FoundNode foundNode)
{
visitedNodes.Add(current);

if (func.Invoke(current))
{
if (foundNode == null || distance < foundNode.Distance)
{
foundNode = new FoundNode(distance, current);
}
}

foreach (var currentChild in current.Children)
{
var found = FindNode(func, currentChild, visitedNodes, distance + 1, foundNode);
if (NodeOk(foundNode, found))
{
foundNode = found;
}
}

if (current.Parent != null && !visitedNodes.Contains(current.Parent))
{
var found = FindNode(func, current.Parent, visitedNodes, distance + 1, foundNode);
if (NodeOk(foundNode, found))
{
foundNode = found;
}
}

return foundNode;
}

private static bool NodeOk(FoundNode foundNode, FoundNode found)
{
return found != null && (foundNode == null || found.Distance < foundNode.Distance);
}

private class FoundNode
{
public FoundNode(int distance, Node node)
{
Distance = distance;
Node = node;
}

public int Distance { get; }

public Node Node { get; }
}
}
}

0 comments on commit 676015a

Please sign in to comment.