0
# Advanced Features
1
2
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.
3
4
## Capabilities
5
6
### Context Management
7
8
Switch between native app and web contexts for hybrid app testing and WebView automation.
9
10
```python {.api}
11
@property
12
def contexts(self) -> list:
13
"""
14
Get list of available contexts (app and web views).
15
16
Returns:
17
list: List of context names (e.g., ['NATIVE_APP', 'WEBVIEW_1'])
18
"""
19
20
@property
21
def current_context(self) -> str:
22
"""
23
Get current active context name.
24
25
Returns:
26
str: Current context name
27
"""
28
29
@property
30
def context(self) -> str:
31
"""
32
Alias for current_context property.
33
34
Returns:
35
str: Current context name
36
"""
37
38
@context.setter
39
def context(self, context_name: str):
40
"""
41
Switch to specified context.
42
43
Args:
44
context_name (str): Context name to switch to
45
"""
46
```
47
48
### Screen Recording
49
50
Record device screen during test execution for debugging and documentation purposes.
51
52
```python {.api}
53
def start_recording_screen(self, **options):
54
"""
55
Start screen recording with specified options.
56
57
Args:
58
**options: Recording options
59
time_limit (int): Maximum recording time in seconds (default: 180)
60
video_type (str): Video format ('mp4', 'mov') - iOS only
61
video_quality (str): Video quality ('low', 'medium', 'high')
62
video_fps (int): Frames per second
63
video_scale (str): Video scale factor
64
video_size (str): Video dimensions (e.g., '1280x720')
65
bit_rate (int): Video bit rate - Android only
66
bug_report (bool): Include bug report - Android only
67
"""
68
69
def stop_recording_screen(self) -> str:
70
"""
71
Stop screen recording and get video data.
72
73
Returns:
74
str: Base64 encoded video data
75
"""
76
```
77
78
### Image Comparison
79
80
Compare images and find sub-images within larger images for visual testing and element location.
81
82
```python {.api}
83
def compare_images(self, base64_image1: str, base64_image2: str, **options) -> dict:
84
"""
85
Compare two images and get similarity metrics.
86
87
Args:
88
base64_image1 (str): First image as base64 string
89
base64_image2 (str): Second image as base64 string
90
**options: Comparison options
91
mode (str): Comparison mode ('matchFeatures', 'getSimilarity', 'matchTemplate')
92
threshold (float): Similarity threshold (0.0-1.0)
93
visualize (bool): Return visualization of differences
94
95
Returns:
96
dict: Comparison results with similarity score and visualization data
97
"""
98
99
def find_image_occurrence(self, full_image: str, partial_image: str, **options) -> dict:
100
"""
101
Find occurrence of partial image within full image.
102
103
Args:
104
full_image (str): Full image as base64 string
105
partial_image (str): Partial image to find as base64 string
106
**options: Search options
107
threshold (float): Match threshold (0.0-1.0)
108
multiple (bool): Find multiple occurrences
109
visualize (bool): Return visualization of matches
110
111
Returns:
112
dict: Match results with coordinates and confidence scores
113
"""
114
```
115
116
### Settings Management
117
118
Configure Appium server settings and behavior during test execution.
119
120
```python {.api}
121
def get_settings(self) -> dict:
122
"""
123
Get current Appium settings.
124
125
Returns:
126
dict: Dictionary of current setting name-value pairs
127
"""
128
129
def update_settings(self, settings: dict):
130
"""
131
Update Appium settings.
132
133
Args:
134
settings (dict): Dictionary of setting name-value pairs to update
135
Examples:
136
{'waitForIdleTimeout': 1000}
137
{'shouldUseCompactResponses': False}
138
{'elementResponseAttributes': 'type,label'}
139
"""
140
```
141
142
### Execute Mobile Commands
143
144
Execute platform-specific mobile commands not available through standard WebDriver API.
145
146
```python {.api}
147
def execute_mobile_command(self, command: str, **params):
148
"""
149
Execute mobile-specific command.
150
151
Args:
152
command (str): Mobile command name (e.g., 'shell', 'deepLink', 'startPerfRecord')
153
**params: Command-specific parameters
154
155
Returns:
156
Any: Command execution result (varies by command)
157
"""
158
```
159
160
### Flutter Integration
161
162
Specialized finders and commands for Flutter app testing.
163
164
```python {.api}
165
class FlutterFinder:
166
@staticmethod
167
def by_key(value: str):
168
"""
169
Find Flutter element by key.
170
171
Args:
172
value (str): Flutter key value
173
174
Returns:
175
FlutterFinder: Finder instance for element location
176
"""
177
178
@staticmethod
179
def by_text(value: str):
180
"""
181
Find Flutter element by text content.
182
183
Args:
184
value (str): Text content to find
185
186
Returns:
187
FlutterFinder: Finder instance for element location
188
"""
189
190
@staticmethod
191
def by_semantics_label(value: str):
192
"""
193
Find Flutter element by semantics label.
194
195
Args:
196
value (str): Semantics label value
197
198
Returns:
199
FlutterFinder: Finder instance for element location
200
"""
201
202
@staticmethod
203
def by_type(value: str):
204
"""
205
Find Flutter element by widget type.
206
207
Args:
208
value (str): Flutter widget type name
209
210
Returns:
211
FlutterFinder: Finder instance for element location
212
"""
213
214
@staticmethod
215
def by_text_containing(value: str):
216
"""
217
Find Flutter element by partial text match.
218
219
Args:
220
value (str): Partial text to match
221
222
Returns:
223
FlutterFinder: Finder instance for element location
224
"""
225
```
226
227
## Usage Examples
228
229
### Context Switching for Hybrid Apps
230
231
```python
232
from appium import webdriver
233
from appium.options.android import UiAutomator2Options
234
from selenium.webdriver.common.by import By
235
236
# Setup for hybrid app testing
237
options = UiAutomator2Options()
238
options.platform_name = "Android"
239
options.device_name = "Android Emulator"
240
options.app = "/path/to/hybrid/app.apk"
241
driver = webdriver.Remote("http://localhost:4723", options=options)
242
243
# Check available contexts
244
print("Available contexts:", driver.contexts)
245
print("Current context:", driver.current_context)
246
247
# Switch to WebView context
248
webview_contexts = [ctx for ctx in driver.contexts if 'WEBVIEW' in ctx]
249
if webview_contexts:
250
driver.context = webview_contexts[0]
251
print(f"Switched to context: {driver.current_context}")
252
253
# Now use standard web element finding
254
web_element = driver.find_element(By.ID, "web-button")
255
web_element.click()
256
257
# Switch back to native context
258
driver.context = "NATIVE_APP"
259
print(f"Switched back to: {driver.current_context}")
260
261
# Use mobile locators again
262
from appium.webdriver.common.appiumby import AppiumBy
263
native_element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "native-button")
264
native_element.click()
265
```
266
267
### Screen Recording for Test Documentation
268
269
```python
270
import base64
271
import time
272
273
def record_test_scenario(driver, test_name):
274
"""Record a test scenario with automatic file saving."""
275
276
try:
277
# Start recording with options
278
driver.start_recording_screen(
279
time_limit=300, # 5 minutes max
280
video_quality="medium",
281
video_fps=10
282
)
283
284
print(f"Started recording for {test_name}")
285
286
# Perform test actions
287
yield # This allows the calling code to run test steps
288
289
finally:
290
# Stop recording and save
291
try:
292
video_data = driver.stop_recording_screen()
293
294
# Decode and save video
295
video_bytes = base64.b64decode(video_data)
296
filename = f"{test_name}_{int(time.time())}.mp4"
297
298
with open(filename, 'wb') as f:
299
f.write(video_bytes)
300
301
print(f"Recording saved as {filename}")
302
303
except Exception as e:
304
print(f"Failed to save recording: {e}")
305
306
# Usage with context manager pattern
307
with record_test_scenario(driver, "login_test"):
308
# Perform login test steps
309
username_field = driver.find_element("id", "username")
310
username_field.send_keys("testuser")
311
312
password_field = driver.find_element("id", "password")
313
password_field.send_keys("password123")
314
315
login_button = driver.find_element("id", "login")
316
login_button.click()
317
318
time.sleep(2) # Wait for login to complete
319
```
320
321
### Image-Based Testing and Validation
322
323
```python
324
import base64
325
import cv2
326
import numpy as np
327
328
def capture_element_screenshot(driver, element):
329
"""Capture screenshot of specific element."""
330
# Take full screenshot
331
screenshot = driver.get_screenshot_as_png()
332
333
# Get element location and size
334
location = element.location
335
size = element.size
336
337
# Convert to numpy array
338
nparr = np.frombuffer(screenshot, np.uint8)
339
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
340
341
# Crop to element bounds
342
x, y = location['x'], location['y']
343
w, h = size['width'], size['height']
344
element_img = img[y:y+h, x:x+w]
345
346
# Convert back to base64
347
_, buffer = cv2.imencode('.png', element_img)
348
return base64.b64encode(buffer).decode('utf-8')
349
350
def visual_regression_test(driver, expected_image_path, element):
351
"""Perform visual regression testing on element."""
352
353
# Capture current element appearance
354
current_image = capture_element_screenshot(driver, element)
355
356
# Load expected image
357
with open(expected_image_path, 'rb') as f:
358
expected_image = base64.b64encode(f.read()).decode('utf-8')
359
360
# Compare images
361
comparison_result = driver.compare_images(
362
expected_image,
363
current_image,
364
mode='getSimilarity',
365
threshold=0.95
366
)
367
368
similarity = comparison_result.get('score', 0)
369
print(f"Visual similarity: {similarity:.2%}")
370
371
if similarity < 0.95:
372
print("Visual regression detected!")
373
# Save diff image if available
374
if 'visualization' in comparison_result:
375
save_diff_image(comparison_result['visualization'])
376
return False
377
378
return True
379
380
def find_ui_element_by_image(driver, template_image_path):
381
"""Find UI element using image template matching."""
382
383
# Take screenshot
384
screenshot = driver.get_screenshot_as_base64()
385
386
# Load template image
387
with open(template_image_path, 'rb') as f:
388
template_image = base64.b64encode(f.read()).decode('utf-8')
389
390
# Find image occurrence
391
result = driver.find_image_occurrence(
392
screenshot,
393
template_image,
394
threshold=0.8,
395
visualize=True
396
)
397
398
if result.get('matches'):
399
match = result['matches'][0] # Get first match
400
center_x = match['x'] + match['width'] // 2
401
center_y = match['y'] + match['height'] // 2
402
403
# Tap at found location
404
driver.tap([(center_x, center_y)])
405
return True
406
407
return False
408
```
409
410
### Settings and Performance Optimization
411
412
```python
413
def optimize_test_performance(driver):
414
"""Optimize Appium settings for faster test execution."""
415
416
# Get current settings
417
current_settings = driver.get_settings()
418
print("Current settings:", current_settings)
419
420
# Update settings for performance
421
performance_settings = {
422
'waitForIdleTimeout': 1000, # Reduce idle wait time
423
'waitForSelectorTimeout': 5000, # Reduce selector timeout
424
'shouldUseCompactResponses': True, # Use compact JSON responses
425
'elementResponseAttributes': 'type,name,label,enabled,visible', # Limit response data
426
'mjpegServerScreenshotQuality': 50, # Lower screenshot quality for speed
427
'mjpegScalingFactor': 50 # Scale down screenshots
428
}
429
430
driver.update_settings(performance_settings)
431
print("Applied performance optimizations")
432
433
# Verify settings were applied
434
updated_settings = driver.get_settings()
435
print("Updated settings:", updated_settings)
436
437
def configure_debug_settings(driver):
438
"""Configure settings for debugging and troubleshooting."""
439
440
debug_settings = {
441
'waitForIdleTimeout': 10000, # Longer idle timeout for debugging
442
'waitForSelectorTimeout': 30000, # Longer selector timeout
443
'shouldUseCompactResponses': False, # Full responses for debugging
444
'elementResponseAttributes': '*', # All element attributes
445
'screenshotQuality': 100, # High quality screenshots
446
'mjpegScalingFactor': 100 # Full resolution screenshots
447
}
448
449
driver.update_settings(debug_settings)
450
print("Applied debug settings")
451
```
452
453
### Mobile Command Execution
454
455
```python
456
def execute_platform_commands(driver):
457
"""Execute various platform-specific mobile commands."""
458
459
# Android shell commands
460
if driver.capabilities.get('platformName').lower() == 'android':
461
# Execute shell command
462
result = driver.execute_mobile_command('shell', command='getprop ro.build.version.release')
463
print(f"Android version: {result}")
464
465
# Deep link
466
driver.execute_mobile_command('deepLink',
467
url='myapp://profile/123',
468
package='com.example.myapp')
469
470
# Start performance recording
471
driver.execute_mobile_command('startPerfRecord',
472
timeout=30000,
473
profileName='cpu')
474
475
# Perform actions to measure
476
perform_cpu_intensive_actions()
477
478
# Stop performance recording
479
perf_data = driver.execute_mobile_command('stopPerfRecord')
480
save_performance_data(perf_data)
481
482
# iOS specific commands
483
elif driver.capabilities.get('platformName').lower() == 'ios':
484
# Siri command
485
driver.execute_mobile_command('siri', text='What time is it?')
486
487
# Face ID simulation
488
driver.execute_mobile_command('enrollBiometric', isEnabled=True)
489
driver.execute_mobile_command('sendBiometricMatch', type='faceId', match=True)
490
491
# Pasteboard access
492
driver.execute_mobile_command('setPasteboard', content='Test clipboard content')
493
clipboard_content = driver.execute_mobile_command('getPasteboard')
494
print(f"Clipboard: {clipboard_content}")
495
496
def perform_cpu_intensive_actions():
497
"""Placeholder for CPU intensive test actions."""
498
pass
499
500
def save_performance_data(data):
501
"""Save performance data for analysis."""
502
pass
503
```
504
505
### Flutter Integration Testing
506
507
```python
508
from appium.webdriver.extensions.flutter_integration.flutter_finder import FlutterFinder
509
510
def flutter_testing_example(driver):
511
"""Example of Flutter-specific testing."""
512
513
# Find elements using Flutter finders
514
submit_button = driver.find_element(
515
AppiumBy.FLUTTER_INTEGRATION_KEY,
516
FlutterFinder.by_key('submit_button')
517
)
518
submit_button.click()
519
520
# Find by text content
521
welcome_text = driver.find_element(
522
AppiumBy.FLUTTER_INTEGRATION_TEXT,
523
FlutterFinder.by_text('Welcome to the app')
524
)
525
assert welcome_text.is_displayed()
526
527
# Find by widget type
528
text_fields = driver.find_elements(
529
AppiumBy.FLUTTER_INTEGRATION_TYPE,
530
FlutterFinder.by_type('TextField')
531
)
532
533
# Fill in all text fields
534
test_data = ['username', 'password', 'email@example.com']
535
for i, field in enumerate(text_fields):
536
if i < len(test_data):
537
field.send_keys(test_data[i])
538
539
# Find by semantics label
540
navigation_button = driver.find_element(
541
AppiumBy.FLUTTER_INTEGRATION_SEMANTICS_LABEL,
542
FlutterFinder.by_semantics_label('Navigate to next page')
543
)
544
navigation_button.click()
545
546
# Find by partial text
547
help_elements = driver.find_elements(
548
AppiumBy.FLUTTER_INTEGRATION_TEXT_CONTAINING,
549
FlutterFinder.by_text_containing('Help')
550
)
551
552
for help_element in help_elements:
553
print(f"Help element found: {help_element.text}")
554
```
555
556
## Types
557
558
```python {.api}
559
# Context types
560
ContextList = List[str] # List of context names
561
ContextName = str # Context identifier (e.g., 'NATIVE_APP', 'WEBVIEW_1')
562
563
# Recording types
564
RecordingOptions = Dict[str, Union[int, str, bool]]
565
VideoData = str # Base64 encoded video data
566
RecordingTimeLimit = int # Maximum recording time in seconds
567
568
# Image types
569
Base64Image = str # Base64 encoded image data
570
ImagePath = str # File path to image
571
SimilarityScore = float # 0.0 to 1.0 similarity score
572
MatchThreshold = float # 0.0 to 1.0 match threshold
573
574
# Comparison results
575
ComparisonResult = Dict[str, Union[float, str, bool]]
576
OccurrenceResult = Dict[str, Union[List[dict], str, bool]]
577
578
# Settings types
579
SettingsDict = Dict[str, Union[str, int, bool]]
580
SettingName = str
581
SettingValue = Union[str, int, bool]
582
583
# Mobile command types
584
MobileCommand = str # Command name
585
CommandParams = Dict[str, Union[str, int, bool, List]]
586
CommandResult = Union[str, dict, bool, None]
587
588
# Flutter types
589
FlutterKey = str # Flutter widget key
590
FlutterText = str # Flutter widget text
591
FlutterType = str # Flutter widget type
592
SemanticsLabel = str # Flutter semantics label
593
```