174 lines
6.8 KiB
Swift
174 lines
6.8 KiB
Swift
//
|
|
// XCUITest+Utilities.swift
|
|
// PaymentSheetUITest
|
|
//
|
|
// Created by Yuki Tokuhiro on 8/20/21.
|
|
// Copyright © 2021 stripe-ios. All rights reserved.
|
|
//
|
|
|
|
import XCTest
|
|
|
|
// There seems to be an issue with our SwiftUI buttons - XCTest fails to scroll to the button's position.
|
|
// Work around this by targeting a coordinate inside the button.
|
|
// https://stackoverflow.com/questions/33422681/xcode-ui-test-ui-testing-failure-failed-to-scroll-to-visible-by-ax-action
|
|
extension XCUIElement {
|
|
func forceTapElement() {
|
|
if self.isHittable {
|
|
self.tap()
|
|
} else {
|
|
// Tap the middle of the element.
|
|
// (Sometimes the edges of rounded buttons aren't tappable in certain web elements.)
|
|
let coordinate: XCUICoordinate = self.coordinate(
|
|
withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
|
|
coordinate.tap()
|
|
}
|
|
}
|
|
|
|
func forceTapWhenHittableInTestCase(_ testCase: XCTestCase) {
|
|
let predicate = NSPredicate(format: "hittable == true")
|
|
testCase.expectation(for: predicate, evaluatedWith: self, handler: nil)
|
|
testCase.waitForExpectations(timeout: 15.0, handler: nil)
|
|
self.forceTapElement()
|
|
}
|
|
|
|
@discardableResult
|
|
func waitForExistenceAndTap(timeout: TimeInterval = 4.0) -> Bool {
|
|
guard waitForExistence(timeout: timeout) else {
|
|
return false
|
|
}
|
|
forceTapElement()
|
|
return true
|
|
}
|
|
}
|
|
|
|
// MARK: - XCUIApplication
|
|
|
|
extension XCUIApplication {
|
|
/// Types a text using the software keyboard.
|
|
///
|
|
/// This method is significantly slower than `XCUIElement.typeText()` but it works with custom controls.
|
|
///
|
|
/// - Parameter text: Text to type.
|
|
func typeTextWithKeyboard(_ text: String) {
|
|
for key in text {
|
|
self.keys[String(key)].tap()
|
|
}
|
|
}
|
|
}
|
|
|
|
// https://gist.github.com/jlnquere/d2cd529874ca73624eeb7159e3633d0f
|
|
func scroll(collectionView: XCUIElement, toFindCellWithId identifier: String) -> XCUIElement? {
|
|
guard collectionView.elementType == .collectionView else {
|
|
fatalError("XCUIElement is not a collectionView.")
|
|
}
|
|
|
|
var reachedTheEnd = false
|
|
var allVisibleElements = [String]()
|
|
|
|
while !reachedTheEnd {
|
|
let cell = collectionView.cells[identifier]
|
|
|
|
// Did we find our cell ?
|
|
if cell.exists {
|
|
return cell
|
|
}
|
|
|
|
// If not: we store the list of all the elements we've got in the CollectionView
|
|
let allElements = collectionView.cells.allElementsBoundByIndex.map({ $0.identifier })
|
|
|
|
// Did we read then end of the CollectionView ?
|
|
// i.e: do we have the same elements visible than before scrolling ?
|
|
reachedTheEnd = (allElements == allVisibleElements)
|
|
allVisibleElements = allElements
|
|
|
|
// Then, we do a scroll right on the scrollview
|
|
let startCoordinate = collectionView.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.99))
|
|
startCoordinate.press(forDuration: 0.01, thenDragTo: collectionView.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.99)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
extension XCTestCase {
|
|
func fillCardData(_ app: XCUIApplication,
|
|
container: XCUIElement? = nil,
|
|
cardNumber: String? = nil,
|
|
postalEnabled: Bool = true) throws {
|
|
let context = container ?? app
|
|
|
|
let numberField = context.textFields["Card number"]
|
|
numberField.forceTapWhenHittableInTestCase(self)
|
|
app.typeText(cardNumber ?? "4242424242424242")
|
|
app.typeText("1228") // Expiry
|
|
app.typeText("123") // CVC
|
|
if postalEnabled {
|
|
app.toolbars.buttons["Done"].tap() // Country picker toolbar's "Done" button
|
|
app.typeText("12345") // Postal
|
|
}
|
|
}
|
|
|
|
func waitToDisappear(_ target: Any?) {
|
|
let exists = NSPredicate(format: "exists == 0")
|
|
expectation(for: exists, evaluatedWith: target, handler: nil)
|
|
waitForExpectations(timeout: 60.0, handler: nil)
|
|
}
|
|
|
|
func reload(_ app: XCUIApplication, settings: PaymentSheetTestPlaygroundSettings) {
|
|
app.buttons["Reload"].tap()
|
|
waitForReload(app, settings: settings)
|
|
}
|
|
|
|
func waitForReload(_ app: XCUIApplication, settings: PaymentSheetTestPlaygroundSettings) {
|
|
if settings.uiStyle == .paymentSheet {
|
|
let presentButton = app.buttons["Present PaymentSheet"]
|
|
expectation(
|
|
for: NSPredicate(format: "enabled == true"),
|
|
evaluatedWith: presentButton,
|
|
handler: nil
|
|
)
|
|
waitForExpectations(timeout: 10, handler: nil)
|
|
} else {
|
|
let confirm = app.buttons["Confirm"]
|
|
expectation(
|
|
for: NSPredicate(format: "enabled == true"),
|
|
evaluatedWith: confirm,
|
|
handler: nil
|
|
)
|
|
waitForExpectations(timeout: 10, handler: nil)
|
|
}
|
|
}
|
|
func loadPlayground(_ app: XCUIApplication, _ settings: PaymentSheetTestPlaygroundSettings) {
|
|
if #available(iOS 15.0, *) {
|
|
// Doesn't work on 16.4. Seems like a bug, can't see any confirmation that this works online.
|
|
// var urlComponents = URLComponents(string: "stripe-paymentsheet-example://playground")!
|
|
// urlComponents.query = settings.base64Data
|
|
// app.open(urlComponents.url!)
|
|
// This should work, but we get an "Open in 'PaymentSheet Example'" consent dialog the first time we run it.
|
|
// And while the dialog is appearing, `open()` doesn't return, so we can't install an interruption handler or anything to handle it.
|
|
// XCUIDevice.shared.system.open(urlComponents.url!)
|
|
app.launchEnvironment = app.launchEnvironment.merging(["STP_PLAYGROUND_DATA": settings.base64Data]) { (_, new) in new }
|
|
app.launch()
|
|
} else {
|
|
XCTFail("This test is only supported on iOS 15.0 or later.")
|
|
}
|
|
waitForReload(app, settings: settings)
|
|
}
|
|
func waitForReload(_ app: XCUIApplication, settings: CustomerSheetTestPlaygroundSettings) {
|
|
let customerId = app.textFields["CustomerId"]
|
|
expectation(
|
|
for: NSPredicate(format: "self BEGINSWITH 'cus_'"),
|
|
evaluatedWith: customerId.value,
|
|
handler: nil
|
|
)
|
|
waitForExpectations(timeout: 10, handler: nil)
|
|
}
|
|
func loadPlayground(_ app: XCUIApplication, _ settings: CustomerSheetTestPlaygroundSettings) {
|
|
if #available(iOS 15.0, *) {
|
|
app.launchEnvironment = app.launchEnvironment.merging(["STP_CUSTOMERSHEET_PLAYGROUND_DATA": settings.base64Data]) { (_, new) in new }
|
|
app.launch()
|
|
} else {
|
|
XCTFail("This test is only supported on iOS 15.0 or later.")
|
|
}
|
|
waitForReload(app, settings: settings)
|
|
}
|
|
}
|