The Android savings screens have been migrated from XML layouts to Jetpack Compose. This broke Appium automation tests because the element IDs changed. XML layouts use android:id (e.g. button_continue), while Compose screens use testTag via the automation system (automationButton, automationText, AutomationContext), which generates different IDs (e.g. savings_set_amount_continue_oz_button_main_button). This guide maps every test element ID to its new Compose equivalent and provides exact code fixes.
4 MISMATCHED IDs found — tests use old XML IDs that don't match the new Compose testTags. These cause immediate test failures.
18+ MATCHING IDs confirmed — many elements already work correctly because Compose automation IDs were set to match, or screens are still XML-based.
6 IDs need VERIFICATION — elements that may or may not exist in Compose screens depending on screen state or dynamic content.
Fix strategy: Update @AndroidFindBy annotations in mobile-tests page objects to match the new Compose testTag IDs. No Android app changes needed for confirmed mismatches.
What it does: Understanding the ID generation is critical before making fixes. The Android project uses a custom automation system built on Compose testTag. The automationId() function in ModifierExtensions.kt combines: (1) AutomationContext — a scoping prefix, (2) prefix — an element-specific name, and (3) type — the element type (button, text, text_field, toggle, etc.). The final testTag format is: packageName:id/{context}_{prefix}_{type}. Appium matches on the part after packageName:id/.
Code that makes it work:
Example: A button inside an AutomationContext produces this chain:
// In Android Compose code:
AutomationContext("savings_set_amount_continue") {
OZButtonMainComponent(...) // internally calls automationButton("oz_button_main")
}
// This produces testTag:
// "il.co.firstdigitalbank:id/savings_set_amount_continue_oz_button_main_button"
// Appium matches with:
// @AndroidFindBy(id = "savings_set_amount_continue_oz_button_main_button")
// Key files in android-banking:
// - libraries/common-compose-ui/.../automation/ModifierExtensions.kt
// - libraries/common-compose-ui/.../automation/AutomationContext.kt
// - libraries/common-compose-ui/.../buttons/main/OZButtonMainComponent.kt
What it does: The Savings Lobby Compose screen (SavingsLobbyScreen.kt) has automation IDs that match what the tests expect. No changes needed for the main elements used in the daily savings creation flow.
Code that makes it work:
ID mapping — all confirmed matches:
Test ID | Compose ID | Status
--------------------------------------------|-------------------------------------------|-------
menu_open_saving_oz_button_main_button | menu_open_saving_oz_button_main_button | MATCH
empty_layout_oz_button_main_button | empty_layout_oz_button_main_button | MATCH
total_amount_text | total_amount_text | MATCH
empty_layout_title_text | empty_layout_title_text | MATCH
empty_layout_subtitle_text | empty_layout_subtitle_text | MATCH
buttonApproval | buttonApproval (XML) | MATCH
buttonCancel | buttonCancel (XML) | MATCH
Files:
Test: mobile-tests/src/main/kotlin/.../pages/currentAccount/savings/SavingsLobbyPage.kt
Android: android-banking/features/savings/savings-impl/.../ui/lobby/SavingsLobbyScreen.kt
What it does: The Plans Catalog screen is still XML-based (PlansCatalogFragment.kt with RecyclerView). All XML IDs remain intact. No changes needed.
Code that makes it work:
ID mapping — all confirmed matches:
Test ID | XML ID | Status
--------------|---------------|-------
plan_title | plan_title | MATCH
Files:
Test: mobile-tests/src/main/kotlin/.../pages/currentAccount/savings/create/SavingsPlanCatalogPage.kt
Android: android-banking/features/savings/savings-impl/.../ui/plans_catalog/PlansCatalogFragment.kt
What it does: The Set Amount screen is hybrid (XML + Compose). The currency input field matches, but the continue button ID has changed from the old XML ID to a new Compose testTag with AutomationContext prefix.
Code that makes it work:
FIX REQUIRED — Update @AndroidFindBy for the submit button:
ID mapping:
Test ID (current) | Compose ID (actual) | Status
-----------------------------------------------|--------------------------------------------------------|----------
savings_set_amount_currency_amount_text_field | savings_set_amount_currency_amount_text_field | MATCH
button_continue | savings_set_amount_continue_oz_button_main_button | MISMATCH
--- FIX in mobile-tests ---
File: src/main/kotlin/.../pages/currentAccount/savings/create/SavingsAmountPage.kt
BEFORE:
@AndroidFindBy(id = "button_continue")
private lateinit var _submitButton: WebElement
AFTER:
@AndroidFindBy(id = "savings_set_amount_continue_oz_button_main_button")
private lateinit var _submitButton: WebElement
WHY: The Compose screen wraps the button in:
AutomationContext("savings_set_amount_continue") { OZButtonMainComponent(...) }
which produces testTag: packageName:id/savings_set_amount_continue_oz_button_main_button
What it does: The Saving Rules screen is hybrid (XML + Compose). The header title and toggle IDs match, but the submit button and subtitle element IDs have changed in the Compose migration.
Code that makes it work:
FIXES REQUIRED — Update submit button and subtitle IDs:
ID mapping:
Test ID (current) | Compose ID (actual) | Status
-----------------------------------|------------------------------------------|----------
saving_rules_header_title_text | saving_rules_header_title_text | MATCH
saving_rules_row_{idx}_toggle | saving_rules_row_{idx}_toggle | MATCH
button | saving_rules_button | MISMATCH
oz_two_lines_subtitle | saving_rules_row_{idx}_subtitle_text | MISMATCH
--- FIX 1: Submit button ---
File: src/main/kotlin/.../pages/currentAccount/savings/create/AutomaticSavingsRulesPage.kt
BEFORE:
@AndroidFindBy(id = "button")
private lateinit var _submitButton: WebElement
AFTER:
@AndroidFindBy(id = "saving_rules_button")
private lateinit var _submitButton: WebElement
WHY: OZButtonComponent inside AutomationContext("saving_rules") produces saving_rules_button
--- FIX 2: Subtitle text (dynamic locator) ---
File: src/main/kotlin/.../pages/currentAccount/savings/create/AutomaticSavingsRulesPage.kt
(in the getSubtitleAndroid function or Android subclass)
BEFORE:
driver.findElements(By.id("oz_two_lines_subtitle"))
AFTER:
driver.findElements(By.id("saving_rules_row_{idx}_subtitle_text"))
NOTE: This is now per-row. You may need to construct the ID dynamically
using the row index, e.g.: By.id("saving_rules_row_${index}_subtitle_text")
What it does: The Savings Reason/Purpose screen is still XML-based (SavingPurposeFragment extends AbstractPurposeSelectionFragment with RecyclerView). All XML IDs remain intact.
Code that makes it work:
ID mapping — all confirmed matches:
Test ID | XML ID | Status
----------------|-----------------|-------
recycler_view | recycler_view | MATCH
label | label | MATCH
Files:
Test: mobile-tests/src/main/kotlin/.../pages/currentAccount/savings/create/SavingsReasonPage.kt
Android: android-banking/features/savings/savings-impl/.../ui/saving_purpose/SavingPurposeFragment.kt
What it does: The Edit Saving Name screen is hybrid (XML + Compose). Both the text field and submit button IDs match between the tests and the Android app.
Code that makes it work:
ID mapping — all confirmed matches:
Test ID | Compose/XML ID | Status
----------------------|-----------------------|-------
saving_name_text_field | saving_name_text_field | MATCH
button | button | MATCH
Files:
Test: mobile-tests/src/main/kotlin/.../pages/currentAccount/savings/create/EditSavingNamePage.kt
Android: android-banking/features/savings/savings-impl/.../ui/saving_name/SavingNameFragment.kt
What it does: The Saving Summary screen is hybrid (XML + Compose). The checkbox, description, and list elements match, but the continue/submit button ID has changed to a Compose testTag with AutomationContext prefix.
Code that makes it work:
FIX REQUIRED — Update @AndroidFindBy for the submit button:
ID mapping:
Test ID (current) | Compose/XML ID (actual) | Status
---------------------|----------------------------------------------------------|----------
button_continue | savings_deposit_summary_continue_oz_button_main_button | MISMATCH
checkbox | checkbox | MATCH
description_text | description_text | MATCH
list_content | list_content (XML) | MATCH
--- FIX in mobile-tests ---
File: src/main/kotlin/.../pages/currentAccount/savings/create/SavingSummaryPage.kt
BEFORE:
@AndroidFindBy(id = "button_continue")
private lateinit var _submitButton: WebElement
AFTER:
@AndroidFindBy(id = "savings_deposit_summary_continue_oz_button_main_button")
private lateinit var _submitButton: WebElement
WHY: The Compose screen wraps the button in:
AutomationContext("savings_deposit_summary_continue") { OZButtonMainComponent(...) }
which produces testTag: packageName:id/savings_deposit_summary_continue_oz_button_main_button
What it does: The Success Bottom Sheet is fully XML-based (DigiBankSuccessBottomSheet.kt with digibank_success_bottomsheet.xml layout). All XML IDs remain intact. No changes needed.
Code that makes it work:
ID mapping — all confirmed matches:
Test ID | XML ID | Status
--------------|---------------|-------
title | title | MATCH
description | description | MATCH
firstButton | firstButton | MATCH
Files:
Test: mobile-tests/src/main/kotlin/.../pages/currentAccount/savings/SavingsSummarySuccessBottomSheetPage.kt
Android: android-banking/core/src/main/java/.../core/widgets/DigiBankSuccessBottomSheet.kt
What it does: The Terms and Conditions bottom sheet uses XML layout (bottomsheet_savings_approval.xml) with a Compose button. The XML IDs for title, subtitle, and cancel button remain intact. The approval button is in a ComposeView but the XML ViewId buttonApproval is what Appium finds first.
Code that makes it work:
ID mapping — all confirmed matches:
Test ID | XML ID | Status
-----------------|------------------|-------
title | title | MATCH
subtitle | subtitle | MATCH
buttonApproval | buttonApproval | MATCH
buttonCancel | buttonCancel | MATCH
Files:
Test: mobile-tests/src/main/kotlin/.../pages/currentAccount/savings/SavingsChaptersTermsAndConditionsBottomSheetPage.kt
Android: android-banking/features/savings/savings-impl/.../ui/depositApprovalBottomSheet/SavingsApprovalBottomSheet.kt
What it does: Below is every code change needed in mobile-tests to fix the daily savings creation test. All fixes are in @AndroidFindBy annotations or dynamic By.id() calls. No changes needed in the Android project — all Compose screens already have the correct automation testTags.
Code that makes it work:
All 4 fixes in order of the test flow:
=== FIX 1 of 4 ===
File: src/main/kotlin/.../create/SavingsAmountPage.kt
Element: _submitButton
OLD: @AndroidFindBy(id = "button_continue")
NEW: @AndroidFindBy(id = "savings_set_amount_continue_oz_button_main_button")
=== FIX 2 of 4 ===
File: src/main/kotlin/.../create/AutomaticSavingsRulesPage.kt
Element: _submitButton
OLD: @AndroidFindBy(id = "button")
NEW: @AndroidFindBy(id = "saving_rules_button")
=== FIX 3 of 4 ===
File: src/main/kotlin/.../create/AutomaticSavingsRulesPage.kt
Element: subtitle (dynamic)
OLD: driver.findElements(By.id("oz_two_lines_subtitle"))
NEW: driver.findElements(By.id("saving_rules_row_${index}_subtitle_text"))
NOTE: Now per-row — may need dynamic ID construction with the row index.
=== FIX 4 of 4 ===
File: src/main/kotlin/.../create/SavingSummaryPage.kt
Element: _submitButton
OLD: @AndroidFindBy(id = "button_continue")
NEW: @AndroidFindBy(id = "savings_deposit_summary_continue_oz_button_main_button")
--- IMPORTANT ---
These IDs may also be used by other savings tests (withdrawal, monthly, yearly).
Search for every usage of the old IDs across the test project before changing them.
The @iOSXCUITFindBy annotations should NOT be modified — iOS IDs are unaffected.