Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions test/e2e_appium/constants/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Shared constants for the e2e Appium test suite."""

from .app_sections import AppSections

__all__ = ["AppSections"]
12 changes: 12 additions & 0 deletions test/e2e_appium/constants/app_sections.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!! ;)

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Shared constants for the e2e Appium test suite."""


class AppSections:
"""Identifiers for the high-level application sections."""

HOME = "home"
WALLET = "wallet"
MARKET = "market"
MESSAGING = "messaging"
COMMUNITIES = "communities"
SETTINGS = "settings"
49 changes: 19 additions & 30 deletions test/e2e_appium/core/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,11 @@ def settings(self):

@property
def welcome_back(self):
class SimpleWelcomeBack:
def is_welcome_back_screen_displayed(self, timeout=10):
return False

def perform_login(self, password):
return False
if not self._welcome_back:
from pages.onboarding import WelcomeBackPage

return SimpleWelcomeBack()
self._welcome_back = WelcomeBackPage(self.driver)
return self._welcome_back

@property
def user_service(self) -> UserProfileService:
Expand Down Expand Up @@ -267,8 +264,15 @@ def restart_app_and_login(self) -> bool:
self.logger.error("App restart failed")
return False

# Wait for app to stabilize and present either home or auth
self.wait_for_app_post_restart()
try:
manager = self._app_initialization or AppInitializationManager(self.driver)
if not self._app_initialization:
self._app_initialization = manager
manager.perform_initial_activation(timeout=3)
except Exception as activation_err:
self.logger.debug(
"Post-restart activation attempt failed: %s", activation_err
)

# Detect new state and handle authentication
self.app_state_manager.detect_current_state()
Expand All @@ -289,14 +293,18 @@ def get_home(self, ensure: bool = True, auto_create: bool = True) -> HomePage:

if not self.app_state.is_home_loaded:
# Try existing-user login if authentication is required
if self.app_state.requires_authentication:
if self.app_state.requires_authentication and self.user_service.current_user:
try:
self.logger.info(
"Auth required - attempting existing user login"
)
self.login_existing_user()
except Exception as e:
self.logger.warning(f"Existing user login failed: {e}")
elif self.app_state.requires_authentication:
self.logger.info(
"Auth required but no known test user; skipping auto-login"
)

# If still not loaded and allowed, create a user
if not self.app_state.is_home_loaded and auto_create:
Expand Down Expand Up @@ -347,6 +355,7 @@ def cleanup(self):
self._user_service = None
self._app_state_manager = None
self._app_initialization = None
self._welcome_back = None

self.logger.info("✅ TestContext cleanup completed")

Expand Down Expand Up @@ -418,26 +427,6 @@ def _handle_post_restart_authentication(self) -> bool:

return False

def wait_for_app_post_restart(
self, timeout: Optional[int] = None, poll_interval: float = 0.5
) -> bool:
"""Public helper to wait for app readiness after a restart using YAML defaults."""
effective_timeout = timeout
try:
if (
effective_timeout is None
and self._session_manager
and self._session_manager.env_config
):
effective_timeout = self._session_manager.env_config.timeouts.get(
"default", 30
)
except Exception:
effective_timeout = effective_timeout or 30
return self._wait_for_app_ready(
timeout=int(effective_timeout or 30), poll_interval=poll_interval
)

def _wait_for_app_ready(
self, timeout: int = 30, poll_interval: float = 0.5
) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

class WalletLocators(BaseLocators):

WALLET_HEADER = BaseLocators.accessibility_id("Wallet")
WALLET_HEADER = BaseLocators.content_desc_contains("walletHeader")
WALLET_FOOTER_SEND_BUTTON = BaseLocators.xpath(
"//android.view.View.VirtualChild[@content-desc='Send [tid:walletFooterSendButton]']"
"//*[contains(@resource-id, 'walletFooterSendButton')]"
)
ASSETS_TAB = BaseLocators.text_contains("Assets")
ACTIVITY_TAB = BaseLocators.text_contains("Activity")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@


class WelcomeBackScreenLocators(BaseLocators):
"""Locators for the Welcome Back screen (returning users)."""
"""Locators for the Welcome Back (returning user) screen."""

# TODO: Replace fallbacks with accessibility_id/tid

# Screen identification
LOGIN_SCREEN = BaseLocators.xpath(
"//*[contains(@resource-id, 'LoginScreen_QMLTYPE')]"
Expand All @@ -16,36 +14,10 @@ class WelcomeBackScreenLocators(BaseLocators):
"QGuiApplication.mainWindow.startupOnboardingLayout"
)

# User selection elements
USER_SELECTOR = BaseLocators.xpath(
"//*[contains(@resource-id, 'loginUserSelector')]"
)
USER_SELECTOR_DELEGATE = BaseLocators.xpath(
"//*[contains(@resource-id, 'LoginUserSelectorDelegate_QMLTYPE')]"
)

