Metadata-Version: 2.4
Name: appium-client-python
Version: 1.3.11
Summary: Modern, Intelligent Appium Mobile Automation Client for Python
Keywords: mobile,automation,android,ios,appium,testing,pytest
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Testing
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pytest>=8.0.0
Requires-Dist: pytest-xdist>=3.5.0
Requires-Dist: rich>=13.0.0
Requires-Dist: loguru>=0.7.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: jinja2>=3.1.0
Requires-Dist: cryptography>=41.0.0
Requires-Dist: requests>=2.31.0
Dynamic: license-file

# 🚀 appium-client-python — Master Layman & API Reference Guide

Welcome to **appium-client-python**! appium-client-python is a high-level, human-readable mobile automation framework built on Appium 2.x and PyTest for Python 3.11+. It handles device management, element discovery, auto-scrolling, explicit waiting, and test failure evidence automatically, allowing manual testers, QA engineers, and developers to write production-grade mobile tests with **zero boilerplate**.

---

## 🏗️ 1. Device Capabilities & 3-Tier Resolution

appium-client-python connects to Android (ADB) devices and iOS Simulators using a **3-tier device resolution system**:
1. **Explicit Settings**: Command-line arguments (`--app-device-name`, `--app-platform`).
2. **Named Configuration**: `appium_client.yaml` or `.env` configuration files.
3. **Auto-Detect**: Automatically scans connected ADB devices or active Xcode Simulators.

### 1.1 Project Configuration (`appium_client.yaml`)
Create `appium_client.yaml` in your project root directory:

```yaml
# appium_client.yaml
project_name: "appium-client-python Automation Suite"

# Target Application Configuration
platform: "Android"            # 'Android' or 'iOS'
device_name: "auto"            # Specific serial (e.g., 'RZCT31905PL'), simulator name, or 'auto'
app_package: "com.example.app"  # Target Android app package or iOS Bundle ID
app_activity: ".MainActivity"   # Initial Android activity

# Smart Engine Settings
timeout: 30                    # Default timeout in seconds for elements to appear
auto_scroll: false             # Automatically scroll down to find missing elements
mirror_screen: false           # Launch scrcpy desktop screen mirror during execution

# Manual Tool Paths (Leave empty to use system defaults)
tools:
  adb_path: 'C:\platform-tools\adb.exe'
  scrcpy_path: 'C:\scrcpy\scrcpy.exe'
  log_path: '.appium_client/logs'

# Devices List for Parallel Execution
devices:
  - platform: "Android"
    device_name: "auto"
```

### 1.2 Environment Variables (`.env`)
You can also configure settings via a `.env` file:
```ini
APP_PLATFORM=Android
APP_DEVICE_NAME=RZCT31905PL
APP_PACKAGE=com.example.app
APP_APPIUM_URL=http://127.0.0.1:4723
APP_EXPLICIT_WAIT_TIMEOUT=30
GEMINI_API_KEY=your_gemini_api_key_here
```

---

## 🔌 2. Complete `conftest.py` & Fixtures Guide

appium-client-python plugs directly into PyTest via `pytest-appium_client`. Below is the **complete, copy-pasteable `conftest.py`** file used in production frameworks.

### 2.1 Complete Production `conftest.py`

