UNPKG

webdriver-manager-replacement

Version:
322 lines 12.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const childProcess = require("child_process"); const fs = require("fs"); const loglevel = require("loglevel"); const os = require("os"); const path = require("path"); const request = require("request"); const provider_1 = require("./provider"); const cloud_storage_xml_1 = require("./utils/cloud_storage_xml"); const file_utils_1 = require("./utils/file_utils"); const http_utils_1 = require("./utils/http_utils"); const version_list_1 = require("./utils/version_list"); const log = loglevel.getLogger('webdriver-manager'); class SeleniumServer extends provider_1.ProviderClass { constructor(config) { super(); this.cacheFileName = 'selenium-server.xml'; this.configFileName = 'selenium-server.config.json'; this.ignoreSSL = false; this.osType = os.type(); this.osArch = os.arch(); this.outDir = provider_1.OUT_DIR; this.port = 4444; this.proxy = null; this.requestUrl = 'https://selenium-release.storage.googleapis.com/'; this.runAsNode = false; this.runAsDetach = false; this.logLevel = null; this.javaOpts = {}; this.version = null; this.maxVersion = null; this.cacheFileName = this.setVar('cacheFileName', this.cacheFileName, config); this.configFileName = this.setVar('configFileName', this.configFileName, config); this.ignoreSSL = this.setVar('ignoreSSL', this.ignoreSSL, config); this.osArch = this.setVar('osArch', this.osArch, config); this.osType = this.setVar('osType', this.osType, config); this.outDir = this.setVar('outDir', this.outDir, config); this.port = this.setVar('port', this.port, config); this.proxy = this.setVar('proxy', this.proxy, config); this.requestUrl = this.setVar('requestUrl', this.requestUrl, config); this.runAsNode = this.setVar('runAsNode', this.runAsNode, config); this.runAsDetach = this.setVar('runAsDetach', this.runAsDetach, config); if (this.runAsDetach) { this.runAsNode = true; } this.version = this.setVar('version', this.version, config); this.maxVersion = this.setVar('maxVersion', this.maxVersion, config); this.logLevel = this.setVar('logLevel', this.logLevel, config); if (this.logLevel) { this.setJavaFlag('-Dselenium.LOGGER.level', this.logLevel); } } /** * Should update the cache and download, find the version to download, * then download that binary. * @param version Optional to provide the version number or latest. * @param maxVersion Optional to provide the max version. */ async updateBinary(version, maxVersion) { if (!version) { version = this.version; } if (!maxVersion) { maxVersion = this.maxVersion; } await cloud_storage_xml_1.updateXml(this.requestUrl, { fileName: path.resolve(this.outDir, this.cacheFileName), ignoreSSL: this.ignoreSSL, proxy: this.proxy }); const versionList = cloud_storage_xml_1.convertXmlToVersionList(path.resolve(this.outDir, this.cacheFileName), 'selenium-server-standalone', versionParser, semanticVersionParser); const versionObj = version_list_1.getVersion(versionList, '', version, maxVersion); const seleniumServerUrl = this.requestUrl + versionObj.url; const seleniumServerJar = path.resolve(this.outDir, versionObj.name); // We should check the jar file size if it exists. The size will // be used to either make the request, or quit the request if the file // size matches. let fileSize = 0; try { fileSize = fs.statSync(seleniumServerJar).size; } catch (err) { } await http_utils_1.requestBinary(seleniumServerUrl, { fileName: seleniumServerJar, fileSize, ignoreSSL: this.ignoreSSL, proxy: this.proxy }); file_utils_1.generateConfigFile(this.outDir, path.resolve(this.outDir, this.configFileName), matchBinaries(), seleniumServerJar); return Promise.resolve(); } /** * Starts selenium standalone server and handles emitted exit events. * @param opts The options to pass to the jar file. * @param version The optional version of the selenium jar file. * @returns A promise so the server can run while awaiting its completion. */ startServer(opts, version) { const java = this.getJava(); return new Promise(async (resolve, _) => { if (this.runAsDetach) { this.runAsNode = true; const cmd = this.getCmdStartServer(opts, version); log.info(`${java} ${cmd.join(' ')}`); this.seleniumProcess = childProcess.spawn(java, cmd, { detached: true, stdio: 'ignore' }); log.info(`selenium process id: ${this.seleniumProcess.pid}`); await new Promise((resolve, _) => { setTimeout(resolve, 2000); }); this.seleniumProcess.unref(); await new Promise((resolve, _) => { setTimeout(resolve, 500); }); resolve(0); } else { const cmd = this.getCmdStartServer(opts, version); log.info(`${java} ${cmd.join(' ')}`); this.seleniumProcess = childProcess.spawn(java, cmd, { stdio: 'inherit' }); log.info(`selenium process id: ${this.seleniumProcess.pid}`); this.seleniumProcess.on('exit', (code) => { log.info(`Selenium Standalone has exited with code: ${code}`); resolve(code); }); this.seleniumProcess.on('error', (err) => { log.error(`Selenium Standalone server encountered an error: ${err}`); }); } }); } /** * Get the binary file path. * @param version Optional to provide the version number or the latest. */ getBinaryPath(version) { try { const configFilePath = path.resolve(this.outDir, this.configFileName); return file_utils_1.getBinaryPathFromConfig(configFilePath, version); } catch (_) { return null; } } /** * Sets a java flag option. * @param key The java option flag. * @param value The value of the flag. */ setJavaFlag(key, value) { if (value) { this.javaOpts[key] = value; } } /** * Get the selenium server start command (not including the java command) * @param opts The options to pass to the jar file. * @param version The optional version of the selenium jar file. * @returns The spawn arguments array. */ getCmdStartServer(opts, version) { const jarFile = this.getBinaryPath(version); const options = []; if (opts) { for (const opt of Object.keys(opts)) { options.push(`${opt}=${opts[opt]}`); } } options.push('-jar'); options.push(jarFile); if (this.runAsNode) { options.push('-role'); options.push('node'); options.push('-servlet'); options.push('org.openqa.grid.web.servlet.LifecycleServlet'); options.push('-registerCycle'); options.push('0'); } options.push('-port'); options.push(this.port.toString()); return options; } /** * Gets the java command either by the JAVA_HOME environment variable or * just the java command. */ getJava() { let java = 'java'; if (process.env.JAVA_HOME) { java = path.resolve(process.env.JAVA_HOME, 'bin', 'java'); if (this.osType === 'Windows_NT') { java += '.exe'; } } return java; } /** * If we are running the selenium server role = node, send * the command to stop the server via http get request. Reference: * https://github.com/SeleniumHQ/selenium/issues/2852#issuecomment-268324091 * * If we are not running as the selenium server role = node, kill the * process with pid. * * @param host The protocol and ip address, default http://127.0.0.1 * @param port The port number, default 4444 * @returns A promise of the http get request completing. */ stopServer(host, port) { if (this.runAsNode) { if (!host) { host = 'http://127.0.0.1'; } if (!port) { port = this.port; } const stopUrl = host + ':' + port + '/extra/LifecycleServlet?action=shutdown'; const options = http_utils_1.initOptions(stopUrl, {}); log.info(http_utils_1.curlCommand(options)); return new Promise((resolve, _) => { const req = request(options); req.on('response', response => { response.on('end', () => { resolve(); }); }); }); } else if (this.seleniumProcess) { process.kill(this.seleniumProcess.pid); return Promise.resolve(); } else { return Promise.reject('Could not stop the server, server is not running.'); } } /** * Gets a comma delimited list of versions downloaded. Also has the "latest" * downloaded noted. */ getStatus() { try { const configFilePath = path.resolve(this.outDir, this.configFileName); const configJson = JSON.parse(fs.readFileSync(configFilePath).toString()); const versions = []; for (const binaryPath of configJson['all']) { let version = ''; const regex = /.*selenium-server-standalone-(\d+.\d+.\d+.*).jar/g; try { const exec = regex.exec(binaryPath); if (exec && exec[1]) { version = exec[1]; } } catch (_) { } if (configJson['last'] === binaryPath) { version += ' (latest)'; } versions.push(version); } return versions.join(', '); } catch (_) { return null; } } /** * Get a line delimited list of files removed. */ cleanFiles() { return file_utils_1.removeFiles(this.outDir, [/selenium-server.*/g]); } } exports.SeleniumServer = SeleniumServer; /** * Captures the version name which includes the semantic version and extra * metadata. So an example for 12.34/selenium-server-standalone-12.34.56.jar, * the version is 12.34.56. For metadata, * 12.34/selenium-server-standalone-12.34.56-beta.jar is 12.34.56-beta. * @param xmlKey The xml key including the partial url. */ function versionParser(xmlKey) { // Capture the version name 12.34.56 or 12.34.56-beta const regex = /.*selenium-server-standalone-(\d+.\d+.\d+.*).jar/g; try { return regex.exec(xmlKey)[1]; } catch (_) { return null; } } exports.versionParser = versionParser; /** * Captures the version name which includes the semantic version and extra * metadata. So an example for 12.34/selenium-server-standalone-12.34.56.jar, * the version is 12.34.56. For metadata, * 12.34/selenium-server-standalone-12.34.56-beta.jar is still 12.34.56. * @param xmlKey The xml key including the partial url. */ function semanticVersionParser(xmlKey) { // Only capture numbers 12.34.56 const regex = /.*selenium-server-standalone-(\d+.\d+.\d+).*.jar/g; try { return regex.exec(xmlKey)[1]; } catch (_) { return null; } } exports.semanticVersionParser = semanticVersionParser; /** * Matches the installed binaries. */ function matchBinaries() { return /selenium-server-standalone-\d+.\d+.\d+.*.jar/g; } exports.matchBinaries = matchBinaries; //# sourceMappingURL=selenium_server.js.map