Appium Automation Contract

SkillDev tools

Appium launch contract, start routes, state presets, and wrong-screen test debugging.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Appium Automation Contract skill

What this skill tells your AI

The instructions your AI receives, as published by po4yka/ripdpi in .agents/skills/appium-automation-contract/SKILL.md and read by ahel’s review.

The automation contract launches the app directly to a specific screen with controlled state, bypassing normal navigation. It is the foundation for every Appium test.

Source of truth: Kotlin AutomationLaunchContract.kt in app/src/main/kotlin/com/poyka/ripdpi/automation/. Python mirror: appium/lib/launch_contract.py.

Launch Flow

conftest.py::launch_app (autouse fixture)
  1. Read @pytest.mark.automation(...) kwargs from test
  2. adb am force-stop com.poyka.ripdpi
  3. sleep 0.5s (process teardown settle)
  4. adb am start -n com.poyka.ripdpi/.activities.MainActivity \
       --ez com.poyka.ripdpi.automation.ENABLED true \
       --es com.poyka.ripdpi.automation.START_ROUTE {route} \
       ... (remaining extras)
  5. wait_for_element(driver, "{route}-screen", timeout=15)
  6. yield (test runs)
  7. Screenshot on failure -> screenshots/{test_name}.png

Intent Extras

Python ConstantExtra KeyTypeDefaultPurpose
ENABLED...automation.ENABLEDbooltrueActivate automation mode
RESET_STATE...automation.RESET_STATEbooltrueClear persisted state before launch
DISABLE_MOTION...automation.DISABLE_MOTIONbooltrueDisable Compose animations
START_ROUTE...automation.START_ROUTEstring"home"Target screen
PERMISSION_PRESET...automation.PERMISSION_PRESETstring"granted"Simulated permission state
SERVICE_PRESET...automation.SERVICE_PRESETstring"idle"Simulated service state
DATA_PRESET...automation.DATA_PRESETstring"clean_home"Pre-populated data fixtures

All extras are prefixed with com.poyka.ripdpi.automation..

Preset Reference

start_route

RouteScreen Resource IDTypical data_preset
homehome-screenclean_home
onboardingonboarding-screenclean_home
configconfig-screensettings_ready
diagnosticsdiagnostics-screendiagnostics_demo
historyhistory-screensettings_ready
logslogs-screensettings_ready
settingssettings-screensettings_ready
mode_editormode_editor-screensettings_ready
dns_settingsdns_settings-screensettings_ready
advanced_settingsadvanced_settings-screensettings_ready
aboutabout-screensettings_ready
data_transparencydata_transparency-screensettings_ready
app_customizationapp_customization-screensettings_ready

permission_preset

ValueSimulates
grantedAll permissions granted (default)
vpn_missingVPN permission not granted
notifications_missingNotification permission not granted
battery_reviewBattery optimization not disabled

service_preset

ValueSimulates
idleService not running (default)
connected_proxyProxy service connected
connected_vpnVPN service connected
liveLive connection with real service

data_preset

ValueSimulates
clean_homeFresh install state (default)
settings_readySettings populated, configs available
diagnostics_demoDemo diagnostic data pre-loaded

Marker Usage

# Minimal -- all defaults (home screen, granted, idle, clean_home)
@pytest.mark.automation()
def test_home_loads(driver): ...

# Deep-link to sub-screen with populated data
@pytest.mark.automation(
    start_route="dns_settings",
    data_preset="settings_ready",
)
def test_dns_plain_save(driver): ...

# Permission-denied scenario
@pytest.mark.automation(
    start_route="home",
    permission_preset="vpn_missing",
)
def test_vpn_permission_banner(driver): ...

# Connected state testing
@pytest.mark.automation(
    start_route="home",
    service_preset="connected_vpn",
    data_preset="clean_home",
)
def test_connected_vpn_stats(driver): ...

All parameters are keyword-only. Omitted params use defaults shown in the Intent Extras table.

Adding a New Route

  1. Add route string to Kotlin Route sealed class
  2. Add handling in AutomationLaunchContract.kt to navigate to the new screen
  3. Set Modifier.testTag("{route}-screen") on the screen's root composable
  4. Use @pytest.mark.automation(start_route="{route}") in the Python test
  5. The conftest.py wait (wait_for_element(driver, f"{route}-screen", timeout=15)) works automatically

Common Mistakes

MistakeFix
Forgetting reset_state=TruePrevious test's data leaks in. Default is True -- only set False if intentionally preserving state.
Wrong data_preset for the screenScreen renders empty or missing elements. Match route to its typical preset (see table above).
Omitting disable_motion=TrueAnimations cause timing issues, especially in CI. Default is True -- only set False to test animations.
start_route value not in Kotlin RouteApp silently falls back to home screen. Verify the route exists in Route.kt.
Screen resource-id doesn't match {route}-screenwait_for_element times out on launch. The convention is {route}-screen.
Testing connected state with service_preset="idle"Stats/metrics cards won't be visible. Use connected_vpn or connected_proxy.

See Also

  • .github/skills/appium-test-authoring/SKILL.md -- How to write tests using the automation marker
  • .github/skills/appium-test-debug/SKILL.md -- Troubleshooting when launch fails
  • .github/skills/android-device-debug/SKILL.md -- Raw ADB commands for device interaction

Signals

GitHub stars
69
Forks
4
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
appium-automation-contract
Source
github.com/po4yka/ripdpi