```python
"""Production-Ready conftest.py for appium-client-python Test Suite.

Provides zero-boilerplate PyTest fixtures for mobile automation, explicit waits,
soft assertions, test data generation, REST API client, visual testing, and reporting.
"""

from __future__ import annotations

import os
import sys
from pathlib import Path
import pytest
from loguru import logger

# Add project root to sys.path for seamless imports (e.g. from screens.login_screen import LoginScreen)
PROJECT_ROOT = Path(__file__).resolve().parent
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

# Import appium-client-python kit components
from appium_client.api.mobile import Mobile
from appium_client.actions import Actions
from appium_client.waits import Waiter
from appium_client.soft_assertions import SoftAssert
from appium_client.visual import VisualTester
from appium_client.api_client import APIClient
from appium_client.test_data import DataFactory
from appium_client.devices.manager import DeviceManager, DeviceInfo
from appium_client.devices.server import AppiumServerManager


# ==============================================================================
# 1. LOGGING & SESSION LIFECYCLE HOOKS
# ==============================================================================

@pytest.fixture(scope="session", autouse=True)
def setup_logging():
    """Configure loguru for pretty, structured CLI logs."""
    logger.remove()
    logger.add(lambda msg: print(msg, end=""), level="INFO", colorize=True)


@pytest.fixture(scope="session", autouse=True)
def mobile_session(request):
    """Global session lifecycle for Appium server management and device resolution."""
    from appium_client.config.models import load_config
    config = load_config()

    manage_server = os.getenv("APP_MANAGE_APPIUM_SERVER", "false").lower() in {"true", "1", "yes"}
    server_mgr = AppiumServerManager(manage_server=manage_server)
    server_info = server_mgr.resolve()

    yield server_info

    server_mgr.stop()


# ==============================================================================
# 2. CORE AUTOMATION FIXTURES
# ==============================================================================

@pytest.fixture
def mobile(mobile_session, request) -> Mobile:
    """Provides the core Mobile API instance to your test functions."""
    m = Mobile()
    m._executor.start_session()

    yield m

    # Auto-Inspection on Failure
    if hasattr(request.node, "rep_call") and request.node.rep_call.failed:
        logger.error(f"Test Failed: {request.node.name}. Capturing failure screenshot & UI dump...")
        try:
            m.screenshot(f"failure_{request.node.name}")
            m.inspect()
        except Exception as e:
            logger.warning(f"Auto-Inspection on failure failed: {e}")

    m.execute()
    m._executor.stop_session()


@pytest.fixture
def waiter(mobile) -> Waiter:
    """Provides Waiter explicit wait instance."""
    driver = getattr(mobile, "_driver", None) or getattr(mobile._executor, "_driver", None) or mobile
    return Waiter(driver)


@pytest.fixture
def actions(mobile, waiter) -> Actions:
    """Provides Actions high-level UI helper instance."""
    driver = getattr(mobile, "_driver", None) or getattr(mobile._executor, "_driver", None) or mobile
    return Actions(driver, waiter)


@pytest.fixture
def soft_assert():
    """Provides SoftAssert non-blocking multi-assertion collector."""
    sa = SoftAssert()
    yield sa
    sa.assert_all()


@pytest.fixture
def visual_tester(mobile):
    """Provides VisualTester instance for visual regression comparison."""
    driver = getattr(mobile, "_driver", None) or getattr(mobile._executor, "_driver", None) or mobile
    return VisualTester(driver)


@pytest.fixture
def api_client() -> APIClient:
    """Provides REST APIClient instance for backend testing."""
    base_url = os.getenv("API_BASE_URL", "http://127.0.0.1:8000")
    return APIClient(base_url)


@pytest.fixture
def test_data() -> DataFactory:
    """Provides DataFactory test data manager with seeded randomness."""
    return DataFactory()


@pytest.fixture
def device_info() -> DeviceInfo:
    """Provides DeviceInfo metadata for the connected device."""
    dm = DeviceManager()
    devices = dm.list_connected_devices()
    if devices:
        return devices[0]
    return DeviceInfo(id="default", name="Default Device", platform="Android", status="online", version="Unknown")


@pytest.fixture
def page_factory(mobile, waiter, actions):
    """Factory fixture for dynamic Page Object instantiation."""
    driver = getattr(mobile, "_driver", None) or getattr(mobile._executor, "_driver", None) or mobile

    def _create_page(page_cls: type, **kwargs):
        page = page_cls(driver, waiter=waiter, actions=actions, **kwargs)
        if hasattr(page, "is_loaded") and callable(page.is_loaded):
            page.is_loaded()
        return page

    return _create_page


# ==============================================================================
# 3. REPORTING HOOKS
# ==============================================================================

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Hook to capture test results for failure handling."""
    outcome = yield
    rep = outcome.get_result()
    if rep.when == "call":
        setattr(item, "rep_call", rep)

## 📱 3. Architecture Connection Chain Guide

Below is the complete architectural flow showing how `conftest.py` feeds into `BaseWindow`, `BasePage`, `BaseScreen`, `BaseFlow`, and test cases:

```mermaid
graph TD
    A["conftest.py (Provides mobile, actions, waiter, soft_assert, test_data)"] --> B["BaseWindow & BasePage (Core System Controls: OK, Cancel, Save, Back)"]
    B --> C["BaseScreen (Screen Objects - e.g. LoginScreen, DashboardScreen)"]
    C --> D["BaseFlow (Business Flows - e.g. AuthFlow)"]
    D --> E["Test Cases (tests/test_login.py - Zero Boilerplate Functions)"]
    A --> E
