webdriverio-automation
Version:
WebdriverIO-Automation android ios project
317 lines (259 loc) • 9.58 kB
JavaScript
"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;