我们正在使用UITest测试我们的Xamarin.Forms应用程序,并且需要在iOS上选择Picker的值。
可以通过以下项目集合中的行号进行选择:
app.Query(x => x.Class("UIPickerView").Invoke("selectRow", 1, "inComponent", 0, "animated", true))
我们在上面的例子中选择第1行。
但是,我们需要能够按值进行选择 - 例如,使用具有标题列表的Picker,选择“Mrs”标题,我们需要执行以下操作:
app.Query(x => x.Class(“UIPickerView”)。Invoke(“selectValue”,“Mrs”,“inComponent”,0,“animated”,true))
但是,UIPickerView(参考here)上没有可用的selectValue(或类似)方法。
一个想法是获取项目中的行数,并迭代它们 - 但是当我尝试调用getNumberOfRows
时,我收到以下错误:
app.Query(x => x.Class(“UIPickerView”)。Invoke(“numberOfRows”,“inComponent”,0))
执行查询时出错([未知])异常:System.Exception:调用iOS选择器需要0或参数数量不均匀(它们必须成对匹配,包括方法名称)。在Xamarin.UITest.Queries.InvokeHelper.AppTypedSelector(Xamarin.UITest.Queries.AppQuery appQuery,Xamarin.UITest.Queries.ITokenContainer tokenContainer,System.Object [] queryParams,System.String methodName,System.Object [] arguments,System。布局explicitRequestedValue)[0x1611a]在<2a16c16730a54859bda72c6bc1c728f7>:0中的Xamarin.UITest.Queries.InvokeHelper.Invoke(Xamarin.UITest.Queries.AppQuery appQuery,System.String methodName,System.Object [] arguments)[0x00000] in <2a16c16730a54859bda72c6bc1c728f7 >:0:Xamarin.UITest.Queries.AppQuery.Invoke(System.String methodName,System.Object arg1,System.Object arg2)[0x00000] in <2a16c16730a54859bda72c6bc1c728f7>:0 at .m__0(Xamarin.UITest.Queries.AppQuery x )<0de9804cff324d049415e25573e8da8a>中的[0x0000b]:Xamarin.UITest.SharedApp.Expand [0]中的0:Xamarin.UITest.Utils.ErrorReporting中的<2a16c16730a54859bda72c6bc1c728f7>:0中的System.Func
2[T,TResult] typedQuery) [0x0000c] in <2a16c16730a54859bda72c6bc1c728f7>:0 at Xamarin.UITest.iOS.iOSApp+<>c__DisplayClass17_0
1 [T] .b__0()[0x00000]。用[T](System.Func`1 [TResult] func,System.Object [] args,System <2a16c16730a54859bda72c6bc1c728f7>中的.string memberName)[0x0000e]:0异常:执行查询时出错([unknown])
错误的原因非常清楚 - 该方法期望特定的参数配对,但我无法看到如何正确地形成查询。
那么关于我如何按价值选择,而不是行号,和/或如何获得项目数量的任何想法/指示?
好的......我有一些有用的东西。
前提是我们在基础项目列表中选择行,然后检查Picker是否显示我们想要的值。
注释1.我有一个UIElement帮助器类,它允许我进入底层Control或AutomationId。
SelectPickerValue
或使用TapAndSelectPickerValue
- 我应该整理一下。 internal static void TapAndSelectPickerValue(UIElement element, string v, int maxRows = 10)
{
app.Tap(element.UIControl);
SelectPickerValue(v, maxRows, element.AutomationId);
}
internal static void SelectPickerValue(string v, int maxItems = 10,string pickerId = "")
{
if (_platform == Platform.iOS)
{
bool managedToSelect = false;
// Check that we haven't already got the value we want selected.
Xamarin.UITest.Queries.AppResult[] valueCheckResult = app.Query(x => x.Text(v).Id(pickerId));
if (valueCheckResult.Length == 0)
{
int rowNumber = 0;
// Select rows until we either reach the max to try, or we get the one we want selected
for (rowNumber = 0; rowNumber < maxItems; rowNumber++)
{
app.Query(x => x.Class("UIPickerView").Invoke("selectRow", rowNumber, "inComponent", 0, "animated", true));
// This is a hack to move the item so that Xamarin Forms will generate the event to update the UI
var rect = app.Query(x => x.Class("UIPickerTableView").Index(0).Child()).First().Rect;
app.DragCoordinates(rect.CenterX, rect.CenterY, rect.CenterX, rect.CenterY + 22);
app.DragCoordinates(rect.CenterX, rect.CenterY, rect.CenterX, rect.CenterY - 22);
// check to see if the Picker is now displaying the value we want
valueCheckResult = app.Query(x => x.Text(v).Id(pickerId));
if (valueCheckResult.Length == 1)
{
managedToSelect = true;
break;
}
}
}
// If we couldn't select the value we wanted, raise an exception
if (!managedToSelect)
{
throw new Exception($"Could not select value '{v}' for picker '{pickerId}'");
}
// Close the Picker
app.Tap(c => c.Text("Done"));
}
else
{
app.ScrollDownTo(m => m.Text(v), x => x.Id("select_dialog_listview"));
app.Tap(x => x.Text(v));
}
}
更好的解决方案是编写一个UITest后门,然后在后门找到Picker的Renderer并直接控制它。
解决方案的核心是由@LandLu在Xamarin论坛上提供给我的:
您应该首先获取当前视图控制器。然后迭代子视图以获取选择器渲染器。这是我的依赖服务,你指的是:
[assembly: Dependency(typeof(FindViewClass))]
namespace Demo.iOS
{
public class FindViewClass : IFindView
{
public void FindViewOfClass()
{
UIViewController currentViewController = topViewController();
getView(currentViewController.View);
}
List<PickerRenderer> pickerList = new List<PickerRenderer>();
void getView(UIView view)
{
if (view is PickerRenderer)
{
pickerList.Add((PickerRenderer)view);
}
else
{
foreach (UIView subView in view.Subviews)
{
getView(subView);
}
}
}
UIViewController topViewController()
{
return topViewControllerWithRootViewController(UIApplication.SharedApplication.KeyWindow.RootViewController);
}
UIViewController topViewControllerWithRootViewController(UIViewController rootViewController)
{
if (rootViewController is UITabBarController)
{
UITabBarController tabbarController = (UITabBarController)rootViewController;
return topViewControllerWithRootViewController(tabbarController.SelectedViewController);
}
else if (rootViewController is UINavigationController)
{
UINavigationController navigationController = (UINavigationController)rootViewController;
return topViewControllerWithRootViewController(navigationController.VisibleViewController);
}
else if (rootViewController.PresentedViewController != null)
{
UIViewController presentedViewController = rootViewController.PresentedViewController;
return topViewControllerWithRootViewController(presentedViewController);
}
return rootViewController;
}
}
}
完成后,我可以通过Renderer的Element属性选择我想要的索引/项目。
编辑我已经把它加工成了一个GitHub repository,它实现了一组后门,使iOS Picker选择更容易