```

### Step 1: `conftest.py` (Provides Automation Fixtures)
`conftest.py` automatically injects the `mobile`, `actions`, `waiter`, `soft_assert`, `api_client`, and `test_data` fixtures into every test run:

```python
# conftest.py
@pytest.fixture
def mobile():
    m = Mobile()
    m._executor.start_session()
    yield m
    m._executor.stop_session()
```

### Step 2: `BaseWindow` / `BasePage` (Common Controls & Shared Actions)
`BaseWindow` provides reusable system elements (OK, Cancel, Save, Back buttons) shared across all screens:

```python
# screens/base_window.py
from appium_client import BaseWindow

class AppBaseWindow(BaseWindow):
    def __init__(self, mobile):
        super().__init__(mobile)
        self.mobile = mobile

    @property
    def ok_button(self): return self.mobile.button("OK")
    @property
    def back_button(self): return self.mobile.button("Back")
```

### Step 3: `BaseScreen` (Screen Object Model for Target Screens)
`BaseScreen` represents a specific screen layout (e.g. `LoginScreen`):

```python
# screens/login_screen.py
from appium_client import BaseScreen

class LoginScreen(BaseScreen):
    SCREEN_IDENTIFIER = ".src.activities.DepotLoginActivity"

    def login(self, username: str, pin: str):
        self.verify_on_screen()
        self.mobile.fill("Username", username)
        self.mobile.fill("PIN", pin)
        self.mobile.tap("Login Button")
        return self
```

### Step 4: `BaseFlow` (Reusable Business User Journeys)
`BaseFlow` combines multiple screen interactions into reusable business end-to-end workflows:

```python
# flows/auth_flow.py
from appium_client import BaseFlow
from screens.login_screen import LoginScreen
from screens.dashboard_screen import DashboardScreen

class AuthFlow(BaseFlow):
    def complete_user_login(self, username: str, pin: str) -> DashboardScreen:
        login_screen = self.get_page(LoginScreen)
        login_screen.login(username, pin)
        return self.get_page(DashboardScreen)
```

### Step 5: `testcase` (Clean PyTest Function)
The test function ties the chain together:

```python
# tests/test_login.py
def test_user_authentication_flow(mobile, test_data):
    user = test_data.user()
    
    auth_flow = AuthFlow(mobile)
    dashboard = auth_flow.complete_user_login(user["username"], "1234")
    
    mobile.verify_contains_text("Welcome")
```

---

## 🧪 4. Writing Production-Ready Test Cases

Writing testcases in appium-client-python is clean, readable, and requires zero boilerplate.

### 4.1 Simple Testcase Example (`tests/test_login.py`)
```python
# tests/test_login.py
import pytest
from screens.login_screen import LoginScreen

def test_successful_login(mobile, test_data):
    """Test user login with generated test data."""
    user = test_data.user()  # Generates realistic test user
    
    login_screen = LoginScreen(mobile)
    login_screen.open_app("com.example.app")
    login_screen.login(user["username"], "1234")
    
    # Verify success
    mobile.verify_contains_text("Welcome")
```

### 4.2 Multi-Assertion Test with `soft_assert`
```python
def test_dashboard_widgets(mobile, soft_assert):
    mobile.launch_app("com.example.app")
    
    soft_assert.check_true(mobile.visible("Header Title"))
    soft_assert.check_true(mobile.visible("Navigation Menu"))
    soft_assert.check_contains(mobile.get_text("User Greeting"), "Hello")
    
    soft_assert.assert_all()  # Reports all failures at the end without stopping early
