Skip to main content
Documentation›Playwright›Playwright Web Testing On Android Devices

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

RequirementNotes
Node.jsCheck the project's .nvmrc file for the exact version expected (commonly v20.x). A newer version may still work but can cause subtle issues.
npmComes bundled with Node.js — no separate install needed.
Pcloudy AccountYou need a username and an API key from your Pcloudy dashboard.
Network AccessYour 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

ItemRequirement
PlatformAndroid only — iOS real devices are not supported by Playwright's Android/CDP model
Browser drivenChrome, via ADB + Chrome DevTools Protocol
Project typeStandard Node.js project with Playwright installed
Capability essentialsbrowserName, pCloudy_Username, pCloudy_ApiKey, platformName, pCloudy_DeviceFullName
Credentials locationTypically constants/constants.js
Session hygienepage.close() in a finally block — mandatory
Common error #1404/index.htm on connect → check credentials + device name
Common error #2EADDRINUSE :9323 → kill the leftover process holding that port

Did this page help you?