Installation
# Install Patrol CLI
dart pub global activate patrol_cli
# Add Patrol to your project
flutter pub add patrol dev:patrol_finders
Basic Test Structure
import 'package:patrol/patrol.dart';
void main() {
patrolTest(
'test description',
($) async {
await $.pumpWidgetAndSettle(MyApp());
// Your test code here
},
);
}
Finders
// Find by text
$("Login")
// Find by key
$(#loginButton)
// Find by type
$(TextField)
// Find by icon
$(Icons.add)
// Chaining finders
$(Scaffold).$(ListView).$("Item")
// Using at() for multiple matches
$("Button").at(0) // First match
$("Button").first // First match
$("Button").last // Last match
Actions
// Tap
await $(#loginButton).tap();
// Enter text
await $(#emailField).enterText('[email protected]');
// Scroll
await $(#item).scrollTo();
// Drag
await $.drag(from: $(#source), to: $(#target));
// Long press
await $(#item).longPress();
Native Automation (Platform)
Permissions
// Grant permission when in use
await $.platform.mobile.grantPermissionWhenInUse();
// Grant permission always
await $.platform.mobile.grantPermissionAlways();
// Deny permission
await $.platform.mobile.denyPermission();
// Select fine location (Android)
await $.platform.mobile.selectFineLocation();
// Select coarse location (Android)
await $.platform.mobile.selectCoarseLocation();
Native Interactions
// Tap on native view
await $.platform.mobile.tap(Selector(text: 'Allow'));
// Enter text in native view
await $.platform.mobile.enterText(
Selector(text: 'Email'),
'[email protected]',
);
// Press back (Android)
await $.platform.mobile.pressBack();
// Press home
await $.platform.mobile.pressHome();
// Open notifications
await $.platform.mobile.openNotifications();
// Open quick settings (Android)
await $.platform.mobile.openQuickSettings();
Device Settings
// Dark mode
await $.platform.mobile.enableDarkMode();
await $.platform.mobile.disableDarkMode();
// WiFi
await $.platform.mobile.enableWifi();
await $.platform.mobile.disableWifi();
// Bluetooth
await $.platform.mobile.enableBluetooth();
await $.platform.mobile.disableBluetooth();
// Airplane mode (Android)
await $.platform.mobile.enableAirplaneMode();
await $.platform.mobile.disableAirplaneMode();
// Location
await $.platform.mobile.enableLocation();
await $.platform.mobile.disableLocation();
Notifications
// Get notifications
final notifications = await $.platform.mobile.getNotifications();
// Tap on notification by index
await $.platform.mobile.tapOnNotificationByIndex(0);
// Tap on notification by selector
await $.platform.mobile.tapOnNotificationBySelector(
Selector(text: 'New message'),
);
Camera & Gallery
// Take a photo
await $.platform.mobile.takeCameraPhoto();
// Pick image from gallery
await $.platform.mobile.pickImageFromGallery();
// Pick multiple images
await $.platform.mobile.pickMultipleImagesFromGallery();
Other Actions
// Open app
await $.platform.mobile.openApp(appId: 'com.example.app');
// Open URL (deep link)
await $.platform.mobile.openUrl('https://example.com');
// Swipe
await $.platform.mobile.swipe(
start: Offset(100, 500),
end: Offset(100, 100),
);
// Pull to refresh
await $.platform.mobile.pullToRefresh();
// Swipe back (iOS)
await $.platform.mobile.swipeBack();
// Press volume buttons
await $.platform.mobile.pressVolumeUp();
await $.platform.mobile.pressVolumeDown();
Web Automation
// Dark mode
await $.platform.web.enableDarkMode();
await $.platform.web.disableDarkMode();
// Navigation
await $.platform.web.goBack();
await $.platform.web.goForward();
// Cookies
await $.platform.web.addCookie(name: 'token', value: 'abc123');
final cookies = await $.platform.web.getCookies();
await $.platform.web.clearCookies();
// Permissions
await $.platform.web.grantPermissions(['camera', 'microphone']);
await $.platform.web.clearPermissions();
// Keyboard
await $.platform.web.pressKey('Enter');
await $.platform.web.pressKeyCombo(['Control', 'c']);
// File operations
await $.platform.web.uploadFile('path/to/file.txt');
await $.platform.web.verifyFileDownloads(['file.pdf']);
// Dialogs
await $.platform.web.acceptNextDialog();
await $.platform.web.dismissNextDialog();
// Window
await $.platform.web.resizeWindow(width: 1920, height: 1080);
// Clipboard
await $.platform.web.setClipboard('Hello, World!');
final text = await $.platform.web.getClipboard();
CLI Commands
# Run tests
patrol test
# Run specific test
patrol test -t patrol_test/login_test.dart
# Run on specific device
patrol test -d <device-id>
# Run with tags
patrol test --tags login
patrol test --exclude-tags slow
# Develop mode (hot restart)
patrol develop
# Build test APK/app
patrol build android
patrol build ios
# Check setup
patrol doctor
# List devices
patrol devices
# Update Patrol CLI
patrol update
Configuration (pubspec.yaml)
patrol:
app_name: My App
test_directory: patrol_test # Default, can be customized
android:
package_name: com.example.myapp
ios:
bundle_id: com.example.MyApp
web:
base_url: http://localhost:8080
Useful Tips
// Use keys for reliable finding
const loginButtonKey = Key('loginButton');
await $(loginButtonKey).tap();
// Pass environment variables
// Command: patrol test --dart-define="USERNAME=test"
const username = String.fromEnvironment('USERNAME');
// Setup and teardown
patrolSetUp(() async {
// Runs before each test
});
patrolTearDown(() async {
// Runs after each test
});
// Logging
$.log('Test checkpoint reached');
// Wait for condition
await $.waitUntilVisible($(#element));
await $.waitUntilExists($(#element));
// Check visibility/existence
final exists = $(#element).exists;
final visible = $(#element).visible;
Common Patterns
Login Flow
patrolTest('user can log in', ($) async {
await $.pumpWidgetAndSettle(MyApp());
await $(#emailField).enterText('[email protected]');
await $(#passwordField).enterText('password123');
await $(#loginButton).tap();
expect($(#homeScreen).visible, true);
});
Handling Permissions
patrolTest('camera permission', ($) async {
await $.pumpWidgetAndSettle(MyApp());
await $(#cameraButton).tap();
await $.platform.mobile.grantPermissionWhenInUse();
expect($(#cameraPreview).visible, true);
});
Scrolling to Item
patrolTest('scroll to item', ($) async {
await $.pumpWidgetAndSettle(MyApp());
await $(#listView).$("Item 50").scrollTo();
await $(#listView).$("Item 50").tap();
expect($(#detailScreen).visible, true);
});