Skip to content

Commit

Permalink
Financial Connections: end-to-end test polish to handle bank maintena…
Browse files Browse the repository at this point in the history
…nce periods (stripe#2854)

## Summary

Two things:
1. Did some small polishes of end to end tests. For example, starting
iOS 16.4, the switch/toggles were not turning on/off.
2. I added support for banks that are down due to maintenance periods.
Before this support/fix, we could get end-to-end test failures.

| Down Example 1 | Down Example 2 |
| --- | --- |
| <img width="178" alt="Screenshot 2023-08-15 at 1 37 17 PM"
src="https://github.com/stripe/stripe-ios/assets/105514761/04a366c2-47ea-414c-a406-d543a2530a97">
| <img width="117" alt="Screenshot 2023-08-15 at 1 38 23 PM"
src="https://github.com/stripe/stripe-ios/assets/105514761/e557f93c-c2f7-4d66-849b-a3a0767233ef">
|

## Testing

**Note that all this code is just testing code. No core code is
modified, so its safe.**

I did all sorts of manual testing to handle down bank screen. 

Below I ran `bitrise run financial-connections-stability-tests` to
verify that all tests pass:

```
Test Suite FinancialConnectionsUITests.xctest started
FinancialConnectionsNetworkingUITests
    ✓ testNativeNetworkingTestMode (95.465 seconds)
FinancialConnectionsUITests
    ✓ testDataLiveModeOAuthNativeAuthFlow (35.275 seconds)
    ✓ testDataLiveModeOAuthWebAuthFlow (29.325 seconds)
    ✓ testDataTestModeOAuthNativeAuthFlow (24.777 seconds)
    ✓ testPaymentTestModeLegacyNativeAuthFlow (26.048 seconds)
    ✓ testPaymentTestModeManualEntryNativeAuthFlow (29.168 seconds)
    ✓ testSearchInLiveModeNativeAuthFlow (27.401 seconds)
```
  • Loading branch information
kgaidis-stripe authored Aug 21, 2023
1 parent 803c901 commit dbe4cd4
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,13 @@ struct PlaygroundMainView: View {
.italic()
}

if viewModel.customPublicKey.isEmpty || viewModel.customSecretKey.isEmpty
{
Toggle("Enable Test Mode", isOn: $viewModel.enableTestMode)
// test mode color
.toggleStyle(
SwitchToggleStyle(
tint: Color(red: 231 / 255.0, green: 151 / 255.0, blue: 104 / 255.0)
)
Toggle("Enable Test Mode", isOn: $viewModel.enableTestMode)
// test mode color
.toggleStyle(
SwitchToggleStyle(
tint: Color(red: 231 / 255.0, green: 151 / 255.0, blue: 104 / 255.0)
)
}
)

if viewModel.flow == .networking {
TextField("Email (existing Link consumer)", text: $viewModel.email)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ final class FinancialConnectionsNetworkingUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "0" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: true)

app.swipeUp() // scroll to see email field

Expand All @@ -58,9 +56,7 @@ final class FinancialConnectionsNetworkingUITests: XCTestCase {

let playgroundTransactionsPermissionsSwitch = app.switches["playground-transactions-permission"]
XCTAssertTrue(playgroundTransactionsPermissionsSwitch.waitForExistence(timeout: 60.0))
if (playgroundTransactionsPermissionsSwitch.value as? String) == "0" {
playgroundTransactionsPermissionsSwitch.tap() // turn ON transactions
}
playgroundTransactionsPermissionsSwitch.turnSwitch(on: true)

app.fc_playgroundShowAuthFlowButton.tap()
app.fc_nativeConsentAgreeButton.tap()
Expand Down Expand Up @@ -122,9 +118,7 @@ final class FinancialConnectionsNetworkingUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "0" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: true)

app.swipeUp() // scroll to see email field

Expand All @@ -137,9 +131,7 @@ final class FinancialConnectionsNetworkingUITests: XCTestCase {

let playgroundTransactionsPermissionsSwitch = app.switches["playground-transactions-permission"]
XCTAssertTrue(playgroundTransactionsPermissionsSwitch.waitForExistence(timeout: 60.0))
if (playgroundTransactionsPermissionsSwitch.value as? String) == "0" {
playgroundTransactionsPermissionsSwitch.tap() // turn ON transactions
}
playgroundTransactionsPermissionsSwitch.turnSwitch(on: true)

app.fc_playgroundShowAuthFlowButton.tap()
app.fc_nativeConsentAgreeButton.tap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ final class FinancialConnectionsUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "0" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: true)

app.fc_playgroundShowAuthFlowButton.tap()
app.fc_nativeConsentAgreeButton.tap()
Expand Down Expand Up @@ -65,9 +63,7 @@ final class FinancialConnectionsUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "0" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: true)

app.fc_playgroundShowAuthFlowButton.tap()
app.fc_nativeConsentAgreeButton.tap()
Expand Down Expand Up @@ -99,9 +95,7 @@ final class FinancialConnectionsUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "0" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: true)

