Skip to content

Commit 4fe762e

Browse files
committed
chore(e2e_mobile): appium config for BrowserStack and Jenkins
1 parent 4ccd686 commit 4fe762e

File tree

11 files changed

+113
-74
lines changed

11 files changed

+113
-74
lines changed

test/e2e_appium/config/environments/base.yaml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ timeouts:
2727

2828
logging:
2929
level: "INFO"
30-
enable_screenshots: true
31-
enable_video_recording: true
3230
enable_xml_report: true
3331
enable_html_report: true
3432
enable_junit_report: true
3533

3634
directories:
37-
logs: "logs"
38-
reports: "reports"
39-
screenshots: "screenshots"
35+
reports: "reports/${E2E_RUN_ID:-local}"
36+
logs: "reports/${E2E_RUN_ID:-local}/logs"
37+
screenshots: "reports/${E2E_RUN_ID:-local}/screenshots"

test/e2e_appium/config/environments/browserstack.yaml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ provider:
1313
auth:
1414
username: "${BROWSERSTACK_USERNAME:-}"
1515
access_key: "${BROWSERSTACK_ACCESS_KEY:-}"
16-
build_name_template: "Status_E2E_Mobile"
16+
build_name_template: "${BROWSERSTACK_BUILD_NAME:-${BROWSERSTACK_BUILD_IDENTIFIER:-${GIT_COMMIT:-Status Mobile}}}"
1717
build_identifier_template: "${BROWSERSTACK_BUILD_IDENTIFIER:-${GIT_COMMIT:-local}}"
18-
session_name_template: "${TEST_NAME:-Status Test}"
19-
project_name: "Status E2E Appium"
18+
session_name_template: "${TEST_NAME:-${PYTEST_CURRENT_TEST:-Status Test}}"
19+
project_name: "${BROWSERSTACK_PROJECT_NAME:-Status E2E Appium}"
2020
hub_url: "https://hub-cloud.browserstack.com/wd/hub"
2121
max_parallel_sessions: 5
2222
sdk:
@@ -29,7 +29,13 @@ device_defaults:
2929
newCommandTimeout: 300
3030
bstack:options:
3131
deviceOrientation: "landscape"
32-
"appiumVersion" : "2.19.0"
32+
"appiumVersion": "2.19.0"
33+
video: true
34+
debug: false
35+
networkLogs: false
36+
appiumLogs: true
37+
deviceLogs: true
38+
appProfiling: false
3339
orientation: "LANDSCAPE"
3440
appium:unicodeKeyboard: true
3541
appium:resetKeyboard: true
@@ -76,7 +82,5 @@ execution:
7682

7783
logging:
7884
level: "INFO"
79-
enable_video_recording: true
80-
enable_screenshots: true
8185
enable_xml_report: true
8286
enable_html_report: true

test/e2e_appium/config/environments/local.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,4 @@ timeouts:
5353

5454
logging:
5555
level: "DEBUG"
56-
enable_video_recording: false
5756

58-
directories:
59-
logs: "logs/local"
60-
reports: "reports/local"
61-
screenshots: "screenshots/local"

test/e2e_appium/config/logging_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,9 @@ def setup_logging(config: Optional[LoggingConfig] = None) -> Dict[str, Any]:
247247
if config is None:
248248
config = LoggingConfig()
249249

250-
# Create logs directory
250+
# Create logs directory (create parents for nested per-run paths)
251251
logs_dir = Path(config.logs_dir)
252-
logs_dir.mkdir(exist_ok=True)
252+
logs_dir.mkdir(parents=True, exist_ok=True)
253253

254254
# Clear any existing handlers
255255
root_logger = logging.getLogger()

test/e2e_appium/config/settings.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
22
from dataclasses import dataclass, field
3+
from datetime import datetime
34
from pathlib import Path
45
from typing import Any, Dict, List, Optional
6+
from uuid import uuid4
57

68
from core.config_manager import ConfigurationManager, EnvironmentSwitcher
79
from core.environment import DeviceConfig, EnvironmentConfig
@@ -23,6 +25,7 @@ class TestConfig:
2325
logging_level: str
2426
concurrency: Dict[str, int]
2527
pytest_addopts: List[str]
28+
run_id: str
2629
provider_options: Dict[str, Any] = field(default_factory=dict)
2730