```

---

## 📊 5. Exhaustive Master API Reference Tables (All Methods Covered)

Below is the exhaustive API reference tables for **every single method** in the appium-client-python library.

---

### Table 5.1: Core `Mobile` API Engine (`mobile` fixture)

| Category | Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- | :--- |
| **App Lifecycle** | `launch_app(package_name=None)` | `mobile.launch_app("com.app")` | Opens the target application on device. |
| | `close_app(package_name=None)` | `mobile.close_app("com.app")` | Force stops the running application. |
| | `restart_app(package_name=None)` | `mobile.restart_app("com.app")` | Closes and re-launches the application. |
| | `install_app(app_path)` | `mobile.install_app("/path/app.apk")` | Installs an APK or IPA file on device. |
| | `uninstall_app(package_name)` | `mobile.uninstall_app("com.app")` | Removes the application from device. |
| | `clear_app_data(package_name=None)` | `mobile.clear_app_data("com.app")` | Resets app storage and cache back to clean state. |
| | `grant_permissions(pkg, perms)` | `mobile.grant_permissions("com.app", ["CAMERA"])` | Grants app system permissions automatically. |
| **Device & App State** | `get_device_info()` | `info = mobile.get_device_info()` | Returns model, OS version, manufacturer, and screen resolution. |
| | `get_current_app()` | `app = mobile.get_current_app()` | Returns package name and current activity name. |
| | `current_screen_name` | `screen = mobile.current_screen_name` | Property returning active activity name. |
| | `get_all_active_screens()` | `screens = mobile.get_all_active_screens()` | Returns list of all active activities. |
| | `set_orientation(mode)` | `mobile.set_orientation("landscape")` | Switches screen to portrait or landscape. |
| | `set_location(lat, lon)` | `mobile.set_location(37.77, -122.41)` | Spoofs device GPS location coordinates. |
| | `get_page_source()` | `xml = mobile.get_page_source()` | Returns raw UI hierarchy XML string. |
| **Touch Actions** | `tap(target)` | `mobile.tap("Login")` | Taps on element by text, ID, or label with auto-scroll. |
| | `double_tap(target)` / `double_click()` | `mobile.double_tap("Card")` | Performs two quick taps on target element. |
| | `long_press(target, duration=1000)` | `mobile.long_press("Item", duration=1000)` | Touches and holds target for specified milliseconds. |
| | `fill(target, value)` / `set_text()` | `mobile.fill("Username", "admin")` | Locates input field and types text into it. |
| | `clear(target)` / `clear_text()` | `mobile.clear("Search")` | Clears text content inside target input field. |
| | `send_keys(value)` / `type()` | `mobile.send_keys("hello")` | Types text directly into currently focused input. |
| | `press_key(key)` | `mobile.press_key("Enter")` | Simulates pressing hardware key (Enter, Back, Home). |
| | `press_back()` / `back()` | `mobile.press_back()` | Presses device Back button. |
| | `press_home()` / `home()` | `mobile.press_home()` | Presses device Home button. |
| | `hide_keyboard()` | `mobile.hide_keyboard()` | Dismisses soft keyboard if visible. |
| | `show_keyboard()` | `mobile.show_keyboard()` | Forces soft keyboard to open. |
| **Gestures** | `swipe(x1, y1, x2, y2, duration=300)`| `mobile.swipe(100, 800, 100, 200)` | Swipes from start coordinates to end coordinates. |
| | `swipe_left()` / `swipe_right()` | `mobile.swipe_left()` | Swipes screen left or right. |
| | `scroll_down()` / `scroll_up()` | `mobile.scroll_down()` | Scrolls screen down or up. |
| | `scroll_to_text(text, direction)` | `mobile.scroll_to_text("Terms")` | Scrolls down automatically until text appears. |
| | `scroll_to_element(target, direction)` | `mobile.scroll_to_element("Submit")` | Scrolls down automatically until element appears. |
| | `pinch()` / `zoom()` | `mobile.zoom()` | Performs pinch to zoom gesture. |
| | `drag_and_drop(src, dst)` | `mobile.drag_and_drop("Item", "Trash")` | Drags source element to destination element. |
| | `flick(x1, y1, x2, y2)` | `mobile.flick(500, 800, 500, 100)` | Fast flick gesture across coordinates. |
| | `multi_touch(points, duration=300)` | `mobile.multi_touch([(100,100),(200,200)])` | Executes multi-point touch gesture. |
| | `open_notifications()` | `mobile.open_notifications()` | Pulls down system notification shade. |
| | `open_quick_settings()` | `mobile.open_quick_settings()` | Pulls down quick settings panel. |
| | `open_widgets()` | `mobile.open_widgets()` | Opens device widgets menu. |
| **Waits & Verifications**| `wait_for(target, condition, timeout)`| `mobile.wait_for("Submit", timeout=15)`| Waits for element condition. |
| | `wait_for_element(target, timeout)` | `mobile.wait_for_element("Logo")` | Waits until element is present. |
| | `wait_until_visible(target, timeout)` | `mobile.wait_until_visible("Welcome")` | Polls until element is visible on screen. |
| | `wait_until_clickable(target, timeout)`| `mobile.wait_until_clickable("Next")` | Polls until element is enabled. |
| | `wait_until_gone(target, timeout)` | `mobile.wait_until_gone("Loader")` | Polls until element vanishes. |
| | `wait_for_loader_disappear(timeout)` | `mobile.wait_for_loader_disappear()` | Waits until progress bar or loading spinner disappears. |
| | `wait_for_page_ready(timeout)` | `mobile.wait_for_page_ready()` | Waits until UI stops changing. |
| | `visible(target)` / `is_visible()` | `mobile.visible("Dashboard")` | Returns True if element is currently visible. |
| | `enabled(target)` / `is_enabled()` | `mobile.enabled("Submit")` | Returns True if element is currently enabled. |
| | `verify_text(text)` | `mobile.verify_text("Saved")` | Asserts exact text is visible (raises AssertionError). |
| | `verify_contains_text(text)` | `mobile.verify_contains_text("Save")` | Asserts partial text is visible. |
| | `verify_element_present(target)` | `mobile.verify_element_present("Logo")` | Asserts target element exists. |
| | `verify_element_not_present(target)` | `mobile.verify_element_not_present("Error")`| Asserts target element does not exist. |
| | `find_text(text)` | `mobile.find_text("Success")` | Returns True if text is anywhere on screen. |
| **Discovery** | `inspect()` | `mobile.inspect()` | Dumps formatted UI tree text to terminal console. |
| | `find_element(target)` | `mobile.find_element("Username")` | Finds single UI element matching target. |
| | `find_elements(type_str=None)` | `mobile.find_elements(type_str="button")` | Returns list of all matching UI elements. |
| | `get_all_texts_displayed()` / `get_screen_text()` | `texts = mobile.get_all_texts_displayed()` | Returns list of all visible text strings on screen. |
| | `get_all_elements_displayed()` | `elements = mobile.get_all_elements_displayed()` | Returns list of detailed dictionaries with bounds, text, ID. |
| | `get_text(target)` / `get_all_text()` | `val = mobile.get_text("Price")` | Reads text content from element. |
| | `get_element_attributes(target)` | `attrs = mobile.get_element_attributes("Btn")` | Gets layout bounds, enabled state, average color, class. |
| | `get_focused_element()` | `active = mobile.get_focused_element()` | Gets properties of currently focused element. |
| | `get_active_element()` / `get_selected_element()` | `active = mobile.get_active_element()` | Alias for `get_focused_element()`. |
| **Fluent Elements** | `element(target)` | `mobile.element("Submit").tap()` | Returns fluent element wrapper for chained calls. |
| | `below("Label").input()` | `mobile.below("Username").input().fill("admin")` | Finds input located visually below target label. |
| | `above("Label").button()` | `mobile.above("Submit").button().tap()` | Finds button located visually above target label. |
| | `left_of("Label")` / `right_of("Label")` | `mobile.right_of("Search").input().fill("abc")` | Finds element to left or right of target. |
| | `button(target)` / `input(target)` | `mobile.button("OK").tap()` | Semantic button and input selectors. |
| | `checkbox(target)` | `mobile.checkbox("Remember").check()` | Semantic checkbox selector. |
| | `radio(target)` | `mobile.radio("Option A").tap()` | Semantic radio button selector. |
| | `dropdown(target)` | `mobile.dropdown("Country").select(option="India")` | Semantic dropdown selector by option text or index. |
| **Diagnostics & Media**| `screenshot(name)` / `take_screenshot()`| `mobile.screenshot("home_page")` | Saves PNG screenshot of full screen. |
| | `capture_element_screenshot(t, n)` | `mobile.capture_element_screenshot("Logo","snap")` | Crops screenshot specifically to target bounds. |
| | `start_screen_recording()` | `mobile.start_screen_recording()` | Starts capturing MP4 video recording of device screen. |
| | `stop_screen_recording(name)` | `mobile.stop_screen_recording("rec1")` | Stops video recording and saves to file. |
| | `visual_assert(baseline_name)` | `mobile.visual_assert("login_baseline")` | Compares current screen against baseline image. |
| | `capture_photo()` | `mobile.capture_photo()` | Triggers device camera shutter and confirms photo. |
| | `switch_camera()` | `mobile.switch_camera()` | Toggles camera between front and rear lenses. |
| | `diagnose()` | `mobile.diagnose()` | Prints real-time device health, CPU, and Memory report. |
| | `get_performance(package=None)` | `perf = mobile.get_performance()` | Returns CPU percentage and memory in MB. |
| | `get_logs()` / `get_crash_logs()` | `logs = mobile.get_crash_logs()` | Fetches device logcat or crash stack traces. |
| | `start_log_capture(path)` / `stop_log_capture()` | `mobile.start_log_capture("app.log")` | Captures background logcat stream to file. |
| | `start_network_capture(path)` / `stop_network_capture()` | `mobile.start_network_capture()` | Captures persistent background network traffic. |
| | `get_api_calls()` | `calls = mobile.get_api_calls()` | Returns list of captured API calls from network logs. |
| | `toggle_wifi(state)` | `mobile.toggle_wifi(False)` | Turns device Wi-Fi connection On or Off. |
| | `toggle_bluetooth(state)` | `mobile.toggle_bluetooth(True)` | Turns device Bluetooth On or Off. |
| | `signature.on(target).draw()` | `mobile.signature.on("Canvas").draw()` | Draws automated canvas signature. |
| | `find_by_image_click(template, threshold)` | `mobile.find_by_image_click("btn.png")` | Finds image template on screen and clicks it. |
| **Config Reader** | `get_config(key=None, default=None)` | `timeout = mobile.get_config("timeout")` | Reads configuration value from `appium_client.yaml` / env. |
| | `get_config_value(key, default=None)`| `path = mobile.get_config_value("tools.adb_path")` | Alias helper for `get_config(key, default)`. |

---

### Table 5.2: High-Level `Actions` Helper (`actions` fixture)

| Category | Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- | :--- |
| **Tap & Click** | `tap(locator, timeout=10.0)` | `actions.tap(("id", "com.app:id/btn"))` | Taps visible element matching locator. |
| | `tap_if_present(locator, timeout=2.0)` | `actions.tap_if_present(("id", "popup"))` | Taps element if visible within timeout, returns bool. |
| | `tap_if_present_first_available(locs)` | `actions.tap_if_present_first_available([loc1, loc2])` | Taps first available visible locator in list. |
| | `click_by_attribute_value(loc, attr, val)`| `actions.click_by_attribute_value(loc, "text", "OK")` | Taps element matching attribute value. |
| | `tap_by_coordinates(x, y)` | `actions.tap_by_coordinates(500, 1000)` | Taps screen at exact pixel coordinates. |
| | `double_tap(locator)` | `actions.double_tap(("id", "card"))` | Performs double tap on locator element. |
| | `long_press(locator, duration=2.0)` | `actions.long_press(("id", "item"))` | Long presses locator element. |
| **Text Entry** | `type_text(locator, text)` | `actions.type_text(("id", "input"), "text")` | Enters text into input field. |
| | `type_if_present(locator, text)` | `actions.type_if_present(("id", "input"), "text")` | Enters text if field is present, returns bool. |
| | `type_text_slowly(locator, text, delay)` | `actions.type_text_slowly(loc, "text", 0.1)` | Types text character by character. |
| | `clear(locator)` | `actions.clear(("id", "input"))` | Clears field text. |
| | `text(locator)` | `val = actions.text(("id", "title"))` | Returns visible text string from element. |
| | `get_attribute(locator, attr)` | `attr = actions.get_attribute(loc, "enabled")` | Reads attribute value from element. |
| **Assertions** | `is_displayed(locator)` | `if actions.is_displayed(("id", "btn")): ...` | Returns True if element is visible. |
| | `is_not_displayed(locator)` | `if actions.is_not_displayed(("id", "loader")): ...` | Returns True if element is absent or hidden. |
| | `is_present(locator)` | `if actions.is_present(("id", "item")): ...` | Returns True if element exists in DOM. |
| | `assert_displayed(locator)` | `actions.assert_displayed(("id", "title"))` | Asserts element is visible. |
| | `assert_text(locator, expected)` | `actions.assert_text(("id", "title"), "Welcome")` | Asserts element text equals expected string. |
| | `assert_text_contains(locator, partial)`| `actions.assert_text_contains(loc, "Welc")` | Asserts element text contains partial string. |
| | `assert_enabled(locator)` | `actions.assert_enabled(("id", "btn"))` | Asserts element is enabled. |
| | `assert_checked(locator)` | `actions.assert_checked(("id", "checkbox"))` | Asserts checkbox is checked. |
| | `count(locator)` | `cnt = actions.count(("xpath", "//button"))` | Returns number of matching elements. |
| | `assert_count(locator, count)` | `actions.assert_count(("xpath", "//item"), 5)` | Asserts exact count of matching elements. |
| **App Control** | `activate_app(app_id)` | `actions.activate_app("com.example.app")` | Launches or resumes specified app. |
| | `terminate_app(app_id)` | `actions.terminate_app("com.example.app")` | Terminates specified app. |
| | `background_app(seconds)` | `actions.background_app(3.0)` | Puts app in background for specified seconds. |
| | `reinstall_app(app_path)` | `actions.reinstall_app("app.apk")` | Uninstalls and reinstalls app. |
| | `open_deep_link(url)` | `actions.open_deep_link("myapp://profile")` | Triggers deep link URL. |
| | `switch_to_webview()` / `switch_to_native()`| `actions.switch_to_webview()` | Context switching for hybrid WebViews. |

---

### Table 5.3: Explicit `Waiter` Primitives (`waiter` fixture)

| Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- |
| `for_presence(locator, timeout=10.0)` | `waiter.for_presence(("id", "target"))` | Waits until element is present in DOM hierarchy. |
| `for_visibility(locator, timeout=10.0)` | `waiter.for_visibility(("id", "target"))` | Waits until element is visible on screen. |
| `for_clickable(locator, timeout=10.0)` | `waiter.for_clickable(("id", "target"))` | Waits until element is enabled and clickable. |
| `for_invisibility(locator, timeout=10.0)` | `waiter.for_invisibility(("id", "target"))` | Waits until element vanishes from screen. |
| `for_text_contains(locator, text, timeout)` | `waiter.for_text_contains(loc, "Saved")` | Waits until element text contains partial string. |
| `for_text_equals(locator, text, timeout)` | `waiter.for_text_equals(loc, "Saved")` | Waits until element text equals exact string. |
| `for_all_visible(locators, timeout=10.0)` | `waiter.for_all_visible([loc1, loc2])` | Waits until all listed locators are visible. |
| `for_all_gone(locators, timeout=10.0)` | `waiter.for_all_gone([loc1, loc2])` | Waits until all listed locators vanish. |
| `for_any_visible(locators, timeout=10.0)` | `waiter.for_any_visible([loc1, loc2])` | Waits until any one of listed locators appears. |
| `for_context_contains(text, timeout=10.0)` | `waiter.for_context_contains("WEBVIEW")` | Waits until Appium context contains specified text. |
| `for_android_activity(activity, timeout)` | `waiter.for_android_activity("MainActivity")` | Waits until Android activity opens. |
| `for_android_toast(text, timeout=10.0)` | `waiter.for_android_toast("Saved")` | Waits until Android toast message appears. |

---

### Table 5.4: Non-Blocking `SoftAssert` (`soft_assert` fixture)

| Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- |
| `check(condition, msg="")` | `soft_assert.check(1 < 2, "Math check")` | Evaluates boolean condition without stopping test. |
| `check_equal(actual, expected, msg="")` | `soft_assert.check_equal(name, "Alice")` | Evaluates equality between actual and expected. |
| `check_true(condition, msg="")` | `soft_assert.check_true(is_active)` | Asserts condition is True. |
| `check_false(condition, msg="")` | `soft_assert.check_false(is_locked)` | Asserts condition is False. |
| `check_in(member, container, msg="")` | `soft_assert.check_in("a", ["a", "b"])` | Asserts member exists inside container. |
| `check_not_none(obj, msg="")` | `soft_assert.check_not_none(result)` | Asserts object is not None. |
| `check_gt(val1, val2, msg="")` | `soft_assert.check_gt(val, 10)` | Asserts val1 is greater than val2. |
| `check_lt(val1, val2, msg="")` | `soft_assert.check_lt(val, 50)` | Asserts val1 is less than val2. |
| `check_contains(container, member, msg="")` | `soft_assert.check_contains(text, "App")` | Asserts container contains member string. |
| `assert_all()` | `soft_assert.assert_all()` | Flushes and raises AssertionError for all collected failures. |

---

### Table 5.5: REST API Client (`api_client` fixture)

| Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- |
| `get(endpoint, params, headers, status)` | `res = api_client.get("/users", expected_status=200)` | Sends HTTP GET request and verifies status code. |
| `post(endpoint, json_data, headers, status)`| `res = api_client.post("/users", json_data=body)` | Sends HTTP POST request with JSON payload. |
| `put(endpoint, json_data, headers, status)` | `res = api_client.put("/users/1", json_data=body)` | Sends HTTP PUT request for updating resources. |
| `delete(endpoint, headers, status)` | `res = api_client.delete("/users/1")` | Sends HTTP DELETE request to remove resource. |

---

### Table 5.6: Test Data Generators (`test_data` fixture)

| Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- |
| `user(**overrides)` | `u = test_data.user()` | Generates realistic user dict (id, email, username, phone, name). |
| `email(prefix="user", domain=None)` | `e = test_data.email()` | Generates unique email address string. |
| `phone(country_code="+1", length=10)` | `p = test_data.phone()` | Generates unique phone number string. |
| `username(prefix="testuser")` | `u = test_data.username()` | Generates unique username string. |
| `password(length=12)` | `p = test_data.password()` | Generates complex password meeting standard rules. |
| `full_name()` | `n = test_data.full_name()` | Generates random first and last name string. |
| `batch_users(count, **overrides)` | `users = test_data.batch_users(5)` | Generates list of N realistic user dictionaries. |
| `random_string(length=8, prefix="")` | `s = test_data.random_string(10)` | Generates random alphanumeric string of specified length. |
| `random_int(low=1, high=10000)` | `num = test_data.random_int(1, 100)` | Generates random integer within range. |
| `unique_id(prefix="")` | `uid = test_data.unique_id(prefix="req")` | Generates UUID4 short unique identifier. |
| `timestamp_id(prefix="ts")` | `ts = test_data.timestamp_id()` | Generates timestamp-suffixed unique string. |

---

### Table 5.7: Self-Healing Locators (`chain`)

| Function & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- |
| `chain(*locators, name=None)` | `btn = chain(("id", "b1"), ("xpath", "//btn"))` | Creates ordered strategy chain for self-healing element lookup. |

---

### Table 5.8: Configuration Reader Methods

| Method & Signature | Usage Example | Layman Description |
| :--- | :--- | :--- |
| `mobile.get_config(key=None, default=None)` | `timeout = mobile.get_config("timeout")` | Reads value(s) from `appium_client.yaml` or env settings. |
| `mobile.get_config_value(key, default)` | `path = mobile.get_config_value("tools.adb_path")` | Alias helper to read specific setting. |
| `load_config()` | `cfg = load_config()` | Standalone function returning `appium-client-pythonConfig` model. |
| `get_config_value(key, default=None)` | `adb = get_config_value("tools.adb_path", "adb")` | Standalone function reading specific config setting. |

---

## 🛠️ 6. CLI Commands Reference (`appium_client`)

appium-client-python provides a straightforward command-line interface:

| Command | Example | Layman Description |
| :--- | :--- | :--- |
| `init` / `setup` | `appium-client init --framework --root my-project` | Scaffolds a complete boilerplate project with `conftest.py`, `appium_client.yaml`, `screens/`, `flows/`, and `tests/`. |
| `run` | `appium-client run tests -n 4` | Runs PyTest test suite in parallel across devices. |
| `doctor` | `appium-client doctor` | Verifies ADB installation, dependencies, and environment setup. |
| `devices` | `appium-client devices` | Lists all connected Android ADB devices and iOS Simulators. |

---

© 2019 appium-client-python Framework — Production Mobile Automation Made Simple.
