Refactorings
This commit is contained in:
Родитель
047c5a539e
Коммит
a1e29e4c5a
|
@ -35,7 +35,7 @@ namespace Microsoft.Maui.Automation
|
|||
|
||||
var multiApp = new MultiPlatformApplication(Platform.Maui, new[]
|
||||
{
|
||||
( Platform.Maui, new MauiApplication(app)),
|
||||
( Platform.Maui, new MauiApplication(platformApp, app)),
|
||||
( platform, platformApp )
|
||||
});
|
||||
|
||||
|
@ -51,36 +51,8 @@ namespace Microsoft.Maui.Automation
|
|||
#endif
|
||||
);
|
||||
|
||||
#if ANDROID
|
||||
client = new GrpcRemoteAppAgent(multiApp, address, new MyAndroidHandler());
|
||||
#else
|
||||
client = new GrpcRemoteAppAgent(multiApp, address);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
public class MyAndroidHandler : Xamarin.Android.Net.AndroidMessageHandler
|
||||
{
|
||||
MyHostnameVerifier verifier = new MyHostnameVerifier();
|
||||
|
||||
protected override Javax.Net.Ssl.IHostnameVerifier GetSSLHostnameVerifier(Javax.Net.Ssl.HttpsURLConnection connection)
|
||||
{
|
||||
return verifier;
|
||||
}
|
||||
protected override Javax.Net.Ssl.SSLSocketFactory ConfigureCustomSSLSocketFactory(Javax.Net.Ssl.HttpsURLConnection connection)
|
||||
{
|
||||
return Android.Net.SSLCertificateSocketFactory.GetInsecure(1000, null);
|
||||
}
|
||||
|
||||
class MyHostnameVerifier : Java.Lang.Object, Javax.Net.Ssl.IHostnameVerifier
|
||||
{
|
||||
public bool Verify(string hostname, Javax.Net.Ssl.ISSLSession session)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Maui.Automation
|
||||
{
|
||||
public class AppHostApplication : MultiPlatformApplication
|
||||
{
|
||||
public AppHostApplication
|
||||
(
|
||||
Platform defaultPlatform
|
||||
#if ANDROID
|
||||
, Android.App.Application application
|
||||
#endif
|
||||
)
|
||||
: base(defaultPlatform, new[] {
|
||||
( Platform.Maui, new MauiApplication() ),
|
||||
( App.GetCurrentPlatform(), App.CreateForCurrentPlatform(
|
||||
#if ANDROID
|
||||
application
|
||||
#endif
|
||||
))
|
||||
})
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
//namespace Microsoft.Maui.Automation
|
||||
//{
|
||||
// public class AppHostApplication : MultiPlatformApplication
|
||||
// {
|
||||
// public AppHostApplication
|
||||
// (
|
||||
// Platform defaultPlatform
|
||||
//#if ANDROID
|
||||
// , Android.App.Application application
|
||||
//#endif
|
||||
// )
|
||||
// : base(defaultPlatform, new[] {
|
||||
// ( Platform.Maui, new MauiApplication() ),
|
||||
// ( App.GetCurrentPlatform(), App.CreateForCurrentPlatform(
|
||||
//#if ANDROID
|
||||
// application
|
||||
//#endif
|
||||
// ))
|
||||
// })
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace Microsoft.Maui.Automation
|
|||
{
|
||||
public override Platform DefaultPlatform => Platform.Ios;
|
||||
|
||||
public override async Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
public override async Task<string> GetProperty(string elementId, string propertyName)
|
||||
{
|
||||
var selector = new ObjCRuntime.Selector(propertyName);
|
||||
var getSelector = new ObjCRuntime.Selector("get" + System.Globalization.CultureInfo.InvariantCulture.TextInfo.ToTitleCase(propertyName));
|
||||
|
||||
var element = (await FindElements(platform, e => e.Id?.Equals(elementId) ?? false))?.FirstOrDefault();
|
||||
var element = (await FindElements(e => e.Id?.Equals(elementId) ?? false))?.FirstOrDefault();
|
||||
|
||||
if (element is not null && element.PlatformElement is NSObject nsobj)
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ namespace Microsoft.Maui.Automation
|
|||
return string.Empty;
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
public override Task<IEnumerable<Element>> GetElements()
|
||||
{
|
||||
var root = GetRootElements(-1);
|
||||
|
||||
|
@ -86,20 +86,20 @@ namespace Microsoft.Maui.Automation
|
|||
return children;
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher)
|
||||
public override Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher)
|
||||
{
|
||||
var windows = GetRootElements(-1);
|
||||
|
||||
var matches = new List<Element>();
|
||||
Traverse(platform, windows, matches, matcher);
|
||||
Traverse(windows, matches, matcher);
|
||||
|
||||
return Task.FromResult<IEnumerable<Element>>(matches);
|
||||
}
|
||||
|
||||
public override Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments)
|
||||
public override Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> Task.FromResult(new PerformActionResult { Result = String.Empty, Status = -1 });
|
||||
|
||||
void Traverse(Platform platform, IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
void Traverse(IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
{
|
||||
foreach (var e in elements)
|
||||
{
|
||||
|
@ -110,7 +110,7 @@ namespace Microsoft.Maui.Automation
|
|||
{
|
||||
var children = uiView.Subviews?.Select(s => s.GetElement(this, e.Id, 1, 1))
|
||||
?.ToList() ?? new List<Element>();
|
||||
Traverse(platform, children, matches, matcher);
|
||||
Traverse(children, matches, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Automation.RemoteGrpc;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Dispatching;
|
||||
using Microsoft.Maui.Platform;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
@ -12,8 +14,10 @@ namespace Microsoft.Maui.Automation
|
|||
{
|
||||
public class MauiApplication : Application
|
||||
{
|
||||
public MauiApplication(Maui.IApplication? mauiApp = default) : base()
|
||||
public MauiApplication(IApplication platformApp, Maui.IApplication mauiApp = default) : base()
|
||||
{
|
||||
PlatformApplication = platformApp;
|
||||
|
||||
MauiPlatformApplication = mauiApp
|
||||
?? App.GetCurrentMauiApplication() ?? throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
@ -29,7 +33,9 @@ namespace Microsoft.Maui.Automation
|
|||
|
||||
public readonly Maui.IApplication MauiPlatformApplication;
|
||||
|
||||
public override Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
public readonly IApplication PlatformApplication;
|
||||
|
||||
public override Task<IEnumerable<Element>> GetElements()
|
||||
=> Dispatch<IEnumerable<Element>>(() =>
|
||||
{
|
||||
var windows = new List<Element>();
|
||||
|
@ -43,7 +49,7 @@ namespace Microsoft.Maui.Automation
|
|||
return Task.FromResult<IEnumerable<Element>>(windows);
|
||||
});
|
||||
|
||||
public override Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher)
|
||||
public override Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher)
|
||||
=> Dispatch<IEnumerable<Element>>(() =>
|
||||
{
|
||||
var windows = new List<Element>();
|
||||
|
@ -55,12 +61,12 @@ namespace Microsoft.Maui.Automation
|
|||
}
|
||||
|
||||
var matches = new List<Element>();
|
||||
Traverse(platform, windows, matches, matcher);
|
||||
Traverse(windows, matches, matcher);
|
||||
|
||||
return Task.FromResult<IEnumerable<Element>>(matches);
|
||||
});
|
||||
|
||||
void Traverse(Platform platform, IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
void Traverse(IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
{
|
||||
foreach (var e in elements)
|
||||
{
|
||||
|
@ -70,25 +76,50 @@ namespace Microsoft.Maui.Automation
|
|||
if (e.PlatformElement is IView view)
|
||||
{
|
||||
var children = view.GetChildren(this, e.Id, 1, 1);
|
||||
Traverse(platform, children, matches, matcher);
|
||||
Traverse(children, matches, matcher);
|
||||
}
|
||||
else if (e.PlatformElement is IWindow window)
|
||||
{
|
||||
var children = window.GetChildren(this, e.Id, 1, 1);
|
||||
Traverse(platform, children, matches, matcher);
|
||||
Traverse(children, matches, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
public override async Task<string> GetProperty(string elementId, string propertyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var element = await this.FirstById(elementId);
|
||||
|
||||
return element.PlatformElement
|
||||
.GetType()
|
||||
.GetProperty(propertyName, System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)
|
||||
?.GetValue(element.Platform)?.ToString() ?? String.Empty;
|
||||
}
|
||||
|
||||
public override Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
public override Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> action switch
|
||||
{
|
||||
Actions.Tap => PerformTap(elementId, arguments),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
async Task<PerformActionResult> PerformTap(string elementId, params string[] arguments)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(elementId))
|
||||
{
|
||||
var element = await this.FirstById(elementId);
|
||||
|
||||
if (element.PlatformElement is IElement mauiElement)
|
||||
{
|
||||
#if ANDROID
|
||||
if (mauiElement.Handler?.PlatformView is Android.Views.View androidView)
|
||||
return await androidView.PerformAction(Actions.Tap, elementId, arguments);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,52 +40,36 @@ namespace Microsoft.Maui.Automation
|
|||
public bool IsActivityCurrent(Activity activity)
|
||||
=> LifecycleListener.Activity == activity;
|
||||
|
||||
public override Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
public override Task<string> GetProperty(string elementId, string propertyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
public override Task<IEnumerable<Element>> GetElements()
|
||||
{
|
||||
return Task.FromResult(LifecycleListener.Activities.Select(a => a.GetElement(this, 1, -1)));
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher)
|
||||
public override Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher)
|
||||
{
|
||||
var windows = LifecycleListener.Activities.Select(a => a.GetElement(this, 1, 1));
|
||||
|
||||
var matches = new List<Element>();
|
||||
Traverse(platform, windows, matches, matcher);
|
||||
Traverse(windows, matches, matcher);
|
||||
|
||||
return Task.FromResult<IEnumerable<Element>>(matches);
|
||||
}
|
||||
|
||||
public override async Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments)
|
||||
{
|
||||
if (action == Actions.Tap)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(elementId))
|
||||
{
|
||||
var element = await this.FirstById(elementId);
|
||||
if (element.PlatformElement is View androidView)
|
||||
{
|
||||
var r = androidView.PerformClick();
|
||||
return PerformActionResult.Ok();
|
||||
}
|
||||
}
|
||||
else if (arguments is not null
|
||||
&& arguments.Length == 2
|
||||
&& ulong.TryParse(arguments[0], out var x)
|
||||
&& ulong.TryParse(arguments[1], out var y))
|
||||
{
|
||||
// tap x and y
|
||||
}
|
||||
}
|
||||
public override async Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
{
|
||||
var element = await this.FirstById(elementId);
|
||||
if (element?.PlatformElement is Android.Views.View androidView)
|
||||
return await androidView.PerformAction(action, elementId, arguments);
|
||||
|
||||
return PerformActionResult.Error($"Unrecognized action: {action}");
|
||||
}
|
||||
}
|
||||
|
||||
void Traverse(Platform platform, IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
void Traverse(IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
{
|
||||
foreach (var e in elements)
|
||||
{
|
||||
|
@ -95,7 +79,7 @@ namespace Microsoft.Maui.Automation
|
|||
if (e.PlatformElement is View view)
|
||||
{
|
||||
var children = view.GetChildren(this, e.Id, 1, 1);
|
||||
Traverse(platform, children, matches, matcher);
|
||||
Traverse(children, matches, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ using Android.Content;
|
|||
using Android.Hardware.Lights;
|
||||
using Android.Views;
|
||||
using AndroidX.Core.View.Accessibility;
|
||||
using Java.Lang;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
namespace Microsoft.Maui.Automation
|
||||
|
@ -143,5 +145,46 @@ namespace Microsoft.Maui.Automation
|
|||
e.Children.AddRange(activity.GetChildren(e.Application, e.Id, currentDepth + 1, maxDepth));
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
public static async Task<PerformActionResult> PerformAction(this Android.Views.View view, string action, string elementId, params string[] arguments)
|
||||
{
|
||||
if (action == Actions.Tap)
|
||||
{
|
||||
await view.PostAsync(() => view.PerformClick());
|
||||
return PerformActionResult.Ok();
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"PerformAction {action} is not supported.");
|
||||
}
|
||||
|
||||
public static Task PostAsync(this Android.Views.View view, Action action)
|
||||
{
|
||||
var r = new AsyncThreadRunner(() => view.PerformClick());
|
||||
view.Post(r);
|
||||
return r.WaitAsync();
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncThreadRunner : Java.Lang.Object, IRunnable
|
||||
{
|
||||
public AsyncThreadRunner(Action action)
|
||||
{
|
||||
runner = action;
|
||||
tcsRunner = new();
|
||||
}
|
||||
|
||||
readonly Action runner;
|
||||
|
||||
readonly TaskCompletionSource<bool> tcsRunner;
|
||||
|
||||
public Task WaitAsync()
|
||||
=> tcsRunner.Task;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
runner?.Invoke();
|
||||
tcsRunner.TrySetResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ namespace Microsoft.Maui.Automation
|
|||
public override Platform DefaultPlatform => Platform.Winappsdk;
|
||||
|
||||
|
||||
public override Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
public override Task<IEnumerable<Element>> GetElements()
|
||||
=> Task.FromResult<IEnumerable<Element>>(new[] { UI.Xaml.Window.Current.GetElement(this, 1, -1) });
|
||||
|
||||
public override async Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
public override async Task<string> GetProperty(string elementId, string propertyName)
|
||||
{
|
||||
var matches = await FindElements(platform, e => e.Id?.Equals(elementId) ?? false);
|
||||
var matches = await FindElements(e => e.Id?.Equals(elementId) ?? false);
|
||||
|
||||
var match = matches?.FirstOrDefault();
|
||||
|
||||
|
@ -29,23 +29,23 @@ namespace Microsoft.Maui.Automation
|
|||
}
|
||||
|
||||
|
||||
public override async Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher)
|
||||
public override async Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher)
|
||||
{
|
||||
var windows = new[] { UI.Xaml.Window.Current.GetElement(this, 1, 1) };
|
||||
|
||||
var matches = new List<Element>();
|
||||
|
||||
await Traverse(platform, windows, matches, matcher);
|
||||
await Traverse(windows, matches, matcher);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
public override Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments)
|
||||
public override Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
{
|
||||
return Task.FromResult(PerformActionResult.Error());
|
||||
}
|
||||
|
||||
async Task Traverse(Platform platform, IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
async Task Traverse(IEnumerable<Element> elements, IList<Element> matches, Func<Element, bool> matcher)
|
||||
{
|
||||
foreach (var e in elements)
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ namespace Microsoft.Maui.Automation
|
|||
children.Add(c);
|
||||
}
|
||||
|
||||
await Traverse(platform, children, matches, matcher);
|
||||
await Traverse(children, matches, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,17 +56,17 @@ namespace Microsoft.Maui.Automation
|
|||
|
||||
public abstract Platform DefaultPlatform { get; }
|
||||
|
||||
public abstract Task<string> GetProperty(Platform platform, string elementId, string propertyName);
|
||||
public abstract Task<string> GetProperty(string elementId, string propertyName);
|
||||
|
||||
public abstract Task<IEnumerable<Element>> GetElements(Platform platform);
|
||||
public abstract Task<IEnumerable<Element>> GetElements();
|
||||
|
||||
public abstract Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher);
|
||||
public abstract Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(Platform platform, string action, params string[] arguments)
|
||||
=> PerformAction(platform, action, string.Empty, arguments);
|
||||
public Task<PerformActionResult> PerformAction(string action, params string[] arguments)
|
||||
=> PerformAction(action, string.Empty, arguments);
|
||||
|
||||
public abstract Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments);
|
||||
public abstract Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,51 +6,40 @@ namespace Microsoft.Maui.Automation;
|
|||
public static class ElementExtensions
|
||||
{
|
||||
|
||||
public static Task<Element?> FirstBy(this IApplication application, string propertyName, string pattern, bool isRegularExpression)
|
||||
=> application.FirstBy(application.DefaultPlatform, propertyName, pattern, isRegularExpression);
|
||||
|
||||
public static Task<Element?> FirstById(this IApplication application, string id)
|
||||
=> application.FirstById(application.DefaultPlatform, id);
|
||||
|
||||
public static Task<Element?> FirstByAutomationId(this IApplication application, string automationId)
|
||||
=> application.FirstByAutomationId(application.DefaultPlatform, automationId);
|
||||
|
||||
|
||||
public static async Task<Element?> FirstBy(this IApplication application, Platform platform, string propertyName, string pattern, bool isRegularExpression)
|
||||
=> (await application.FindElements(platform, e => e.Matches(propertyName, pattern, isRegularExpression))).FirstOrDefault();
|
||||
|
||||
public static async Task<Element?> FirstById(this IApplication application, Platform platform, string id)
|
||||
=> (await application.FindElements(platform, e => e.Id.Equals(id))).FirstOrDefault();
|
||||
|
||||
public static async Task<Element?> FirstByAutomationId(this IApplication application, Platform platform, string automationId)
|
||||
=> (await application.FindElements(platform, e => e.AutomationId.Equals(automationId))).FirstOrDefault();
|
||||
|
||||
public static async Task<Element?> FirstBy(this IApplication application,string propertyName, string pattern, bool isRegularExpression)
|
||||
=> (await application.FindElements(e => e.PropertyMatches(propertyName, pattern, isRegularExpression))).FirstOrDefault();
|
||||
|
||||
public static async Task<Element?> FirstById(this IApplication application, string id)
|
||||
=> (await application.FindElements(e => e.Id.Equals(id))).FirstOrDefault();
|
||||
|
||||
public static async Task<Element?> FirstByAutomationId(this IApplication application, string automationId)
|
||||
=> (await application.FindElements(e => e.AutomationId.Equals(automationId))).FirstOrDefault();
|
||||
|
||||
|
||||
|
||||
public static Element? FirstBy(this IEnumerable<Element> elements, string propertyName, string pattern, bool isRegularExpression)
|
||||
=> elements.Traverse(e => e.PropertyMatches(propertyName, pattern, isRegularExpression)).FirstOrDefault();
|
||||
|
||||
public static Element? FirstById(this IEnumerable<Element> elements, string id)
|
||||
=> elements.Traverse(e => e.Id.Equals(id)).FirstOrDefault();
|
||||
|
||||
public static Element? FirstByAutomationId(this IEnumerable<Element> elements, string automationId)
|
||||
=> elements.Traverse(e => e.AutomationId.Equals(automationId)).FirstOrDefault();
|
||||
|
||||
|
||||
|
||||
public static Task<IEnumerable<Element>> By(this IApplication application, string propertyName, string pattern, bool isRegularExpression)
|
||||
=> application.By(application.DefaultPlatform, propertyName, pattern, isRegularExpression);
|
||||
=> application.FindElements(e => e.PropertyMatches(propertyName, pattern, isRegularExpression));
|
||||
|
||||
public static Task<IEnumerable<Element>> ById(this IApplication application, string id)
|
||||
=> application.ById(application.DefaultPlatform, id);
|
||||
=> application.FindElements(e => e.Id.Equals(id));
|
||||
|
||||
public static Task<IEnumerable<Element>> ByAutomationId(this IApplication application, string automationId)
|
||||
=> application.ByAutomationId(application.DefaultPlatform, automationId);
|
||||
|
||||
|
||||
public static Task<IEnumerable<Element>> By(this IApplication application, Platform platform, string propertyName, string pattern, bool isRegularExpression)
|
||||
=> application.FindElements(platform, e => e.Matches(propertyName, pattern, isRegularExpression));
|
||||
|
||||
public static Task<IEnumerable<Element>> ById(this IApplication application, Platform platform, string id)
|
||||
=> application.FindElements(platform, e => e.Id.Equals(id));
|
||||
|
||||
public static Task<IEnumerable<Element>> ByAutomationId(this IApplication application, Platform platform, string automationId)
|
||||
=> application.FindElements(platform, e => e.AutomationId.Equals(automationId));
|
||||
=> application.FindElements(e => e.AutomationId.Equals(automationId));
|
||||
|
||||
|
||||
public static IEnumerable<Element> By(this IEnumerable<Element> elements, string propertyName, string pattern, bool isRegularExpression)
|
||||
=> elements.Traverse(e => e.Matches(propertyName, pattern, isRegularExpression));
|
||||
=> elements.Traverse(e => e.PropertyMatches(propertyName, pattern, isRegularExpression));
|
||||
|
||||
public static IEnumerable<Element> ById(this IEnumerable<Element> elements, string id)
|
||||
=> elements.Traverse(e => e.Id.Equals(id));
|
||||
|
@ -59,30 +48,30 @@ public static class ElementExtensions
|
|||
=> elements.Traverse(e => e.AutomationId.Equals(automationId));
|
||||
|
||||
|
||||
public static IEnumerable<Element> Find(this IEnumerable<Element> elements, Func<Element, bool> matcher)
|
||||
=> elements.Traverse(matcher);
|
||||
public static IEnumerable<Element> Find(this IEnumerable<Element> elements, Predicate<Element> predicate)
|
||||
=> elements.Traverse(predicate);
|
||||
|
||||
static IEnumerable<Element> Traverse(this IEnumerable<Element> elements, Func<Element, bool> matcher)
|
||||
static IEnumerable<Element> Traverse(this IEnumerable<Element> elements, Predicate<Element> predicate)
|
||||
{
|
||||
var matches = new List<Element>();
|
||||
|
||||
elements.Traverse(matches, matcher);
|
||||
elements.Traverse(matches, predicate);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
static void Traverse(this IEnumerable<Element> source, IList<Element> matches, Func<Element, bool> matcher)
|
||||
static void Traverse(this IEnumerable<Element> source, IList<Element> matches, Predicate<Element> predicate)
|
||||
{
|
||||
foreach (var s in source)
|
||||
{
|
||||
if (matcher(s))
|
||||
if (predicate(s))
|
||||
matches.Add(s);
|
||||
|
||||
Traverse(s.Children, matches, matcher);
|
||||
Traverse(s.Children, matches, predicate);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool Matches(this Element e, string propertyName, string pattern, bool isRegularExpression = false)
|
||||
public static bool PropertyMatches(this Element e, string propertyName, string pattern, bool isRegularExpression = false)
|
||||
{
|
||||
var value =
|
||||
propertyName.ToLowerInvariant() switch
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using Grpc.Net.Client.Configuration;
|
||||
using Grpc.Net.Client.Web;
|
||||
using Microsoft.Maui.Automation.RemoteGrpc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -29,39 +30,11 @@ namespace Microsoft.Maui.Automation
|
|||
|
||||
private bool disposedValue;
|
||||
|
||||
public GrpcRemoteAppAgent(IApplication application, string address, HttpMessageHandler? httpMessageHandler = null)
|
||||
public GrpcRemoteAppAgent(IApplication application, string address)
|
||||
{
|
||||
Application = application;
|
||||
|
||||
var grpc = GrpcChannel.ForAddress(address, new GrpcChannelOptions
|
||||
{
|
||||
//ServiceConfig = new ServiceConfig
|
||||
//{
|
||||
// MethodConfigs =
|
||||
// {
|
||||
// new MethodConfig
|
||||
// {
|
||||
// Names = { MethodName.Default },
|
||||
// RetryPolicy = new RetryPolicy
|
||||
// {
|
||||
// MaxAttempts = 60,
|
||||
// InitialBackoff = TimeSpan.FromSeconds(1),
|
||||
// MaxBackoff = TimeSpan.FromSeconds(5),
|
||||
// BackoffMultiplier = 1.1,
|
||||
// RetryableStatusCodes = { StatusCode.NotFound, StatusCode.Unavailable }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//},
|
||||
//HttpHandler = new Grpc.Net.Client.Web.GrpcWebHandler(Grpc.Net.Client.Web.GrpcWebMode.GrpcWeb)
|
||||
//,
|
||||
//httpMessageHandler ?? new HttpClientHandler())
|
||||
//HttpHandler = new HttpClientHandler
|
||||
//{
|
||||
// ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
//}
|
||||
}); ;
|
||||
|
||||
var grpc = GrpcChannel.ForAddress(address);
|
||||
client = new RemoteApp.RemoteAppClient(grpc);
|
||||
|
||||
elementsCall = client.GetElementsRoute();
|
||||
|
@ -87,7 +60,8 @@ namespace Microsoft.Maui.Automation
|
|||
var response = await HandleFindElementsRequest(findElementsCall.ResponseStream.Current);
|
||||
await findElementsCall.RequestStream.WriteAsync(response);
|
||||
}
|
||||
} catch (Exception ex)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
@ -114,56 +88,104 @@ namespace Microsoft.Maui.Automation
|
|||
|
||||
async Task<ElementsResponse> HandleElementsRequest(ElementsRequest request)
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var elements = await Application.GetElements(request.Platform);
|
||||
|
||||
var response = new ElementsResponse();
|
||||
response.RequestId = request.RequestId;
|
||||
response.Elements.AddRange(elements);
|
||||
|
||||
try
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var elements = await GetApp(request.Platform).GetElements();
|
||||
response.Elements.AddRange(elements);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async Task<ElementsResponse> HandleFindElementsRequest(FindElementsRequest request)
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var elements = await Application.FindElements(request.Platform, e =>
|
||||
e.Matches(request.PropertyName, request.Pattern, request.IsExpression));
|
||||
|
||||
var response = new ElementsResponse();
|
||||
response.RequestId = request.RequestId;
|
||||
response.Elements.AddRange(elements);
|
||||
|
||||
try
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var elements = await GetApp(request.Platform).FindElements(e =>
|
||||
e.PropertyMatches(request.PropertyName, request.Pattern, request.IsExpression));
|
||||
response.Elements.AddRange(elements);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async Task<PropertyResponse> HandleGetPropertyRequest(PropertyRequest request)
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var v = await Application.GetProperty(request.Platform, request.ElementId, request.PropertyName);
|
||||
|
||||
var response = new PropertyResponse();
|
||||
response.Platform = request.Platform;
|
||||
response.RequestId = request.RequestId;
|
||||
response.Value = v;
|
||||
|
||||
try
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var v = await GetApp(request.Platform).GetProperty(request.ElementId, request.PropertyName);
|
||||
|
||||
response.Value = v;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
response.Value = string.Empty;
|
||||
}
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
async Task<PerformActionResponse> HandlePerformActionRequest(PerformActionRequest request)
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var result = await Application.PerformAction(request.Platform, request.Action, request.ElementId, request.Arguments.ToArray());
|
||||
|
||||
var response = new PerformActionResponse();
|
||||
response.Platform = request.Platform;
|
||||
response.RequestId = request.RequestId;
|
||||
response.Status = result.Status;
|
||||
response.Result = result.Result;
|
||||
|
||||
try
|
||||
{
|
||||
// Get the elements from the running app host
|
||||
var result = await GetApp(request.Platform).PerformAction(request.Action, request.ElementId, request.Arguments.ToArray());
|
||||
response.Status = result.Status;
|
||||
response.Result = result.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Status = PerformActionResult.ErrorStatus;
|
||||
response.Result = ex.Message;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
IApplication GetApp(Platform platform)
|
||||
{
|
||||
var unsupportedText = $"Platform {platform} is not supported on this app agent";
|
||||
|
||||
if (Application is MultiPlatformApplication multiApp)
|
||||
{
|
||||
if (!multiApp.PlatformApps.ContainsKey(platform))
|
||||
throw new NotSupportedException(unsupportedText);
|
||||
|
||||
return multiApp.PlatformApps[platform];
|
||||
}
|
||||
|
||||
if (Application.DefaultPlatform != platform)
|
||||
throw new NotSupportedException(unsupportedText);
|
||||
return Application;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
|
|
|
@ -7,13 +7,14 @@ namespace Microsoft.Maui.Automation
|
|||
{
|
||||
public Platform DefaultPlatform { get; }
|
||||
|
||||
public Task<IEnumerable<Element>> GetElements(Platform platform);
|
||||
public Task<IEnumerable<Element>> GetElements();
|
||||
|
||||
public Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher);
|
||||
|
||||
public Task<string> GetProperty(Platform platform, string elementId, string propertyName);
|
||||
public Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments);
|
||||
public Task<PerformActionResult> PerformAction(Platform platform, string action, params string[] arguments);
|
||||
public Task<string> GetProperty(string elementId, string propertyName);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(string action, params string[] arguments);
|
||||
}
|
||||
}
|
|
@ -29,18 +29,18 @@ namespace Microsoft.Maui.Automation
|
|||
return PlatformApps[platform];
|
||||
}
|
||||
|
||||
public override Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
=> GetApp(platform).GetElements(platform);
|
||||
public override Task<IEnumerable<Element>> GetElements()
|
||||
=> GetApp(DefaultPlatform).GetElements();
|
||||
|
||||
public override Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
=> GetApp(platform).GetProperty(platform, elementId, propertyName);
|
||||
public override Task<string> GetProperty(string elementId, string propertyName)
|
||||
=> GetApp(DefaultPlatform).GetProperty(elementId, propertyName);
|
||||
|
||||
public override Task<IEnumerable<Element>> FindElements(Platform platform, Func<Element, bool> matcher)
|
||||
=> GetApp(platform).FindElements(platform, matcher);
|
||||
public override Task<IEnumerable<Element>> FindElements(Func<Element, bool> matcher)
|
||||
=> GetApp(DefaultPlatform).FindElements(matcher);
|
||||
|
||||
|
||||
public override Task<PerformActionResult> PerformAction(Platform platform, string action, string elementId, params string[] arguments)
|
||||
=> GetApp(platform).PerformAction(platform, action, elementId, arguments);
|
||||
public override Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> GetApp(DefaultPlatform).PerformAction(action, elementId, arguments);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,19 @@
|
|||
public const string Tap = "TAP";
|
||||
|
||||
}
|
||||
public class PerformActionResult
|
||||
public partial class PerformActionResult
|
||||
{
|
||||
public static PerformActionResult Ok(string result = "")
|
||||
=> new PerformActionResult { Result = result, Status = 0 };
|
||||
public const int SuccessStatus = 0;
|
||||
public const int ErrorStatus = -1;
|
||||
|
||||
public static PerformActionResult Error(string result = "", int status = -1)
|
||||
public static PerformActionResult Ok(string result = "")
|
||||
=> new PerformActionResult { Result = result, Status = SuccessStatus };
|
||||
|
||||
public static PerformActionResult Error(string result = "", int status = ErrorStatus)
|
||||
=> new PerformActionResult { Result = result, Status = status };
|
||||
|
||||
public int Status { get; set; }
|
||||
public int Status { get; set; } = ErrorStatus;
|
||||
|
||||
public string Result { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ public class AndroidDriver : IDriver
|
|||
{
|
||||
Configuration = configuration;
|
||||
|
||||
int port = 10882;
|
||||
var address = IPAddress.Any.ToString();
|
||||
int port = 5000;
|
||||
//var address = IPAddress.Any.ToString();
|
||||
var adbDeviceSerial = configuration.Device;
|
||||
|
||||
androidSdkManager = new AndroidSdk.AndroidSdkManager();
|
||||
|
@ -40,8 +40,8 @@ public class AndroidDriver : IDriver
|
|||
|
||||
Name = $"Android ({Adb.GetDeviceName(Device)})";
|
||||
|
||||
//var forwardResult = Adb.RunCommand("-s", $"\"{Device}\"", "reverse", $"tcp:{port}", $"tcp:{port}")?.GetAllOutput();
|
||||
//System.Diagnostics.Debug.WriteLine(forwardResult);
|
||||
var forwardResult = Adb.RunCommand("-s", $"\"{Device}\"", "reverse", $"tcp:{port}", $"tcp:{port}")?.GetAllOutput();
|
||||
System.Diagnostics.Debug.WriteLine(forwardResult);
|
||||
|
||||
grpc = new GrpcHost();
|
||||
}
|
||||
|
@ -52,8 +52,9 @@ public class AndroidDriver : IDriver
|
|||
protected readonly AndroidSdk.PackageManager Pm;
|
||||
|
||||
readonly AndroidSdk.AndroidSdkManager androidSdkManager;
|
||||
protected readonly string Device;
|
||||
|
||||
protected readonly string Device;
|
||||
private bool disposedValue;
|
||||
|
||||
public IAutomationConfiguration Configuration { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
@ -177,19 +178,31 @@ public class AndroidDriver : IDriver
|
|||
}
|
||||
|
||||
public Task Tap(int x, int y)
|
||||
=> grpc.Client.PerformAction(Platform.Android, Actions.Tap, string.Empty, x.ToString(), y.ToString());
|
||||
=> grpc.Client.PerformAction(Configuration.AutomationPlatform, Actions.Tap, string.Empty, x.ToString(), y.ToString());
|
||||
|
||||
public Task Tap(Element element)
|
||||
=> grpc.Client.PerformAction(Configuration.AutomationPlatform, Actions.Tap, element.Id);
|
||||
|
||||
|
||||
bool IsAppInstalled(string appId)
|
||||
=> androidSdkManager.PackageManager.ListPackages()
|
||||
.Any(p => p.PackageName?.Equals(appId, StringComparison.OrdinalIgnoreCase) ?? false);
|
||||
|
||||
public Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
=> grpc.Client.GetProperty(platform, elementId, propertyName);
|
||||
public Task<string> GetProperty(string elementId, string propertyName)
|
||||
=> grpc.Client.GetProperty(Configuration.AutomationPlatform, elementId, propertyName);
|
||||
|
||||
public Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
=> grpc.Client.GetElements(platform);
|
||||
public Task<IEnumerable<Element>> GetElements()
|
||||
=> grpc.Client.GetElements(Configuration.AutomationPlatform);
|
||||
|
||||
public Task<IEnumerable<Element>> FindElements(Platform platform, string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> grpc.Client.FindElements(platform, propertyName, pattern, isExpression, ancestorId);
|
||||
public Task<IEnumerable<Element>> FindElements(string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> grpc.Client.FindElements(Configuration.AutomationPlatform, propertyName, pattern, isExpression, ancestorId);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> grpc.Client.PerformAction(Configuration.AutomationPlatform, action, elementId, arguments);
|
||||
|
||||
public async void Dispose()
|
||||
{
|
||||
if (grpc is not null)
|
||||
await grpc.Stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
namespace Microsoft.Maui.Automation.Driver;
|
||||
|
||||
public class AppDriver : IDriver
|
||||
public class AppDriver : Driver
|
||||
{
|
||||
public AppDriver(IAutomationConfiguration configuration)
|
||||
: base(configuration)
|
||||
{
|
||||
Driver = configuration.DevicePlatform switch
|
||||
{
|
||||
|
@ -19,61 +20,70 @@ public class AppDriver : IDriver
|
|||
|
||||
public readonly IDriver Driver;
|
||||
|
||||
public IAutomationConfiguration Configuration => Driver!.Configuration;
|
||||
public override string Name => Driver.Name;
|
||||
|
||||
public string Name => Driver.Name;
|
||||
|
||||
public Task Back()
|
||||
public override Task Back()
|
||||
=> Driver.Back();
|
||||
|
||||
public Task ClearAppState(string appId)
|
||||
public override Task ClearAppState(string appId)
|
||||
=> Driver.ClearAppState(appId);
|
||||
|
||||
public Task<IEnumerable<Element>> FindElements(Platform platform, string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> Driver.FindElements(platform, propertyName, pattern, isExpression, propertyName);
|
||||
public override Task<IEnumerable<Element>> FindElements(string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> Driver.FindElements(propertyName, pattern, isExpression, propertyName);
|
||||
|
||||
public Task<IDeviceInfo> GetDeviceInfo()
|
||||
public override Task<IDeviceInfo> GetDeviceInfo()
|
||||
=> Driver.GetDeviceInfo();
|
||||
|
||||
public Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
=> Driver.GetElements(platform);
|
||||
public override Task<IEnumerable<Element>> GetElements()
|
||||
=> Driver.GetElements();
|
||||
|
||||
public Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
=> Driver.GetProperty(platform, elementId, propertyName);
|
||||
public override Task<string> GetProperty(string elementId, string propertyName)
|
||||
=> Driver.GetProperty(elementId, propertyName);
|
||||
|
||||
public Task InputText(string text)
|
||||
public override Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> Driver.PerformAction(action, elementId, arguments);
|
||||
|
||||
public override Task InputText(string text)
|
||||
=> Driver.InputText(text);
|
||||
|
||||
public Task InstallApp(string file, string appId)
|
||||
public override Task InstallApp(string file, string appId)
|
||||
=> Driver.InstallApp(file, appId);
|
||||
|
||||
public Task KeyPress(char keyCode)
|
||||
public override Task KeyPress(char keyCode)
|
||||
=> Driver.KeyPress(keyCode);
|
||||
|
||||
public Task LaunchApp(string appId)
|
||||
public override Task LaunchApp(string appId)
|
||||
=> Driver.LaunchApp(appId);
|
||||
|
||||
public Task LongPress(int x, int y)
|
||||
public override Task LongPress(int x, int y)
|
||||
=> Driver.LongPress(x, y);
|
||||
|
||||
public Task OpenUri(string uri)
|
||||
public override Task OpenUri(string uri)
|
||||
=> Driver.OpenUri(uri);
|
||||
|
||||
public Task PullFile(string appId, string remoteFile, string localDirectory)
|
||||
public override Task PullFile(string appId, string remoteFile, string localDirectory)
|
||||
=> Driver.PullFile(appId, remoteFile, localDirectory);
|
||||
|
||||
public Task PushFile(string appId, string localFile, string destinationDirectory)
|
||||
public override Task PushFile(string appId, string localFile, string destinationDirectory)
|
||||
=> Driver.PushFile(appId, localFile, destinationDirectory);
|
||||
|
||||
public Task RemoveApp(string appId)
|
||||
public override Task RemoveApp(string appId)
|
||||
=> Driver.RemoveApp(appId);
|
||||
|
||||
public Task StopApp(string appId)
|
||||
public override Task StopApp(string appId)
|
||||
=> Driver.StopApp(appId);
|
||||
|
||||
public Task Swipe((int x, int y) start, (int x, int y) end)
|
||||
public override Task Swipe((int x, int y) start, (int x, int y) end)
|
||||
=> Driver.Swipe(start, end);
|
||||
|
||||
public Task Tap(int x, int y)
|
||||
public override Task Tap(int x, int y)
|
||||
=> Driver.Tap(x, y);
|
||||
|
||||
public override Task Tap(Element element)
|
||||
=> Driver.PerformAction(Actions.Tap, element.Id);
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Driver.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,10 @@ namespace Microsoft.Maui.Automation.Driver
|
|||
{
|
||||
}
|
||||
|
||||
public AutomationConfiguration(Platform devicePlatform, string? device = null, Platform? automationPlatform = null)
|
||||
public AutomationConfiguration(string appId, string appFilename, Platform devicePlatform, string? device = null, Platform? automationPlatform = null)
|
||||
{
|
||||
AppId = appId;
|
||||
AppFilename = appFilename;
|
||||
DevicePlatform = devicePlatform;
|
||||
if (!string.IsNullOrEmpty(device))
|
||||
Device = device;
|
||||
|
@ -58,7 +60,7 @@ namespace Microsoft.Maui.Automation.Driver
|
|||
|
||||
public int AppAgentPort
|
||||
{
|
||||
get => int.Parse(GetOrDefault(nameof(AppAgentPort), 10882).ToString());
|
||||
get => int.Parse(GetOrDefault(nameof(AppAgentPort), 5000).ToString());
|
||||
set => this[nameof(AppAgentPort)] = value;
|
||||
}
|
||||
|
||||
|
@ -80,7 +82,19 @@ namespace Microsoft.Maui.Automation.Driver
|
|||
set => this[nameof(AutomationPlatform)] = Enum.GetName<Platform>(value) ?? nameof(Platform.Maui);
|
||||
}
|
||||
|
||||
private object GetOrDefault(string key, object defaultValue)
|
||||
public string AppId
|
||||
{
|
||||
get => GetOrDefault(nameof(AppId), "").ToString();
|
||||
set => this[nameof(AppId)] = value;
|
||||
}
|
||||
|
||||
public string AppFilename
|
||||
{
|
||||
get => GetOrDefault(nameof(AppFilename), "").ToString();
|
||||
set => this[nameof(AppFilename)] = value;
|
||||
}
|
||||
|
||||
private object GetOrDefault(string key, object defaultValue)
|
||||
{
|
||||
if (this.ContainsKey(key))
|
||||
return this[key];
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
namespace Microsoft.Maui.Automation.Driver
|
||||
{
|
||||
public abstract class Driver : IDriver
|
||||
{
|
||||
public Driver(IAutomationConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public abstract string Name { get; }
|
||||
|
||||
public IAutomationConfiguration Configuration { get; }
|
||||
|
||||
public abstract Task Back();
|
||||
|
||||
public abstract Task ClearAppState(string appId);
|
||||
public Task ClearAppState()
|
||||
=> ClearAppState(Configuration.AppId);
|
||||
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract Task<IDeviceInfo> GetDeviceInfo();
|
||||
|
||||
public abstract Task InputText(string text);
|
||||
|
||||
public abstract Task InstallApp(string file, string appId);
|
||||
|
||||
public Task InstallApp()
|
||||
=> InstallApp(Configuration.AppFilename, Configuration.AppId);
|
||||
|
||||
public abstract Task KeyPress(char keyCode);
|
||||
|
||||
public abstract Task LaunchApp(string appId);
|
||||
|
||||
public Task LaunchApp()
|
||||
=> LaunchApp(Configuration.AppId);
|
||||
|
||||
public abstract Task LongPress(int x, int y);
|
||||
|
||||
public abstract Task OpenUri(string uri);
|
||||
|
||||
public abstract Task<IEnumerable<Element>> FindElements(string propertyName, string pattern, bool isExpression = false, string ancestorId = "");
|
||||
|
||||
public abstract Task<IEnumerable<Element>> GetElements();
|
||||
|
||||
public abstract Task<string> GetProperty(string elementId, string propertyName);
|
||||
|
||||
public abstract Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments);
|
||||
|
||||
public abstract Task PullFile(string appId, string remoteFile, string localDirectory);
|
||||
|
||||
public Task PullFile(string remoteFile, string localDirectory)
|
||||
=> PullFile(Configuration.AppId, remoteFile, localDirectory);
|
||||
|
||||
public abstract Task PushFile(string appId, string localFile, string destinationDirectory);
|
||||
|
||||
public Task PushFile(string localFile, string destinationDirectory)
|
||||
=> PushFile(Configuration.AppId, localFile, destinationDirectory);
|
||||
|
||||
public abstract Task RemoveApp(string appId);
|
||||
|
||||
public Task RemoveApp()
|
||||
=> RemoveApp(Configuration.AppId);
|
||||
|
||||
public abstract Task StopApp(string appId);
|
||||
|
||||
public Task StopApp()
|
||||
=> StopApp(Configuration.AppId);
|
||||
|
||||
public abstract Task Swipe((int x, int y) start, (int x, int y) end);
|
||||
|
||||
public abstract Task Tap(int x, int y);
|
||||
|
||||
public abstract Task Tap(Element element);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Maui.Automation.Driver;
|
||||
|
||||
public static class DriverExtensions
|
||||
{
|
||||
|
||||
public static async Task<Element?> FirstById(this IDriver driver, string id)
|
||||
{
|
||||
var elements = await driver.GetElements();
|
||||
|
||||
return elements.FirstById(id);
|
||||
}
|
||||
|
||||
public static async Task<Element?> FirstByAutomationId(this IDriver driver, string automationId)
|
||||
{
|
||||
var elements = await driver.GetElements();
|
||||
|
||||
var e = elements.FirstByAutomationId(automationId);
|
||||
return e;
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<Element>> By(this IDriver driver, Predicate<Element> matching)
|
||||
{
|
||||
var elements = await driver.GetElements();
|
||||
|
||||
return elements.Find(matching);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Task<IEnumerable<Element>> ByType(this IDriver driver, string elementType)
|
||||
=> driver.FindElements("Type", elementType);
|
||||
}
|
|
@ -1,44 +1,68 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Maui.Automation.Remote;
|
||||
|
||||
namespace Microsoft.Maui.Automation.Remote;
|
||||
|
||||
public class GrpcHost
|
||||
{
|
||||
public GrpcHost()
|
||||
{
|
||||
var builder = CreateHostBuilder(this, Array.Empty<string>());
|
||||
public GrpcHost()
|
||||
{
|
||||
var builder = CreateHostBuilder(this, Array.Empty<string>());
|
||||
|
||||
host = builder.Build();
|
||||
host.StartAsync();
|
||||
host = builder.Build();
|
||||
host.StartAsync();
|
||||
|
||||
Client = host.Services.GetRequiredService<GrpcRemoteAppClient>();
|
||||
}
|
||||
Services = host.Services;
|
||||
Client = host.Services.GetRequiredService<GrpcRemoteAppClient>();
|
||||
}
|
||||
|
||||
public readonly GrpcRemoteAppClient Client;
|
||||
public readonly GrpcRemoteAppClient Client;
|
||||
|
||||
readonly IHost host;
|
||||
readonly IWebHost host;
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(GrpcHost grpcHost, string[] args)
|
||||
=> Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder => webBuilder
|
||||
.ConfigureServices(services => {
|
||||
services.AddGrpc();
|
||||
|
||||
services.AddSingleton<GrpcHost>(grpcHost);
|
||||
services.AddSingleton<GrpcRemoteAppClient>();
|
||||
public readonly IServiceProvider Services;
|
||||
|
||||
})
|
||||
.Configure(app =>
|
||||
app.UseRouting()
|
||||
.UseGrpcWeb()
|
||||
.UseEndpoints(endpoints =>
|
||||
endpoints.MapGrpcService<GrpcRemoteAppClient>().EnableGrpcWeb())));
|
||||
public ILogger<GrpcHost> Logger => Services.GetRequiredService<ILogger<GrpcHost>>();
|
||||
|
||||
public static IWebHostBuilder CreateHostBuilder(GrpcHost grpcHost, string[] args)
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureLogging(log =>
|
||||
{
|
||||
log.AddConsole();
|
||||
})
|
||||
.UseKestrel(kestrel =>
|
||||
{
|
||||
kestrel.ListenAnyIP(5000, listen =>
|
||||
{
|
||||
listen.Protocols = HttpProtocols.Http2;
|
||||
});
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddGrpc();
|
||||
services.AddSingleton<GrpcHost>(grpcHost);
|
||||
services.AddSingleton<GrpcRemoteAppClient>();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app
|
||||
.UseRouting()
|
||||
.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true })
|
||||
.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapGrpcService<GrpcRemoteAppClient>();
|
||||
});
|
||||
});
|
||||
|
||||
public Task Stop()
|
||||
=> host?.StopAsync() ?? Task.CompletedTask;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public Task Stop()
|
||||
=> host?.StopAsync() ?? Task.CompletedTask;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
using Grpc.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Maui.Automation.RemoteGrpc;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Maui.Automation.Remote
|
||||
{
|
||||
public class GrpcRemoteAppClient : RemoteGrpc.RemoteApp.RemoteAppBase
|
||||
public class GrpcRemoteAppClient : RemoteApp.RemoteAppBase
|
||||
{
|
||||
public GrpcRemoteAppClient()
|
||||
public GrpcRemoteAppClient(ILogger<GrpcRemoteAppClient> logger)
|
||||
: base()
|
||||
{
|
||||
Console.WriteLine("New grpc Service");
|
||||
logger.LogInformation("GRPC Service Created");
|
||||
}
|
||||
|
||||
Dictionary<string, TaskCompletionSource<IResponseMessage>> pendingResponses = new();
|
||||
|
|
|
@ -10,5 +10,8 @@
|
|||
Platform DevicePlatform { get; set; }
|
||||
|
||||
Platform AutomationPlatform { get; set; }
|
||||
|
||||
string AppId { get; set; }
|
||||
string AppFilename { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
namespace Microsoft.Maui.Automation.Driver
|
||||
{
|
||||
public interface IDriver
|
||||
public interface IDriver : IDisposable
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
|||
Task<IDeviceInfo> GetDeviceInfo();
|
||||
|
||||
Task InstallApp(string file, string appId);
|
||||
|
||||
Task RemoveApp(string appId);
|
||||
|
||||
Task LaunchApp(string appId);
|
||||
|
@ -17,12 +18,14 @@
|
|||
Task ClearAppState(string appId);
|
||||
|
||||
Task PushFile(string appId, string localFile, string destinationDirectory);
|
||||
Task PullFile(string appId, string remoteFile, string localDirectory);
|
||||
Task PullFile(string appId, string remoteFile, string localDirectory);
|
||||
|
||||
Task Tap(int x, int y);
|
||||
Task Tap(int x, int y);
|
||||
|
||||
Task LongPress(int x, int y);
|
||||
|
||||
Task Tap(Element element);
|
||||
|
||||
Task KeyPress(char keyCode);
|
||||
|
||||
Task Swipe((int x, int y) start, (int x, int y) end);
|
||||
|
@ -33,10 +36,12 @@
|
|||
|
||||
Task OpenUri(string uri);
|
||||
|
||||
Task<string> GetProperty(Platform platform, string elementId, string propertyName);
|
||||
Task<string> GetProperty(string elementId, string propertyName);
|
||||
|
||||
Task<IEnumerable<Element>> GetElements(Platform platform);
|
||||
Task<IEnumerable<Element>> GetElements();
|
||||
|
||||
Task<IEnumerable<Element>> FindElements(Platform platform, string propertyName, string pattern, bool isExpression = false, string ancestorId = "");
|
||||
Task<IEnumerable<Element>> FindElements(string propertyName, string pattern, bool isExpression = false, string ancestorId = "");
|
||||
|
||||
Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments);
|
||||
}
|
||||
}
|
|
@ -6,104 +6,115 @@ using System.Net;
|
|||
|
||||
namespace Microsoft.Maui.Automation.Driver
|
||||
{
|
||||
public class WindowsDriver : IDriver
|
||||
{
|
||||
public class WindowsDriver : IDriver
|
||||
{
|
||||
public WindowsDriver(IAutomationConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
Configuration = configuration;
|
||||
|
||||
grpc = new GrpcHost();
|
||||
}
|
||||
grpc = new GrpcHost();
|
||||
}
|
||||
|
||||
readonly GrpcHost grpc;
|
||||
readonly GrpcHost grpc;
|
||||
|
||||
|
||||
public string Name => "Windows";
|
||||
public string Name => "Windows";
|
||||
|
||||
public IAutomationConfiguration Configuration { get; }
|
||||
public IAutomationConfiguration Configuration { get; }
|
||||
|
||||
public Task Back()
|
||||
=> Task.CompletedTask;
|
||||
public Task Back()
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public Task ClearAppState(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task ClearAppState(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IDeviceInfo> GetDeviceInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task<IDeviceInfo> GetDeviceInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task InputText(string text)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task InputText(string text)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task InstallApp(string file, string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task InstallApp(string file, string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task KeyPress(char keyCode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task KeyPress(char keyCode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task LaunchApp(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task LaunchApp(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task LongPress(int x, int y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task LongPress(int x, int y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task OpenUri(string uri)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task OpenUri(string uri)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task PullFile(string appId, string remoteFile, string localDirectory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task PullFile(string appId, string remoteFile, string localDirectory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task PushFile(string appId, string localFile, string destinationDirectory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task PushFile(string appId, string localFile, string destinationDirectory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RemoveApp(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task RemoveApp(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task StopApp(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task StopApp(string appId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task Swipe((int x, int y) start, (int x, int y) end)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task Swipe((int x, int y) start, (int x, int y) end)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task Tap(int x, int y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public Task Tap(int x, int y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
=> grpc.Client.GetProperty(platform, elementId, propertyName);
|
||||
public Task Tap(Element element)
|
||||
=> grpc.Client.PerformAction(Configuration.AutomationPlatform, Actions.Tap, element.Id);
|
||||
|
||||
public Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
=> grpc.Client.GetElements(platform);
|
||||
public Task<string> GetProperty(string elementId, string propertyName)
|
||||
=> grpc.Client.GetProperty(Configuration.AutomationPlatform, elementId, propertyName);
|
||||
|
||||
public Task<IEnumerable<Element>> FindElements(Platform platform, string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> grpc.Client.FindElements(platform, propertyName, pattern, isExpression, ancestorId);
|
||||
public Task<IEnumerable<Element>> GetElements()
|
||||
=> grpc.Client.GetElements(Configuration.AutomationPlatform);
|
||||
|
||||
}
|
||||
public Task<IEnumerable<Element>> FindElements(string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> grpc.Client.FindElements(Configuration.AutomationPlatform, propertyName, pattern, isExpression, ancestorId);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> grpc.Client.PerformAction(Configuration.AutomationPlatform, action, elementId, arguments);
|
||||
|
||||
public async void Dispose()
|
||||
{
|
||||
if (grpc is not null)
|
||||
await grpc.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -187,7 +187,11 @@ public class iOSDriver : IDriver
|
|||
=> idb.hid().SendStream<HIDEvent, HIDResponse>(keyCode.AsHidEvents().ToArray());
|
||||
|
||||
public Task Tap(int x, int y)
|
||||
=> press(x, y, TimeSpan.FromMilliseconds(50));
|
||||
=> press(x, y, TimeSpan.FromMilliseconds(50));
|
||||
|
||||
public Task Tap(Element element)
|
||||
=> grpc.Client.PerformAction(Platform.Ios, Actions.Tap, element.Id);
|
||||
|
||||
|
||||
public Task LongPress(int x, int y)
|
||||
=> press(x, y, TimeSpan.FromSeconds(3));
|
||||
|
@ -247,15 +251,17 @@ public class iOSDriver : IDriver
|
|||
}
|
||||
});
|
||||
|
||||
public Task<string> GetProperty(Platform platform, string elementId, string propertyName)
|
||||
=> grpc.Client.GetProperty(platform, elementId, propertyName);
|
||||
public Task<string> GetProperty(string elementId, string propertyName)
|
||||
=> grpc.Client.GetProperty(Configuration.AutomationPlatform, elementId, propertyName);
|
||||
|
||||
public Task<IEnumerable<Element>> GetElements(Platform platform)
|
||||
=> grpc.Client.GetElements(platform);
|
||||
public Task<IEnumerable<Element>> GetElements()
|
||||
=> grpc.Client.GetElements(Configuration.AutomationPlatform);
|
||||
|
||||
public Task<IEnumerable<Element>> FindElements(Platform platform, string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> grpc.Client.FindElements(platform, propertyName, pattern, isExpression, ancestorId);
|
||||
public Task<IEnumerable<Element>> FindElements(string propertyName, string pattern, bool isExpression = false, string ancestorId = "")
|
||||
=> grpc.Client.FindElements(Configuration.AutomationPlatform, propertyName, pattern, isExpression, ancestorId);
|
||||
|
||||
public Task<PerformActionResult> PerformAction(string action, string elementId, params string[] arguments)
|
||||
=> grpc.Client.PerformAction(Configuration.AutomationPlatform, action, elementId, arguments);
|
||||
|
||||
string UnpackIdb()
|
||||
{
|
||||
|
@ -272,4 +278,10 @@ public class iOSDriver : IDriver
|
|||
|
||||
return exePath;
|
||||
}
|
||||
|
||||
public async void Dispose()
|
||||
{
|
||||
if (grpc is not null)
|
||||
await grpc.Stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using Grpc.Net.Client;
|
||||
using Grpc.Core.Logging;
|
||||
using Grpc.Net.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Maui.Automation;
|
||||
using Microsoft.Maui.Automation.Driver;
|
||||
using Microsoft.Maui.Automation.Remote;
|
||||
using Spectre.Console;
|
||||
using System.Net;
|
||||
|
@ -8,52 +11,35 @@ using System.Net;
|
|||
|
||||
var platform = Platform.Maui;
|
||||
|
||||
var grpc = new GrpcHost();
|
||||
|
||||
Console.WriteLine("Started GRPC Host.");
|
||||
|
||||
|
||||
var client = grpc.Client;
|
||||
|
||||
while (true)
|
||||
var config = new AutomationConfiguration
|
||||
{
|
||||
var input = Console.ReadLine() ?? string.Empty;
|
||||
AppAgentPort = 5000,
|
||||
DevicePlatform = Platform.Android,
|
||||
AutomationPlatform = platform,
|
||||
Device = "emulator-5554"
|
||||
};
|
||||
var driver = new AppDriver(config);
|
||||
|
||||
try
|
||||
{
|
||||
if (input.StartsWith("tree"))
|
||||
var mappings = new Dictionary<string, Func<Task>>
|
||||
{
|
||||
{ "tree", Tree },
|
||||
{ "windows", Windows },
|
||||
{ "test2", async () =>
|
||||
{
|
||||
var children = await client.GetElements(platform);
|
||||
var button = await driver.FirstByAutomationId("buttonOne");
|
||||
|
||||
foreach (var w in children)
|
||||
{
|
||||
var tree = new Tree(w.ToTable(ConfigureTable));
|
||||
await driver.Tap(button!);
|
||||
|
||||
var label = await driver.FirstByAutomationId("labelCount");
|
||||
|
||||
foreach (var d in w.Children)
|
||||
{
|
||||
PrintTree(tree, d, 1);
|
||||
}
|
||||
|
||||
AnsiConsole.Write(tree);
|
||||
}
|
||||
|
||||
Console.WriteLine(label.Text);
|
||||
}
|
||||
else if (input.StartsWith("windows"))
|
||||
},
|
||||
{ "test", async () =>
|
||||
{
|
||||
var children = await client.GetElements(platform);
|
||||
var elements = await driver.FindElements("AutomationId", "buttonOne");
|
||||
|
||||
foreach (var w in children)
|
||||
{
|
||||
var tree = new Tree(w.ToTable(ConfigureTable));
|
||||
|
||||
AnsiConsole.Write(tree);
|
||||
}
|
||||
}
|
||||
else if (input.StartsWith("test"))
|
||||
{
|
||||
var elements = await client.FindElements(platform, "AutomationId", "buttonOne");
|
||||
|
||||
foreach (var w in elements)
|
||||
{
|
||||
var tree = new Tree(w.ToTable(ConfigureTable));
|
||||
|
@ -62,9 +48,37 @@ while (true)
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
var input = Console.ReadLine() ?? string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var kvp in mappings)
|
||||
{
|
||||
if (input.StartsWith(kvp.Key))
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await kvp.Value();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.WriteException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
AnsiConsole.WriteException(ex);
|
||||
}
|
||||
|
||||
if (input != null && (input.Equals("quit", StringComparison.OrdinalIgnoreCase)
|
||||
|
@ -73,8 +87,37 @@ while (true)
|
|||
break;
|
||||
}
|
||||
|
||||
await grpc.Stop();
|
||||
driver.Dispose();
|
||||
|
||||
async Task Tree()
|
||||
{
|
||||
var children = await driver.GetElements();
|
||||
|
||||
foreach (var w in children)
|
||||
{
|
||||
var tree = new Tree(w.ToTable(ConfigureTable));
|
||||
|
||||
|
||||
foreach (var d in w.Children)
|
||||
{
|
||||
PrintTree(tree, d, 1);
|
||||
}
|
||||
|
||||
AnsiConsole.Write(tree);
|
||||
}
|
||||
}
|
||||
|
||||
async Task Windows()
|
||||
{
|
||||
var children = await driver.GetElements();
|
||||
|
||||
foreach (var w in children)
|
||||
{
|
||||
var tree = new Tree(w.ToTable(ConfigureTable));
|
||||
|
||||
AnsiConsole.Write(tree);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTree(IHasTreeNodes node, Element element, int depth)
|
||||
{
|
||||
|
|
|
@ -10,37 +10,39 @@ namespace Microsoft.Maui.Automation.Test
|
|||
{
|
||||
public class Tests
|
||||
{
|
||||
readonly AppDriver driver;
|
||||
|
||||
public Tests()
|
||||
{
|
||||
|
||||
configuration = new AutomationConfiguration(
|
||||
Platform.Android,
|
||||
automationPlatform: Platform.Maui,
|
||||
device: "emulator-5554");
|
||||
configuration.AppAgentPort = 5000;
|
||||
|
||||
driver = new Driver.AppDriver(configuration);
|
||||
driver = new AppDriver(
|
||||
new AutomationConfiguration(
|
||||
"com.companyname.samplemauiapp",
|
||||
"C:\\code\\Maui.UITesting\\samples\\SampleMauiApp\\bin\\Debug\\net6.0-android\\com.companyname.samplemauiapp-Signed.apk",
|
||||
Platform.Android,
|
||||
automationPlatform: Platform.Maui,
|
||||
device: "emulator-5554"));
|
||||
}
|
||||
|
||||
readonly IAutomationConfiguration configuration;
|
||||
readonly AppDriver driver;
|
||||
|
||||
[Fact]
|
||||
public async Task RunApp()
|
||||
{
|
||||
var appId = "com.companyname.samplemauiapp";
|
||||
var file = "C:\\code\\Maui.UITesting\\samples\\SampleMauiApp\\bin\\Debug\\net6.0-android\\com.companyname.samplemauiapp-Signed.apk";
|
||||
// Install and launch the app
|
||||
await driver.InstallApp();
|
||||
await driver.LaunchApp();
|
||||
|
||||
// Find the button by its MAUI AutomationId property
|
||||
var button = await driver.FirstByAutomationId("buttonOne");
|
||||
Assert.NotNull(button);
|
||||
|
||||
//await driver.InstallApp(file, appId);
|
||||
// Tap the button to increment the counter
|
||||
await driver.Tap(button);
|
||||
|
||||
//await driver.InstallApp(@"C:\code\Maui.UITesting\samples\SampleMauiApp\bin\Debug\net6.0-android\com.companyname.samplemauiapp-Signed.apk", appId);
|
||||
//await driver.LaunchApp(appId);
|
||||
|
||||
var elements = await driver.FindElements(Platform.Maui, "AutomationId", "buttonOne");
|
||||
|
||||
var e = elements.FirstOrDefault();
|
||||
// Find the label we expect to have changed
|
||||
var label = await driver.By(e =>
|
||||
e.Type == "Label"
|
||||
&& e.Text.Contains("1"));
|
||||
|
||||
Assert.NotEmpty(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace SampleMauiApp
|
|||
MainPage = new MainPage();
|
||||
|
||||
//#if ANDROID
|
||||
this.StartAutomationServiceListener("http://127.0.0.1:5000");
|
||||
this.StartAutomationServiceListener("http://localhost:5000");
|
||||
//#else
|
||||
//this.StartAutomationServiceListener("https://localhost:5001");
|
||||
//#endif
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
<Label
|
||||
Text="Current count: 0"
|
||||
AutomationId="labelCount"
|
||||
Grid.Row="2"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
|
|
Загрузка…
Ссылка в новой задаче