{"id":15043951,"url":"https://github.com/mykola-mokhnach/appium-devtools-plugin","last_synced_at":"2025-10-23T15:30:28.065Z","repository":{"id":195069781,"uuid":"691740180","full_name":"mykola-mokhnach/appium-devtools-plugin","owner":"mykola-mokhnach","description":"Appium plugin that adds proxy features to Android mobile webviews","archived":false,"fork":false,"pushed_at":"2024-03-25T12:02:39.000Z","size":33,"stargazers_count":3,"open_issues_count":4,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-30T19:11:14.785Z","etag":null,"topics":["android","appium","cdp","devtools","proxy"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mykola-mokhnach.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-14T19:38:11.000Z","updated_at":"2025-01-10T17:12:44.000Z","dependencies_parsed_at":"2023-11-17T14:06:05.354Z","dependency_job_id":"c63c6989-81f5-4236-93d2-ca708450178c","html_url":"https://github.com/mykola-mokhnach/appium-devtools-plugin","commit_stats":null,"previous_names":["mykola-mokhnach/appium-devtools-plugin"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mykola-mokhnach%2Fappium-devtools-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mykola-mokhnach%2Fappium-devtools-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mykola-mokhnach%2Fappium-devtools-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mykola-mokhnach%2Fappium-devtools-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mykola-mokhnach","download_url":"https://codeload.github.com/mykola-mokhnach/appium-devtools-plugin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237843841,"owners_count":19375213,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","appium","cdp","devtools","proxy"],"created_at":"2024-09-24T20:49:52.021Z","updated_at":"2025-10-23T15:30:28.055Z","avatar_url":"https://github.com/mykola-mokhnach.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Appium Devtools Plugin\n\nThis plugin is created to expose [Chrome Devtools Protocol](https://chromedevtools.github.io/devtools-protocol/) API from a **mobile Android** browser or a webview to the running Appium server.\nAfterwards this API could be used to establish a connection to it from an external client and to\nperform an extended automation, like [performance metrics gathering](https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/#collect-performance-metrics) or [geolocation emulation](https://www.selenium.dev/documentation/webdriver/bidirectional/chrome_devtools/#emulate-geo-location-with-the-remote-webdriver).\n\n\u003e [!IMPORTANT]\n\u003e Since major version *1.0.0*, this plugin is only compatible with Appium 3.\n\n## Features\n\n* Adds the following execute methods:\n  - [devtools: listTargets](#devtools-listtargets)\n  - [devtools: proxyTarget](#devtools-proxytarget)\n  - [devtools: unproxyTarget](#devtools-unproxytarget)\n* Adds the following server HTTP endpoints:\n  - GET /cdp/:uuid/:alias/json/version\n  - GET /cdp/:uuid/:alias/json/list\n  - GET /cdp/:uuid/:alias/json/\n  - GET /cdp/:uuid/:alias/json/protocol\n  - PUT /cdp/:uuid/:alias/json/new\n  - GET /cdp/:uuid/:alias/json/activate/:targetId\n  - GET /cdp/:uuid/:alias/json/close/:targetId\n  - GET /cdp/:uuid/:alias/devtools/inspector.html\n* Adds the following Websocket endpoints\n  - /cdp/:uuid/:alias/devtools/browser\n  - /cdp/:uuid/:alias/devtools/browser/:browserId\n  - /cdp/:uuid/:alias/devtools/page/:pageId\n\n## Prerequisites\n\n* Appium Server 3.0+\n* [UIAutomator2](https://github.com/appium/appium-uiautomator2-driver) or [Espresso](https://github.com/appium/appium-espresso-driver) driver\n\n## Installation - Server\n\nInstall the plugin using Appium's plugin CLI:\n\n```\nappium plugin install --source npm appium-devtools-plugin\n```\n\n## Installation - Client\n\nOn the client side this plugin requires a proper the [Chrome Devtools Protocol](https://chromedevtools.github.io/devtools-protocol/) client implementation. For example, it should be included into the [Selenium Java library](https://github.com/SeleniumHQ/selenium/tree/trunk/java/src/org/openqa/selenium/devtools).\n\n## Activation\n\nThe plugin will not be active unless turned on when invoking the Appium server:\n\n```\nappium --use-plugins=devtools\n```\n\n## Execute Methods\n\n### devtools: listTargets\n\nScans if there are any active CDP sockets in the system and exposes the list of such sockets with their properties. Also, the API exposes which sockets are currently being proxied and which are not.\n\n#### Returned Result\n\nAn object with a single `targets` property. This is a list of entries, where each has the following properties:\n\nName | Type | Description | Example\n--- | --- | --- | ---\nname | string | The name of the CDP socket. Usually starts with `@` | @chrome_devtools_remote\npages | map[] | The list of pages in this webview. The output of [/json/list](https://chromedevtools.github.io/devtools-protocol/) endpoint | [ {\"description\": \"\", \"devtoolsFrontendUrl\": \"/devtools/inspector.html?ws=localhost:9222/devtools/page/DAB7FB6187B554E10B0BD18821265734\", \"id\": \"DAB7FB6187B554E10B0BD18821265734\", \"title\": \"Yahoo\", \"type\": \"page\", \"url\": \"https://www.yahoo.com/\", \"webSocketDebuggerUrl\": \"ws://localhost:9222/devtools/page/DAB7FB6187B554E10B0BD18821265734\"} ]\ninfo | map | The basic information about the current webview. This is the output of [/json/version](https://chromedevtools.github.io/devtools-protocol/) endpoint | {\"Browser\": \"Chrome/72.0.3601.0\", \"Protocol-Version\": \"1.3\", \"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3601.0 Safari/537.36\", \"V8-Version\": \"7.2.233\", \"WebKit-Version\": \"537.36 (@cfede9db1d154de0468cb0538479f34c0755a0f4)\", \"webSocketDebuggerUrl\": \"ws://localhost:9222/devtools/browser/b0b8a4fb-bb17-4359-9533-a8d9f3908bd8\"}\nisProxied | boolean | Whether the current webview is being proxied | false\nproxyInfo | map | The proxy information if the webview is being proxied, which consists of the following entries: `alias`, `name`, `root`, `uuid`. It is always `null` if `isProxied` is false. | {\"alias\":\"1ca57bc449240dfeb716e8a5adb95849bcfdd49f\",\"name\":\"@chrome_devtools_remote\",\"uuid\": \"ed3c4d2f-7b34-4563-9a3c-e6af20418500\", \"root\":\"http://127.0.0.1:4723/cdp/ed3c4d2f-7b34-4563-9a3c-e6af20418500/1ca57bc449240dfeb716e8a5adb95849bcfdd49f\"}\n\n### devtools: proxyTarget\n\nStarts the proxy for the given CDP target name. An exception is thrown if the target is already being proxied or does not exist.\n\n#### Arguments\n\nName | Type | Required | Description | Example\n--- | --- | --- | --- | ---\nname | string | yes | The name of the webview socket to proxy. Usually starts with `@`. Could be retrieved from [devtools: listTargets](#devtools-listtargets) output | @chrome_devtools_remote\nport | number | no | The port number on the Appium server machine to be used for the proxy. The port must not be in use. If not provided then a random free port from the [15900, 16000] range will be selected. | 12345\n\n#### Returned Result\n\nIf the corresponding CDP API has been forwarded successfully then the following map is returned\n\nName | Type | Description | Example\n--- | --- | --- | ---\nname | string | The name of the webview being forwarded | @chrome_devtools_remote\nalias | string | Unique alias for the given webview name. It is used to construct the forward URL | 1ca57bc449240dfeb716e8a5adb95849bcfdd49f\nuuid | string | The unique identifier of the current plugin instance. It is used to construct the forward URL | ed3c4d2f-7b34-4563-9a3c-e6af20418500\nroot | string | The forwarding root URL used as a base for all CDP endpoints | http://127.0.0.1:4723/cdp/ed3c4d2f-7b34-4563-9a3c-e6af20418500/1ca57bc449240dfeb716e8a5adb95849bcfdd49f\n\n### devtools: unproxyTarget\n\nStops the proxy for the given CDP target name. An exception is thrown if the target is not being proxied or does not exist.\n\n#### Arguments\n\nName | Type | Required | Description | Example\n--- | --- | --- | --- | ---\nname | string | yes | The name of the webview socket to stop the proxy for. Usually starts with `@`. Could be retrieved from [devtools: listTargets](#devtools-listtargets) output | @chrome_devtools_remote\n\n## Usage\n\n```java\n\nprivate AndroidDriver driver;\nprivate static final Pattern BROWSER_MAJOR_VER_PATTERN = Pattern.compile(\"/(\\\\d+)\");\n\n@BeforeEach\nvoid setup() {\n  // Espresso driver is supported as well\n  UiAutomator2Options options = new UiAutomator2Options();\n  driver = new AndroidDriver(options);\n}\n\n@AfterEach\nvoid teardown(){\n  driver.quit();\n}\n\nprivate DevTools initDevTools(URI uri, String browserMajorVersion) {\n  HttpClient.Factory factory = HttpClient.Factory.createDefault();\n  HttpClient client = CdpEndpointFinder.getHttpClient(factory, uri);\n  URI cdpUri = CdpEndpointFinder.getCdpEndPoint(client);\n  Connection connection = new Connection(client, cdpUri.toString());\n  CdpInfo cdpInfo = new CdpVersionFinder()\n    .match(browserMajorVersion)\n    .orElseThrow(\n      () -\u003e new RuntimeException(\n        String.format(\n          \"Unable to find version of CDP to use for %s. You may need to include a\"\n              + \" dependency on a specific version of the CDP using something\"\n              + \" similar to `org.seleniumhq.selenium:selenium-devtools-v86:%s`\"\n              + \" where the version (\\\"v86\\\") matches the version of the\"\n              + \" chromium-based browser you're using and the version number of the\"\n              + \" artifact is the same as Selenium's.\",\n          browserMajorVersion, new BuildInfo().getReleaseLabel()\n        )\n      );\n    );\n  return new DevTools(cdpInfo::getDomains, connection);\n}\n\nprivate String extractMajorBrowserVersion(String version) {\n  Matcher matcher = BROWSER_MAJOR_VER_PATTERN.matcher(version);\n  if (!matcher.find()) {\n    throw new RuntimeException(String.format(\n      \"No major browser version could be parsed from '%s'\", version\n    ));\n  }\n  return matcher.group(1);\n}\n\n@Test\nvoid verifyMobileCdp() {\n  // ....\n  // manipulate the App under test or the browser, so a web view is active\n  // ....\n  Map\u003cString, Object\u003e wvInfo = (Map\u003cString, Object\u003e) driver.executeScript(\"devtools: listTargets\");\n  List\u003cMap\u003cString, Object\u003e\u003e targets = (List\u003cMap\u003cString, Object\u003e\u003e) wvInfo.get(\"targets\");\n  // There might be multiple targets or none\n  // depending on which web views are currently active and debuggable.\n  // We just want to interact with the very first one in this example\n  Map\u003cString, Object\u003e target = (Map\u003cString, Object\u003e) targets.get(0);\n  String wvName = (String) target.get(\"name\");\n  Map\u003cString, Object\u003e proxyInfo = (Map\u003cString, Object\u003e) driver.executeScript(\n    \"devtools: proxyTarget\",\n    Map.of(\"name\", wvName)\n  );\n\n  // Ususally, it looks like \"Chrome/91.0.4472.114\"\n  String browserVersion = (String) target.get(\"Browser\");\n  try {\n    DevTools devtools = initDevtools(\n      Uri.parse((String) proxyInfo.get(\"root\")),\n      extractMajorBrowserVersion(browserVersion)\n    );\n\n    devTools.createSession();\n    devTools.send(Network.clearBrowserCookies());\n    devTools.send(Network.setCacheDisabled(true));\n    // ...\n    // Continue doing crazy stuff with the devtools object\n    // ...\n  } finally {\n    // This will be done anyway automatically upon quitting the driver\n    driver.executeScript(\n      \"devtools: unproxyTarget\",\n      Map.of(\"name\", wvName)\n    );\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmykola-mokhnach%2Fappium-devtools-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmykola-mokhnach%2Fappium-devtools-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmykola-mokhnach%2Fappium-devtools-plugin/lists"}