How to Run Detox Tests on BrowserStack App Automate
Build and upload a Detox Android app and test client, configure BrowserStack’s patched Detox integration, run the cloud test, and troubleshoot common failures.
To run Detox tests on BrowserStack App Automate, build your Android app and Detox app client, upload both APKs, configure BrowserStack’s patched Detox package with the returned bs:// IDs, and run the cloud configuration. BrowserStack’s documented Detox flow is for real Android devices, and BrowserStack currently labels the feature beta. Check the current official guide before upgrading or rolling this setup into CI.
This guide covers the Android cloud workflow. The broader App Automate device catalog does not establish Detox cloud support for iOS; use the cloud configuration below only for Android unless BrowserStack confirms otherwise.
1. Check prerequisites and select the Detox package
You need a React Native project with Detox tests, a BrowserStack Username and Access Key, Android build tools for the project, and access to the App Automate service. Store credentials in environment variables or your CI secret store; do not commit them to source control.
BrowserStack’s current guide specifies this package for Detox 20.51.3 and later:
{
"devDependencies": {
"detox": "npm:@browserstack/detox@20.51.3-cloud.0"
}
}
For older Detox versions, the guide documents a legacy package configuration:
{
"devDependencies": {
"detox": "npm:@avinashbharti97/detox@^20.26.3"
}
}
BrowserStack says older versions remain supported with their previous configurations, while new patches and updates go to @browserstack/detox. These package instructions can change. Follow the current BrowserStack guide for your installed Detox version. After switching packages, BrowserStack advises deleting node_modules and package-lock.json, then reinstalling dependencies. If the app build fails after the switch, its guide suggests trying the original Detox version; that is a diagnostic suggestion, not a guaranteed fix.
rm -rf node_modules package-lock.json
npm install
npm install --global jest detox-cli
If your project uses another package manager, use its equivalent clean reinstall and lockfile workflow. Keep the lockfile produced by the successful install in version control so local and CI builds resolve the same dependency graph.
2. Bundle and build the Android app and test client
BrowserStack needs two separate artifacts: the app under test and the generated Detox app client. The client is the test-suite APK produced by the Android test build.
For a React Native project using Metro and an index.js entry point, create the bundle and build both artifacts from the project root:
mkdir -p android/app/src/main/assets
npx react-native bundle \
--platform android \
--dev false \
--entry-file index.js \
--bundle-output android/app/src/main/assets/index.android.bundle \
--assets-dest android/app/src/main/res
cd android
./gradlew assembleDebug
./gradlew assembleAndroidTest
cd ..
Change the entry file or bundler command to match your app. BrowserStack’s guide also says Detox uses unencrypted requests to the loopback interface. Configure Android’s network_security_config.xml to permit cleartext traffic to 127.0.0.1, following your app’s existing network security configuration structure:
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">127.0.0.1</domain>
</domain-config>
Find the exact output paths for your Gradle variant before uploading. The guide’s example commands build a debug app and Android test suite; release variants or custom flavors can produce different paths. Make sure the app and test client were built from the same source revision and compatible build variant.
3. Upload both artifacts to BrowserStack
Upload the app and app client separately. The upload endpoints accept APK or AAB artifacts and return distinct IDs. Use the ID returned as app_url for the app and app_client_url for the client.
Upload with cURL
export BROWSERSTACK_USERNAME="your_username"
export BROWSERSTACK_ACCESS_KEY="your_access_key"
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/app" \
-F "file=@android/app/build/outputs/apk/debug/app-debug.apk"
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/app-client" \
-F "file=@android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk"
Replace paths with the files produced by your build. Each successful response is JSON; copy the returned bs://... URL into the matching Detox configuration field. The endpoints also accept a publicly accessible artifact URL instead of a multipart file. The API supports custom_id for a stable name across uploads; consult the app-client API reference for its character and length rules.
Upload with Python
This optional script uploads both files using the same REST endpoints. Install the dependency with python -m pip install requests, set the two environment variables above, and update the paths if your build output differs.
import os
import requests
username = os.environ["BROWSERSTACK_USERNAME"]
access_key = os.environ["BROWSERSTACK_ACCESS_KEY"]
base = "https://api-cloud.browserstack.com/app-automate/detox/v2/android"
artifacts = [
("app", "android/app/build/outputs/apk/debug/app-debug.apk"),
("app-client", "android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk"),
]
for endpoint, path in artifacts:
with open(path, "rb") as artifact:
response = requests.post(
f"{base}/{endpoint}",
auth=(username, access_key),
files={"file": (os.path.basename(path), artifact)},
timeout=180,
)
response.raise_for_status()
print(endpoint, response.json())
Upload with Node.js
This Node.js example uses built-in fetch and FormData on a current Node.js runtime. Set the credentials in the environment and adjust the artifact paths as needed.
import { createReadStream } from "node:fs";
import { basename } from "node:path";
import { Readable } from "node:stream";
import { FormData } from "undici";
const username = process.env.BROWSERSTACK_USERNAME;
const accessKey = process.env.BROWSERSTACK_ACCESS_KEY;
if (!username || !accessKey) throw new Error("Set BrowserStack credentials in the environment");
const base = "https://api-cloud.browserstack.com/app-automate/detox/v2/android";
const artifacts = [
["app", "android/app/build/outputs/apk/debug/app-debug.apk"],
["app-client", "android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk"],
];
for (const [endpoint, path] of artifacts) {
const form = new FormData();
form.set("file", new Blob([await new Response(Readable.toWeb(createReadStream(path))).arrayBuffer()]), basename(path));
const response = await fetch(`${base}/${endpoint}`, {
method: "POST",
headers: {
Authorization: `Basic ${Buffer.from(`${username}:${accessKey}`).toString("base64")}`,
},
body: form,
});
if (!response.ok) throw new Error(`${endpoint} upload failed: ${response.status} ${await response.text()}`);
console.log(endpoint, await response.json());
}
For large artifacts, use a streaming multipart implementation suited to your Node.js runtime and CI environment rather than buffering the whole file in memory. cURL is the simplest documented upload path.
4. Configure Detox for a BrowserStack cloud device
Add a cloud app entry that references both upload IDs, a cloud device, and a cloud configuration containing authentication and session metadata. This minimal .detoxrc.js follows the current BrowserStack sample shape; replace the placeholders with your actual values.
/** @type {Detox.DetoxConfig} */
module.exports = {
logger: {
level: process.env.CI ? "debug" : undefined,
},
testRunner: {
args: {
config: "e2e/jest.config.js",
maxWorkers: process.env.CI ? 2 : undefined,
_: ["e2e"],
},
},
artifacts: {
plugins: {
log: process.env.CI ? "failing" : undefined,
screenshot: process.env.CI ? "failing" : undefined,
},
},
apps: {
"android.cloud.debug": {
type: "android.cloud",
app: "bs://YOUR_APP_UPLOAD_ID",
appClient: "bs://YOUR_APP_CLIENT_UPLOAD_ID",
},
},
devices: {
cloud: {
type: "android.cloud",
device: {
name: "Samsung Galaxy S22",
osVersion: "12.0",
},
},
},
configurations: {
"android.cloud.debug": {
device: "cloud",
app: "android.cloud.debug",
cloudAuthentication: {
username: process.env.BROWSERSTACK_USERNAME,
accessKey: process.env.BROWSERSTACK_ACCESS_KEY,
},
session: {
server: "wss://detox.browserstack.com/init",
name: "detox-session",
build: "detox-build",
project: "my-react-native-app",
},
},
},
};
BrowserStack’s sample uses a device name and OS version; use a currently supported real Android device configuration from its guide or account options. Session name, build, and project are labels that help identify the run. Keep the authentication values in environment variables; if your config loader does not read .detoxrc.js, place equivalent configuration in the Detox config file your project actually uses.
5. Run the tests and inspect the result
Run the cloud configuration from the project root:
npx detox test -c android.cloud.debug --loglevel trace
The command starts the cloud session using the patched Detox integration. Use the App Automate dashboard to inspect results and debugging details. BrowserStack’s guide says the session ID appears in CLI output and on the dashboard. Its session API can retrieve session details/logs using that ID:
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/detox/v2/android/sessions/SESSION_ID"
See the Detox setup guide for the current configuration and result workflow, and the Detox Android API overview for the API surface.
6. Connect local or private app services when needed
Uploading an app binary does not make private backend services reachable from a cloud device. If the app calls localhost, a staging system, or an internal network, start BrowserStack Local before running Detox and configure the Detox session to use it.
./BrowserStackLocal --key "$BROWSERSTACK_ACCESS_KEY"
Keep the tunnel running for the duration of the test. In the cloud session configuration, enable local; BrowserStack documents forcelocal to route all traffic through the internal network and localIdentifier to distinguish concurrent tunnel connections. For example:
session: {
server: "wss://detox.browserstack.com/init",
name: "detox-session",
build: "detox-build",
project: "my-react-native-app",
local: true,
forcelocal: true,
localIdentifier: "detox-ci-1",
}
Use the Detox Local Testing guide for operating-system binary setup, proxy requirements, and advanced network cases. This tunnel is separate from the Android loopback cleartext setting required by the app build.
Or skip the browser setup
If what you need is a screenshot of a website during development or from an AI agent, ScreenshotNeo is a website screenshot API and MCP server. It does not run Detox or test a native app; it captures a web URL with one GET request. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write("shot.webp", res);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, failed loads, and cache hits are not billed. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month with no card.
Configuration choices and practical limits
| Choice | What to do |
|---|---|
| Detox version | For version 20.51.3 and later, use the current @browserstack/detox package and verify its configuration against the guide. Earlier versions use the documented legacy package path. |
| Artifact type | The upload APIs support APK and AAB. Upload the application and app client to their separate endpoints. |
| Upload source | Use multipart file upload or a publicly accessible url. Use custom_id when a stable reference across uploads is useful. |
| Device | Set the Android cloud device name and OS version in the Detox device configuration. Verify availability with BrowserStack. |
| Local networking | Start BrowserStack Local and set session local options only when the app needs internal services. |
| Artifacts and logs | The sample enables failing logs and screenshots in CI and uses debug logging there. Choose artifact collection to fit your storage and debugging needs. |
| Artifact lifetime | Uploaded app and app-client builds expire after 30 days according to the API documentation. Re-upload and update IDs when needed. |
BrowserStack marks Detox support beta. Its docs do not establish a Detox-specific execution speed, concurrency allowance, or price for this workflow, so check current plan and product terms before estimating CI cost. For reliable runs, pin dependency versions, build matching app/client artifacts from one revision, preserve upload responses with the CI run, and re-upload expired builds.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| Detox app build fails after package replacement | Patched package integration or version mismatch | Confirm the package version against BrowserStack’s current guide, remove node_modules and lockfile as instructed, reinstall, and retry. The guide suggests trying the original Detox version if the app build still fails. |
| Upload request fails or returns an error | Bad credentials, wrong endpoint, missing file, or malformed multipart upload | Check the username and access key, verify the endpoint is app versus app-client, and ensure the path exists. Use -F "file=@path" with cURL. |
| Cloud run cannot locate app or test client | Wrong, stale, or mismatched bs:// IDs |
Use app_url for app and app_client_url for appClient. Confirm both uploads succeeded and came from compatible builds. |
| An earlier upload ID no longer works | Uploaded builds are deleted after 30 days | Upload both artifacts again and update the Detox configuration. The API response includes an expiry field. |
| App cannot reach localhost or a private API | BrowserStack cloud device has no tunnel to the internal network | Start BrowserStack Local before the test, enable local: true, and use the appropriate identifier or routing options. |
| Detox fails around loopback communication | Android network security config blocks cleartext loopback requests | Allow 127.0.0.1 in the app’s network_security_config.xml as documented, then rebuild both artifacts. |
| Expected APK path is missing | Different build type, flavor, or Gradle output layout | Inspect the Gradle build output and upload the actual app APK and generated Android test APK. Do not upload the app twice. |
| Test output lacks useful failure evidence | Debug logging or failure artifact collection is not enabled | Run with --loglevel trace, enable the documented failing log and screenshot plugins in CI, and inspect the dashboard or session API. |
FAQ
Does this workflow test Detox on iOS?
The documented BrowserStack Detox cloud flow described here is Android-focused. Do not infer cloud Detox iOS support from App Automate’s broader iOS device catalog or from local iOS simulator examples.
Can I upload an AAB instead of an APK?
Yes. The Android app and app-client upload API references list APK and AAB as supported formats. The build commands in this guide use APK outputs.
Do I need BrowserStack Local just to upload my app?
No. Local Testing is for connectivity from the cloud device to private or local services used by the app. It is separate from artifact upload.
Is BrowserStack Detox generally available?
BrowserStack’s getting-started guide currently describes Detox as beta, so verify current support and package guidance before relying on it for a release pipeline.


