Playwright Web Testing on Real Android Devices
Overview
This guide explains how to run Playwright web tests on real Android devices using Pcloudy. Instead of relying on emulated device profiles, you can connect Playwright to physical Android devices and test websites in the actual Chrome browser. The guide covers the prerequisites, project setup, Pcloudy configuration, test execution, session management, and troubleshooting steps required to get started with real device web testing.
1. What This Setup Actually Does
Pcloudy is a real-device cloud: instead of testing on emulators, your Playwright script connects to a genuine physical Android phone sitting in Pcloudy's lab and drives the actual Chrome browser installed on it.
This works because Android exposes two open, standard interfaces that any automation tool can use:
- ADB (Android Debug Bridge) — Google's official bridge for reaching any Android device and the Chrome instance running on it.
- CDP (Chrome DevTools Protocol) — the same protocol Playwright already uses to control desktop Chromium. Pcloudy exposes the device's Chrome over ADB, and Playwright forwards a CDP connection to it.
Because both are open, Google-sanctioned protocols, no emulator or approximation is involved — you get real touch input, real rendering, and real hardware performance.
Important distinction:
Playwright's built-in device emulation (e.g. picking a "Pixel 7" profile) just resizes a desktop browser and changes a user-agent string — it never touches real hardware. The Pcloudy connection described in this guide is different: it's a real physical device, not an emulation profile.
Why this only works for Android, not iOS
Apple does not expose an ADB-equivalent bridge, and Mobile Safari cannot be driven by CDP — it only speaks Apple's own private remote-debugging protocol, which Playwright doesn't support. Playwright's "WebKit" engine is also not the real Mobile Safari binary. This is a platform restriction, not a Playwright limitation, and it is confirmed directly in Playwright's own GitHub issue tracker and documentation.
2. Prerequisites
| Requirement | Notes |
|---|---|
| Node.js | Check the project's .nvmrc file for the exact version expected (commonly v20.x). A newer version may still work but can cause subtle issues. |
| npm | Comes bundled with Node.js — no separate install needed. |
| Pcloudy Account | You need a username and an API key from your Pcloudy dashboard. |
| Network Access | Your machine/CI runner must reach Pcloudy's WebSocket endpoint (e.g. wss://device.pcloudy.com/playwright). |
3. Installing Node.js on
3.1 Windows:
Download and run the LTS .msi installer from nodejs.org, keeping "Add to PATH" checked. Verify with:
node -v
npm -v
3.2 macOS:
Install Node via the official .pkg installer from nodejs.org, or via Homebrew (brew install node), or via nvm:
nvm install 20
nvm use 20
Verify with:
node -v
npm -v
3.3 Linux:
Install via your distro's package manager or nvm (recommended, avoids permission issues):
nvm install 20
nvm use 20
Verify with:
node -v
npm -v
If "node" is not recognized after installing
Windows:
Check whether C:\Program Files\nodejs\node.exe exists. If missing, reinstall. If present but still not recognized, add C:\Program Files\nodejs\ to your system PATH manually via Environment Variables settings, then reopen your terminal.
macOS / Linux:
Run which node. If it returns nothing, Node isn't installed or isn't on PATH. If you installed via nvm, run nvm use 20 in the current shell (nvm doesn't persist across new terminal tabs unless added to ~/.zshrc or ~/.bashrc). Re-source your shell config with source ~/.zshrc (or ~/.bash_profile) if needed.
4. Project Structure Gotchas
Sample projects downloaded as zip files sometimes extract with a duplicated nested folder, for example:
playwright-android-sample
└── playwright-android-sample\
├── package-lock.json <- not the real root
└── playwright-android-sample\ <- this is the actual project root
├── package.json
├── constants\
├── test\
└ ── README.md
If npm install fails with an ENOENT ... package.json error, run "dir" and check whether there's another folder with the same name nested one level deeper. The real project root is the folder that actually contains package.json.
5. Installing Project Dependencies
From inside the correct project root (the folder containing package.json):
npm install
If PowerShell blocks this with a script-execution error
You'll see something like:
npm.ps1 cannot be loaded because running scripts is disabled on this system
This is Windows' default PowerShell security policy, not an npm problem. Two ways to fix it:
- Option A — switch to Command Prompt (fastest, one-time use): open cmd instead of PowerShell and run npm install there — Command Prompt isn't subject to this policy.
- Option B — allow local scripts in PowerShell (better long-term fix): run the command below and confirm with Y.
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
This permits locally-created scripts to run while still blocking unsigned scripts downloaded from the internet.
6. Configuring Pcloudy Credentials
Sample projects typically read credentials from a constants/constants.js file:
export const credentials = {
pCloudy_Username: "YOUR_PCLOUDY_USERNAME",
pCloudy_ApiKey: "YOUR_PCLOUDY_API_KEY",
};
Replace both placeholder values with your actual Pcloudy account username and API key before running any test. These credentials authenticate your session with the device cloud.
7. The Capability Object
The minimum required configuration to open a real Chrome session on a Pcloudy Android device looks like this:
const capability = {
browserName: "chrome",
"pcloudy:options": {
pcloudy_Username: "YOUR_PCLOUDY_USERNAME",
pcloudy_ApiKey: "YOUR_PCLOUDY_API_KEY",
platformName: "android",
pcloudy_DeviceFullName: "<DEVICE-FULLNAME>",
},
};
Keep this minimal on purpose. Fewer fields means fewer opportunities for a mismatch between what you request and what Pcloudy can actually provision. Only add extra options (resolution, orientation, network profile, app-under-test) once a basic session connects successfully.
Check the device name carefully — Pcloudy_DeviceFullName must exactly match a device name available on your Pcloudy account. You can see available devices from the Pcloudy dashboard.
8. Running the Test
npm test
This typically runs a script like test/sample.test.js, which connects to Pcloudy's WebSocket endpoint, opens the target site on the real device, logs the page title, and saves a screenshot (commonly to a Screenshot/ folder).
Mandatory: always close the page
Every test — including failure paths — must explicitly close the page, or the device session gets left in an unknown state and can't be safely reused:
try {
// ... test steps ...
} finally {
await page.close(); // releases the session for reuse
}
Skipping this can block the device slot and cause subsequent test runs to fail to acquire a device — even if your credentials and device name are correct.
9. Troubleshooting Common Errors
browserType.connectOverCDP: Invalid URL: 404/index.htm
Log output shows the WebSocket connecting, then immediately failing and falling back to a 404 page. This almost always means Pcloudy's backend rejected the session request before a real device could be handed out. Common causes, in order of likelihood:
- Incorrect or expired API key / username — double-check both values in constants.js against your Pcloudy dashboard exactly (no extra spaces).
- Device name mismatch — Pcloudy_DeviceFullName doesn't match any device currently available on your account.
- Account/session limits — no available device slots at that moment, or your plan doesn't include the requested device.
Start by re-verifying credentials and device name — these two account for the vast majority of this error.
Error: listen EADDRINUSE: address already in use ::1:9323
This is unrelated to Pcloudy — it means something (usually a leftover Playwright HTML report server from a previous run) is still holding port 9323. Fix it with:
netstat -ano | findstr :9323
taskkill /PID <that_number> /F
Note - The number at the end of the matching line from the first command — that's the process ID (PID) to use in the second command. Re-run your original command afterward.
'node' is not recognized as an internal or external command
See Section 3 — this is a PATH issue, fixed by reinstalling Node.js with "Add to PATH" checked, or manually adding C:\Program Files\nodejs\ to your system PATH.
PowerShell "running scripts is disabled on this system"
See Section 5 — use Command Prompt instead, or run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned.
npm install fails with ENOENT ... package.json
You're not in the actual project root. See Section 4 — look for a nested duplicate folder.
10. Quick Reference
| Item | Requirement |
|---|---|
| Platform | Android only — iOS real devices are not supported by Playwright's Android/CDP model |
| Browser driven | Chrome, via ADB + Chrome DevTools Protocol |
| Project type | Standard Node.js project with Playwright installed |
| Capability essentials | browserName, pCloudy_Username, pCloudy_ApiKey, platformName, pCloudy_DeviceFullName |
| Credentials location | Typically constants/constants.js |
| Session hygiene | page.close() in a finally block — mandatory |
| Common error #1 | 404/index.htm on connect → check credentials + device name |
| Common error #2 | EADDRINUSE :9323 → kill the leftover process holding that port |
11. Reference Links
- Playwright Android API — authoritative source for the connection model
- Playwright Emulation docs — explains device profiles vs. real devices
- Playwright Browsers/WebKit docs — explains the iOS limitation
Did this page help you?