Skip to content

test(uitests): finish the tab-bar rewrite for convert, settings, give, chat and discover - #659

Merged
bmc08gt merged 8 commits into
mainfrom
test/ui-test-tab-bar-rewrite
Aug 26, 2026
Merged

test(uitests): finish the tab-bar rewrite for convert, settings, give, chat and discover#659
bmc08gt merged 8 commits into
mainfrom
test/ui-test-tab-bar-rewrite

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Finishes the UI-test rewrite that #653 started. That PR moved the Add Money and currency flows onto the tab-bar wallet; this one covers everything still entering through the v1 scanner chrome, and retires BaseUITestCase.skipPendingTabBarRewrite(_:) with its last call site.

Nothing here changes app behaviour. The one non-test change is two accessibility identifiers on CurrencyPickerSheet rows.

Buy and Sell became Convert

CurrencyInfoContentV2 gates its action tiles on isOwned, and the owned branch is Give / Convert / Withdraw — there is no Buy and no Sell. The replacement for both is Convert, entered from the balance you are paying from rather than the one you are acquiring. So the three regressions are rewritten as the routes that replaced them, and renamed for what they now exercise:

was is
CurrencySellRegressionTests ConvertToDollarsRegressionTests
BuyReservesRegressionTests ConvertFromDollarsRegressionTests
BuyWithCurrencyRegressionTests ConvertBetweenTokensRegressionTests

Convert only offers balances the account already holds — ConvertAmountViewModel.destinationOptions is session.balances(for:) minus the source — so it cannot acquire a currency the account has never held. That is what Get is for, on an unheld currency. The defaults matter for the tests: a non-Dollars source defaults to Dollars and a Dollars source defaults to the largest other holding, so only the token→token case has to open the picker. CurrencyPickerSheet rows gained currency-picker-row / currency-picker-row-usdf for it.

A finished convert lands on the Wallet, not back on the currency: ConvertFlowDestinationView hands the processing screen a dismissParentContainer that calls popToRoot() and dismissExpandedCard().

BuyReservesRegressionTests used to swipe down on the processing screen to prove the .buy sheet survived a nested dismissal. Convert is pushed, not presented, so there is no sheet to lose — that assertion was dropped rather than ported. SellConfirmationScreen went with the v1 sell sheet.

Settings moved to the You tab

The scanner's Settings button is gone; the list is on the You tab, under the tip card, so each test opens the tab and scrolls to its row. Access Key and Application Logs are on AdvancedSettingsMyAccountScreen keeps Access Key, Log Out and Delete Account off itself and holds only Change Display Name, Blocked, and the beta-gated Switch Accounts.

Covers AccessKeyBackupSmokeTests (×3), BlockedUsersSmokeTests, ApplicationLogsRegressionTests.

Add Money and Withdraw Money are Wallet tiles

SettingsUIScreen loses both properties with the rows. WithdrawSmokeTests, WithdrawPickerEmptyRegressionTests and DepositSmokeTests (×2) enter through the tiles instead.

Give starts from a currency

navigateToGiveAmount() goes Wallet → first currency card → its Give tile, replacing the scanner's Cash button. The keypad is pushed and pops itself as the bill appears (GiveScreen.onBillPresented, when isPushed), so both callers finish on CurrencyInfoScreen rather than a tab root. GiveSmokeTests asserts that on cancel, and CashLinkRegressionTests reaches its history from there without a second trip through the wallet — that history is the "Recent" section header now, since CurrencyInfoContentV2 has no "Transaction History" button.

The balance-retry loop is gone from the helper: the Give tile is only drawn for a currency the account holds, so this path raises no gate dialog to dismiss.

Chat and profile

BlockUnblockSmokeTests opened the Tips list as a sheet off the scanner's bottom bar. Embedded as the Chat tab it has no toolbar and no leading tip-card cell, so TipsUIScreen opens app.buttons["Chat"], waits on the "Chats" title instead of show-my-tipcard-button, and treats every cell as a conversation.

Its tearDown unblock still tapped app.buttons["Settings"], so it had been silently no-opping — a run that failed after the block left the shared test account with a user blocked. It goes through You › My Account › Blocked now, with a non-asserting scroll of its own: scrollUpToAndTap would fail the teardown and mask the failure that triggered it.

ProfileCreationSmokeTests walked Tips intro → name → photo → tipcard, and none of those steps has an entry left. OnboardingNameScreen is mandatory after the access key, so no account reaches the app without a name and both name-less prompts stay gated off; ProfileNameScreen skips the photo step for every caller because the card omits the photo, so .profilePhoto has no caller at all.

