使用 Webview

UITest 提供用于在本机移动应用程序中查找和与视图交互的 Api。 但是,某些移动应用是使用 webview 向用户显示 HTML 的混合应用。 Android 提供 android.webkit.WebView ,而 iOS 应用程序可以使用 UIWebViewWKWebView。 UIWebView 是旧的并且与所有版本的 iOS 兼容,而 WKWebView 适用于 iOS 8 和更高版本。

与 webview 交互比 UITest 更多地涉及,因为必须首先获取对 web 视图的引用,然后获取它包含的 DOM 元素。

UITest 中的 Web 视图 Api

在 UITest 中,有两个 Api 可获取对 web 视图控件的引用:

下面将更详细地讨论这些内容。

UIWebView用 AppQuery 查找控件

UITest 提供 AppQuery 方法以获取对控件的引用 UIWebView 。 以下代码片段是一个示例:

>>> app.Query(c=>c.WebView())       

如果屏幕上只有一个 web 视图,则 WebView 默认为 web 视图。 如果屏幕上有多个 webview, WebView(Int32) 可通过传递从零开始的索引来将其隔离。

定位 WKWebView 控件

UITest 提供 AppQuery.Class 方法以获取对 控件 WKWebView 的引用。 以下代码片段是一个示例:

>>> app.Query(c=>c.Class("WKWebView"))

与 Web 视图控件中的 DOM 元素交互

隔离 Web 视图后,有两个主要 API 用于与 DOM 元素进行交互:

  • CSS – 此 API 使用 CSS 选择器查找 Web 视图显示的 DOM 元素。
  • XPath – UITest 可以使用 XPath 表达式在 Webview 中查找 DOM 元素。

XPath API 功能强大,使用起来也更复杂。 一般情况下,应优先使用 CSS API,并在必要时使用 XPath API。

CSS

使用 AppQuery.Css 方法可以匹配 HTML 元素。 此方法采用 CSS 选择器,并返回匹配的所有 HTML 元素的数组。 在功能上,此 API 在 Android 和 iOS 上是相同的。

例如,以下 Android 活动包含 android.webkit.WebView

Android 设备上的 WebView

此代码片段演示如何在 Webview 中查找所有 H1 DOM 元素:

>>> app.Query(c=>c.Css("H1"))                                                                                
Query for Css("H1") gave 1 results.
[
    [0] {
        Id => "",
        NodeType => "ELEMENT_NODE",
        NodeName => "H1",
        Class => "",
        Html => "<h1>H1 Header!</h1>",
        Value => null,
        WebView => "webView1",
        TextContent => "H1 Header!",
        Rect => {
            Width => 1032,
            Height => 117,
            CenterX => 540,
            CenterY => 408,
            Top => 44,
            Bottom => 83,
            Left => 8,
            Right => 352,
            X => 24,
            Y => 351
        }
    }
]

如果给定屏幕上存在多个 Web 视图,则 UITest 将自动默认为第一个 Web 视图。 如果有多个 Web 视图,则有必要使用 显式标识 Web 视图 IApp.WebView(Int32) 。 例如,如果某个屏幕有两个 Web 视图,并且我们想要查找第二个 Web 视图的所有 H1 元素:

>>> app.Query(c=>c.WebView(1).Css("H1"))                                                                                
Query for WebView().Css("H1") gave 1 results.
[
    [0] {
        Id => "",
        NodeType => "ELEMENT_NODE",
        NodeName => "H1",
        Class => "",
        Html => "<h1>H1 Header!</h1>",
        Value => null,
        WebView => "webView1",
        TextContent => "H1 Header!",
        Rect => {
            Width => 1032,
            Height => 117,
            CenterX => 540,
            CenterY => 408,
            Top => 44,
            Bottom => 83,
            Left => 8,
            Right => 352,
            X => 24,
            Y => 351
        }
    }
]

如果多个 HTML 元素与 CSS 查询匹配,则 方法可用于访问结果 Index 集的单个元素。 例如,以下代码片段演示如何使用 样式访问 HTML 元素的第三 .user 个元素:

>>> app.Tap(c => c.Css(".user").Index(2));

XPath

XPath 是一种功能强大的 API,用于搜索 DOM,但与 CSS API 相比,使用起来可能稍微困难一些。 UITest 可以通过传递到 AppQuery 方法的 XPath 选择器定位 DOM 元素。 此 API 在 Android 和 iOS 上是相同的。

以下代码片段是一个示例,演示如何使用 XPath 匹配上一部分中的 H1 DOM 元素:

>>> app.Query(c=>c.XPath("//h1"))
Query for XPath("//h1") gave 1 results.
[
    [0] {
        Id => "",
        NodeType => "ELEMENT_NODE",
        NodeName => "H1",
        Class => "",                                                                                                   
        Html => "<h1>H1 Header!</h1>",
        Value => null,
        WebView => "webView1",
        TextContent => "H1 Header!",
        Rect => {
            Width => 1032,
            Height => 117,
            CenterX => 540,
            CenterY => 408,
            Top => 44,
            Bottom => 83,
            Left => 8,
            Right => 352,
            X => 24,
            Y => 351
        }
    }
]

调用 JavaScript

