1

I am writing test cases for IOS using the XCTest framework. I want to select a photo for setting the user profiles, which can be done in 2 ways: 1. Capturing the image using the camera. 2. Selecting an image from the file chooser.

I am having following issues : 1. I am running this test on simulators. So, how to access cameras? 2. I am able to open page file chooser page by clicking on "Choose on Photo album". But, not able to detect tap on page file chooser page.

My code is as follows :

app.buttons["Choose from Photo Album"].tap()
app.tables.firstMatch.cells.element(boundBy: 0).tap()

Line 2 gives me error : NSInternalInconsistencyException invalid parameter not satisfying

Not found a single documentation for the above mentioned cases.

Deepak Terse
  • 652
  • 8
  • 30

2 Answers2

2

I have an addition to the answer of @deepak-terse:

You can use index instead of the string filename, so you don't depend on the simulator.

func selectFromCameraRoll(_ app: XCUIApplication, index: Int = 1) {
    app.tables.cells.element(boundBy: 1).tap()
    app.collectionViews.cells.element(boundBy: index).tap()
}

You can even randomize your selected photo using arc4random_uniform()

UPDATE

In case somebody finds this usable, here is the helper function for the Camera in the UI Testing I use when testing on real device:

 func addPhotoCamera(_ app: XCUIApplication) {
   let pleaseSelectSheet = app.sheets.element

   // Take Picture button, it is first:
   pleaseSelectSheet.buttons.element(boundBy: 0).tap()

   // this monstrosity finds Capture button
   let element = app
       .children(matching: .window).element(boundBy: 0)
       .children(matching: .other).element
       .children(matching: .other).element
       .children(matching: .other).element
       .children(matching: .other).element

   let photoCapture = element.children(matching: .other).element
       .children(matching: .other).element(boundBy: 1)
       .children(matching: .other).element

   photoCapture.tap()

   // I have slow computer, so I need this so test does not fail
   sleep(5)

   app.buttons["Use Photo"].tap()
 }

UPDATE 2

On the device the Camera Roll above did not work because usually there are a lot of photos so tapping first that is not on the screen won't help. I ended up using the following snippet:

let photoCells = app.collectionViews.cells
if Platform.isSimulator {
    photoCells.element(boundBy: index).tap()
} else {
    photoCells.allElementsBoundByIndex.last!.firstMatch.tap()
}

where Platform.isSimulator part is taken from https://stackoverflow.com/a/30284266/2875219:

import Foundation

struct Platform {
   static var isSimulator: Bool {
      return TARGET_OS_SIMULATOR != 0
   }
}

And the whole piece of code together:

struct Platform {
    static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }
}


extension XCUIElement {
    func tapIfExists() {
        if exists {
            tap()
        }
    }
}

// MARK: - Helper functions
extension XCTestCase {
  func addPhotoCamera(_ app: XCUIApplication) {
    let pleaseSelectSheet = app.sheets.element

    //        ["Take Picture"].tap()
    pleaseSelectSheet.buttons.element(boundBy: 0).tap()

    // use coordinates and tap on Take picture button
    let element = app
        .children(matching: .window).element(boundBy: 0)
        .children(matching: .other).element
        .children(matching: .other).element
        .children(matching: .other).element
        .children(matching: .other).element

    let photoCapture = element.children(matching: .other).element
        .children(matching: .other).element(boundBy: 1)
        .children(matching: .other).element

    photoCapture.tap()

    sleep(5)

    app.buttons["Use Photo"].tap()
  }

  func addPhotoLibrary(_ app: XCUIApplication, index: Int = 0) {
    let pleaseSelectSheet = app.sheets["Add Photo"]

    pleaseSelectSheet.buttons.element(boundBy: 1).tap()

    sleep(10)

    // Camera Roll
    app.tables.cells.element(boundBy: 1).tap()

    sleep(2)

    let photoCells = app.collectionViews.cells
    if Platform.isSimulator {
        photoCells.element(boundBy: index).tap()
    } else {
        photoCells.allElementsBoundByIndex.last!.firstMatch.tap()
    }

    sleep(2)

    app.buttons["Choose"].tapIfExists()
  }
}
caffeinum
  • 401
  • 5
  • 13
  • 1
    Thanks @caffenium for the answer. – Deepak Terse Mar 03 '18 at 10:19
  • Thanks a lot. Will try this. – Deepak Terse Mar 07 '18 at 09:12
  • @caffeinum `addPhotoCamera` Function is not working, on `photoCapture.tap()` `Multiple matches found for Other. Sparse tree of matches: →Application, pid: 662 ↳Window (Main) ↳Other ↳Other ↳Other ↳Other ↳Other ↳Other ↳Other ↳Other ↳Other` – Jibran SiddiQui Feb 06 '19 at 12:29
  • @JibranSiddiQui maybe the element tree have changed from year ago. The one you see was generated using Record feature in Xcode. You could try re-recording and then replace broken part with fresh record – caffeinum Feb 06 '19 at 23:35
1

I am able to solve the issue for the second use case i.e.Setting an image using the file chooser. Here is my code.

app.tables.cells.element(boundBy: 1).tap()
app.collectionViews["PhotosGridView"].cells["Photo, Landscape, March 13, 2011, 5:47 AM"].tap()
app.buttons["Choose"].tap()
app.buttons["Confirm"].tap()

Line 1 selects 1 of the two options i.e."Moments" and "Camera Roll" Line 2 selects 1 image from the list Line 3 and 4 confirms and sets the image.

But I wonder whether it will work on different emulators or not because the date and time of the image might be different on that emulator.

Deepak Terse
  • 652
  • 8
  • 30