Search code examples
swiftxcodedrag-and-dropxcuitest

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


In my application I allow the user to drag and drop items from Finder (or any other source of a file based URL) into my application. What I want to do is to add a mechanism that will allow me to test this in the Xcode UI testing.

I can see how to use XCUIElement.press(forDuration:thenDragTo:) to test the drag and drop of a source and destination within the application, but I have been unable to find a way to test when the source of the drag is outside of the application.

In a somewhat related test, I test the copy and paste portion of the application by setting the string I want to paste into NSPasteboard.general, then using XCUIElement.typeKey("v", modifierFlags: .command) to paste it into the desired element. That is a little less than ideal as it depends on Command-v actually being implemented as the paste command, but that is unlikely to change so it is acceptable for my needs. (In fact I've written an XCUIElement.paste(_ s: String) extension that makes it easy for me to add this in a test.)

I believe that drag and drop is also using an NSPasteboard for its communications, so with a little investigation into the underlying mechanism, I should be able to set my object into the correct pasteboard just like I do for the cut and paste. I'm reasonably certain I can figure that part out. But I haven't figured out how to perform the actual drop.

My goal would be to create an XCUIElement.drop(_ url) that would setup the proper "public.file-url" object into the correct pasteboard, and then simulate/perform the drop into the element.

Any ideas?

I should note that I have already tried the following two items:

First, I did use the Xcode record feature to attempt to record the drag and drop operation and see what events it would give me. Unfortunately, it records absolutely nothing.

Second, I do have a menu based alternative where the user selects a file via the file selector. So if I could simulate the file selection, that would be a suitable testing alternative for my purposes. Unfortunately, I didn't make any progress along that path either. When I used Xcode to record the events, it recorded the menu selection, nothing that was actually done in the dialog.


Solution

  • Based on your comments I would recommend you to read this article documentation piece https://developer.apple.com/documentation/xctest/xcuiapplication

    Notice the init(bundleIdentifier: String) and init(url: URL) methods. These allow you to interact with apps apart from the target application.

    Then you can use 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. In this example I use XCTApps because I don't want to remember or google bundle identifiers :D

    https://github.com/rzakhar/XCTApps