Python client library for Appium mobile automation framework extending Selenium WebDriver with iOS and Android testing capabilities
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advanced capabilities including context switching, screen recording, image comparison, clipboard operations, settings management, and Flutter integration. These features enable sophisticated testing scenarios and cross-platform automation.
Switch between native app and web contexts for hybrid app testing and WebView automation.
@property
def contexts(self) -> list:
"""
Get list of available contexts (app and web views).
Returns:
list: List of context names (e.g., ['NATIVE_APP', 'WEBVIEW_1'])
"""
@property
def current_context(self) -> str:
"""
Get current active context name.
Returns:
str: Current context name
"""
@property
def context(self) -> str:
"""
Alias for current_context property.
Returns:
str: Current context name
"""
@context.setter
def context(self, context_name: str):
"""
Switch to specified context.
Args:
context_name (str): Context name to switch to
"""Record device screen during test execution for debugging and documentation purposes.
def start_recording_screen(self, **options):
"""
Start screen recording with specified options.
Args:
**options: Recording options
time_limit (int): Maximum recording time in seconds (default: 180)
video_type (str): Video format ('mp4', 'mov') - iOS only
video_quality (str): Video quality ('low', 'medium', 'high')
video_fps (int): Frames per second
video_scale (str): Video scale factor
video_size (str): Video dimensions (e.g., '1280x720')
bit_rate (int): Video bit rate - Android only
bug_report (bool): Include bug report - Android only
"""
def stop_recording_screen(self) -> str:
"""
Stop screen recording and get video data.
Returns:
str: Base64 encoded video data
"""Compare images and find sub-images within larger images for visual testing and element location.
def compare_images(self, base64_image1: str, base64_image2: str, **options) -> dict:
"""
Compare two images and get similarity metrics.
Args:
base64_image1 (str): First image as base64 string
base64_image2 (str): Second image as base64 string
**options: Comparison options
mode (str): Comparison mode ('matchFeatures', 'getSimilarity', 'matchTemplate')
threshold (float): Similarity threshold (0.0-1.0)
visualize (bool): Return visualization of differences
Returns:
dict: Comparison results with similarity score and visualization data
"""
def find_image_occurrence(self, full_image: str, partial_image: str, **options) -> dict:
"""
Find occurrence of partial image within full image.
Args:
full_image (str): Full image as base64 string
partial_image (str): Partial image to find as base64 string
**options: Search options
threshold (float): Match threshold (0.0-1.0)
multiple (bool): Find multiple occurrences
visualize (bool): Return visualization of matches
Returns:
dict: Match results with coordinates and confidence scores
"""Configure Appium server settings and behavior during test execution.
def get_settings(self) -> dict:
"""
Get current Appium settings.
Returns:
dict: Dictionary of current setting name-value pairs
"""
def update_settings(self, settings: dict):
"""
Update Appium settings.
Args:
settings (dict): Dictionary of setting name-value pairs to update
Examples:
{'waitForIdleTimeout': 1000}
{'shouldUseCompactResponses': False}
{'elementResponseAttributes': 'type,label'}
"""Execute platform-specific mobile commands not available through standard WebDriver API.
def execute_mobile_command(self, command: str, **params):
"""
Execute mobile-specific command.
Args:
command (str): Mobile command name (e.g., 'shell', 'deepLink', 'startPerfRecord')
**params: Command-specific parameters
Returns:
Any: Command execution result (varies by command)
"""Specialized finders and commands for Flutter app testing.
class FlutterFinder:
@staticmethod
def by_key(value: str):
"""
Find Flutter element by key.
Args:
value (str): Flutter key value
Returns:
FlutterFinder: Finder instance for element location
"""
@staticmethod
def by_text(value: str):
"""
Find Flutter element by text content.
Args:
value (str): Text content to find
Returns:
FlutterFinder: Finder instance for element location
"""
@staticmethod
def by_semantics_label(value: str):
"""
Find Flutter element by semantics label.
Args:
value (str): Semantics label value
Returns:
FlutterFinder: Finder instance for element location
"""
@staticmethod
def by_type(value: str):
"""
Find Flutter element by widget type.
Args:
value (str): Flutter widget type name
Returns:
FlutterFinder: Finder instance for element location
"""
@staticmethod
def by_text_containing(value: str):
"""
Find Flutter element by partial text match.
Args:
value (str): Partial text to match
Returns:
FlutterFinder: Finder instance for element location
"""from appium import webdriver
from appium.options.android import UiAutomator2Options
from selenium.webdriver.common.by import By
# Setup for hybrid app testing
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "Android Emulator"
options.app = "/path/to/hybrid/app.apk"
driver = webdriver.Remote("http://localhost:4723", options=options)
# Check available contexts
print("Available contexts:", driver.contexts)
print("Current context:", driver.current_context)
# Switch to WebView context
webview_contexts = [ctx for ctx in driver.contexts if 'WEBVIEW' in ctx]
if webview_contexts:
driver.context = webview_contexts[0]
print(f"Switched to context: {driver.current_context}")
# Now use standard web element finding
web_element = driver.find_element(By.ID, "web-button")
web_element.click()
# Switch back to native context
driver.context = "NATIVE_APP"
print(f"Switched back to: {driver.current_context}")
# Use mobile locators again
from appium.webdriver.common.appiumby import AppiumBy
native_element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "native-button")
native_element.click()import base64
import time
def record_test_scenario(driver, test_name):
"""Record a test scenario with automatic file saving."""
try:
# Start recording with options
driver.start_recording_screen(
time_limit=300, # 5 minutes max
video_quality="medium",
video_fps=10
)
print(f"Started recording for {test_name}")
# Perform test actions
yield # This allows the calling code to run test steps
finally:
# Stop recording and save
try:
video_data = driver.stop_recording_screen()
# Decode and save video
video_bytes = base64.b64decode(video_data)
filename = f"{test_name}_{int(time.time())}.mp4"
with open(filename, 'wb') as f:
f.write(video_bytes)
print(f"Recording saved as {filename}")
except Exception as e:
print(f"Failed to save recording: {e}")
# Usage with context manager pattern
with record_test_scenario(driver, "login_test"):
# Perform login test steps
username_field = driver.find_element("id", "username")
username_field.send_keys("testuser")
password_field = driver.find_element("id", "password")
password_field.send_keys("password123")
login_button = driver.find_element("id", "login")
login_button.click()
time.sleep(2) # Wait for login to completeimport base64
import cv2
import numpy as np
def capture_element_screenshot(driver, element):
"""Capture screenshot of specific element."""
# Take full screenshot
screenshot = driver.get_screenshot_as_png()
# Get element location and size
location = element.location
size = element.size
# Convert to numpy array
nparr = np.frombuffer(screenshot, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# Crop to element bounds
x, y = location['x'], location['y']
w, h = size['width'], size['height']
element_img = img[y:y+h, x:x+w]
# Convert back to base64
_, buffer = cv2.imencode('.png', element_img)
return base64.b64encode(buffer).decode('utf-8')
def visual_regression_test(driver, expected_image_path, element):
"""Perform visual regression testing on element."""
# Capture current element appearance
current_image = capture_element_screenshot(driver, element)
# Load expected image
with open(expected_image_path, 'rb') as f:
expected_image = base64.b64encode(f.read()).decode('utf-8')
# Compare images
comparison_result = driver.compare_images(
expected_image,
current_image,
mode='getSimilarity',
threshold=0.95
)
similarity = comparison_result.get('score', 0)
print(f"Visual similarity: {similarity:.2%}")
if similarity < 0.95:
print("Visual regression detected!")
# Save diff image if available
if 'visualization' in comparison_result:
save_diff_image(comparison_result['visualization'])
return False
return True
def find_ui_element_by_image(driver, template_image_path):
"""Find UI element using image template matching."""
# Take screenshot
screenshot = driver.get_screenshot_as_base64()
# Load template image
with open(template_image_path, 'rb') as f:
template_image = base64.b64encode(f.read()).decode('utf-8')
# Find image occurrence
result = driver.find_image_occurrence(
screenshot,
template_image,
threshold=0.8,
visualize=True
)
if result.get('matches'):
match = result['matches'][0] # Get first match
center_x = match['x'] + match['width'] // 2
center_y = match['y'] + match['height'] // 2
# Tap at found location
driver.tap([(center_x, center_y)])
return True
return Falsedef optimize_test_performance(driver):
"""Optimize Appium settings for faster test execution."""
# Get current settings
current_settings = driver.get_settings()
print("Current settings:", current_settings)
# Update settings for performance
performance_settings = {
'waitForIdleTimeout': 1000, # Reduce idle wait time
'waitForSelectorTimeout': 5000, # Reduce selector timeout
'shouldUseCompactResponses': True, # Use compact JSON responses
'elementResponseAttributes': 'type,name,label,enabled,visible', # Limit response data
'mjpegServerScreenshotQuality': 50, # Lower screenshot quality for speed
'mjpegScalingFactor': 50 # Scale down screenshots
}
driver.update_settings(performance_settings)
print("Applied performance optimizations")
# Verify settings were applied
updated_settings = driver.get_settings()
print("Updated settings:", updated_settings)
def configure_debug_settings(driver):
"""Configure settings for debugging and troubleshooting."""
debug_settings = {
'waitForIdleTimeout': 10000, # Longer idle timeout for debugging
'waitForSelectorTimeout': 30000, # Longer selector timeout
'shouldUseCompactResponses': False, # Full responses for debugging
'elementResponseAttributes': '*', # All element attributes
'screenshotQuality': 100, # High quality screenshots
'mjpegScalingFactor': 100 # Full resolution screenshots
}
driver.update_settings(debug_settings)
print("Applied debug settings")def execute_platform_commands(driver):
"""Execute various platform-specific mobile commands."""
# Android shell commands
if driver.capabilities.get('platformName').lower() == 'android':
# Execute shell command
result = driver.execute_mobile_command('shell', command='getprop ro.build.version.release')
print(f"Android version: {result}")
# Deep link
driver.execute_mobile_command('deepLink',
url='myapp://profile/123',
package='com.example.myapp')
# Start performance recording
driver.execute_mobile_command('startPerfRecord',
timeout=30000,
profileName='cpu')
# Perform actions to measure
perform_cpu_intensive_actions()
# Stop performance recording
perf_data = driver.execute_mobile_command('stopPerfRecord')
save_performance_data(perf_data)
# iOS specific commands
elif driver.capabilities.get('platformName').lower() == 'ios':
# Siri command
driver.execute_mobile_command('siri', text='What time is it?')
# Face ID simulation
driver.execute_mobile_command('enrollBiometric', isEnabled=True)
driver.execute_mobile_command('sendBiometricMatch', type='faceId', match=True)
# Pasteboard access
driver.execute_mobile_command('setPasteboard', content='Test clipboard content')
clipboard_content = driver.execute_mobile_command('getPasteboard')
print(f"Clipboard: {clipboard_content}")
def perform_cpu_intensive_actions():
"""Placeholder for CPU intensive test actions."""
pass
def save_performance_data(data):
"""Save performance data for analysis."""
passfrom appium.webdriver.extensions.flutter_integration.flutter_finder import FlutterFinder
def flutter_testing_example(driver):
"""Example of Flutter-specific testing."""
# Find elements using Flutter finders
submit_button = driver.find_element(
AppiumBy.FLUTTER_INTEGRATION_KEY,
FlutterFinder.by_key('submit_button')
)
submit_button.click()
# Find by text content
welcome_text = driver.find_element(
AppiumBy.FLUTTER_INTEGRATION_TEXT,
FlutterFinder.by_text('Welcome to the app')
)
assert welcome_text.is_displayed()
# Find by widget type
text_fields = driver.find_elements(
AppiumBy.FLUTTER_INTEGRATION_TYPE,
FlutterFinder.by_type('TextField')
)
# Fill in all text fields
test_data = ['username', 'password', 'email@example.com']
for i, field in enumerate(text_fields):
if i < len(test_data):
field.send_keys(test_data[i])
# Find by semantics label
navigation_button = driver.find_element(
AppiumBy.FLUTTER_INTEGRATION_SEMANTICS_LABEL,
FlutterFinder.by_semantics_label('Navigate to next page')
)
navigation_button.click()
# Find by partial text
help_elements = driver.find_elements(
AppiumBy.FLUTTER_INTEGRATION_TEXT_CONTAINING,
FlutterFinder.by_text_containing('Help')
)
for help_element in help_elements:
print(f"Help element found: {help_element.text}")# Context types
ContextList = List[str] # List of context names
ContextName = str # Context identifier (e.g., 'NATIVE_APP', 'WEBVIEW_1')
# Recording types
RecordingOptions = Dict[str, Union[int, str, bool]]
VideoData = str # Base64 encoded video data
RecordingTimeLimit = int # Maximum recording time in seconds
# Image types
Base64Image = str # Base64 encoded image data
ImagePath = str # File path to image
SimilarityScore = float # 0.0 to 1.0 similarity score
MatchThreshold = float # 0.0 to 1.0 match threshold
# Comparison results
ComparisonResult = Dict[str, Union[float, str, bool]]
OccurrenceResult = Dict[str, Union[List[dict], str, bool]]
# Settings types
SettingsDict = Dict[str, Union[str, int, bool]]
SettingName = str
SettingValue = Union[str, int, bool]
# Mobile command types
MobileCommand = str # Command name
CommandParams = Dict[str, Union[str, int, bool, List]]
CommandResult = Union[str, dict, bool, None]
# Flutter types
FlutterKey = str # Flutter widget key
FlutterText = str # Flutter widget text
FlutterType = str # Flutter widget type
SemanticsLabel = str # Flutter semantics labelInstall with Tessl CLI
npx tessl i tessl/pypi-appium-python-client