因此,使用新的xcode更新,apple改进了我们进行UI测试的方式。在乐器中,我们使用java脚本函数“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];
}
但是这不会滑动,因为加载视图时元素存在。请帮助因为这让我发疯。
答案 0 :(得分:46)
您应该扩展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)
}
斯威夫特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)
}
}
答案 1 :(得分:18)
之前的所有答案都不是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)
}
注意:如果您的视图是基于tableview的
,请使用app.tables答案 2 :(得分:7)
使用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
方法
答案 3 :(得分:4)
扩展@Kade's answer,在我的情况下,必须考虑scrollToElement中的tabbar,否则如果视图位于tabbar下,则可能会点击一个tabbar按钮:
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 :(得分:3)
不幸的是.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()
}
}
答案 5 :(得分:3)
这是我的版本,我认为是防弹(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 }
}
}
考虑到可能找不到该项目(在这种情况下,它不应该挂起)。 此外,滚动是按照项目大小的步骤执行的,因此搜索元素将不会通过可见区域,以便在滑动的情况下。
答案 6 :(得分:1)
你可以这样做:
extension XCUIElement {
internal func scrollToElement(element: XCUIElement) {
while !element.exists {
swipeDown()
}
}
}
然后使用scrollToElement查找元素
答案 7 :(得分:0)
迅速4.2, 如果您的元素存在于表格视图的底部框架或表格视图的顶部框架,则可以使用此命令上下滚动以查找元素
let app = XCUIApplication()
app.swipeUp()
或
app.swipeDown()
答案 8 :(得分:0)
更新至@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)
}
}
}
答案 9 :(得分:0)
这里的问题是,在大多数情况下,滑动动量会将所需的单元格滚动出屏幕。所以我们最终会在搜索部分得到错误的滑动。
尝试滚动到当前表格视图中最后一个可点击单元格之后的单元格,并提供上述所有答案。他们将滚动所需的元素,在大多数情况下,我们找不到所需的单元格。
要求:
我的解决方案:
public let app = XCUIApplication()
extension XCUIElement {
func scrollTo(_ element: XCUIElement) {
if self.elementType == .table {
if element.isHittable { return }
let lastCell = self.cells.element(boundBy: self.cells.count-1)
let yOffset = calculatedYOffset()
while !element.isHittable
{
if lastCell.isHittable {
//Error - Table bottom reached
}
let start = self.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
let end = start.withOffset(CGVector(dx: 0.0, dy: -yOffset));
start.press(forDuration: 0.01, thenDragTo: end)
}
} else {
// Error - Only applicable for table views
}
}
func calculatedYOffset() -> Double {
var indexOfLastVisibleCell = 0
for i in 0...self.cells.count-1 {
if self.cells.element(boundBy: i).visible() {
indexOfLastVisibleCell = i
} else {
if indexOfLastVisibleCell != 0 { break }
}
}
let lastVisibleCellEndYPosition = Double(self.cells.element(boundBy: indexOfLastVisibleCell).frame.maxY)
let adjustmentYValue = Double(self.cells.firstMatch.frame.minY)
let screenScale = Double(UIScreen.main.scale)
return (lastVisibleCellEndYPosition-adjustmentYValue)/screenScale
}
func visible() -> Bool {
guard self.exists && self.isHittable && !self.frame.isEmpty else { return false }
return app.windows.element(boundBy: 0).frame.contains(self.frame)
}
}
现在,这应该可以了:
App.tables["Bar"].scrollTo(App.cells.staticTexts["Foo"])
优点:
缺点:
注意:此答案仅适用于表格视图和向底部滑动