【问题标题】:Select an item in an iOS Picker using Xamarin UITest?使用 Xamarin UITest 在 iOS 选取器中选择一个项目?
【发布时间】:2019-04-03 08:07:13
【问题描述】:

我们正在使用 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 上没有可用的 selectValue(或类似)方法(参考 here)。

一个想法是获取项目中的行数,并遍历它们 - 但是当我尝试调用 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 方法名,System.Object[] 参数, System.Boolean explicitRequestedValue) [0x0011a] in :0 在 Xamarin.UITest.Queries.InvokeHelper.Invoke (Xamarin.UITest.Queries.AppQuery appQuery,System.String methodName, System.Object[] 参数)[0x00000] 在 :0 在 Xamarin.UITest.Queries.AppQuery.Invoke(System.String 方法名, System.Object arg1,System.Object arg2) [0x00000] 在 :0 在 .m__0 (Xamarin.UITest.Queries.AppQuery x) [0x0000b] 在 :0 在 Xamarin.UITest.SharedApp.Expand[T] (System.Func2[T,TResult] typedQuery) [0x0000c] in <2a16c16730a54859bda72c6bc1c728f7>:0 at Xamarin.UITest.iOS.iOSApp+<>c__DisplayClass17_01[T].b__0 () [0x00000] 在 :0 处 Xamarin.UITest.Utils.ErrorReporting.With[T] (System.Func`1[TResult] func, System.Object[] args, System.String memberName) [0x0000e] in :0 异常:错误同时 执行查询([未知])

错误的原因很清楚 - 该方法需要特定的参数配对,但我看不出如何正确地制定查询。

那么关于如何按值而不是行号和/或如何获取项目数的任何想法/指针?

【问题讨论】:

    标签: ios picker xamarin.uitest


    【解决方案1】:

    更好的方案是写一个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;
            }
        }
    }
    

    完成后,我可以通过渲染器的 Element 属性选择我想要的索引/项目。

    编辑 我已经把它变成了一个GitHub repository,它实现了一组后门,使 iOS 选择器的选择更容易

    【讨论】:

      【解决方案2】:

      好的...我有一些有用的东西。

      前提是我们在底层的item列表中选择行,然后检查Picker是否显示了我们想要的值。

      备注 1. 我有一个 UIElement 帮助类,它允许我获取底层 Control 或 AutomationId。

      1. 使用以下两种方法,以便我可以直接从我的 TestPages 调用 SelectPickerValue 或使用 TapAndSelectPickerValue - 我应该整理一下。

      2. 如果当前值在可用值的中间,我还没有满足以其他方式滚动的能力。

      3. 如果项目在视图中,可以只点击它 - 但如果不是,我们必须进行滚动/行选择。

             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));
                  }
              }
      

      【讨论】:

        【解决方案3】:

        从 Xamarin.UITest v3.2.2 iOS 15 开始,我注意到您可以在 selectRow Invoke 方法中插入一个字符串:

        app.Query(x => x.Class("UIPickerView").Invoke("selectRow", "[ITEM_NAME]", "inComponent", 0, "animated", false));
        

        [ITEM_NAME] 替换为一行的确切字符串。您仍然需要通过轻推 Picker (DragCoordinates) 来触发更改委托,如 James Lavery's answer 所示:

        // 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);
        

        【讨论】:

          【解决方案4】:

          您可以在此官方文档中找到 DatePickers 和 TimePickers 的最新代码:https://docs.microsoft.com/en-us/appcenter/test-cloud/frameworks/uitest/features/date-time-pickers

          【讨论】:

            猜你喜欢
            • 2016-11-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-18
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多