web-dev-qa-db-ja.com

要素が表示されるまでスクロールしますiOS UI Automation with xcode7

そのため、新しいxcodeの更新Appleにより、UIテストの方法が刷新されました。機器では、Javaスクリプト関数「isVisible」を使用して、ターゲット要素が表示されているかどうかを判断しました。

Objective Cでこれを複製しようとしていますが、これに相当するものを見つけることができないようです。テーブルビュー、2つのラベルのあるプロトタイプセルがあります。このプロトタイプセルは50回再利用できます。

最後のセルが表示されるまでスクロールしようとしていますが、これを行うことでこれを行いました:

if (![[[[[[XCUIApplication alloc] init].tables childrenMatchingType:XCUIElementTypeCell] matchingIdentifier:@"cell"] elementBoundByIndex:49].staticTexts[@"text"] exists]) {
        [[[[[[XCUIApplication alloc] init].tables childrenMatchingType:XCUIElementTypeCell] matchingIdentifier:@"cell"] elementBoundByIndex:0].staticTexts[@"text"] swipeUp];
}

ただし、ビューがロードされるときに要素が存在するため、これはスワイプしません。これが私を夢中にさせているのを助けてください。

32
CodeGeass

XCUIElementのメソッドリストを拡張する必要があります。最初のメソッド(scrollToElement:)はtableViewで呼び出されます。2番目の拡張メソッドは、要素がメインウィンドウにあるかどうかを判断するのに役立ちます。

extension XCUIElement {

    func scrollToElement(element: XCUIElement) {
        while !element.visible() {
            swipeUp()
        }
    }

    func visible() -> Bool {
        guard self.exists && !CGRectIsEmpty(self.frame) else { return false }
        return CGRectContainsRect(XCUIApplication().windows.elementBoundByIndex(0).frame, self.frame)
    }

}

スクロールコードは次のようになります(最後のセルまでスクロールするなど)。

func testScrollTable() {
    let app = XCUIApplication()
    let table = app.tables.elementBoundByIndex(0)
    let lastCell = table.cells.elementBoundByIndex(table.cells.count-1)
    table.scrollToElement(lastCell)
}

スウィフト3:

extension XCUIElement {
    func scrollToElement(element: XCUIElement) {
        while !element.visible() {
            swipeUp()
        }
    }

    func visible() -> Bool {
        guard self.exists && !self.frame.isEmpty else { return false }
        return XCUIApplication().windows.element(boundBy: 0).frame.contains(self.frame)
    }
}
47
Kádi

これまでの回答はすべて、100%フェイルプルーフではありません。私が直面していた問題は、swipeUp()のオフセットが大きくなり、ビューポートに要素があるときにスクロールを停止する方法が見つからなかったことです。過剰なスクロールのために要素がスクロールされてしまい、結果としてテストケースが失敗することがあります。ただし、次のコードを使用してスクロールを制御できました。

/**
Scrolls to a particular element until it is rendered in the visible rect
- Parameter elememt: the element we want to scroll to
*/
func scrollToElement(element: XCUIElement)
{
    while element.visible() == false
    {
        let app = XCUIApplication()
        let startCoord = app.collectionViews.element.coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.5))
        let endCoord = startCoord.coordinateWithOffset(CGVector(dx: 0.0, dy: -262));
        startCoord.pressForDuration(0.01, thenDragToCoordinate: endCoord)
    }
}

func visible() -> Bool
{
    guard self.exists && self.hittable && !CGRectIsEmpty(self.frame) else
    {
        return false
    }

    return CGRectContainsRect(XCUIApplication().windows.elementBoundByIndex(0).frame, self.frame)
}

注:ビューがテーブルビューベースの場合は、app.tablesを使用してください

20
ravisekahrp

swipeUp()swipeDown()を使用するソリューションは、スワイプの勢いによりターゲット要素をスクロールする可能性があるため、理想的ではありません。多くの検索とフラストレーションの後、私はXCUICoordinateに魔法のメソッドを見つけました:

func press(forDuration duration: TimeInterval, thenDragTo otherCoordinate: XCUICoordinate)

そのため、次のようなことができます。

let topCoordinate = XCUIApplication().statusBars.firstMatch.coordinate(withNormalizedOffset: .zero)
let myElement = XCUIApplication().staticTexts["My Element"].coordinate(withNormalizedOffset: .zero)
// drag from element to top of screen (status bar)
myElement.press(forDuration: 0.1, thenDragTo: topCoordinate)

何かが見えるかどうかを確認する限り、isHittableexistsと組み合わせて使用​​する必要があります。以下の拡張機能のscrollDownToElementを参照してください

要素が画面に表示されるまでスクロールし、その要素を画面の上部までスクロールする便利な拡張機能を次に示します。

extension XCUIApplication {
    private struct Constants {
        // Half way accross the screen and 10% from top
        static let topOffset = CGVector(dx: 0.5, dy: 0.1)

        // Half way accross the screen and 90% from top
        static let bottomOffset = CGVector(dx: 0.5, dy: 0.9)
    }

    var screenTopCoordinate: XCUICoordinate {
        return windows.firstMatch.coordinate(withNormalizedOffset: Constants.topOffset)
    }

    var screenBottomCoordinate: XCUICoordinate {
        return windows.firstMatch.coordinate(withNormalizedOffset: Constants.bottomOffset)
    }

    func scrollDownToElement(element: XCUIElement, maxScrolls: Int = 5) {
        for _ in 0..<maxScrolls {
            if element.exists && element.isHittable { element.scrollToTop(); break }
            scrollDown()
        }
    }

    func scrollDown() {
        screenBottomCoordinate.press(forDuration: 0.1, thenDragTo: screenTopCoordinate)
    }
}

