Patrol provides comprehensive support for testing Flutter web applications using Playwright , a powerful browser automation framework. This enables you to write cross-platform tests that work on both mobile and web.
How It Works
When running Patrol tests on web, the following architecture is used:
Flutter Web Server
Patrol starts a development server that serves your Flutter web application.
Playwright Test Runner
Patrol automatically launches Chromium using Playwright to load your app.
Platform Actions Bridge
For platform automation calls (like $.platform.web.*), your Dart test code sends requests to Playwright, which executes browser-specific actions.
Results Collection
Test results are collected and reported back in Patrol’s standard format.
This architecture allows you to write the same Patrol tests that work across mobile and web platforms, with Playwright handling browser-specific interactions.
Prerequisites
Before running Patrol tests on web:
Install Node.js
Playwright requires Node.js. Install it from nodejs.org . Verify installation: node --version
npm --version
First Run Setup
On first run, Patrol automatically installs:
Node.js dependencies
Playwright package
Chromium browser binaries
This setup happens automatically the first time you run patrol test --device chrome. It may take a moment.
Running Tests
Basic Usage
Run your Patrol tests on Chrome:
patrol test --device chrome --target patrol_test/login_test.dart
On first run, Patrol will install Playwright and browser binaries. This is a one-time setup.
Headless Mode
By default, Patrol runs web tests in headed mode (visible browser). For CI/CD pipelines, use headless mode:
patrol test \
--device chrome \
--target patrol_test/login_test.dart \
--web-headless true
CI environments typically don’t provide a graphical display. Always use --web-headless true in CI pipelines.
Running All Tests
Run all web tests in your project:
patrol test --device chrome
CI/CD Integration
GitHub Actions
.github/workflows/test.yml
name : Patrol Web Tests
on : [ push , pull_request ]
jobs :
test :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v3
- uses : actions/setup-java@v3
with :
distribution : 'temurin'
java-version : '17'
- uses : subosito/flutter-action@v2
with :
channel : 'stable'
- name : Install Patrol CLI
run : flutter pub global activate patrol_cli
- name : Run web tests
run : |
patrol test \
--device chrome \
--web-headless true
GitLab CI
test:web :
image : ghcr.io/cirruslabs/flutter:stable
before_script :
- flutter pub global activate patrol_cli
- export PATH="$PATH:$HOME/.pub-cache/bin"
script :
- patrol test --device chrome --web-headless true
Ensure your CI environment has Node.js installed. Most CI images include it by default.
Web-Specific Features
Patrol provides comprehensive browser automation through $.platform.web:
Dark Mode
// Enable dark mode
await $.platform.web. enableDarkMode ();
// Disable dark mode
await $.platform.web. disableDarkMode ();
// Press a single key
await $.platform.web. pressKey (key : 'Enter' );
await $.platform.web. pressKey (key : 'Escape' );
await $.platform.web. pressKey (key : 'a' );
// Press key combinations
await $.platform.web. pressKeyCombo (keys : [ 'Control' , 'a' ]); // Select all
await $.platform.web. pressKeyCombo (keys : [ 'Control' , 'c' ]); // Copy
await $.platform.web. pressKeyCombo (keys : [ 'Control' , 'v' ]); // Paste
await $.platform.web. pressKeyCombo (keys : [ 'Meta' , 's' ]); // Save (Cmd+S on Mac)
Permissions
// Grant browser permissions
await $.platform.web. grantPermissions (
permissions : [ 'clipboard-read' , 'clipboard-write' ],
);
// Available permissions:
// - 'clipboard-read'
// - 'clipboard-write'
// - 'geolocation'
// - 'notifications'
// - 'camera'
// - 'microphone'
// Clear all permissions
await $.platform.web. clearPermissions ();
Clipboard
// Set clipboard content
await $.platform.web. setClipboard (text : 'Hello, Patrol!' );
// Read clipboard content
final clipboardText = await $.platform.web. getClipboard ();
expect (clipboardText, 'Hello, Patrol!' );
Browser Navigation
// Navigate back
await $.platform.web. goBack ();
// Navigate forward
await $.platform.web. goForward ();
Cookies
// Add a cookie
await $.platform.web. addCookie (
name : 'session_id' ,
value : 'abc123' ,
url : 'http://localhost:8080' ,
);
// Get all cookies
final cookies = await $.platform.web. getCookies ();
for ( final cookie in cookies) {
print ( ' ${ cookie . name } : ${ cookie . value } ' );
}
// Clear all cookies
await $.platform.web. clearCookies ();
File Operations
Upload Files
import 'dart:convert' ;
// Create file data
final fileContent = utf8. encode ( 'Hello from Patrol!' );
final file = UploadFileData (
name : 'test_file.txt' ,
content : fileContent,
mimeType : 'text/plain' ,
);
// Upload single file
await $.platform.web. uploadFile (files : [file]);
// Upload multiple files
final files = [
UploadFileData (
name : 'file1.txt' ,
content : utf8. encode ( 'Content 1' ),
mimeType : 'text/plain' ,
),
UploadFileData (
name : 'file2.txt' ,
content : utf8. encode ( 'Content 2' ),
mimeType : 'text/plain' ,
),
];
await $.platform.web. uploadFile (files : files);
Verify Downloads
// Trigger download in your app
await $( 'Download Report' ). tap ();
// Verify files were downloaded
final downloads = await $.platform.web. verifyFileDownloads ();
expect (downloads, isNotEmpty);
expect (downloads.first.suggestedFilename, 'report.pdf' );
Dialog Handling
Dialogs block JavaScript execution. You must subscribe to dialog events before triggering the dialog.
// Accept alert/confirm dialog
final dialogFuture = $.platform.web. acceptNextDialog ();
await $( 'Show Alert' ). tap (); // Trigger the dialog
final dialogText = await dialogFuture;
expect (dialogText, 'Are you sure?' );
// Dismiss dialog
final dialogFuture = $.platform.web. dismissNextDialog ();
await $( 'Show Confirm' ). tap ();
final dialogText = await dialogFuture;
expect (dialogText, 'Delete this item?' );
Web Element Interactions
For external HTML content (like iframes):
// Scroll to element in iframe
await $.platform.web. scrollToWeb (
selector : '#email-input' ,
iframeSelector : '#payment-iframe' ,
);
// Enter text in iframe input
await $.platform.web. enterTextWeb (
selector : '#email-input' ,
text : '[email protected] ' ,
iframeSelector : '#payment-iframe' ,
);
// Click button in iframe
await $.platform.web. tapWeb (
selector : '#submit-button' ,
iframeSelector : '#payment-iframe' ,
);
// For non-iframe elements, omit iframeSelector
await $.platform.web. tapWeb (selector : '#external-button' );
Window Operations
// Resize browser window
await $.platform.web. resizeWindow (size : Size ( 1920 , 1080 ));
await $.platform.web. resizeWindow (size : Size ( 375 , 667 )); // Mobile size
Complete Example
Here’s a comprehensive example using multiple web features:
import 'dart:convert' ;
import 'package:flutter_test/flutter_test.dart' ;
import 'package:patrol/patrol.dart' ;
void main () {
patrolTest (
'complete web test example' ,
($) async {
await $. pumpWidgetAndSettle ( const MyApp ());
// Test dark mode
await $.platform.web. enableDarkMode ();
await $. pumpAndSettle ();
expect ($(#darkModeIndicator), findsOneWidget);
// Test clipboard operations
await $.platform.web. grantPermissions (
permissions : [ 'clipboard-read' , 'clipboard-write' ],
);
await $.platform.web. setClipboard (text : '[email protected] ' );
await $( 'Paste Email' ). tap ();
final clipboard = await $.platform.web. getClipboard ();
expect (clipboard, '[email protected] ' );
// Test keyboard shortcuts
await $.platform.web. pressKeyCombo (keys : [ 'Control' , 'a' ]);
await $.platform.web. pressKey (key : 'Delete' );
// Test file upload
await $( 'Upload File' ). tap ();
final file = UploadFileData (
name : 'document.txt' ,
content : utf8. encode ( 'Test document' ),
mimeType : 'text/plain' ,
);
await $.platform.web. uploadFile (files : [file]);
// Test dialog handling
final dialogFuture = $.platform.web. acceptNextDialog ();
await $( 'Delete Account' ). tap ();
final message = await dialogFuture;
expect (message, contains ( 'confirm' ));
// Test browser navigation
await $( 'Go Back' ). tap ();
await $.platform.web. goBack ();
},
);
}
Capabilities
Web platform features available through $.platform.web:
Feature Status Dark mode toggle ✅ Keyboard input ✅ Key combinations ✅ Permissions management ✅ Clipboard operations ✅ Browser navigation ✅ Cookie management ✅ File uploads ✅ File download verification ✅ Dialog handling ✅ Iframe interactions ✅ Window resizing ✅ External element tapping ✅ External text input ✅
For a complete feature parity comparison across all platforms, see the Feature Parity page.
Limitations
Browser Support
Currently, Patrol web tests only support Chromium (Chrome). Firefox and Safari support may be added in future releases.
Mobile Browser Testing
Patrol web testing is designed for desktop browsers. For mobile browser testing, use actual mobile devices with Android/iOS platforms.
Dialog Timing
Always subscribe to dialog events before triggering them:
// ✅ Correct
final future = $.platform.web. acceptNextDialog ();
await $( 'Show Dialog' ). tap ();
await future;
// ❌ Wrong - dialog already shown
await $( 'Show Dialog' ). tap ();
await $.platform.web. acceptNextDialog (); // Too late!
File System Access
Uploaded files and downloads are handled through Playwright’s APIs. Direct file system access from the Flutter app is limited by browser security.
Troubleshooting
Playwright Installation Fails
If automatic installation fails:
# Navigate to Patrol's web runner directory
cd ~/.patrol/web_runner # or your custom location
# Install manually
npm install
npx playwright install chromium
Tests Fail in Headless Mode
Some visual tests may behave differently in headless mode:
# Run in headed mode for debugging
patrol test --device chrome --web-headless false
Port Already in Use
If the web server port is occupied:
# Kill process using port 8080
lsof -ti:8080 | xargs kill -9 # macOS/Linux
# Or specify a different port
patrol test --device chrome --web-port 8081
Node.js Not Found
Ensure Node.js is in your PATH:
# Check Node.js
which node
node --version
# Add to PATH if needed (add to ~/.bashrc or ~/.zshrc)
export PATH = "/usr/local/bin: $PATH "
Next Steps
Feature Parity See all available web features
Write Your First Test Start writing Patrol tests
CI Integration Run tests in CI/CD pipelines
Android Platform Testing on Android devices