Integrate your Maestro test suite with App Percy
Run App Percy visual tests on your Maestro mobile flows using the @percy/maestro-app SDK on BrowserStack Maestro sessions.
Use the @percy/maestro-app SDK to add App Percy visual testing to your Maestro flows. You add a screenshot step to a flow, then run that flow on a BrowserStack Maestro session, where App Percy captures the screen and uploads it to your Percy project.
App Percy with Maestro runs only on BrowserStack Maestro sessions, on Android and iOS. Local maestro test runs aren’t supported and screenshot uploads fail.
Prerequisites
Before you begin, ensure you have the following:
- A Percy account with an App Percy project.
- Node.js and npm installed.
- A Maestro flow that runs on BrowserStack App Automate Maestro.
- Your BrowserStack username and access key.
Install the SDK
Install the SDK and the Percy CLI as development dependencies:
npm install --save-dev @percy/maestro-app @percy/cli
The SDK ships a percy/ directory that contains the flow and script files your Maestro flow references at runtime. You can reference this directory in either of the following ways before you zip your test suite for upload to BrowserStack.
Mode A: Reference under node_modules
Keep the SDK in node_modules and reference it from there in your flow. Include the node_modules/@percy/maestro-app/percy directory when you zip your test suite.
Mode B: Vendor copy (recommended)
Copy the SDK’s percy/ directory into your workspace to produce a smaller zip:
cp -r node_modules/@percy/maestro-app/percy ./percy
Your workspace then has the following layout:
your-maestro-workspace/
percy/
flows/
percy-init.yaml
percy-screenshot.yaml
scripts/
percy-healthcheck.js
percy-screenshot.js
your-flow.yaml
Capture a screenshot
To capture a screenshot at any point in your flow, add a runFlow step that references the SDK’s percy-screenshot.yaml and sets the SCREENSHOT_NAME environment variable:
- runFlow:
file: percy/flows/percy-screenshot.yaml
env:
SCREENSHOT_NAME: Homepage
The first screenshot step runs a lazy healthcheck and initializes App Percy, so you don’t need a separate setup step.
SCREENSHOT_NAME must match the pattern ^[a-zA-Z0-9_-]+$. Names with spaces, dots, or slashes are rejected with an HTTP 400 response.
Run your tests
Run your flow through the Percy CLI:
npx percy app:exec -- maestro test your-flow.yaml
App Percy starts, creates a build, captures each screenshot in the flow, uploads the screenshots to your project, and then stops.
Configuration
Set the following environment variables to control how App Percy captures each screenshot. On BrowserStack Maestro sessions, the device variables are injected for you, so you only set the ones you want to override.
| Variable | Required | Default | Purpose |
|---|---|---|---|
SCREENSHOT_NAME |
Yes | N/A | Name for the screenshot. Must match ^[a-zA-Z0-9_-]+$. |
PERCY_DEVICE_NAME |
No | Injected on BrowserStack | Device identifier, such as Pixel 7. BrowserStack sets this from the session device. |
PERCY_OS_VERSION |
No | Injected on BrowserStack | OS version, such as 13 on Android or 17 on iOS. BrowserStack sets this from the session device. |
PERCY_ORIENTATION |
No | portrait |
Screen orientation, either portrait or landscape. |
PERCY_REGIONS |
No | N/A | JSON array of region masks. |
PERCY_SYNC |
No | false |
Set to true to wait for comparison results. |
PERCY_FULLSCREEN |
No | false |
Set to true when the screen has no system chrome. |
PERCY_STATUS_BAR_HEIGHT |
No | Android 120, iOS 100
|
Height in image pixels to mask at the top of the screen. |
PERCY_NAV_BAR_HEIGHT |
No | Android 100, iOS 80
|
Height in image pixels to mask at the bottom of the screen. |
Status bar and navigation bar masking
App Percy masks the device status bar and navigation bar so that the clock, battery, and other dynamic system elements don’t trigger false visual differences. The defaults differ by platform:
| Variable | Android default | iOS default |
|---|---|---|
PERCY_STATUS_BAR_HEIGHT |
120 |
100 |
PERCY_NAV_BAR_HEIGHT |
100 |
80 |
Device pixel densities vary, so the default heights don’t fit every device. Override PERCY_STATUS_BAR_HEIGHT and PERCY_NAV_BAR_HEIGHT per device when the mask doesn’t cover the full bar. For example, an iPhone with a Dynamic Island needs a taller status bar mask than the iOS default.
Regions
Use the PERCY_REGIONS environment variable to mark areas of a screenshot for a specific comparison algorithm. Set it to a JSON array, where each object defines a region and the algorithm to apply.
The following algorithms are available:
-
ignore: Excludes the region from comparison. Any change inside the region is ignored. -
standard: Compares the region with the standard sensitivity. -
intelliignore: Uses AI to ignore dynamic content such as advertisements and carousels. -
layout: Checks structural layout with a pixel-level tolerance.
Coordinate-based regions
Define a region by its pixel boundaries:
PERCY_REGIONS: '[{"top":0,"bottom":50,"left":0,"right":1080,"algorithm":"ignore"}]'
Element-based regions
Define a region by a UI element. The supported locators differ by platform.
On Android, use resource-id, text, content-desc, or class:
PERCY_REGIONS: '[{"element":{"resource-id":"com.app:id/clock"},"algorithm":"ignore"}]'
On iOS, use id, the accessibility identifier, or class:
PERCY_REGIONS: '[{"element":{"id":"clock-label"},"algorithm":"ignore"}]'
Run on BrowserStack
When you trigger a Maestro build through the BrowserStack App Automate API, pass your Percy token in the appPercy object of the build payload. This field is the same on both Android and iOS.
Use the appPercy field to pass your Percy token. The percyOptions field is silently dropped, so screenshots won’t reach your project if you use it.
The following request triggers an Android build:
curl -u "BROWSERSTACK_USERNAME:BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/android/build" \
-H "Content-Type: application/json" \
-d '{
"app": "<APP_URL>",
"testSuite": "<TEST_SUITE_URL>",
"devices": ["Samsung Galaxy S22-12.0"],
"project": "my-percy-maestro-project",
"appPercy": {
"PERCY_TOKEN": "<PERCY_TOKEN>"
}
}'
The following request triggers an iOS build and passes an extra Percy variable through the env sub-object:
curl -u "BROWSERSTACK_USERNAME:BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/maestro/v2/ios/build" \
-H "Content-Type: application/json" \
-d '{
"app": "<APP_URL>",
"testSuite": "<TEST_SUITE_URL>",
"devices": ["iPhone 14-16"],
"project": "my-percy-maestro-project",
"appPercy": {
"PERCY_TOKEN": "<PERCY_TOKEN>",
"env": {
"PERCY_BRANCH": "main"
}
}
}'
Pass any PERCY_* variable through the appPercy.env sub-object.
Unsupported features
The following features aren’t supported in version 1.0.0 of the SDK:
- Session-to-build correlation through
browserstack_executor. - Full-page or scrollable capture.
- Animation freezing and Percy-specific CSS.
- XPath region selectors on Android.
- Automatic device-metadata detection on iOS.
- Local
maestro testruntime.
The following features are planned for a later release:
- The
PERCY_IGNORE_ERRORSandPERCY_ENABLEDswitches. - The
textandxpathelement selectors on iOS. - Rendering of
PERCY_LABELSon the dashboard.
Related topics
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
Thank you for your valuable feedback!