extension XCUIElement {
    func scrollToTop() {
        let topCoordinate = XCUIApplication().screenTopCoordinate
        let elementCoordinate = coordinate(withNormalizedOffset: .zero)

        // Adjust coordinate so that the drag is straight up, otherwise
        // an embedded horizontal scrolling element will get scrolled instead
        let delta = topCoordinate.screenPoint.x - elementCoordinate.screenPoint.x
        let deltaVector = CGVector(dx: delta, dy: 0.0)

        elementCoordinate.withOffset(deltaVector).press(forDuration: 0.1, thenDragTo: topCoordinate)
    }
}

要点 こちらscrollUpメソッドを追加

8
RyanM

@ Kade's answer を展開すると、私の場合はscrollToElementのタブバーを考慮しなければなりませんでした。

    func scrollToElement(element: XCUIElement) {
        while !element.visible() {
            swipeUp()
        }
        // Account for tabBar
        let tabBar = XCUIApplication().tabBars.element(boundBy: 0)
        if (tabBar.visible()) {
            while element.frame.intersects(tabBar.frame) {
                swipeUp()
           }
       }
    }
4
Justin Zealand

これが防弾だと思う私のバージョンです(Swift 4.0):

import XCTest

enum TestSwipeDirections {
    case up
    case down
    case left
    case right
}

fileprivate let min = 0.05
fileprivate let mid = 0.5
fileprivate let max = 0.95

fileprivate let leftPoint = CGVector(dx: min, dy: mid)
fileprivate let rightPoint = CGVector(dx: max, dy: mid)
fileprivate let topPoint = CGVector(dx: mid, dy: min)
fileprivate let bottomPoint = CGVector(dx: mid, dy: max)

extension TestSwipeDirections {
    var vector: (begin: CGVector, end: CGVector) {
        switch self {
        case .up:
            return (begin: bottomPoint,
                    end:   topPoint)
        case .down:
            return (begin: topPoint,
                    end:   bottomPoint)
        case .left:
            return (begin: rightPoint,
                    end:   leftPoint)
        case .right:
            return (begin: leftPoint,
                    end:   rightPoint)
        }
    }
}

extension XCUIElement {
    @discardableResult func swipeOnIt(_ direction: TestSwipeDirections,
                                      swipeLimit: Int = 6,
                                      swipeDuration: TimeInterval = 1.0,
                                      until: () -> Bool) -> Bool {
        XCTAssert(exists)

        let begining = coordinate(withNormalizedOffset: direction.vector.begin)
        let ending = coordinate(withNormalizedOffset: direction.vector.end)

        var swipesRemaining = swipeLimit
        while !until() && swipesRemaining > 0 {
            begining.press(forDuration: swipeDuration, thenDragTo: ending)
            swipesRemaining = swipesRemaining - 1
        }
        return !until()
    }

    @discardableResult func swipeOnIt(_ direction: TestSwipeDirections,
                                      swipeLimit: Int = 6,
                                      swipeDuration: TimeInterval = 1.0,
                                      untilHittable element: XCUIElement) -> Bool {
        return swipeOnIt(direction, swipeLimit: swipeLimit, swipeDuration: swipeDuration) { element.isHittable }
    }

    @discardableResult func swipeOnIt(_ direction: TestSwipeDirections,
                                      swipeLimit: Int = 6,
                                      swipeDuration: TimeInterval = 1.0,
                                      untilExists element: XCUIElement) -> Bool {
        return swipeOnIt(direction, swipeLimit: swipeLimit, swipeDuration: swipeDuration) { element.exists }
    }
}

アイテムが見つからない可能性があることを考慮します(この場合、ハングアップしないでください)。また、スクロールはアイテムのサイズのステップで実行されるため、検索要素はスワイプの場合に可能な可視領域を通過しません。

3
Marek R

残念ながら.existsは、要素が現在表示されていることを確認しません-このようなものはまだ完全ではありませんが、テーブルまたはコレクションビューセルで動作する信頼性の高い検証を提供します。

extension XCUIElement {
    var displayed: Bool {
        guard self.exists && !CGRectIsEmpty(frame) else { return false }
        return CGRectContainsRect(XCUIApplication().windows.elementBoundByIndex(0).frame, frame)
    }
}

次に、次のような単純なループを記述できます。

func scrollDownUntilVisible(element: XCUIElement) {
    while !element.displayed {
        swipeDown()
    }
}
3
Tucker Sherman

次のようなことができます:

extension XCUIElement {
    internal func scrollToElement(element: XCUIElement) {
        while !element.exists {
            swipeDown()
        }
    }
}

scrollToElementを使用して要素を見つけるよりも

1
Andrey

新しいSwiftに対する@ravisekahrpの回答の更新:

extension XCUIElement {
    func isVisible() -> Bool {
        if !self.exists || !self.isHittable || self.frame.isEmpty {
            return false
        }

        return XCUIApplication().windows.element(boundBy: 0).frame.contains(self.frame)
    }
}

extension XCTestCase {
    func scrollToElement(_ element: XCUIElement) {
        while !element.isVisible() {
            let app = XCUIApplication()
            let startCoord = app.tables.element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
            let endCoord = startCoord.withOffset(CGVector(dx: 0.0, dy: -262))
            startCoord.press(forDuration: 0.01, thenDragTo: endCoord)
        }
    }
}
0
Sunkas

in Swift 4.2、テーブルビューの下部フレームまたはテーブルビューの上部フレームに要素が存在する場合、このコマンドを使用して要素を見つけるために上下にスクロールできます

let app = XCUIApplication()
app.swipeUp()

または

app.swipeDown()
0
Ashim Dahal