0
# App Snapshots
1
2
Specialized commands for visual testing of native mobile applications with Percy's app testing platform. These commands provide Percy's visual testing capabilities for iOS and Android applications.
3
4
## Capabilities
5
6
### Percy App Exec
7
8
Execute command with Percy environment configured for native app visual testing. Similar to `percy exec` but optimized for mobile app testing workflows.
9
10
```bash { .api }
11
/**
12
* Execute command with Percy for native apps
13
* Starts Percy in app mode, runs command, then stops Percy
14
* Optimized for mobile app testing with native SDKs
15
*
16
* Usage: percy app exec -- <command>
17
*
18
* Arguments:
19
* command Command to execute (required, after --)
20
*
21
* Configuration:
22
* projectType: 'app' Native app project type
23
* server: true Percy server enabled
24
* skipDiscovery: true Asset discovery disabled (not needed for apps)
25
*
26
* Environment variables set:
27
* PERCY_SERVER_ADDRESS Local Percy server URL
28
* PERCY_BUILD_ID Current build identifier
29
* PERCY_BUILD_URL Percy dashboard URL for build
30
* PERCY_LOGLEVEL Current logging level
31
*/
32
percy app exec -- <command>
33
```
34
35
**Usage Examples:**
36
37
```bash
38
# iOS testing with XCTest
39
percy app exec -- xcodebuild test -project MyApp.xcodeproj -scheme MyAppTests
40
41
# Android testing with Espresso
42
percy app exec -- ./gradlew connectedAndroidTest
43
44
# React Native testing
45
percy app exec -- npm run test:e2e:ios
46
percy app exec -- npm run test:e2e:android
47
48
# Flutter testing
49
percy app exec -- flutter test integration_test/
50
```
51
52
### Percy App Start
53
54
Start Percy process specifically configured for native app testing. Creates a build and starts the server in app mode.
55
56
```bash { .api }
57
/**
58
* Start Percy process for native applications
59
* Initializes Percy server in app mode for mobile testing
60
* Must be paired with 'percy app stop' to finalize build
61
*
62
* Usage: percy app start
63
*
64
* Configuration:
65
* projectType: 'app' Native app project type
66
* server: true Percy server enabled
67
* skipDiscovery: true Asset discovery disabled
68
*/
69
percy app start
70
```
71
72
**Usage Examples:**
73
74
```bash
75
# Start Percy for app testing
76
percy app start
77
78
# Run your app tests (Percy server available for native SDKs)
79
xcodebuild test -project MyApp.xcodeproj -scheme MyAppTests
80
81
# Stop Percy when done
82
percy app stop
83
```
84
85
### Percy App Stop
86
87
Stop the Percy app process and finalize the build. Uploads any remaining snapshots and marks the build as complete.
88
89
```bash { .api }
90
/**
91
* Stop Percy app process and finalize build
92
* Uploads pending snapshots and marks build as complete
93
* Should be called after 'percy app start' and test execution
94
*
95
* Usage: percy app stop
96
*/
97
percy app stop
98
```
99
100
**Usage Examples:**
101
102
```bash
103
# Stop Percy app process
104
percy app stop
105
106
# Use in scripts
107
percy app start
108
./run-app-tests.sh
109
percy app stop
110
```
111
112
### Percy App Ping
113
114
Check if Percy app process is running and responsive. Useful for debugging and health checks in app testing environments.
115
116
```bash { .api }
117
/**
118
* Check if Percy app process is running
119
* Tests connectivity to local Percy server in app mode
120
*
121
* Usage: percy app ping
122
*
123
* Exit codes:
124
* 0 - Percy app is running and responsive
125
* 1 - Percy app is not running or not responsive
126
*/
127
percy app ping
128
```
129
130
**Usage Examples:**
131
132
```bash
133
# Check if Percy app is running
134
percy app ping
135
echo $? # 0 if running, 1 if not
136
137
# Use in conditional scripts
138
if percy app ping; then
139
echo "Percy app is running"
140
else
141
echo "Percy app is not running"
142
percy app start
143
fi
144
```
145
146
## Native SDK Integration
147
148
### iOS Integration (Swift/Objective-C)
149
150
Percy app commands work with the Percy iOS SDK to capture screenshots during XCTest execution:
151
152
```swift
153
// In your XCTest files
154
import Percy
155
156
class MyAppUITests: XCTestCase {
157
func testHomeScreen() {
158
let app = XCUIApplication()
159
app.launch()
160
161
// Navigate to home screen
162
app.buttons["Home"].tap()
163
164
// Capture Percy snapshot
165
Percy.screenshot(name: "Home Screen")
166
}
167
168
func testProfileScreen() {
169
let app = XCUIApplication()
170
app.launch()
171
172
// Navigate to profile
173
app.tabBars.buttons["Profile"].tap()
174
175
// Capture with options
176
Percy.screenshot(name: "Profile Screen", options: [
177
"widths": [375, 414]
178
])
179
}
180
}
181
```
182
183
```bash
184
# Run tests with Percy
185
percy app exec -- xcodebuild test \
186
-project MyApp.xcodeproj \
187
-scheme MyAppUITests \
188
-destination 'platform=iOS Simulator,name=iPhone 13'
189
```
190
191
### Android Integration (Java/Kotlin)
192
193
Integration with Android Espresso tests using Percy Android SDK:
194
195
```kotlin
196
// In your Android test files
197
import io.percy.espresso.Percy
198
199
@RunWith(AndroidJUnit4::class)
200
class MainActivityTest {
201
202
@get:Rule
203
val activityRule = ActivityTestRule(MainActivity::class.java)
204
205
@Test
206
fun testHomeScreen() {
207
// Navigate to home screen
208
onView(withId(R.id.home_button)).perform(click())
209
210
// Capture Percy snapshot
211
Percy.screenshot(activityRule.activity, "Home Screen")
212
}
213
214
@Test
215
fun testProfileScreen() {
216
// Navigate to profile
217
onView(withId(R.id.profile_tab)).perform(click())
218
219
// Capture with options
220
val options = mutableMapOf<String, Any>()
221
options["widths"] = listOf(375, 414)
222
Percy.screenshot(activityRule.activity, "Profile Screen", options)
223
}
224
}
225
```
226
227
```bash
228
# Run Android tests with Percy
229
percy app exec -- ./gradlew connectedAndroidTest
230
```
231
232
### React Native Integration
233
234
Integration with React Native testing frameworks:
235
236
```javascript
237
// detox-tests.js (Detox example)
238
import { device, element, by } from 'detox';
239
import { percyScreenshot } from '@percy/detox';
240
241
describe('React Native App', () => {
242
beforeAll(async () => {
243
await device.launchApp();
244
});
245
246
it('should display home screen correctly', async () => {
247
await element(by.id('home-screen')).waitToBeVisible();
248
249
// Capture Percy snapshot
250
await percyScreenshot(device, 'Home Screen');
251
});
252
253
it('should display profile screen correctly', async () => {
254
await element(by.id('profile-tab')).tap();
255
await element(by.id('profile-screen')).waitToBeVisible();
256
257
// Capture with options
258
await percyScreenshot(device, 'Profile Screen', {
259
widths: [375, 414]
260
});
261
});
262
});
263
```
264
265
```bash
266
# Run Detox tests with Percy
267
percy app exec -- detox test --configuration ios.sim.debug
268
```
269
270
### Flutter Integration
271
272
Integration with Flutter integration tests:
273
274
```dart
275
// integration_test/app_test.dart
276
import 'package:flutter/services.dart';
277
import 'package:flutter_test/flutter_test.dart';
278
import 'package:integration_test/integration_test.dart';
279
import 'package:percy_flutter/percy_flutter.dart';
280
import 'package:myapp/main.dart' as app;
281
282
void main() {
283
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
284
285
group('App Screenshots', () {
286
testWidgets('Home screen', (WidgetTester tester) async {
287
app.main();
288
await tester.pumpAndSettle();
289
290
// Navigate to home
291
await tester.tap(find.text('Home'));
292
await tester.pumpAndSettle();
293
294
// Capture Percy snapshot
295
await PercyFlutter.screenshot('Home Screen');
296
});
297
298
testWidgets('Profile screen', (WidgetTester tester) async {
299
app.main();
300
await tester.pumpAndSettle();
301
302
// Navigate to profile
303
await tester.tap(find.text('Profile'));
304
await tester.pumpAndSettle();
305
306
// Capture with options
307
await PercyFlutter.screenshot('Profile Screen',
308
widths: [375, 414]);
309
});
310
});
311
}
312
```
313
314
```bash
315
# Run Flutter integration tests with Percy
316
percy app exec -- flutter test integration_test/
317
```
318
319
## Configuration
320
321
### App-Specific Settings
322
323
Percy app commands use specialized configuration optimized for mobile testing:
324
325
```yaml
326
# .percy.yml for app projects
327
version: 2
328
app:
329
projectType: 'app'
330
server: true
331
skipDiscovery: true
332
333
snapshot:
334
widths: [375, 414, 768] # Common mobile widths
335
minHeight: 667
336
```
337
338
### Environment Variables
339
340
App commands set specific environment variables:
341
342
```bash { .api }
343
# Set by Percy app commands
344
PERCY_SERVER_ADDRESS # Local Percy server URL
345
PERCY_BUILD_ID # Current build identifier
346
PERCY_BUILD_URL # Percy dashboard URL for build
347
PERCY_LOGLEVEL # Current logging level
348
349
# App-specific configuration
350
PERCY_PROJECT_TYPE=app # Identifies as app project
351
PERCY_SKIP_DISCOVERY=true # Disables web asset discovery
352
```
353
354
## Device and Simulator Support
355
356
### iOS Simulators
357
358
```bash
359
# Test on specific iOS simulator
360
percy app exec -- xcodebuild test \
361
-project MyApp.xcodeproj \
362
-scheme MyAppUITests \
363
-destination 'platform=iOS Simulator,name=iPhone 13 Pro'
364
365
# Test on multiple simulators
366
percy app exec -- xcodebuild test \
367
-project MyApp.xcodeproj \
368
-scheme MyAppUITests \
369
-destination 'platform=iOS Simulator,name=iPhone 13' \
370
-destination 'platform=iOS Simulator,name=iPad Air'
371
```
372
373
### Android Emulators
374
375
```bash
376
# Test on specific Android emulator
377
percy app exec -- ./gradlew connectedAndroidTest
378
379
# Test with specific device configuration
380
percy app exec -- ./gradlew connectedAndroidTest \
381
-Pandroid.testInstrumentationRunnerArguments.size=medium
382
```
383
384
### Physical Devices
385
386
```bash
387
# iOS physical device testing
388
percy app exec -- xcodebuild test \
389
-project MyApp.xcodeproj \
390
-scheme MyAppUITests \
391
-destination 'platform=iOS,name=My iPhone'
392
393
# Android physical device testing
394
percy app exec -- ./gradlew connectedAndroidTest \
395
-Pandroid.testInstrumentationRunnerArguments.device=physical
396
```
397
398
## CI/CD Integration
399
400
### GitHub Actions
401
402
```yaml
403
# .github/workflows/app-visual-tests.yml
404
name: App Visual Tests
405
406
on: [push, pull_request]
407
408
jobs:
409
ios-tests:
410
runs-on: macos-latest
411
steps:
412
- uses: actions/checkout@v2
413
414
- name: Setup iOS Simulator
415
run: |
416
xcrun simctl boot "iPhone 13"
417
418
- name: Run Percy iOS tests
419
env:
420
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
421
run: |
422
percy app exec -- xcodebuild test \
423
-project MyApp.xcodeproj \
424
-scheme MyAppUITests \
425
-destination 'platform=iOS Simulator,name=iPhone 13'
426
427
android-tests:
428
runs-on: ubuntu-latest
429
steps:
430
- uses: actions/checkout@v2
431
432
- name: Setup Android Emulator
433
uses: reactivecircus/android-emulator-runner@v2
434
with:
435
api-level: 29
436
script: |
437
export PERCY_TOKEN=${{ secrets.PERCY_TOKEN }}
438
percy app exec -- ./gradlew connectedAndroidTest
439
```
440
441
### Jenkins Pipeline
442
443
```groovy
444
pipeline {
445
agent any
446
447
environment {
448
PERCY_TOKEN = credentials('percy-token')
449
}
450
451
stages {
452
stage('iOS Tests') {
453
steps {
454
sh '''
455
percy app exec -- xcodebuild test \
456
-project MyApp.xcodeproj \
457
-scheme MyAppUITests \
458
-destination 'platform=iOS Simulator,name=iPhone 13'
459
'''
460
}
461
}
462
463
stage('Android Tests') {
464
steps {
465
sh '''
466
percy app exec -- ./gradlew connectedAndroidTest
467
'''
468
}
469
}
470
}
471
}
472
```
473
474
## Error Handling
475
476
### Common Issues
477
478
```bash
479
# Handle simulator/emulator startup issues
480
if ! xcrun simctl list | grep -q "iPhone 13 (Booted)"; then
481
echo "Starting iOS Simulator..."
482
xcrun simctl boot "iPhone 13"
483
sleep 10
484
fi
485
486
percy app exec -- xcodebuild test -project MyApp.xcodeproj -scheme MyAppUITests
487
488
# Handle Android emulator issues
489
adb wait-for-device
490
adb shell input keyevent 82 # Unlock device
491
492
percy app exec -- ./gradlew connectedAndroidTest
493
```
494
495
### Debugging
496
497
```bash
498
# Enable debug logging
499
PERCY_LOGLEVEL=debug percy app exec -- npm run test:e2e
500
501
# Check Percy server status
502
percy app ping || {
503
echo "Percy app server not responding"
504
percy app stop
505
percy app start
506
}
507
508
# Verify SDK integration
509
# Check that native SDK calls are reaching Percy server
510
tail -f ~/.percy/logs/percy.log
511
```
512
513
## Best Practices
514
515
### Test Organization
516
517
```bash
518
# Organize tests by feature
519
percy app exec -- xcodebuild test \
520
-project MyApp.xcodeproj \
521
-scheme AuthenticationTests
522
523
percy app exec -- xcodebuild test \
524
-project MyApp.xcodeproj \
525
-scheme NavigationTests
526
527
percy app exec -- xcodebuild test \
528
-project MyApp.xcodeproj \
529
-scheme ProfileTests
530
```
531
532
### Screenshot Naming
533
534
```swift
535
// Use descriptive, hierarchical names
536
Percy.screenshot(name: "Login - Empty Form")
537
Percy.screenshot(name: "Login - With Errors")
538
Percy.screenshot(name: "Login - Success")
539
540
Percy.screenshot(name: "Profile - View Mode")
541
Percy.screenshot(name: "Profile - Edit Mode")
542
Percy.screenshot(name: "Profile - Saving State")
543
```
544
545
### Performance Optimization
546
547
```bash
548
# Run tests in parallel where possible
549
percy app exec -- xcodebuild test \
550
-project MyApp.xcodeproj \
551
-scheme MyAppUITests \
552
-parallel-testing-enabled YES
553
554
# Use specific test classes to reduce runtime
555
percy app exec -- xcodebuild test \
556
-project MyApp.xcodeproj \
557
-scheme MyAppUITests \
558
-only-testing:MyAppUITests/LoginTests
559
```