使用 Webview
UITest 提供用于在本机移动应用程序中查找和与视图交互的 Api。 但是,某些移动应用是使用 webview 向用户显示 HTML 的混合应用。 Android 提供 android.webkit.WebView ,而 iOS 应用程序可以使用 UIWebView 或 WKWebView。 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 :
此代码片段演示如何在 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 使用 和 的任意组合, (GestureProgramatic首选项来滚动Programatic) 。timeout- 一个可选参数,指定 UITest 在超时查询之前应等待的时间。
作为使用这些 API 的示例,请考虑 Android 应用程序中嵌入的 Web 视图的屏幕截图:
如果在 REPL 中检查此活动的视图层次结构,可以看到以下内容:
>>> tree
[[object CalabashRootView] > PhoneWindow$DecorView]
[ActionBarOverlayLayout] id: "decor_content_parent"
[FrameLayout > LinearLayout] id: "content"
[WebView] id: "webView1", label: "WebView"
[dom] id: "show_secret"
[dom] id: "firstname"
[dom] id: "lastname"
[ActionBarContainer] id: "action_bar_container"
[Toolbar] id: "action_bar"
[TextView] text: "TestingWebviews.Droid"
[View] id: "statusBarBackground"
[View] id: "navigationBarBackground"
命令的输出 tree 告诉我们,Web 视图具有 ID webView1 ,并且包含三个 DOM 元素:
- 使用 标记调用的 HTML
show_secret按钮<button id="show_secret">Toggle the Secret</button> - 具有标记的 HTML
firstname输入字段<input type="text" name="firstname" id='firstname'> - 具有标记的 HTML
lastname输入字段<input type="text" name="lastname" id='lastname'>
show_secret按钮在屏幕上可见,但 firstname lastname 和 不可见。 在 UITest 可能与这些 DOM 元素交互之前,有必要使用滚动 API 来查看这些字段。 REPL 中的以下代码片段演示如何使用 API 将 HTML 输入元素滚动到 ScrollDownTo 视图中:
>>> app.ScrollDownTo(c=>c.Css("#firstname"))
Scrolling down to Css("#firstname")
输入文本
在 HTML 输入元素中输入文本是通过提供文本和使用 AppWebQuery API 完成的 AppQuery.EnterText 。 给定 Android 应用程序中 Web 视图的以下屏幕截图:
以下代码片段将显示以在 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");
}


