Подтвердить что ты не робот

Прокрутка до тех пор, пока элемент не будет виден iOS UI Automation с помощью xcode7

Итак, с новым обновлением xcode обновление обновило способ тестирования UI. В инструментах мы использовали функцию java script "isVisible", чтобы определить, является ли наш целевой элемент видимым.

Я пытаюсь воспроизвести это в объективе c, но я не могу найти эквивалент этого. У меня есть табличный вид, прототип ячейки с двумя метками на нем. Эта прототипная ячейка повторно используется 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];
}

Но это не будет прокручиваться, поскольку элемент существует при загрузке представления. Пожалуйста, помогите, потому что это сводит меня с ума.

4b9b3361

Ответ 1

Вам следует расширить список методов XCUIElement. Первый метод (scrollToElement:) будет вызываться в tableView, второй метод расширения поможет вам решить, находится ли элемент в главном окне.

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)
}

Swift 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)
    }
}

Ответ 2

Все предыдущие ответы не являются 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, если ваше представление основано на таблице.

Ответ 3

Решения, использующие 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)

Насколько проверки видно ли что - то идет, вы хотите использовать isHittable в сочетании с exists. смотрите 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 здесь с добавленными методами scrollUp

Ответ 4

Расширение на @Kade 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()
           }
       }
    }

Ответ 5

К сожалению, .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()
    }
}

Ответ 6

Вот моя версия, которую я считаю пуленепробиваемой (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 }
    }
}

Следует учитывать, что предмет может быть не найден (в этом случае он не должен висеть). Также прокрутка выполняется с шагом по размеру элемента, чтобы элемент поиска не проходил через видимую область, что возможно в случае пролистывания.

Ответ 7

вы можете сделать что-то вроде этого:

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

и использовать scrollToElement для поиска элемента

Ответ 8

в swift 4.2, если ваш элемент существует в нижнем фрейме табличного представления или верхнем фрейме табличного представления, вы можете использовать эту команду для прокрутки вверх и прокрутки вниз, чтобы найти элемент

let app = XCUIApplication()
app.swipeUp()

или же

app.swipeDown()

Ответ 9

Обновите ответ @ravisekahrp для более нового Swift:

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)
        }
    }
}