Прокрутка до тех пор, пока элемент не будет виден 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];
}
Но это не будет прокручиваться, поскольку элемент существует при загрузке представления. Пожалуйста, помогите, потому что это сводит меня с ума.
Ответы
Ответ 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)
}
}
}