2831
@property
@@ -45,6 +48,15 @@ def platform_version(self) -> str:
4548
_CONFIG_CACHE: Optional[TestConfig] = None
4649

4750

51+
def _ensure_run_id() -> str:
52+
run_id = os.getenv("E2E_RUN_ID")
53+
if run_id:
54+
return run_id
55+
generated = f"local-{datetime.utcnow().strftime('%Y%m%d-%H%M%S')}-{uuid4().hex[:6]}"
56+
os.environ["E2E_RUN_ID"] = generated
57+
return generated
58+
59+
4860
def _select_device(env_config: EnvironmentConfig) -> DeviceConfig:
4961
device_id = os.getenv("TEST_DEVICE_ID")
5062
tag_env = os.getenv("TEST_DEVICE_TAGS", "")
@@ -77,6 +89,7 @@ def _ensure_directories(*paths: str) -> None:
7789

7890

7991
def load_config() -> TestConfig:
92+
run_id = _ensure_run_id()
8093
env_name = os.getenv("TEST_ENVIRONMENT")
8194
manager = ConfigurationManager()
8295
if not env_name:
@@ -89,9 +102,15 @@ def load_config() -> TestConfig:
89102
env_config.device_defaults.get("capabilities", {})
90103
)
91104

92-
reports_dir = env_config.directories.get("reports", "reports")
93-
logs_dir = env_config.directories.get("logs", "logs")
94-
screenshots_dir = env_config.directories.get("screenshots", "screenshots")
105+
reports_dir = env_config.resolve_template(
106+
env_config.directories.get("reports", "reports")
107+
)
108+
logs_dir = env_config.resolve_template(
109+
env_config.directories.get("logs", "logs")
110+
)
111+
screenshots_dir = env_config.resolve_template(
112+
env_config.directories.get("screenshots", "screenshots")
113+
)
95114
_ensure_directories(reports_dir, logs_dir, screenshots_dir)
96115

97116
execution = env_config.execution or {}
@@ -114,6 +133,7 @@ def load_config() -> TestConfig:
114133
logging_level=logging_config.get("level", "INFO"),
115134
concurrency=env_config.concurrency_limits(),
116135
pytest_addopts=pytest_addopts,
136+
run_id=run_id,
117137
provider_options=env_config.provider.options,
118138
)
119139
return config

test/e2e_appium/conftest.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
from pathlib import Path
55
from typing import List
66

7-
from .config import setup_logging, log_test_start, log_test_end
8-
from .config.logging_config import get_logger
9-
from .core import EnvironmentSwitcher
7+
from .config import get_config, setup_logging, log_test_start, log_test_end
8+
from .config.logging_config import get_logger, LoggingConfig
109
from .utils.cloud_reporter import CloudResultReporter
1110
from .utils.screenshot import save_screenshot, save_page_source
1211

@@ -23,45 +22,52 @@
2322

2423
def pytest_configure(config):
2524
global _logging_setup
26-
_logging_setup = setup_logging()
2725

2826
# Normalize CLI --env to CURRENT_TEST_ENVIRONMENT so all components agree
2927
try:
3028
cli_env = getattr(config.option, "env", None)
3129
if cli_env:
3230
os.environ["CURRENT_TEST_ENVIRONMENT"] = cli_env
31+
os.environ["TEST_ENVIRONMENT"] = cli_env
3332
except Exception:
3433
# Do not block test runs if normalization fails
3534
pass
3635

37-
# Use YAML-based configuration
38-
switcher = EnvironmentSwitcher()
39-
env_name = (
40-
os.getenv("CURRENT_TEST_ENVIRONMENT") or switcher.auto_detect_environment()
41-
)
42-
4336
try:
44-
env_config = switcher.switch_to(env_name)
37+
config_obj = get_config(refresh=True)
4538