DisplayNameSmokeTests replaces it with what survived: a freshly registered account lands on the You tab with a card it can share, and the name behind that card changes through My Account. The editor pops back to My Account rather than the tab root (completion: .back pops one screen) and only once SetDisplayName returns, so landing there is still the proof that the name was accepted and moderated.

Discover and currency creation are sibling tiles

v1 reached currency creation through Discover, from the promo card at the top of its list. The tab-bar UI splits them into two Wallet tiles and hides the card (CurrencyDiscoveryScreen.hidesPromo), so DiscoverCurrenciesSmokeTests checks the two entries side by side and asserts the card is gone.

Both tiles are gated on funding: WalletScreen draws walletTiles only when session.hasEverAddedMoney(), and gives an unfunded account the new-user tutorial in their place. So the test takes the standing account rather than creating a fresh one. It only navigates.

Three gate regressions lose their fixture

All three needed an account with nothing spendable, and the tab-bar UI gives such an account no door to the screen that raises the gate.

GiveDiscoverGateRegressionTests and GiveRegressionTests are deleted. ScanBottomBar was the only caller that gated a give from a tab root, and it renders under if !isEmbedded — the Scan tab embeds ScanScreen. GiveCashGate still fires from a chat's Send Cash, TipFlow and the give deeplink, none of which a fresh empty account reaches. The Discover branch is doubly gone: USDF is giveable now (BetaFlags.allowsDollarsGive), so GiveCashGate.discoverCurrencies is unreachable either way.

AddMoneyGateRegressionTests is deleted for the same reason on the buy side. The Discover tile needs hasEverAddedMoney(); flipcash://discover reaches the same destination, but app.open relaunches the app and a freshly created account comes back on "Create a New Account"; and both standing accounts hold displayable USDF, so BuyAmountViewModel.paymentOptions is non-empty and the button reads Next.

The creation gate survives, in its own CurrencyCreationGateRegressionTests on the standing account, because shouldAddMoneyBeforeLaunch is a shortfall check rather than a $0 check: the account holds money but not newCurrencyPurchaseAmount + newCurrencyFeeAmount, so Get Started still raises the prompt instead of the wizard. That depends on a balance the test doesn't control, so it skips if the account can afford the launch.

All three gates are live app code. The rewrite plan records what a spent-down fixture would restore.

The tab-bar UI has no Buy or Sell tile on a currency the account holds —
`CurrencyInfoContentV2` offers Give / Convert / Withdraw, and Convert is
what replaced both. It moves value between balances you already hold, so
each of the three skipped tests is a convert in one direction: token →
Dollars was Sell, Dollars → token was buy-paying-with-reserves, and
token → token was buy-with-currency. The tests are renamed for the
direction they exercise, and `SellConfirmationScreen` goes with the v1
sell sheet that was its only subject.

Two of the three need no picker interaction: `ConvertAmountViewModel`
defaults a non-Dollars source to Dollars and a Dollars source to the
largest other holding. `ConvertBetweenTokensRegressionTests` is the one
that opens the picker, so `CurrencyPickerSheet` rows get
`currency-picker-row` with Dollars distinguished as
`currency-picker-row-usdf` — otherwise picking "the first token" could
land on Dollars depending on how balances sort.

Two v1 assertions are dropped rather than ported. The nested-sheet
swipe-down regression can't recur: convert is pushed, so there is no
sheet behind the processing screen. And a finished convert now pops to
the Wallet root instead of returning to the currency page, per
`ConvertFlowDestinationView`.

`CurrencyInfoUIScreen` splits `assertReached` into held and unheld
variants, since which tiles exist depends on the balance.
…e tab bar

Nine more `skipPendingTabBarRewrite` call sites, in three groups.

Settings rows: the You tab's list has only My Account and Advanced, so each
test opens the You tab and scrolls to its row. Access Key and Application Logs
are on Advanced, not My Account — `SettingsMyAccountScreen` keeps the
account-level actions off itself and says so in its header doc. Blocked stays
on My Account.

Money rows: Add Money and Withdraw Money are Wallet tiles now, so
`SettingsUIScreen` loses both properties and the four tests enter through
`wallet-tile-add-money` / `wallet-tile-withdraw-money`.

Give: `navigateToGiveAmount()` goes Wallet → first currency card → its Give
tile. The keypad pops itself as the bill appears — `GiveScreen.onBillPresented`
when `isPushed` — so both callers end on `CurrencyInfoScreen` rather than a tab
root, and the cash link reaches its history from there without a second trip
through the wallet. That history is the "Recent" section header in the
tab-bar UI; the v1 "Transaction History" button went with the old footer.