还可以使用方法找到 webview AppQuery.InvokeJS 。 此方法采用字符串形式的查询,并对查询所匹配的视图元素调用 JavaScript。 如果找到除 Webview 以外的视图元素,执行将暂停,并将引发异常。

Android 上的 InvokeJS

通常情况下,Android 要求 JavaScript 为 returns 值; 否则,查询将返回 null

>>> app.Query (w => w.WebView ().InvokeJS ("document.getElementById('lastname').value"))
[
[0] "null"
]
>>> app.Query (w => w.WebView ().InvokeJS ("return document.getElementById('lastname').value"))
[
[0] "Smith"
]

IOS 上的 InvokeJS

一般而言,在 iOS 上, return 不使用:

>>> app.Query (w => w.WebView ().InvokeJS ("return document.getElementById('lastname').value"))
[
[0] ""
]

>>> app.Query (w => w.WebView ().InvokeJS("document.getElementById('lastname').value"))
[
[0] "Smith"
]

示例

本部分介绍编写涉及 webview 的 Calabash 测试时可能会发现的一些常见用例。

滚动

可以滚动 web 视图将 DOM 元素放到屏幕上。 使用或方法可以实现此目的 IApp.ScrollDownTo IApp.ScrollUpTo 。 这些方法的签名类似:

IApp.ScrollDownTo(Func<AppQuery, AppWebQuery> toQuery,
                  string withinMarked,
                  ScrollStrategy strategy,
                  Nullable<TimeSpan> timeout)

以下是参数的快速说明:

  • toQuery -这是将在 web 视图中查找 DOM 元素的 UITest web 查询。
  • withinMarked -这是将在屏幕上定位到 web 视图的字符串。 如果屏幕上只有一个 web 视图,则此参数是可选的。 将使用此字符串 IApp.Marked 来查找屏幕上的 web 视图。
  • strategy - 此可选参数告知 UITest 如何在 Web 视图内滚动。 ScrollStrategy.Gesture 将尝试通过拖动屏幕来模拟用户滚动模式。 ScrollStrategy.Programatic 释放 UITest 以尽可能快的方式滚动。 ScrollStrategy.Auto 告知 UITest 使用 和 的任意组合, (Gesture Programatic 首选项来滚动 Programatic) 。
  • timeout - 一个可选参数,指定 UITest 在超时查询之前应等待的时间。

作为使用这些 API 的示例,请考虑 Android 应用程序中嵌入的 Web 视图的屏幕截图:

显示以下示例中使用的 WebView 的屏幕截图。

如果在 REPL 中检查此活动的视图层次结构,可以看到以下内容:

>>> tree                                                                                                                
[[object CalabashRootView] > PhoneWindow$DecorView]                                                                     
  [ActionBarOverlayLayout] id: "decor_content_parent&quot;
    [FrameLayout > LinearLayout] id: &quot;content&quot;
      [WebView] id: &quot;webView1&quot;,  label: &quot;WebView&quot;
        [dom] id: &quot;show_secret&quot;
        [dom] id: &quot;firstname&quot;
        [dom] id: &quot;lastname&quot;
    [ActionBarContainer] id: &quot;action_bar_container&quot;
      [Toolbar] id: &quot;action_bar&quot;
        [TextView] text: &quot;TestingWebviews.Droid&quot;
  [View] id: &quot;statusBarBackground&quot;
  [View] id: &quot;navigationBarBackground&quot;

命令的输出 tree 告诉我们,Web 视图具有 ID webView1 ,并且包含三个 DOM 元素:

  • 使用 标记调用的 HTML show_secret 按钮 <button id=&quot;show_secret&quot;>Toggle the Secret</button>
  • 具有标记的 HTML firstname 输入字段 <input type=&quot;text&quot; name=&quot;firstname&quot; id='firstname'>
  • 具有标记的 HTML lastname 输入字段 <input type=&quot;text&quot; name=&quot;lastname&quot; id='lastname'>

show_secret按钮在屏幕上可见,但 firstname lastname 和 不可见。 在 UITest 可能与这些 DOM 元素交互之前,有必要使用滚动 API 来查看这些字段。 REPL 中的以下代码片段演示如何使用 API 将 HTML 输入元素滚动到 ScrollDownTo 视图中:

>>> app.ScrollDownTo(c=>c.Css(&quot;#firstname"))                              
Scrolling down to Css("#firstname")

输入文本

在 HTML 输入元素中输入文本是通过提供文本和使用 AppWebQuery API 完成的 AppQuery.EnterText 。 给定 Android 应用程序中 Web 视图的以下屏幕截图:

显示以下示例中使用的 WebView 的屏幕截图

以下代码片段将显示以在 HTML 输入元素 firstname 中输入文本:

[Test]
public void ScrollDownAndEnterName()
{
  app.ScrollDownTo(c => c.Css("input#firstname"), );
  app.EnterText(c => c.Css("input#firstname"), "Monkey");       

  // A more verbose way to enter text, explicitly identifying the webview and ScrollStrategy
  app.ScrollDownTo(c => c.Css("input#lastname"), "webView1", ScrollStrategy.Gesture);
  app.EnterText(c => c.Css("input#lastname"), "Chimpanzee");

}