# Password input elements
PASSWORD_BOX = BaseLocators.xpath("//*[contains(@resource-id, 'passwordBox')]")
PASSWORD_INPUT = BaseLocators.xpath(
"//*[contains(@resource-id, 'loginPasswordInput')]"
)
PASSWORD_INPUT_BY_DESC = BaseLocators.content_desc_exact("Password")

# Login action
LOGIN_BUTTON = BaseLocators.xpath("//*[contains(@resource-id, 'loginButton')]")
LOGIN_BUTTON_BY_DESC = BaseLocators.content_desc_exact("Log In")

# Fallback locators for robustness
LOGIN_BUTTON_FALLBACKS = [
BaseLocators.xpath("//*[contains(@resource-id, 'loginButton')]"),
BaseLocators.content_desc_exact("Log In"),
BaseLocators.text_exact("Log In"),
]

PASSWORD_INPUT_FALLBACKS = [
BaseLocators.xpath("//*[contains(@resource-id, 'loginPasswordInput')]"),
BaseLocators.content_desc_exact("Password"),
BaseLocators.xpath(
"//android.widget.EditText[contains(@content-desc, 'Password')]"
),
]
PASSWORD_INPUT_OVERLAY = BaseLocators.xpath(
"//*[contains(@resource-id, 'loginPasswordInput')]"
)
LOGIN_BUTTON = BaseLocators.content_desc_contains("[tid:loginButton]")
34 changes: 34 additions & 0 deletions test/e2e_appium/locators/settings/password_change_locators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from ..base_locators import BaseLocators


class PasswordChangeLocators(BaseLocators):
CURRENT_PASSWORD_CONTAINER = BaseLocators.content_desc_exact(
"Enter current password"
)
CURRENT_PASSWORD_INPUT = BaseLocators.xpath(
"//*[contains(@resource-id, 'passwordViewCurrentPassword')]"
)
NEW_PASSWORD_INPUT = BaseLocators.xpath(
"//*[contains(@resource-id, 'passwordViewNewPassword') and not(contains(@resource-id, 'Confirm'))]"
)
CONFIRM_PASSWORD_INPUT = BaseLocators.xpath(
"//*[contains(@resource-id, 'passwordViewNewPasswordConfirm')]"
)
CHANGE_PASSWORD_BUTTON = BaseLocators.xpath(
"//*[contains(@resource-id, 'changePasswordModalSubmitButton') or "
"contains(@content-desc, 'changePasswordModalSubmitButton')]"
)


class ChangePasswordModalLocators(BaseLocators):
MODAL_CONTAINER = BaseLocators.xpath(
"//*[@resource-id='QGuiApplication.mainWindow.ConfirmChangePasswordModal']"
)
PRIMARY_BUTTON = BaseLocators.xpath(
"//*[@resource-id='QGuiApplication.mainWindow.ConfirmChangePasswordModal']"
"//*[contains(@content-desc, 'tid:changePasswordModalSubmitButton')]"
)
STATUS_MESSAGE = BaseLocators.xpath(
"//*[@resource-id='QGuiApplication.mainWindow.ConfirmChangePasswordModal']"
"//*[contains(@resource-id, 'statusListItemSubTitle')]"
)
2 changes: 2 additions & 0 deletions test/e2e_appium/locators/settings/settings_locators.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class SettingsLocators(BaseLocators):
BACKUP_RECOVERY_MENU_ITEM = BaseLocators.content_desc_contains("[tid:19-MenuItem]")

PROFILE_MENU_ITEM = BaseLocators.xpath("//*[contains(@resource-id,'0-MenuItem')]")
PASSWORD_MENU_ITEM = BaseLocators.content_desc_contains("[tid:1-MenuItem]")
PASSWORD_MENU_ITEM_TEXT = BaseLocators.text_contains("Password")

SIGN_OUT_AND_QUIT = BaseLocators.text_contains("Sign out & Quit")
SIGN_OUT_AND_QUIT_ALT = BaseLocators.xpath(
Expand Down
2 changes: 2 additions & 0 deletions test/e2e_appium/pages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
CreateProfilePage,
PasswordPage,
SplashScreen,
WelcomeBackPage,
)

__all__ = [
Expand All @@ -20,4 +21,5 @@
"CreateProfilePage",
"PasswordPage",
"SplashScreen",
"WelcomeBackPage",
]
1 change: 1 addition & 0 deletions test/e2e_appium/pages/base_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def safe_click(
)
self.logger.error(message)
self.take_screenshot(f"click_failure_{locators_to_try[0][1]}")
self.dump_page_source(f"click_failure_{locators_to_try[0][1]}")
raise ElementInteractionError(message, str(locators_to_try[0]), "click")

def safe_input(self, locator, text: str, timeout: Optional[int] = None) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions test/e2e_appium/pages/onboarding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .loading_page import SplashScreen
from .home_page import HomePage
from .seed_phrase_input_page import SeedPhraseInputPage
from .welcome_back_page import WelcomeBackPage
from .main_app_page import MainAppPage

__all__ = [
Expand All @@ -17,5 +18,6 @@
"SplashScreen",
"HomePage",
"SeedPhraseInputPage",
"WelcomeBackPage",
"MainAppPage",
]
Loading