xcuitest test source open ios objective-c xcode-ui-testing

test - Desplácese hasta que el elemento esté visible iOS UI Automation con xcode7



automation mobile (7)

Aquí está mi versión que creo que es a prueba de balas (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 } } }

Tenga en cuenta que ese elemento no se puede encontrar (en este caso, no debe colgarse). Además, el desplazamiento se realiza en pasos del tamaño del elemento para que el elemento de búsqueda no pase por el área visible, lo que es posible en caso de deslizar.

Así que con la nueva actualización de xcode Apple ha renovado la forma en que hacemos las pruebas de interfaz de usuario. En instrumentos usamos la función de script java "isVisible" para determinar si nuestro elemento objetivo es visible.

Estoy tratando de replicar esto en el objetivo c pero parece que no puedo encontrar el equivalente a esto. Tengo una vista de tabla, una célula prototipo con dos etiquetas en ella. Esta célula prototipo se reutiliza 50 veces, digamos.

Estoy intentando desplazarme hasta que se vea la última celda. Hice esto haciendo esto:

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]; }

Pero esto no se deslizará ya que el elemento existe cuando se carga la vista. Por favor ayuda porque esto me está volviendo loco.


Debe ampliar la lista de métodos de XCUIElement. Se llamará al primer método ( scrollToElement: :) en el tableView, el segundo método de extensión le ayuda a decidir si el elemento está en la ventana principal.

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

El código de desplazamiento debería tener este aspecto (por ejemplo, desplazamiento a la última celda):

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


Desafortunadamente .exists no confirma que un elemento esté actualmente visible, algo como esto todavía no es perfecto pero proporcionará una validación más confiable al trabajar con celdas de vista de tabla o colección:

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

entonces puedes escribir un simple bucle como:

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


La expansión de la respuesta de @ Kade , en mi caso, tenía que dar cuenta de la barra de pestañas en scrollToElement, de lo contrario, se podría tocar un botón de la barra de pestañas si la vista estaba debajo de la barra de pestañas:

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


Las soluciones que usan swipeUp() y swipeDown() no son ideales porque potencialmente pueden desplazarse más allá del elemento objetivo debido al impulso del golpe. Después de mucha búsqueda y frustración encontré un método mágico en XCUICoordinate :

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

Entonces podemos hacer algo como:

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)

En lo que respecta a verificar si algo está visible, desea utilizar isHittable junto con exists . Ver scrollDownToElement en la extensión de abajo

Aquí hay una extensión útil que se desplazará hasta que un elemento esté en la pantalla y luego se desplazará hacia la parte superior de la pantalla :)

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 here con métodos scrollUp añadidos


Puedes hacer algo como esto:

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

y que use scrollToElement para encontrar el elemento


Todas las respuestas anteriores no son 100% a prueba de fallos. El problema al que me enfrentaba es que swipeUp () tiene un desplazamiento mayor y no pude encontrar una manera de detener el desplazamiento cuando tengo el elemento en el puerto de vista. A veces, el elemento se desplaza debido al desplazamiento excesivo y, como resultado, el caso de prueba falla. Sin embargo, logré controlar el desplazamiento utilizando el siguiente fragmento de código.

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

Nota: utilice app.tables si su vista está basada en tableview