diff --git a/CHANGELOG.md b/CHANGELOG.md index 95758e75..d2ed81b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added +- Added `--clipboardPermission` to enable clipboard permission for Chromium. + ## 14.1.5 - 2024-10-18 ### Added diff --git a/README.md b/README.md index c9bfe793..ea46a9c9 100644 --- a/README.md +++ b/README.md @@ -91,12 +91,11 @@ You can start a remote selenium docker but bedrock does not play nice with this ## Why is the junit XML output not pretty-printed? -It's to do with "escaping" CDATA end tokens ("]]>") in the output. -If we pretty-print it, we get extraneous whitespace in the junit output rendered in Jenkins. +It's to do with "escaping" CDATA end tokens ("]]>") in the output. +If we pretty-print it, we get extraneous whitespace in the junit output rendered in Jenkins. See the comment in modules/server/src/main/ts/bedrock/core/Reporter.ts # Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) - diff --git a/modules/server/src/main/ts/bedrock/auto/Driver.ts b/modules/server/src/main/ts/bedrock/auto/Driver.ts index b5665878..60c707e5 100644 --- a/modules/server/src/main/ts/bedrock/auto/Driver.ts +++ b/modules/server/src/main/ts/bedrock/auto/Driver.ts @@ -1,4 +1,3 @@ - import * as path from 'path'; import * as childProcess from 'child_process'; import * as os from 'os'; @@ -33,6 +32,7 @@ export interface DriverSettings { browserVersion: string; tunnel?: Tunnel; name?: string; + clipboardPermission?: boolean; } export interface Driver { @@ -111,7 +111,8 @@ const getOptions = (port: number, browserName: string, settings: DriverSettings, // https://stackoverflow.com/questions/43261516/selenium-chrome-i-just-cant-use-driver-maximize-window-to-maximize-window const caps: Record = options.capabilities; if (browserName === 'chrome') { - addArguments(caps, 'goog:chromeOptions', ['--start-maximized', '--disable-extensions']); + const chromeArgs = ['--start-maximized', '--disable-extensions', '--enable-clipboard']; + addArguments(caps, 'goog:chromeOptions', chromeArgs); addArguments(caps, 'goog:chromeOptions', extraCaps); } else if (browserName === 'firefox') { addArguments(caps, 'moz:firefoxOptions', extraCaps); @@ -136,9 +137,10 @@ const getOptions = (port: number, browserName: string, settings: DriverSettings, 'devtools.chrome.enabled': true }; } else if (browserName === 'chrome') { - addArguments(caps, 'goog:chromeOptions', [ '--headless', '--remote-debugging-port=' + debuggingPort ]); + const headlessArgs = ['--headless', '--remote-debugging-port=' + debuggingPort]; + addArguments(caps, 'goog:chromeOptions', headlessArgs); if (settings.useSandboxForHeadless) { - addArguments(caps, 'goog:chromeOptions', [ '--no-sandbox' ]); + addArguments(caps, 'goog:chromeOptions', ['--no-sandbox']); } } } @@ -159,20 +161,20 @@ const getOptions = (port: number, browserName: string, settings: DriverSettings, }; const logDriverDetails = (driver: WebdriverIO.Browser, headless: boolean, debuggingPort: number) => { - const caps: Record = driver.capabilities; + const caps = driver.capabilities as any; const browserName = caps.browserName; const browserVersion = caps.browserVersion || caps.version; if (browserName === 'chrome') { - console.log('chrome version:', browserVersion, 'driver:', caps.chrome.chromedriverVersion); + console.log('chrome version:', browserVersion); } else if (browserName === 'firefox') { - console.log('firefox version:', browserVersion, 'driver:', caps['moz:geckodriverVersion']); + console.log('firefox version:', browserVersion); } else if (browserName === 'phantomjs') { - console.log('phantom version:', browserVersion, 'driver:', caps.driverVersion); + console.log('phantom version:', browserVersion); } else if (browserName === 'MicrosoftEdge') { console.log('Edge version:', browserVersion); } else if (browserName === 'msedge') { - console.log('MSEdge version:', browserVersion, 'driver:', caps.msedge.msedgedriverVersion); + console.log('MSEdge version:', browserVersion); } if (headless) { @@ -194,9 +196,9 @@ const setupShutdown = (driver: WebdriverIO.Browser, driverApi: DriverLoader.Driv const driverShutdown = async (immediate?: boolean) => { try { if (immediate) { - driver.deleteSession(); + await driver.deleteSession(); } else { - await driver.pause(shutdownDelay); + await new Promise(resolve => setTimeout(resolve, shutdownDelay)); await driver.deleteSession(); } } finally { @@ -240,22 +242,14 @@ const getDriverSpec = (settings: DriverSettings, browserName: string): DriverLoa const driverSetup = async (driver: WebdriverIO.Browser, settings: DriverSettings, debuggingPort: number): Promise => { // Browsers have a habit of reporting via the webdriver that they're ready before they are (particularly FireFox). - // setTimeout is a temporary solution, VAN-66 has been logged to investigate properly - await driver.pause(1500); + await new Promise(resolve => setTimeout(resolve, 1500)); // Log driver details logDriverDetails(driver, settings.headless, debuggingPort); - // Some tests require large windows, so make it as large as it can be. - // Headless modes can't use maximize, so just set the dimensions to 1280x1024 - if (settings.headless) { - await driver.setWindowSize(1280, 1024); - } else { - await driver.maximizeWindow(); - } - - return Promise.resolve(); + // Focus the browser window + await focusBrowser((driver.capabilities as any).browserName, settings); }; /* Settings: @@ -293,7 +287,7 @@ export const create = async (settings: DriverSettings): Promise => { // Wait for the driver to start up and then start the webdriver session await DriverLoader.startAndWaitForAlive(driverSpec, port, webdriverTimeout); - + const driver = await WebdriverIO.remote(webdriverOptions); // IEDriverServer ignores a delete session call if done too quickly so it needs a small delay @@ -304,7 +298,6 @@ export const create = async (settings: DriverSettings): Promise => { const driverShutdown = setupShutdown(driver, driverSpec.driverApi, shutdownDelay); await driverSetup(driver, settings, debuggingPort); - await focusBrowser(browserName, settings); // Return the public driver api return { @@ -315,9 +308,9 @@ export const create = async (settings: DriverSettings): Promise => { try { driverSpec.driverApi.stop(); } catch { - // Ignore + // Ignore } return Promise.reject(e); } } -}; \ No newline at end of file +}; diff --git a/modules/server/src/main/ts/bedrock/cli/ClOptions.ts b/modules/server/src/main/ts/bedrock/cli/ClOptions.ts index 6703f3fb..fc30fd6d 100644 --- a/modules/server/src/main/ts/bedrock/cli/ClOptions.ts +++ b/modules/server/src/main/ts/bedrock/cli/ClOptions.ts @@ -395,3 +395,11 @@ export const webdriverPort: ClOption = { defaultValue: 4444, uncommon: true }; + +export const clipboardPermission: ClOption = { + name: 'clipboardPermission', + type: Boolean, + defaultValue: true, + description: 'Enable clipboard permission for Chromium', + validate: Extraction.any +}; diff --git a/modules/server/src/main/ts/bedrock/cli/Clis.ts b/modules/server/src/main/ts/bedrock/cli/Clis.ts index e30cc842..8e7979ac 100644 --- a/modules/server/src/main/ts/bedrock/cli/Clis.ts +++ b/modules/server/src/main/ts/bedrock/cli/Clis.ts @@ -58,7 +58,8 @@ export const forAuto = (directories: Directories, argv: string[] = process.argv) ClOptions.devicefarmRegion, ClOptions.browserVersion, ClOptions.platformName, - ClOptions.useSelenium + ClOptions.useSelenium, + ClOptions.clipboardPermission ]), argv) as Attempt; }; diff --git a/modules/server/src/test/ts/ClipboardPermissionTest.ts b/modules/server/src/test/ts/ClipboardPermissionTest.ts new file mode 100644 index 00000000..e60310d2 --- /dev/null +++ b/modules/server/src/test/ts/ClipboardPermissionTest.ts @@ -0,0 +1,67 @@ +import { assert } from 'chai'; +import * as Driver from '../../main/ts/bedrock/auto/Driver'; +import * as webdriverio from 'webdriverio'; + +describe('ClipboardPermission', function () { + this.timeout(10000); // Increase timeout to 10 seconds + + it('should add clipboard permission for Chrome when enabled', async () => { + const settings: Driver.DriverSettings = { + basedir: '', + browser: 'chrome', + headless: false, + useSandboxForHeadless: false, + extraBrowserCapabilities: '', + verbose: false, + browserVersion: 'latest', + clipboardPermission: true + }; + + let capturedOptions: any; + const originalRemote = (webdriverio as any).remote; + (webdriverio as any).remote = (opts: any) => { + capturedOptions = opts; + return Promise.resolve({ capabilities: { browserName: 'chrome', version: '1.0' } }); + }; + + try { + await Driver.create(settings); + + const chromeOptions = capturedOptions.capabilities['goog:chromeOptions']; + assert.isTrue(chromeOptions.args.includes('--enable-clipboard'), + 'Chrome options should include --enable-clipboard when clipboardPermission is true'); + } finally { + (webdriverio as any).remote = originalRemote; + } + }); + + it('should not add clipboard permission for Chrome when disabled', async () => { + const settings: Driver.DriverSettings = { + basedir: '', + browser: 'chrome', + headless: false, + useSandboxForHeadless: false, + extraBrowserCapabilities: '', + verbose: false, + browserVersion: 'latest', + clipboardPermission: false + }; + + let capturedOptions: any; + const originalRemote = (webdriverio as any).remote; + (webdriverio as any).remote = (opts: any) => { + capturedOptions = opts; + return Promise.resolve({ capabilities: { browserName: 'chrome', version: '1.0' } }); + }; + + try { + await Driver.create(settings); + + const chromeOptions = capturedOptions.capabilities['goog:chromeOptions']; + assert.isFalse(chromeOptions.args.includes('--enable-clipboard'), + 'Chrome options should not include --enable-clipboard when clipboardPermission is false'); + } finally { + (webdriverio as any).remote = originalRemote; + } + }); +});