app.fc_playgroundShowAuthFlowButton.tap()

Expand Down Expand Up @@ -152,55 +146,72 @@ final class FinancialConnectionsUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "1" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: false)

app.fc_playgroundShowAuthFlowButton.tap()
app.fc_nativeConsentAgreeButton.tap()

// find + tap an institution; we add extra institutions in case
// they don't get featured
let institutionButton: XCUIElement?
let institutionTextInWebView: String?
let chaseInstitutionButton = app.cells["Chase"]
let institutionName: String?
let chaseBankName = "Chase"
let chaseInstitutionButton = app.cells[chaseBankName]
if chaseInstitutionButton.waitForExistence(timeout: 10) {
institutionButton = chaseInstitutionButton
institutionTextInWebView = "Chase"
institutionName = chaseBankName
} else {
let bankOfAmericaInstitutionButton = app.cells["Bank of America"]
let bankOfAmericaBankName = "Bank of America"
let bankOfAmericaInstitutionButton = app.cells[bankOfAmericaBankName]
if bankOfAmericaInstitutionButton.waitForExistence(timeout: 10) {
institutionButton = bankOfAmericaInstitutionButton
institutionTextInWebView = "Bank of America"
institutionName = bankOfAmericaBankName
} else {
let wellsFargoInstitutionButton = app.cells["Wells Fargo"]
let wellsFargoBankName = "Wells Fargo"
let wellsFargoInstitutionButton = app.cells[wellsFargoBankName]
if wellsFargoInstitutionButton.waitForExistence(timeout: 10) {
institutionButton = wellsFargoInstitutionButton
institutionTextInWebView = "Wells Fargo"
institutionName = wellsFargoBankName
} else {
institutionButton = nil
institutionTextInWebView = nil
institutionName = nil
}
}
}
guard let institutionButton = institutionButton, let institutionTextInWebView = institutionTextInWebView else {
guard let institutionButton = institutionButton, let institutionName = institutionName else {
XCTFail("Couldn't find a Live Mode institution.")
return
}
institutionButton.tap()

app.fc_nativePrepaneContinueButton.tap()
// ...at this point the bank is either:
// 1. active, which means prepane is visible
// 2. under maintenance, which means an 'error' screen is visible

// check that the WebView loaded
let institutionWebViewText = app.webViews
.staticTexts
.containing(NSPredicate(format: "label CONTAINS '\(institutionTextInWebView)'"))
.firstMatch
XCTAssertTrue(institutionWebViewText.waitForExistence(timeout: 120.0))
// (1) bank is NOT under maintenance
if app.fc_nativePrepaneContinueButton_noWait.waitForExistence(timeout: 60) {
app.fc_nativePrepaneContinueButton.tap()

let secureWebViewCancelButton = app.buttons["Cancel"]
XCTAssertTrue(secureWebViewCancelButton.waitForExistence(timeout: 60.0))
secureWebViewCancelButton.tap()
// check that the WebView loaded
let institutionWebViewText = app.webViews
.staticTexts
.containing(NSPredicate(format: "label CONTAINS '\(institutionName)'"))
.firstMatch
XCTAssertTrue(institutionWebViewText.waitForExistence(timeout: 120.0))

let secureWebViewCancelButton = app.buttons["Cancel"]
XCTAssertTrue(secureWebViewCancelButton.waitForExistence(timeout: 60.0))
secureWebViewCancelButton.tap()
}
// (2) bank IS under maintenance
else {
// check that we see a maintenance error
let errorViewText = app
.textViews
.containing(NSPredicate(format: "label CONTAINS 'unavailable' OR label CONTAINS 'maintenance' OR label CONTAINS 'scheduled'"))
.firstMatch
XCTAssertTrue(errorViewText.waitForExistence(timeout: 10))
}

let navigationBarCloseButton = app.navigationBars.buttons["close"]
XCTAssertTrue(navigationBarCloseButton.waitForExistence(timeout: 60.0))
Expand Down Expand Up @@ -231,9 +242,7 @@ final class FinancialConnectionsUITests: XCTestCase {
webSegmentPickerButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "1" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: false)

app.fc_playgroundShowAuthFlowButton.tap()

Expand All @@ -247,46 +256,65 @@ final class FinancialConnectionsUITests: XCTestCase {
// find + tap an institution; we add extra institutions in case
// they don't get featured
let institutionButton: XCUIElement?
let institutionTextInWebView: String?
let chaseInstitutionButton = app.webViews.buttons["Chase"]
let institutionName: String?
let chaseBankName = "Chase"
let chaseInstitutionButton = app.webViews.buttons[chaseBankName]
if chaseInstitutionButton.waitForExistence(timeout: 10) {
institutionButton = chaseInstitutionButton
institutionTextInWebView = "Chase"
institutionName = chaseBankName
} else {
let bankOfAmericaInstitutionButton = app.webViews.buttons["Bank of America"]
let bankOfAmericaBankName = "Bank of America"
let bankOfAmericaInstitutionButton = app.webViews.buttons[bankOfAmericaBankName]
if bankOfAmericaInstitutionButton.waitForExistence(timeout: 10) {
institutionButton = bankOfAmericaInstitutionButton
institutionTextInWebView = "Bank of America"
institutionName = bankOfAmericaBankName
} else {
let wellsFargoInstitutionButton = app.webViews.buttons["Wells Fargo"]
let wellsFargoBankName = "Wells Fargo"
let wellsFargoInstitutionButton = app.webViews.buttons[wellsFargoBankName]
if wellsFargoInstitutionButton.waitForExistence(timeout: 10) {
institutionButton = wellsFargoInstitutionButton
institutionTextInWebView = "Wells Fargo"
institutionName = wellsFargoBankName
} else {
institutionButton = nil
institutionTextInWebView = nil
institutionName = nil
}
}
}
guard let institutionButton = institutionButton, let institutionTextInWebView = institutionTextInWebView else {
guard let institutionButton = institutionButton, let institutionName = institutionName else {
XCTFail("Couldn't find a Live Mode institution.")
return
}
institutionButton.tap()

// ...at this point the bank is either:
// 1. active, which means prepane is visible
// 2. under maintenance, which means an 'error' screen is visible

let prepaneContinueButton = app.webViews
.buttons
.containing(NSPredicate(format: "label CONTAINS 'Continue'"))
.firstMatch
XCTAssertTrue(prepaneContinueButton.waitForExistence(timeout: 60.0))
prepaneContinueButton.tap()

// check that the WebView loaded
let institutionWebViewText = app.webViews
.staticTexts
.containing(NSPredicate(format: "label CONTAINS '\(institutionTextInWebView)'"))
.firstMatch
XCTAssertTrue(institutionWebViewText.waitForExistence(timeout: 120.0))
// (1) bank is NOT under maintenance
if prepaneContinueButton.waitForExistence(timeout: 60.0) {
prepaneContinueButton.tap()

// check that the WebView loaded
let institutionWebViewText = app.webViews
.staticTexts
.containing(NSPredicate(format: "label CONTAINS '\(institutionName)'"))
.firstMatch
XCTAssertTrue(institutionWebViewText.waitForExistence(timeout: 120.0))
}
// (2) bank IS under maintenance
else {
// check that we see a maintenance error
let errorViewText = app.webViews
.staticTexts
.containing(NSPredicate(format: "label CONTAINS 'unavailable' OR label CONTAINS 'maintenance' OR label CONTAINS 'scheduled'"))
.firstMatch
XCTAssertTrue(errorViewText.waitForExistence(timeout: 10))
}

let secureWebViewCancelButton = app.buttons["Cancel"]
XCTAssertTrue(secureWebViewCancelButton.waitForExistence(timeout: 60.0))
Expand All @@ -305,9 +333,7 @@ final class FinancialConnectionsUITests: XCTestCase {
app.fc_playgroundNativeButton.tap()

let enableTestModeSwitch = app.fc_playgroundEnableTestModeSwitch
if (enableTestModeSwitch.value as? String) == "1" {
enableTestModeSwitch.tap()
}
enableTestModeSwitch.turnSwitch(on: false)

app.fc_playgroundShowAuthFlowButton.tap()
app.fc_nativeConsentAgreeButton.tap()
Expand Down Expand Up @@ -345,20 +371,3 @@ extension XCTestCase {
_ = XCTWaiter.wait(for: [XCTestExpectation(description: "")], timeout: timeout)
}
}

extension XCUIElement {

fileprivate func wait(
until expression: @escaping (XCUIElement) -> Bool,
timeout: TimeInterval
) -> Bool {
let expectation = XCTNSPredicateExpectation(
predicate: NSPredicate { _, _ in
expression(self)
},
object: nil
)
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
return (result == .completed)
}
}
Loading

0 comments on commit dbe4cd4

Please sign in to comment.