From 0d277fa589e9508c04de38a4e4806d595f2134bf Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Fri, 17 Dec 2021 11:33:24 -0800 Subject: [PATCH] fix(selenium): allow setting additional capabilities (#11000) Also clarify docs about Selenium v4. --- docs/src/selenium-grid.md | 32 +++++++++++++++++-- .../src/server/chromium/chromium.ts | 16 ++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/src/selenium-grid.md b/docs/src/selenium-grid.md index 70f92c4e53..3c83c63a27 100644 --- a/docs/src/selenium-grid.md +++ b/docs/src/selenium-grid.md @@ -3,7 +3,7 @@ id: selenium-grid title: "Selenium Grid" --- -Playwright can connect to [Selenium Grid Hub](https://www.selenium.dev/documentation/grid/) to launch **Chrome** browser, instead of running browser on the local machine. To enable this mode, set `SELENIUM_REMOTE_URL` environment variable pointing to your Selenium Grid Hub. +Playwright can connect to [Selenium Grid Hub](https://www.selenium.dev/documentation/grid/) that runs **Selenium 4** to launch **Chrome** browser, instead of running browser on the local machine. To enable this mode, set `SELENIUM_REMOTE_URL` environment variable pointing to your Selenium Grid Hub. ```bash js # Playwright Test @@ -33,6 +33,34 @@ You don't have to change your code, just use [`method: BrowserType.launch`] as u When using Selenium Grid Hub, you can [skip browser downloads](./browsers.md#skip-browser-downloads). +If your grid requires additional capabilities to be set (for example, you use an external service), you can use `SELENIUM_REMOTE_CAPABILITIES` environment variable to provide JSON-serialized capabilities. + +```bash js +# Playwright Test +SELENIUM_REMOTE_URL=http://internal.grid:4444/wd/hub SELENIUM_REMOTE_CAPABILITIES="{'mygrid:options':{os:'windows',username:'John',password:'secure'}}" npx playwright test + +# Playwright Library +SELENIUM_REMOTE_URL=http://internal.grid:4444/wd/hub SELENIUM_REMOTE_CAPABILITIES="{'mygrid:options':{os:'windows',username:'John',password:'secure'}}" node script.js +``` + +```bash python +# With Pytest +SELENIUM_REMOTE_URL=http://internal.grid:4444/wd/hub SELENIUM_REMOTE_CAPABILITIES="{'mygrid:options':{os:'windows',username:'John',password:'secure'}}" pytest --browser chromium + +# Plain Python +SELENIUM_REMOTE_URL=http://internal.grid:4444/wd/hub SELENIUM_REMOTE_CAPABILITIES="{'mygrid:options':{os:'windows',username:'John',password:'secure'}}" python script.py +``` + +```bash java +SELENIUM_REMOTE_URL=http://internal.grid:4444/wd/hub SELENIUM_REMOTE_CAPABILITIES="{'mygrid:options':{os:'windows',username:'John',password:'secure'}}" mvn test +``` + +```bash csharp +SELENIUM_REMOTE_URL=http://internal.grid:4444/wd/hub SELENIUM_REMOTE_CAPABILITIES="{'mygrid:options':{os:'windows',username:'John',password:'secure'}}" dotnet test +``` + :::note -Internally, Playwright connects to the browser using [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) websocket. This requires Selenium Grid nodes to be directly accessible from the machine that runs Playwright. +Internally, Playwright connects to the browser using [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) websocket. Selenium 4 hub exposes this capability. + +Selenium 3 is supported in a best-effort manner. Grid nodes must be directly accessible from the machine that runs Playwright. ::: diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 9081b81878..42bf63147c 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -156,7 +156,16 @@ export class Chromium extends BrowserType { const args = this._innerDefaultArgs(options); args.push('--remote-debugging-port=0'); - const desiredCapabilities = { 'browserName': 'chrome', 'goog:chromeOptions': { args } }; + let desiredCapabilities = { 'browserName': 'chrome', 'goog:chromeOptions': { args } }; + try { + if (process.env.SELENIUM_REMOTE_CAPABILITIES) { + const parsed = JSON.parse(process.env.SELENIUM_REMOTE_CAPABILITIES); + desiredCapabilities = { ...desiredCapabilities, ...parsed }; + progress.log(` using additional capabilities "${process.env.SELENIUM_REMOTE_CAPABILITIES}"`); + } + } catch (e) { + progress.log(` ignoring additional capabilities "${process.env.SELENIUM_REMOTE_CAPABILITIES}": ${e}`); + } progress.log(` connecting to ${hubUrl}`); const response = await fetchData({ @@ -189,12 +198,15 @@ export class Chromium extends BrowserType { if (capabilities['se:cdp']) { // Selenium 4 - use built-in CDP websocket proxy. + progress.log(` using selenium v4`); const endpointURLString = addProtocol(capabilities['se:cdp']); endpointURL = new URL(endpointURLString); - endpointURL.hostname = new URL(hubUrl).hostname; + if (endpointURL.hostname === 'localhost' || endpointURL.hostname === '127.0.0.1') + endpointURL.hostname = new URL(hubUrl).hostname; progress.log(` retrieved endpoint ${endpointURL.toString()} for sessionId=${sessionId}`); } else { // Selenium 3 - resolve target node IP to use instead of localhost ws url. + progress.log(` using selenium v3`); const maybeChromeOptions = capabilities['goog:chromeOptions']; const chromeOptions = maybeChromeOptions && typeof maybeChromeOptions === 'object' ? maybeChromeOptions : undefined; const debuggerAddress = chromeOptions && typeof chromeOptions.debuggerAddress === 'string' ? chromeOptions.debuggerAddress : undefined;