`GiveDiscoverGateRegressionTests` is deleted rather than rewritten: USDF is
giveable now, so `GiveCashGate.discoverCurrencies` is unreachable. The
"No Balance Yet" sibling keeps its skip with a corrected reason — the gate has
no fresh-account entry either, since the only caller that gated a give was
`ScanBottomBar`, which the embedded Scan tab does not render.

Also drops the balance-retry loop in `navigateToGiveAmount()`: the Give tile is
only drawn for a currency the account holds, so that path raises no gate
dialog.
`BlockUnblockSmokeTests` opened the Tips list as a sheet off the scanner's
bottom bar and closed it through `navigationBars["Tips"]`. Embedded as the
Chat tab, that list has no toolbar and no leading tip-card cell, so the page
object now opens `app.buttons["Chat"]`, waits on the "Chats" title, and treats
every cell as a conversation. Its `tearDown` unblock also still tapped
`app.buttons["Settings"]`, which the tab bar removed — so a run that failed
after the block left the shared account with a user blocked. It goes through
You › My Account › Blocked now, with a non-asserting scroll of its own:
`scrollUpToAndTap` would fail the teardown and mask the real failure.

`ProfileCreationSmokeTests` walked Tips intro → name → photo → tipcard, and
none of those four steps has an entry left. `OnboardingNameScreen` is
mandatory after the access key, so no account reaches the app without a name
and both name-less prompts stay gated off; `ProfileNameScreen` skips the photo
step for every caller because the card omits the photo. Replaced by
`DisplayNameSmokeTests`, covering what survived: a freshly registered account
lands on the You tab with a card it can share, and the name behind it changes
through My Account. `selectFirstPhotoFromLibrary` went with its last caller.

Both pass on iPhone 17.
`GiveCashGate`'s "No Balance Yet" branch has no entry a test can reach. The
only caller that gated a give from a tab root was `ScanBottomBar`, which
renders under `if !isEmbedded`, and the Scan tab embeds `ScanScreen`. The gate
still fires from a chat's Send Cash, `TipFlow`, and the give deeplink, but the
test's premise was a fresh empty account, which reaches none of those — and
give now starts from a held currency's Give tile, which an empty account has
no card for.

Same call as `GiveDiscoverGateRegressionTests`, which went for the same reason.
…tiles

Discover moved from a scanner sheet to a Wallet tile, and currency creation
moved from Discover's promo card to a sibling tile, so the two entries are now
checked side by side instead of one through the other.

Both tiles are gated on funding: WalletScreen draws walletTiles only for
session.hasEverAddedMoney(), and gives an unfunded account the new-user tutorial
in their place. So DiscoverCurrenciesSmokeTests takes the standing account
rather than creating a fresh one, and the currency-creation gate moves out of
AddMoneyGateRegressionTests into its own class on the same account. That gate
still fires there because shouldAddMoneyBeforeLaunch is a shortfall check, not a
$0 check — the account holds money but not the launch cost — and the test skips
if that ever stops being true.

The buy gate has no fixture left and stays skipped, with the reason recorded on
the class: flipcash://discover reaches the same destination, but app.open
relaunches the app and a freshly created account comes back on "Create a New
Account", while both standing accounts hold displayable USDF, so
BuyAmountViewModel.paymentOptions is non-empty and the button reads Next
instead of Add Money.
Every test that entered through the v1 scanner chrome has been rewritten or
deleted, so the helper has no callers. The rewrite plan keeps the map of where
each flow moved.
The test needs an account with nothing spendable, and the tab-bar UI gives such
an account no door to a currency's Get button. The Wallet's Discover tile is
drawn only for session.hasEverAddedMoney(); flipcash://discover reaches the same
destination, but app.open relaunches the app and a freshly created account comes
back on "Create a New Account"; and both standing accounts hold displayable
USDF, so BuyAmountViewModel.paymentOptions is non-empty and the button reads
Next instead of Add Money.

The gate is still live app code — a funded account spent down to nothing hits
it. Restoring the test needs a spent-down fixture, which the rewrite plan
records.
@bmc08gt bmc08gt self-assigned this Aug 26, 2026
`FlipcashAPI/Package.swift` moved to `exact: "0.2.0"` in #658, but the
workspace `Package.resolved` still held 0.1.0, so every build re-resolved
it and left the tree dirty.
@bmc08gt
bmc08gt merged commit 4c4564e into main Aug 26, 2026
@bmc08gt
bmc08gt deleted the test/ui-test-tab-bar-rewrite branch August 26, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant