Skip to content

Commit f74a8e6

Browse files
authored
fix(ui-tests): tap the uncovered strip of a wallet card (#763)
* fix(ui-tests): tap the uncovered strip of a wallet card `selectFirstCurrency()` and `selectUsdfCurrency()` call `tap()`, which aims at the element's frame centre. `TokenCardStack` lays its cards coincident in a `ZStack` and offsets each one 64pt below the card behind it, so on a 224pt card the centre belongs to whichever card is drawn in front — that is the card the tap opens. On the test account both helpers missed: the first currency row opened Dollars, and the Dollars row opened the token below it. Tap 32pt below the card's top edge instead, inside the strip no other card covers. The offset is in points rather than a fraction of the frame because the Dollars card's `$` watermark overflows its bounds and inflates its accessibility frame by ~19pt on each side, so the same fraction lands deeper there than on a plain card. `isHittable` reports `true` on a fully covered card, so the helper has nothing it can assert. The guard goes at the destination: the convert tests now call `assertHeldTokenReached()` / `assertDollarsReached()`, which check Buy More and Withdraw. `assertHeldCurrencyReached()` checks Convert, which both held layouts show — that is why the wrong card went unnoticed. * fix(ui-tests): assert the Give flow reached a token, not Dollars `navigateToGiveAmount()` documents that it picks the first non-USDF card, but checks that with `assertHeldCurrencyReached()`, which looks for the Convert tile that Dollars shows too. The check passes on either card, so it does not hold the helper to what it promises. `assertHeldTokenReached()` looks for Buy More, which only a token has.
1 parent 5c1399a commit f74a8e6

6 files changed

Lines changed: 59 additions & 8 deletions

‎FlipcashUITests/Regression/ConvertBetweenTokensRegressionTests.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ final class ConvertBetweenTokensRegressionTests: BaseUITestCase {
3737
// Wallet tab → first non-USDF card → its info page.
3838
wallet.open(from: self)
3939
wallet.selectFirstCurrency()
40-
currencyInfo.assertHeldCurrencyReached()
40+
currencyInfo.assertHeldTokenReached()
4141

4242
waitAndTap(currencyInfo.convertButton)
4343
convert.assertAmountStepReached()

‎FlipcashUITests/Regression/ConvertFromDollarsRegressionTests.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ final class ConvertFromDollarsRegressionTests: BaseUITestCase {
4040
// Wallet tab → the Dollars card → its info page.
4141
wallet.open(from: self)
4242
wallet.selectUsdfCurrency()
43-
currencyInfo.assertHeldCurrencyReached()
43+
currencyInfo.assertDollarsReached()
4444

4545
// Convert → $0.01 → Next. Dollars can't be its own destination, so the
4646
// amount screen opens on a token already.

‎FlipcashUITests/Regression/ConvertToDollarsRegressionTests.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ final class ConvertToDollarsRegressionTests: BaseUITestCase {
3838
// Wallet tab → first non-USDF card → its info page.
3939
wallet.open(from: self)
4040
wallet.selectFirstCurrency()
41-
currencyInfo.assertHeldCurrencyReached()
41+
currencyInfo.assertHeldTokenReached()
4242

4343
// Convert → $0.01 → Next. The destination is already Dollars.
4444
waitAndTap(currencyInfo.convertButton)

‎FlipcashUITests/Support/BaseUITestCase.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ class BaseUITestCase: XCTestCase {
193193

194194
wallet.open(from: self)
195195
wallet.selectFirstCurrency()
196-
currencyInfo.assertHeldCurrencyReached(timeout: 30)
196+
currencyInfo.assertHeldTokenReached(timeout: 30)
197197
waitAndTap(currencyInfo.giveButton)
198198

199199
XCTAssertTrue(

‎FlipcashUITests/Support/Screens/CurrencyInfoScreen.swift‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,27 @@ struct CurrencyInfoUIScreen {
6262
)
6363
}
6464

65+
/// Asserts the page for a held currency that is *not* Dollars.
66+
///
67+
/// Give and Convert are on both held layouts, so a test that only checks
68+
/// those cannot tell a token's page from the Dollars page — which is how a
69+
/// wallet tap that opened the wrong card used to pass. Buy More is the tile
70+
/// only a token has.
71+
func assertHeldTokenReached(timeout: TimeInterval = 10) {
72+
XCTAssertTrue(
73+
buyMoreButton.waitForExistence(timeout: timeout),
74+
"Expected a held token's CurrencyInfoScreen, with a Buy More tile — Dollars shows Withdraw there"
75+
)
76+
}
77+
78+
/// Asserts the Dollars page, which swaps Buy More for Withdraw.
79+
func assertDollarsReached(timeout: TimeInterval = 10) {
80+
XCTAssertTrue(
81+
withdrawButton.waitForExistence(timeout: timeout),
82+
"Expected the Dollars CurrencyInfoScreen, with a Withdraw tile — a token shows Buy More there"
83+
)
84+
}
85+
6586
/// Asserts the page for a currency the account doesn't hold.
6687
func assertUnheldCurrencyReached(timeout: TimeInterval = 10) {
6788
XCTAssertTrue(

‎FlipcashUITests/Support/Screens/WalletScreen.swift‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import XCTest
1010
@MainActor
1111
struct WalletScreen {
1212

13+
/// How far below a card's top edge to tap: half the deck's 64pt per-card
14+
/// reveal, so the point sits inside the strip no card overlaps.
15+
private static let visibleStripInset: CGFloat = 32
16+
1317
private let app: XCUIApplication
1418

1519
init(app: XCUIApplication) {
@@ -26,6 +30,9 @@ struct WalletScreen {
2630
/// stacked cards rather than a `List`. The USDF card carries the distinct
2731
/// identifier "currency-row-usdf" so this selector reliably targets an
2832
/// investable token regardless of where USDF sorts.
33+
///
34+
/// Tap it with ``selectFirstCurrency()``, never with `tap()` — see
35+
/// ``tapVisibleStrip(of:)``.
2936
var firstCurrencyRow: XCUIElement {
3037
app.buttons.matching(identifier: "currency-row").firstMatch
3138
}
@@ -87,22 +94,45 @@ struct WalletScreen {
8794
testCase.scrollUpToAndTap(createCurrencyTile, in: scrollView)
8895
}
8996

90-
/// Selects the first currency card and verifies CurrencyInfoScreen is reached.
97+
/// Opens the first non-USDF currency's info page.
9198
func selectFirstCurrency() {
9299
XCTAssertTrue(
93100
firstCurrencyRow.waitForExistence(timeout: 30),
94101
"Expected at least one currency in the Wallet"
95102
)
96-
firstCurrencyRow.tap()
103+
tapVisibleStrip(of: firstCurrencyRow)
97104
}
98105

99-
/// Selects the USDF (Dollars) card — the source for a convert that buys
106+
/// Opens the USDF (Dollars) info page — the source for a convert that buys
100107
/// more of a currency the account already holds.
108+
///
109+
/// Dollars needs the strip tap as much as a token does: it sorts by value
110+
/// like every other card, so anything worth less is drawn on top of it and
111+
/// a centre tap opens that card instead.
101112
func selectUsdfCurrency() {
102113
XCTAssertTrue(
103114
usdfRow.waitForExistence(timeout: 30),
104115
"Expected the USDF card in the Wallet"
105116
)
106-
usdfRow.tap()
117+
tapVisibleStrip(of: usdfRow)
118+
}
119+
120+
/// Taps the middle of a card's uncovered top strip.
121+
///
122+
/// `TokenCardStack` lays the cards coincident and offsets each one
123+
/// `fannedReveal` (64pt) below the card behind it, so all a card leaves
124+
/// uncovered is that top sliver — the frame centre `XCUIElement.tap()` aims
125+
/// at belongs to the card in front, which is the one the tap then opens. A
126+
/// covered card still reports `isHittable == true`, so there is nothing to
127+
/// assert here; the guard is `assertHeldTokenReached()` at the destination.
128+
///
129+
/// The offset is in points from the top edge rather than a fraction of the
130+
/// frame, because the Dollars card's watermark overflows its bounds and
131+
/// inflates its accessibility frame — the same fraction lands lower there
132+
/// than on a plain card.
133+
private func tapVisibleStrip(of card: XCUIElement) {
134+
card.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0))
135+
.withOffset(CGVector(dx: 0, dy: Self.visibleStripInset))
136+
.tap()
107137
}
108138
}

0 commit comments

Comments
 (0)