46-
# Use directories from YAML config
47-
reports_dir = Path(env_config.directories.get("reports", "reports"))
48-
enable_xml_report = env_config.logging.get("enable_xml_report", True)
49-
enable_html_report = env_config.logging.get("enable_html_report", True)
39+
reports_dir = Path(config_obj.reports_dir)
40+
logs_dir = Path(config_obj.logs_dir)
41+
enable_xml_report = config_obj.enable_xml_report
42+
enable_html_report = config_obj.enable_html_report
43+
44+
logging_cfg = LoggingConfig(
45+
logs_dir=str(logs_dir),
46+
console_level=config_obj.logging_level,
47+
file_level=config_obj.logging_level,
48+
)
49+
_logging_setup = setup_logging(logging_cfg)
5050

5151
logger = get_logger("conftest")
52-
logger.info("Using reports directory from %s config: %s", env_name, reports_dir)
52+
logger.info(
53+
"Using reports directory from %s config: %s",
54+
config_obj.environment_name,
55+
reports_dir,
56+
)
5357

5458
except Exception as e:
5559
# Simplified fallback using defaults
5660
reports_dir = Path("reports")
5761
enable_xml_report = True
5862
enable_html_report = True
5963

64+
_logging_setup = setup_logging()
65+
6066
logger = get_logger("conftest")
6167
logger.warning("Using default configuration: %s", e)
6268
logger.warning("Ensure YAML config files are properly set up")
6369

64-
reports_dir.mkdir(exist_ok=True)
70+
reports_dir.mkdir(parents=True, exist_ok=True)
6571

6672
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
6773

@@ -178,16 +184,9 @@ def pytest_runtest_makereport(item, call):
178184
if driver:
179185
# Resolve screenshots directory from environment config; fallback to 'screenshots'
180186
try:
181-
env_switcher = EnvironmentSwitcher()
182-
env_name = (
183-
os.getenv("CURRENT_TEST_ENVIRONMENT")
184-
or env_switcher.auto_detect_environment()
185-
)
186-
env_config = env_switcher.switch_to(env_name)
187-
screenshots_dir = env_config.directories.get(
188-
"screenshots", "screenshots"
189-
)
190-
logs_dir = env_config.directories.get("logs", "logs")
187+
config_obj = get_config()
188+
screenshots_dir = config_obj.screenshots_dir or "screenshots"
189+
logs_dir = config_obj.logs_dir or "logs"
191190
except Exception:
192191
screenshots_dir = "screenshots"
193192
logs_dir = "logs"

