UNPKG

webdriverio-automation

Version:

WebdriverIO-Automation android ios project

317 lines (259 loc) 9.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _path = _interopRequireDefault(require("path")); var _fsExtra = _interopRequireDefault(require("fs-extra")); var _asyncExitHook = _interopRequireDefault(require("async-exit-hook")); var _logger = _interopRequireDefault(require("@wdio/logger")); var _config = require("@wdio/config"); var _utils = require("@wdio/utils"); var _interface = _interopRequireDefault(require("./interface")); var _utils2 = require("./utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const log = (0, _logger.default)('@wdio/cli:launcher'); class Launcher { constructor(configFilePath, args = {}, isWatchMode = false) { this.args = args; this.configFilePath = configFilePath; this.configParser = new _config.ConfigParser(); this.configParser.addConfigFile(configFilePath); this.configParser.merge(args); const config = this.configParser.getConfig(); const capabilities = this.configParser.getCapabilities(); this.isWatchMode = isWatchMode; if (config.outputDir) { _fsExtra.default.ensureDirSync(_path.default.join(config.outputDir)); process.env.WDIO_LOG_PATH = _path.default.join(config.outputDir, 'wdio.log'); } _logger.default.setLogLevelsConfig(config.logLevels, config.logLevel); const totalWorkerCnt = Array.isArray(capabilities) ? capabilities.map(c => this.configParser.getSpecs(c.specs, c.exclude).length).reduce((a, b) => a + b, 0) : 1; const Runner = (0, _utils.initialisePlugin)(config.runner, 'runner').default; this.runner = new Runner(configFilePath, config); this.interface = new _interface.default(config, totalWorkerCnt, this.isWatchMode); config.runnerEnv.FORCE_COLOR = Number(this.interface.hasAnsiSupport); this.isMultiremote = !Array.isArray(capabilities); this.exitCode = 0; this.hasTriggeredExitRoutine = false; this.hasStartedAnyProcess = false; this.schedule = []; this.rid = []; this.runnerStarted = 0; this.runnerFailed = 0; } async run() { (0, _asyncExitHook.default)(this.exitHandler.bind(this)); let exitCode; let error; try { const config = this.configParser.getConfig(); const caps = this.configParser.getCapabilities(); const { ignoredWorkerServices, launcherServices } = (0, _utils.initialiseLauncherService)(config, caps); this.launcher = launcherServices; this.args.ignoredWorkerServices = ignoredWorkerServices; await this.runner.initialise(); log.info('Run onPrepare hook'); await (0, _utils2.runLauncherHook)(config.onPrepare, config, caps); await (0, _utils2.runServiceHook)(this.launcher, 'onPrepare', config, caps); exitCode = await this.runMode(config, caps); log.info('Run onComplete hook'); await (0, _utils2.runServiceHook)(this.launcher, 'onComplete', exitCode, config, caps); const onCompleteResults = await (0, _utils2.runOnCompleteHook)(config.onComplete, config, caps, exitCode, this.interface.result); exitCode = onCompleteResults.includes(1) ? 1 : exitCode; await _logger.default.waitForBuffer(); this.interface.finalise(); } catch (err) { error = err; } finally { if (!this.hasTriggeredExitRoutine) { this.hasTriggeredExitRoutine = true; await this.runner.shutdown(); } } if (error) { throw error; } return exitCode; } runMode(config, caps) { if (!caps || !this.isMultiremote && !caps.length) { return new Promise(resolve => { log.error('Missing capabilities, exiting with failure'); return resolve(1); }); } const specFileRetries = this.isWatchMode ? 0 : config.specFileRetries; let cid = 0; if (this.isMultiremote) { this.schedule.push({ cid: cid++, caps, specs: this.configParser.getSpecs(caps.specs, caps.exclude).map(s => ({ files: [s], retries: specFileRetries })), availableInstances: config.maxInstances || 1, runningInstances: 0 }); } else { for (let capabilities of caps) { this.schedule.push({ cid: cid++, caps: capabilities, specs: this.configParser.getSpecs(capabilities.specs, capabilities.exclude).map(s => ({ files: [s], retries: specFileRetries })), availableInstances: capabilities.maxInstances || config.maxInstancesPerCapability, runningInstances: 0 }); } } return new Promise(resolve => { this.resolve = resolve; if (Object.values(this.schedule).reduce((specCnt, schedule) => specCnt + schedule.specs.length, 0) === 0) { log.error('No specs found to run, exiting with failure'); return resolve(1); } if (this.runSpecs()) { resolve(0); } }); } runSpecs() { let config = this.configParser.getConfig(); if (this.hasTriggeredExitRoutine) { return true; } while (this.getNumberOfRunningInstances() < config.maxInstances) { let schedulableCaps = this.schedule.filter(() => { const filter = typeof config.bail !== 'number' || config.bail < 1 || config.bail > this.runnerFailed; if (!filter) { this.schedule.forEach(t => { t.specs = []; }); } return filter; }).filter(() => this.getNumberOfRunningInstances() < config.maxInstances).filter(a => a.availableInstances > 0).filter(a => a.specs.length > 0).sort((a, b) => a.runningInstances > b.runningInstances); if (schedulableCaps.length === 0) { break; } let specs = schedulableCaps[0].specs.shift(); this.startInstance(specs.files, schedulableCaps[0].caps, schedulableCaps[0].cid, specs.rid, specs.retries); schedulableCaps[0].availableInstances--; schedulableCaps[0].runningInstances++; } return this.getNumberOfRunningInstances() === 0 && this.getNumberOfSpecsLeft() === 0; } getNumberOfRunningInstances() { return this.schedule.map(a => a.runningInstances).reduce((a, b) => a + b); } getNumberOfSpecsLeft() { return this.schedule.map(a => a.specs.length).reduce((a, b) => a + b); } async startInstance(specs, caps, cid, rid, retries) { var _context; let config = this.configParser.getConfig(); cid = rid || this.getRunnerId(cid); let processNumber = this.runnerStarted + 1; let debugArgs = []; let debugType; let debugHost = ''; let debugPort = process.debugPort; for (let i in process.execArgv) { const debugArgs = process.execArgv[i].match('--(debug|inspect)(?:-brk)?(?:=(.*):)?'); if (debugArgs) { let [, type, host] = debugArgs; if (type) { debugType = type; } if (host) { debugHost = `${host}:`; } } } if (debugType) { debugArgs.push(`--${debugType}=${debugHost}${debugPort + processNumber}`); } let capExecArgs = [...(config.execArgv || []), ...(caps.execArgv || [])]; let defaultArgs = capExecArgs.length ? process.execArgv : []; let execArgv = [...defaultArgs, ...debugArgs, ...capExecArgs]; this.runnerStarted++; log.info('Run onWorkerStart hook'); await (0, _utils2.runLauncherHook)(config.onWorkerStart, cid, caps, specs, this.args, execArgv); await (0, _utils2.runServiceHook)(this.launcher, 'onWorkerStart', cid, caps, specs, this.args, execArgv); const worker = this.runner.run({ cid, command: 'run', configFile: this.configFilePath, args: this.args, caps, specs, execArgv, retries }); worker.on('message', (_context = this.interface).onMessage.bind(_context)); worker.on('error', (_context = this.interface).onMessage.bind(_context)); worker.on('exit', this.endHandler.bind(this)); } getRunnerId(cid) { if (!this.rid[cid]) { this.rid[cid] = 0; } return `${cid}-${this.rid[cid]++}`; } endHandler({ cid, exitCode, specs, retries }) { const passed = this.isWatchModeHalted() || exitCode === 0; if (!passed && retries > 0) { const requeue = this.configParser.getConfig().specFileRetriesDeferred !== false ? 'push' : 'unshift'; this.schedule[parseInt(cid)].specs[requeue]({ files: specs, retries: retries - 1, rid: cid }); } else { this.exitCode = this.isWatchModeHalted() ? 0 : this.exitCode || exitCode; this.runnerFailed += !passed ? 1 : 0; } if (!this.isWatchModeHalted()) { this.interface.emit('job:end', { cid, passed, retries }); } cid = parseInt(cid, 10); this.schedule[cid].availableInstances++; this.schedule[cid].runningInstances--; const shouldRunSpecs = this.runSpecs(); if (!shouldRunSpecs || this.isWatchMode && !this.hasTriggeredExitRoutine) { return; } this.resolve(passed ? this.exitCode : 1); } exitHandler(callback) { if (!callback) { return; } if (this.hasTriggeredExitRoutine) { return callback(); } this.hasTriggeredExitRoutine = true; this.interface.sigintTrigger(); return this.runner.shutdown().then(callback); } isWatchModeHalted() { return this.isWatchMode && this.hasTriggeredExitRoutine; } } var _default = Launcher; exports.default = _default;