var fs = require('fs') var path = require('path') var which = require('which') function isJSFlags (flag) { return flag.indexOf('--js-flags=') === 0 } function sanitizeJSFlags (flag) { var test = /--js-flags=(['"])/.exec(flag) if (!test) { return flag } var escapeChar = test[1] var endExp = new RegExp(escapeChar + '$') var startExp = new RegExp('--js-flags=' + escapeChar) return flag.replace(startExp, '--js-flags=').replace(endExp, '') } var ChromeBrowser = function (baseBrowserDecorator, args) { baseBrowserDecorator(this) var flags = args.flags || [] var userDataDir = args.chromeDataDir || this._tempDir this._getOptions = function (url) { // Chrome CLI options // http://peter.sh/experiments/chromium-command-line-switches/ flags.forEach(function (flag, i) { if (isJSFlags(flag)) { flags[i] = sanitizeJSFlags(flag) } }) return [ '--user-data-dir=' + userDataDir, // https://github.com/GoogleChrome/chrome-launcher/blob/master/docs/chrome-flags-for-tools.md#--enable-automation '--enable-automation', '--no-default-browser-check', '--no-first-run', '--disable-default-apps', '--disable-popup-blocking', '--disable-translate', '--disable-background-timer-throttling', // on macOS, disable-background-timer-throttling is not enough // and we need disable-renderer-backgrounding too // see https://github.com/karma-runner/karma-chrome-launcher/issues/123 '--disable-renderer-backgrounding', '--disable-device-discovery-notifications' ].concat(flags, [url]) } } // Return location of chrome.exe file for a given Chrome directory (available: "Chrome", "Chrome SxS"). function getChromeExe (chromeDirName) { // Only run these checks on win32 if (process.platform !== 'win32') { return null } var windowsChromeDirectory, i, prefix var suffix = '\\Google\\' + chromeDirName + '\\Application\\chrome.exe' var prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']] for (i = 0; i < prefixes.length; i++) { prefix = prefixes[i] try { windowsChromeDirectory = path.join(prefix, suffix) fs.accessSync(windowsChromeDirectory) return windowsChromeDirectory } catch (e) {} } return windowsChromeDirectory } var ChromiumBrowser = function (baseBrowserDecorator, args) { baseBrowserDecorator(this) var flags = args.flags || [] var userDataDir = args.chromeDataDir || this._tempDir this._getOptions = function (url) { // Chromium CLI options // http://peter.sh/experiments/chromium-command-line-switches/ flags.forEach(function (flag, i) { if (isJSFlags(flag)) { flags[i] = sanitizeJSFlags(flag) } }) return [ '--user-data-dir=' + userDataDir, '--no-default-browser-check', '--no-first-run', '--disable-default-apps', '--disable-popup-blocking', '--disable-translate', '--disable-background-timer-throttling' ].concat(flags, [url]) } } // Return location of Chromium's chrome.exe file. function getChromiumExe (chromeDirName) { // Only run these checks on win32 if (process.platform !== 'win32') { return null } var windowsChromiumDirectory, i, prefix var suffix = '\\Chromium\\Application\\chrome.exe' var prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']] for (i = 0; i < prefixes.length; i++) { prefix = prefixes[i] try { windowsChromiumDirectory = path.join(prefix, suffix) fs.accessSync(windowsChromiumDirectory) return windowsChromiumDirectory } catch (e) {} } return windowsChromiumDirectory } function getBin (commands) { // Don't run these checks on win32 if (process.platform !== 'linux') { return null } var bin, i for (i = 0; i < commands.length; i++) { try { if (which.sync(commands[i])) { bin = commands[i] break } } catch (e) {} } return bin } function getChromeDarwin (defaultPath) { if (process.platform !== 'darwin') { return null } try { var homePath = path.join(process.env.HOME, defaultPath) fs.accessSync(homePath) return homePath } catch (e) { return defaultPath } } ChromeBrowser.prototype = { name: 'Chrome', DEFAULT_CMD: { linux: getBin(['google-chrome', 'google-chrome-stable']), darwin: getChromeDarwin('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'), win32: getChromeExe('Chrome') }, ENV_CMD: 'CHROME_BIN' } ChromeBrowser.$inject = ['baseBrowserDecorator', 'args'] function headlessGetOptions (url, args, parent) { var mergedArgs = parent.call(this, url, args).concat([ '--headless', '--disable-gpu', '--disable-dev-shm-usage' ]) var isRemoteDebuggingFlag = function (flag) { return flag.indexOf('--remote-debugging-port=') !== -1 } return mergedArgs.some(isRemoteDebuggingFlag) ? mergedArgs : mergedArgs.concat(['--remote-debugging-port=9222']) } var ChromeHeadlessBrowser = function (baseBrowserDecorator, args) { ChromeBrowser.apply(this, arguments) var parentOptions = this._getOptions this._getOptions = function (url) { return headlessGetOptions.call(this, url, args, parentOptions) } } ChromeHeadlessBrowser.prototype = { name: 'ChromeHeadless', DEFAULT_CMD: { linux: getBin(['google-chrome', 'google-chrome-stable']), darwin: getChromeDarwin('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'), win32: getChromeExe('Chrome') }, ENV_CMD: 'CHROME_BIN' } ChromeHeadlessBrowser.$inject = ['baseBrowserDecorator', 'args'] function canaryGetOptions (url, args, parent) { // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0) var flags = args.flags || [] var augmentedFlags var customFlags = '--nocrankshaft --noopt' flags.forEach(function (flag) { if (isJSFlags(flag)) { augmentedFlags = sanitizeJSFlags(flag) + ' ' + customFlags } }) return parent.call(this, url).concat([augmentedFlags || '--js-flags=' + customFlags]) } var ChromeCanaryBrowser = function (baseBrowserDecorator, args) { ChromeBrowser.apply(this, arguments) var parentOptions = this._getOptions this._getOptions = function (url) { return canaryGetOptions.call(this, url, args, parentOptions) } } ChromeCanaryBrowser.prototype = { name: 'ChromeCanary', DEFAULT_CMD: { linux: getBin(['google-chrome-canary', 'google-chrome-unstable']), darwin: getChromeDarwin('/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'), win32: getChromeExe('Chrome SxS') }, ENV_CMD: 'CHROME_CANARY_BIN' } ChromeCanaryBrowser.$inject = ['baseBrowserDecorator', 'args'] var ChromeCanaryHeadlessBrowser = function (baseBrowserDecorator, args) { ChromeCanaryBrowser.apply(this, arguments) var parentOptions = this._getOptions this._getOptions = function (url) { return headlessGetOptions.call(this, url, args, parentOptions) } } ChromeCanaryHeadlessBrowser.prototype = { name: 'ChromeCanaryHeadless', DEFAULT_CMD: { linux: getBin(['google-chrome-canary', 'google-chrome-unstable']), darwin: getChromeDarwin('/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'), win32: getChromeExe('Chrome SxS') }, ENV_CMD: 'CHROME_CANARY_BIN' } ChromeCanaryHeadlessBrowser.$inject = ['baseBrowserDecorator', 'args'] ChromiumBrowser.prototype = { name: 'Chromium', DEFAULT_CMD: { // Try chromium-browser before chromium to avoid conflict with the legacy // chromium-bsu package previously known as 'chromium' in Debian and Ubuntu. linux: getBin(['chromium-browser', 'chromium']), darwin: '/Applications/Chromium.app/Contents/MacOS/Chromium', win32: getChromiumExe() }, ENV_CMD: 'CHROMIUM_BIN' } ChromiumBrowser.$inject = ['baseBrowserDecorator', 'args'] var ChromiumHeadlessBrowser = function (baseBrowserDecorator, args) { ChromiumBrowser.apply(this, arguments) var parentOptions = this._getOptions this._getOptions = function (url) { return headlessGetOptions.call(this, url, args, parentOptions) } } ChromiumHeadlessBrowser.prototype = { name: 'ChromiumHeadless', DEFAULT_CMD: { // Try chromium-browser before chromium to avoid conflict with the legacy // chromium-bsu package previously known as 'chromium' in Debian and Ubuntu. linux: getBin(['chromium-browser', 'chromium']), darwin: '/Applications/Chromium.app/Contents/MacOS/Chromium', win32: getChromiumExe() }, ENV_CMD: 'CHROMIUM_BIN' } var DartiumBrowser = function () { ChromeBrowser.apply(this, arguments) var checkedFlag = '--checked' var dartFlags = process.env.DART_FLAGS || '' var flags = dartFlags.split(' ') if (flags.indexOf(checkedFlag) === -1) { flags.push(checkedFlag) process.env.DART_FLAGS = flags.join(' ') } } DartiumBrowser.prototype = { name: 'Dartium', DEFAULT_CMD: {}, ENV_CMD: 'DARTIUM_BIN' } DartiumBrowser.$inject = ['baseBrowserDecorator', 'args'] // PUBLISH DI MODULE module.exports = { 'launcher:Chrome': ['type', ChromeBrowser], 'launcher:ChromeHeadless': ['type', ChromeHeadlessBrowser], 'launcher:ChromeCanary': ['type', ChromeCanaryBrowser], 'launcher:ChromeCanaryHeadless': ['type', ChromeCanaryHeadlessBrowser], 'launcher:Chromium': ['type', ChromiumBrowser], 'launcher:ChromiumHeadless': ['type', ChromiumHeadlessBrowser], 'launcher:Dartium': ['type', DartiumBrowser] } module.exports.test = { isJSFlags: isJSFlags, sanitizeJSFlags: sanitizeJSFlags, headlessGetOptions: headlessGetOptions, canaryGetOptions: canaryGetOptions }