test/e2e_appium/core/providers/browserstack.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ def __init__(self, env_config):
2929
"and BROWSERSTACK_ACCESS_KEY."
3030
)
3131
self.hub_url = env_config.get_provider_option("hub_url", self.HUB_URL)
32-
self.project_name = env_config.get_provider_option(
32+
project_name_option = env_config.get_provider_option(
3333
"project_name", "Status E2E Appium"
3434
)
35+
if isinstance(project_name_option, str):
36+
self.project_name = env_config.resolve_template(project_name_option)
37+
else:
38+
self.project_name = project_name_option
3539
self.sdk_options = env_config.get_provider_option("sdk", {})
3640

3741
def create_driver(

test/e2e_appium/core/test_context.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from appium.webdriver.webdriver import WebDriver
99

1010
from core.session_manager import SessionManager
11-
from core.config_manager import EnvironmentSwitcher
1211
from pages.onboarding import HomePage
1312
from pages.app import App
1413
from utils.exceptions import SessionManagementError
14+
from config import get_config
1515
from config.logging_config import get_logger
1616
from utils.gestures import Gestures
1717
from utils.screenshot import save_screenshot
@@ -180,9 +180,8 @@ def driver(self) -> WebDriver:
180180

181181
def take_screenshot(self, name: Optional[str] = None) -> Optional[str]:
182182
try:
183-
switcher = EnvironmentSwitcher()
184-
env_config = switcher.switch_to(self.environment)
185-
base_dir = env_config.directories.get("screenshots", "screenshots")
183+
config = get_config()
184+
base_dir = config.screenshots_dir or "screenshots"
186185
except Exception:
187186
base_dir = "screenshots"
188187
try:

test/e2e_appium/fixtures/onboarding_fixture.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88

99
from dataclasses import dataclass, field
10+
from pathlib import Path
1011
from typing import Optional, Dict, Any
1112
import pytest
1213
import time
@@ -23,6 +24,7 @@
2324
)
2425
from utils.generators import generate_seed_phrase
2526
from models.user_model import User, UserProfile
27+
from config import get_config
2628
from config.logging_config import get_logger
2729

2830

@@ -67,6 +69,16 @@ def __init__(self, driver, config: OnboardingConfig = None, logger=None):
6769
self.config = config or OnboardingConfig()
6870
self.logger = logger or get_logger("onboarding_flow")
6971

72+
if self.config.take_screenshots and not self.config.screenshot_path:
73+
try:
74+
resolved_dir = get_config().screenshots_dir
75+
if resolved_dir:
76+
Path(resolved_dir).mkdir(parents=True, exist_ok=True)
77+
self.config.screenshot_path = resolved_dir
78+
except Exception:
79+
# Leave as None if config unavailable
80+
pass
81+
7082
# Initialize page objects
7183
self.welcome_page = WelcomePage(self.driver)
7284
self.analytics_page = AnalyticsPage(self.driver)
@@ -362,12 +374,20 @@ def _execute_main_app_verification(self):
362374

363375
def _take_screenshot(self, name: str):
364376
"""Take screenshot during flow execution"""
365-
if self.config.screenshot_path:
377+
try:
378+
base_dir = self.config.screenshot_path
379+
if not base_dir:
380+
base_dir = get_config().screenshots_dir
381+
except Exception:
382+
base_dir = None
383+
384+
if base_dir:
366385
try:
386+
Path(base_dir).mkdir(parents=True, exist_ok=True)
367387
timestamp = datetime.now().strftime("%H%M%S")
368388
screenshot_name = f"{name}_{timestamp}.png"
369-
screenshot_path = f"{self.config.screenshot_path}/{screenshot_name}"
370-
self.driver.save_screenshot(screenshot_path)
389+
screenshot_path = Path(base_dir) / screenshot_name
390+
self.driver.save_screenshot(str(screenshot_path))
371391
self.logger.debug(f"📷 Screenshot saved: {screenshot_path}")
372392
except Exception as e:
373393
self.logger.warning(f"⚠️ Failed to take screenshot '{name}': {e}")

test/e2e_appium/pages/base_page.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import time
2-
import os
32
import logging
43
from datetime import datetime
4+
from pathlib import Path
55
from typing import Optional, List
66

77
from selenium.webdriver.common.action_chains import ActionChains
88
from selenium.webdriver.support.ui import WebDriverWait
99
from selenium.webdriver.support import expected_conditions as EC
1010

11-
from config import log_element_action
12-
from core import EnvironmentSwitcher
11+
from config import get_config, log_element_action
1312
from utils.gestures import Gestures
1413
from utils.screenshot import save_screenshot, save_page_source
1514
from utils.app_lifecycle_manager import AppLifecycleManager
@@ -23,16 +22,16 @@ def __init__(self, driver):
2322
self.gestures = Gestures(driver)
2423
self.app_lifecycle = AppLifecycleManager(driver)
2524
self.keyboard = KeyboardManager(driver)
26-
env_name = os.getenv("CURRENT_TEST_ENVIRONMENT", "browserstack")
27-
2825
try:
29-
switcher = EnvironmentSwitcher()
30-
env_config = switcher.switch_to(env_name)
31-
self.timeouts = env_config.timeouts
32-
element_wait_timeout = self.timeouts["element_wait"]
33-
self._screenshots_dir = env_config.directories.get(
34-
"screenshots", "screenshots"
35-
)
26+
config = get_config()
27+
self.timeouts = config.environment.timeouts
28+
element_wait_timeout = self.timeouts.get("element_wait", 30)
29+
self._screenshots_dir = config.screenshots_dir or "screenshots"
30+
try:
31+
Path(self._screenshots_dir).mkdir(parents=True, exist_ok=True)
32+
except Exception:
33+
# Do not block tests if directory creation fails
34+
pass
3635
except Exception:
3736
self.timeouts = {
3837
"element_wait": 30,

0 commit comments

Comments
 (0)