如何通过 Xcode UI 测试确定滚动视图中文本的哪一部分在屏幕上可见?

How can I determine what part of text in a scroll view is visible on screen from an Xcode UI test?

我是 Xcode 用户界面测试框架的新手。我可以成功地操纵屏幕元素,但无法弄清楚如何就滚动视图中可见的文本生成有意义的断言。

我想编写的测试如下:启动应用程序,在文本视图中键入大量文本(足以让第一行滚出视图),断言第一行文本不是可见,将视图向上滚动到顶部,然后断言第一行现在可见。请注意,此测试的目的是确保我的应用程序正确连接,而不是测试 Apple 的代码。

XCUIApplication 允许我在我的 NSTextView 实例中键入内容,还允许我滚动关联的 NSScrollView。但是我如何断言第一行文本当前是否可见? XCUIElement 的 value 属性提供了视图的全部文本内容,无论它当前是否显示。

NSTextView 上的 accessibilityRange(forLine:) 和 accessibilityString(for:) 方法是理想的,但我看不到如何访问它们,因为 UI 测试只能访问 XCUI元素,而不是底层的 NSTextView。

我是不是漏掉了什么,或者是否有更好的方法来解决这个问题?

如果您在故事板或文本视图的代码中设置了可访问性标识符,您可以通过(假设您给它的 ID "textview1" 和它所在的 window 获取文本视图"Window" 的默认可访问性标识符):

let textview1TextView = app.windows["Window"].textViews["textview1"]

但这实际上并不能满足您的需求。

相反,设置滚动视图的可访问性标识符并获取:

let scrollview = app.windows["Window"].scrollViews["scrollview1"]

然后用它来获取滚动条(在这种情况下你应该只有一个;你可以使用 scrollbars.count 来检查。

let scrollbars = scrollview.scrollBars
print("scrollbars count: \(scrollbars.count)")

然后你可以使用滚动条的value属性来获取它的值: (您正在将 XCUIElemenTypeQueryProvider 转换为 XCUIElement 以便您可以获得它的值):

let val = scrollbars.element.value

顶部为 0,滚动时为浮点值(我的测试代码中的一行文本显示值为 {{0.02409638554216868}}。

有助于您进一步探索的文档:

XCUIElementTypeQueryProvider

XCUIElementAttributes

请注意,您可以在测试中间放置一个断点,运行 然后使用调试器控制台检查内容:

(lldb) po scrollbars.element.value
    t =   749.66s Find the ScrollBar ▿ Optional<Any>
  - some : 0

(lldb) po scrollbars.element.value
    t =   758.17s Find the ScrollBar ▿ Optional<Any>
  - some : 0.05421686746987952

在调试器中,您甚至可以与应用程序的 window 交互以手动滚动它(这是我在输入这两个 po 调用之间所做的),或者添加文本和依此类推


OK OP 现在注意到他们感兴趣的是特定文本是否显示,而不是第一行是否显示(这是我之前在上面回答的内容)。

这里有一些技巧,但我认为它会起作用:

使用XCUICoordinateclick(forDuration:, thenDragTo:)方法select第一行文字(使用view frame计算坐标)然后使用typeKey( modifierFlags:)调用编辑菜单 "copy" 命令。然后使用NSPasteboard方法获取粘贴板内容并检查文本。

这是我为验证该方法所做的快速测试(select如上所述使用 XCUICoordinate 编辑第一行文本留作 reader 的练习):

NSPasteboard.general.clearContents()

// stopped at break point on next line and I manually selected the text of the first line of text in my test app and then hit continue in the debugger

textview1TextView.typeKey("c", modifierFlags:.command)
print( NSPasteboard.general.pasteboardItems?.first?.string(forType: NSPasteboard.PasteboardType.string) ?? "none" );

-> "the text of the first line" 被打印到控制台。

请注意,您可以将 selection 滚动到屏幕外,这样您在执行 select 后就不必滚动,否则您将得不到想要的答案。