Patrol E2E Testing Suite
Comprehensive end-to-end testing for all 5 Flutter shells using Patrol native automation framework.
Overview
The Patrol E2E testing suite provides automated testing across all Flutter applications, catching API endpoint issues, integration bugs, and UI regressions before deployment.
Key Benefits
| Benefit | Description |
|---|---|
| Early Detection | Catch API issues in CI/CD before merge |
| Cross-Shell Coverage | Test all 5 shells with shared utilities |
| Native Automation | Real device/browser testing capabilities |
| CI Integration | Automated runs on every PR |
Coverage Statistics
| Shell | Test Files | Test Cases | Coverage |
|---|---|---|---|
| Staff | 14 | 56+ | 80%+ critical flows |
| Customer | 12 | 48+ | 80%+ critical flows |
| Admin | 8 | 32+ | 80%+ critical flows |
| Web | 6 | 24+ | 80%+ critical flows |
| Cockpit | 6 | 24+ | 80%+ critical flows |
Test Structure
frontend/integration_test/patrol/
├── common/
│ ├── test_config.dart # Configuration and credentials
│ ├── test_helpers.dart # Shared helper functions
│ └── test_app.dart # App initialization
├── staff/
│ ├── staff_login_test.dart
│ ├── staff_order_entry_test.dart
│ ├── staff_tables_test.dart
│ ├── staff_kitchen_test.dart
│ ├── staff_inventory_test.dart
│ ├── staff_payment_test.dart
│ ├── staff_offline_test.dart
│ └── ...
├── customer/
│ ├── customer_auth_test.dart
│ ├── customer_menu_test.dart
│ ├── customer_cart_test.dart
│ ├── customer_checkout_test.dart
│ └── ...
├── admin/
│ ├── admin_auth_test.dart
│ ├── admin_tenant_test.dart
│ ├── admin_users_test.dart
│ ├── admin_gating_test.dart
│ └── ...
├── web/
│ ├── web_public_test.dart
│ ├── web_portal_test.dart
│ ├── web_forms_test.dart
│ └── ...
└── cockpit/
├── cockpit_login_test.dart
├── cockpit_dashboard_test.dart
├── cockpit_incidents_test.dart
└── ...
Test Categories
Authentication Tests
| Shell | Test Coverage |
|---|---|
| Staff | PIN login, employee ID, role routing |
| Customer | Email signup, social login (Google, Apple) |
| Admin | SSO login, MFA verification |
| Web | Owner login, forgot password |
| Cockpit | Operator SSO, tenant switching |
Feature Tests by Shell
Staff Shell
| Feature | Test File | Coverage |
|---|---|---|
| PIN Login | staff_login_test.dart | Email/ID login, invalid PIN, role routing |
| Order Entry | staff_order_entry_test.dart | Menu selection, modifiers, cart |
| Tables | staff_tables_test.dart | Status updates, section filtering |
| Kitchen | staff_kitchen_test.dart | Order queue, station routing |
| Inventory | staff_inventory_test.dart | Stock levels, adjustments |
| Payments | staff_payment_test.dart | Cash, card, split, refund |
| Offline | staff_offline_test.dart | Graceful degradation, sync |
| Time Clock | staff_clock_test.dart | Clock in/out, breaks |
Customer Shell
| Feature | Test File | Coverage |
|---|---|---|
| Authentication | customer_auth_test.dart | Signup, login, social |
| Menu Browse | customer_menu_test.dart | Categories, search, filters |
| Cart | customer_cart_test.dart | Add, remove, quantities |
| Checkout | customer_checkout_test.dart | Payment flow, tips |
| Orders | customer_orders_test.dart | History, tracking |
| Loyalty | customer_loyalty_test.dart | Points, rewards |
| Favorites | customer_favorites_test.dart | Save, manage |
Admin Shell
| Feature | Test File | Coverage |
|---|---|---|
| Authentication | admin_auth_test.dart | SSO, session management |
| Tenants | admin_tenant_test.dart | CRUD, lifecycle |
| Users | admin_users_test.dart | Roles, permissions |
| Gating | admin_gating_test.dart | Feature flags, policies |
| Audit | admin_audit_test.dart | Log viewing, search |
| Locations | admin_locations_test.dart | Branch management |
| Subscriptions | admin_subscriptions_test.dart | Billing, plans |
Writing Tests
Test Pattern
import 'package:patrol/patrol.dart';
import '../common/test_helpers.dart';
import '../common/test_config.dart';
void main() {
group('Feature Tests', () {
patrolTest(
'AC1: User can perform action',
config: patrolTesterConfig,
nativeAutomatorConfig: TestConfig.nativeAutomatorConfig,
($) async {
// Arrange: Start app and setup
await TestHelpers.startStaffApp($);
await TestHelpers.loginAsStaff($, 'manager');
// Act: Perform the action
await $(find.text('Dashboard')).tap();
await $.pumpAndSettle();
// Assert: Verify result
expect($(find.text('Revenue')).exists, isTrue);
},
);
});
}
Using Test Helpers
The TestHelpers class provides reusable functions:
// App initialization
await TestHelpers.startStaffApp($);
await TestHelpers.startCustomerApp($);
await TestHelpers.startAdminApp($);
await TestHelpers.startCockpitApp($);
await TestHelpers.startWebApp($);
// Authentication
await TestHelpers.loginAsStaff($, 'manager');
await TestHelpers.loginAsStaff($, 'server');
await TestHelpers.loginAsStaff($, 'chef');
await TestHelpers.loginAsCustomer($, email: 'test@example.com');
await TestHelpers.loginAsAdmin($);
await TestHelpers.loginAsOperator($);
// Navigation
await TestHelpers.navigateToTimeTracking($);
await TestHelpers.navigateToInventory($);
await TestHelpers.navigateToAnalytics($);
await TestHelpers.navigateToCart($);
await TestHelpers.navigateToOrders($);
await TestHelpers.navigateToSettings($);
// Actions
await TestHelpers.addItemToCart($, 'Burger');
await TestHelpers.searchFor($, 'pizza');
await TestHelpers.pullToRefresh($);
// Verification
await TestHelpers.verifyToast($, 'Success');
await TestHelpers.verifyDialog($);
await TestHelpers.verifyLoading($);
await TestHelpers.verifyError($);
Test Credentials
// Test credentials (from test_config.dart)
final creds = TestConfig.staffCredentials['manager']!;
// creds.identifier - email or employee ID
// creds.pin - 6-digit PIN
// creds.employeeId - short ID (e.g., MGR01)
// Available roles
TestConfig.staffCredentials['manager'];
TestConfig.staffCredentials['server'];
TestConfig.staffCredentials['chef'];
TestConfig.staffCredentials['host'];
TestConfig.staffCredentials['bartender'];
Running Tests
Local Execution
# Run all Patrol tests
cd frontend
flutter test integration_test/patrol/ --dart-define=PATROL=true
# Run specific shell tests
flutter test integration_test/patrol/staff/ --dart-define=PATROL=true
flutter test integration_test/patrol/customer/ --dart-define=PATROL=true
# Run single test file
flutter test integration_test/patrol/staff/staff_login_test.dart
# Run with verbose output
flutter test integration_test/patrol/staff/ --reporter expanded
# Run on specific device
flutter test integration_test/patrol/staff/ -d chrome
flutter test integration_test/patrol/staff/ -d emulator-5554
Using Patrol CLI
# Install patrol_cli
dart pub global activate patrol_cli
# Build and run tests
patrol test
# Run with native automation
patrol test --target integration_test/patrol/staff/staff_login_test.dart
# Generate test artifacts
patrol test --record-video
patrol test --take-screenshots
CI/CD Integration
GitHub Actions Workflow
name: Patrol E2E Tests
on:
pull_request:
paths:
- 'frontend/**'
push:
branches: [main, develop]
jobs:
patrol-tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shell: [staff, customer, admin, web, cockpit]
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.x'
channel: 'stable'
- name: Install dependencies
run: |
cd frontend
flutter pub get
- name: Run Patrol tests
run: |
cd frontend
flutter test integration_test/patrol/${{ matrix.shell }}/ \
--dart-define=PATROL=true \
--reporter github
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: patrol-results-${{ matrix.shell }}
path: frontend/test-results/
Test Artifacts
| Artifact | Description | Storage |
|---|---|---|
| Screenshots | Captured on failure | GitHub Artifacts |
| Videos | Test execution recording | GitHub Artifacts |
| Logs | Detailed test output | Job logs |
| Reports | JUnit XML reports | Test summary |
Timeouts Configuration
class TestConfig {
// Network operations (API calls)
static const networkTimeout = Duration(seconds: 10);
// Short UI animations
static const shortTimeout = Duration(seconds: 3);
// Medium operations (form submissions)
static const mediumTimeout = Duration(seconds: 5);
// Long operations (file uploads, complex flows)
static const longTimeout = Duration(seconds: 15);
// Splash screen duration
static const splashTimeout = Duration(seconds: 5);
}
Best Practices
Do's
- Use TestHelpers - Leverage shared utilities for consistency
- Test acceptance criteria - Each test maps to an AC
- Use semantic finders -
find.text(),find.byKey()over widget types - Wait for settle - Use
$.pumpAndSettle()after actions - Handle timeouts - Specify appropriate timeouts for operations
- Group related tests - Use
group()for organization
Don'ts
- Don't hardcode delays - Use
pumpAndSettle()instead - Don't skip cleanup - Clear state between tests
- Don't test implementation - Focus on user behavior
- Don't ignore flakiness - Fix or quarantine flaky tests
- Don't couple tests - Each test should be independent
Handling Flaky Tests
// Retry flaky network operations
for (var attempt = 0; attempt < 3; attempt++) {
try {
await $.pumpAndSettle(timeout: TestConfig.networkTimeout);
break;
} catch (_) {
if (attempt == 2) rethrow;
await $.pump(const Duration(seconds: 1));
}
}
// Use flexible matchers
expect(
$(find.textContaining('Success')).exists ||
$(find.textContaining('Saved')).exists,
isTrue,
);
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| Tests timeout | Increase timeout, check network |
| Element not found | Wait for element, use waitUntilVisible() |
| Keyboard obscures | Dismiss with $.native.pressBack() |
| State leaks between tests | Clear storage in startTestApp() |
| Flaky on CI | Add retry logic, increase timeouts |
Debug Mode
// Enable debug logging
patrolTest(
'Debug test',
($) async {
debugPrint('Starting test...');
// Print widget tree
debugDumpApp();
// Check element existence
debugPrint('Button exists: ${$(find.text('Login')).exists}');
},
);
Performance Targets
| Metric | Target |
|---|---|
| Full suite runtime | under 10 minutes |
| Individual test | under 30 seconds |
| Flaky test rate | under 5% |
| CI detection rate | 95%+ API issues |
Related Documentation
Changelog
| Version | Date | Changes |
|---|---|---|
| 3.0.0 | 2026-01-07 | Initial release with 5 shell coverage |