UITesting Xcode 7: How to tell if XCUIElement is visible?

后端 未结 5 763
感动是毒
感动是毒 2021-01-02 02:43

I am automating an app using UI Testing in Xcode 7. I have a scrollview with XCUIElements (including buttons, etc) all the way down it. Sometimes the XCUIElements are visi

相关标签:
5条回答
  • 2021-01-02 03:04

    What i had to do to address this problem is to actually swipe up or down in my UI testing code. Have you tried this?

    XCUIApplication().swipeUp()
    

    Or you can also do WhateverUIElement.swipUp() and it will scroll up/down with respect to that element.

    Hopefully they will fix the auto scroll or auto find feature so we don't have to do this manually.

    0 讨论(0)
  • 2021-01-02 03:04

    You should check isHittable property.

    If view is not hidden, the corresponding XCUIElement is hittable. But there is a caveat. "View 1" can be overlapped by "View 2", but the element corresponding to "View 1" can be hittable.

    0 讨论(0)
  • 2021-01-02 03:12

    Since you have some XCUIElements in the bottom of the tableview (table footer view), the way of scrolling the tableview all the way to the bottom in the UI test, supposing your tableview has a lot data, is by tap().

    .swipeUp() also does the job but the problem is when your test data is huge, it takes forever to swipe, as oppose to .tap() which directly jumps to the bottom of the tableView.

    More specially:

    XCUIElementsInTheBottomOrTableFooterView.tap()
    XCTAssert(XCUIElementsInTheBottomOrTableFooterView.isHittable, "message") 
    
    0 讨论(0)
  • 2021-01-02 03:20

    Looks like this is a known bug :-(

    https://forums.developer.apple.com/thread/9934

    0 讨论(0)
  • 2021-01-02 03:21

    Unfortunately Apple hasn't provided any scrollTo method or a .visible parameter on XCUIElement. That said, you can add a couple helper methods to achieve some of this functionality. Here is how I've done it in Swift.

    First for checking if an element is visible:

    func elementIsWithinWindow(element: XCUIElement) -> Bool {
        guard element.exists && !CGRectIsEmpty(element.frame) && element.hittable else { return false }
        return CGRectContainsRect(XCUIApplication().windows.elementBoundByIndex(0).frame, element.frame)
    }
    

    Unfortunately .exists returns true if an element has been loaded but is not on screen. Additionally we have to check that the target element has a frame larger than 0 by 0 (also sometimes true) - then we can check if this frame is within the main window.

    Then we need a method for scrolling a controllable amount up or down:

    func scrollDown(times: Int = 1) {
        let topScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.05))
        let bottomScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.90))
        for _ in 0..<times {
            bottomScreenPoint.pressForDuration(0, thenDragToCoordinate: topScreenPoint)
        }
    }
    
    func scrollUp(times: Int = 1) {
        let topScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.05))
        let bottomScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.90))
        for _ in 0..<times {
            topScreenPoint.pressForDuration(0, thenDragToCoordinate: bottomScreenPoint)
        }
    }  
    

    Changing the CGVector values for topScreenPoint and bottomScreenPoint will change the scale of the scroll action - be aware if you get too close to the edges of the screen you will pull out one of the OS menus.

    With these two methods in place you can write a loop that scrolls to a given threshold one way until an element becomes visible, then if it doesn't find its target it scrolls the other way:

    func scrollUntilElementAppears(element: XCUIElement, threshold: Int = 10) {
        var iteration = 0
    
        while !elementIsWithinWindow(element) {
            guard iteration < threshold else { break }
            scrollDown()
            iteration++
        }
    
        if !elementIsWithinWindow(element) { scrollDown(threshold) }
    
        while !elementIsWithinWindow(element) {
            guard iteration > 0 else { break }
            scrollUp()
            iteration--
        }
    }
    

    This last method isn't super efficient, but it should at least enable you to find elements off screen. Of course if you know your target element is always going to be above or below your starting point in a given test you could just write a scrollDownUntil or a scrollUpUntill method without the threshold logic here. Hope this helps!

    0 讨论(0)
提交回复
热议问题