如何拖放外部项目以进行 Xcode-ui-testing

How to drag and drop an external item for Xcode-ui-testing

在我的应用程序中,我允许用户将项目从 Finder(或任何其他基于 URL 的文件源)拖放到我的应用程序中。我想要做的是添加一种机制,让我可以在 Xcode UI 测试中对此进行测试。

我可以看到如何使用 XCUIElement.press(forDuration:thenDragTo:) 来测试应用程序内源和目标的拖放,但是当拖放源在外部时我一直找不到测试方法应用程序。

在一个有点相关的测试中,我通过设置要粘贴到 NSPasteboard.general 的字符串来测试应用程序的复制和粘贴部分,然后使用 XCUIElement.typeKey("v", modifierFlags: .command) 将其粘贴到所需的元素中.这有点不太理想,因为它取决于 Command-v 实际上是作为粘贴命令实现的,但这不太可能改变,所以它可以满足我的需要。 (实际上,我已经编写了一个 XCUIElement.paste(_ s: String) 扩展,使我可以轻松地将其添加到测试中。)

我相信拖放也使用 NSPasteboard 进行通信,所以通过对底层机制的一些调查,我应该能够像我一样将我的对象设置到正确的粘贴板中用于剪切和粘贴。我相当确定我能弄清楚那部分。但是我还没有想出如何执行实际的下降。

我的目标是创建一个 XCUIElement.drop(_ url) 来将正确的“public.file-url”对象设置到正确的粘贴板中,然后 simulate/perform 放置进入元素。

有什么想法吗?

请注意,我已经尝试了以下两项:

首先,我确实使用了 Xcode 记录功能来尝试记录拖放操作并查看它会给我什么事件。不幸的是,它什么也没记录。

其次,我有一个基于菜单的替代方案,用户可以通过文件选择器选择一个文件。因此,如果我可以模拟文件选择,那将是适合我目的的测试替代方案。不幸的是,我也没有沿着这条路取得任何进展。当我使用 Xcode 记录事件时,它记录了菜单选择,对话框中实际没有做任何事情。

好的,所以我还没有找到问题的答案(如何测试拖放),但我已经为我的测试想出了一个可接受的解决方法。

具体来说,当我更多地考虑粘贴板时,我意识到如果我允许用​​户将文件拖放到我的应用程序中,那么我也应该允许他们将文件剪切并粘贴到应用程序中.

一旦我意识到这一点,通过粘贴 URL 而不是拖放 URL 来测试我的应用程序的必要功能是一个相当简单的过程。这有一个额外的好处,我可以将必要的测试文件添加到我的测试包中,让一切都很好地独立。

为此,我已将以下功能添加到我的 XCUIElement 扩展中:

extension XCUIElement {
    func paste(url: URL) {
        precondition(url.isFileURL, "This must be a file URL to match the pasteboard type.")
        let pasteboard = NSPasteboard.general
        pasteboard.clearContents()
        pasteboard.setString(url.absoluteString, forType: .fileURL)
        click()
        typeKey("v", modifierFlags: .command)
    }
}

然后在我的测试代码中添加以下内容来触发事件:

let mainWindow = app.windows[/*...my main window name goes here...*/]
let testBundle = Bundle(for: type(of: self))
let fileURL = testBundle.url(forResource: "Resources/simple", withExtension: "json")
mainWindow.paste(url: fileURL!)

当然,这实际上并没有测试拖放,但它确实测试了我代码的相同部分,因为在我的 AppDelegate 中我有我的 onPaste 操作方法调用相同的底层方法作为我的 performDrop 方法。

我会等几天,看看是否有人提出实际问题的答案(因为我仍然会发现它有用),但如果没有人提出,我会接受我自己的答案。

根据您的评论,我建议您阅读这篇文章文档 https://developer.apple.com/documentation/xctest/xcuiapplication

注意 init(bundleIdentifier: String)init(url: URL) 方法。这些允许您与目标应用程序之外的应用程序进行交互。

那么你可以使用XCUIElement.press(forDuration:thenDragTo:)

import XCTest
import XCTApps
import ScreenObject

let notes = XCTApps.notes.app
let photos = XCTApps.photos.app

class Tests: XCTestCase {
    func testDragAndDrop() {
        photos.launch()
        notes.launch()
        photos.images.lastMatch.press(forDuration: 1, thenDragTo: notes.textViews["Note Body Text View"])
    }
}

P.S。在这个例子中我使用 XCTApps 因为我不想记住或 google 包标识符 :D

https://github.com/rzakhar/XCTApps