@kronoslive/codeceptjs
Version:
Supercharged End 2 End Testing Framework for NodeJS
185 lines (151 loc) • 5.61 kB
JavaScript
const { fork } = require('child_process');
const path = require('path');
const crypto = require('crypto');
const runHook = require('../hooks');
const event = require('../event');
const collection = require('./run-multiple/collection');
const { clearString, replaceValueDeep } = require('../utils');
const {
getConfig, getTestRoot, fail,
} = require('./utils');
const runner = path.join(__dirname, '/../../bin/codecept');
let config;
const childOpts = {};
const copyOptions = ['override', 'steps', 'reporter', 'verbose', 'config', 'reporter-options', 'grep', 'fgrep', 'invert', 'debug', 'plugins', 'colors'];
let overrides = {};
// codeceptjs run-multiple smoke:chrome regression:firefox - will launch smoke run in chrome and regression in firefox
// codeceptjs run-multiple smoke:chrome regression - will launch smoke run in chrome and regression in firefox and chrome
// codeceptjs run-multiple --all - will launch all runs
// codeceptjs run-multiple smoke regression'
let runId = 1;
let subprocessCount = 0;
let totalSubprocessCount = 0;
let processesDone;
module.exports = async function (selectedRuns, options) {
// registering options globally to use in config
process.env.profile = options.profile;
const configFile = options.config;
const testRoot = getTestRoot(configFile);
global.codecept_dir = testRoot;
// copy opts to run
Object.keys(options)
.filter(key => copyOptions.indexOf(key) > -1)
.forEach((key) => {
childOpts[key] = options[key];
});
try {
overrides = JSON.parse(childOpts.override);
delete childOpts.override;
} catch (e) {
overrides = {};
}
config = {
...getConfig(configFile),
...overrides,
};
if (!config.multiple) {
fail('Multiple runs not configured, add "multiple": { /../ } section to config');
}
selectedRuns = options.all ? Object.keys(config.multiple) : selectedRuns;
if (!selectedRuns.length) {
fail('No runs provided. Use --all option to run all configured runs');
}
await runHook(config.bootstrapAll, 'bootstrapAll');
event.emit(event.multiple.before, null);
if (options.config) { // update paths to config path
if (config.tests) {
config.tests = path.resolve(testRoot, config.tests);
}
if (config.gherkin && config.gherkin.features) {
config.gherkin.features = path.resolve(testRoot, config.gherkin.features);
}
}
if (options.features) {
config.tests = '';
}
if (options.tests && config.gherkin) {
config.gherkin.features = '';
}
const childProcessesPromise = new Promise((resolve) => {
processesDone = resolve;
});
const runsToExecute = [];
collection.createRuns(selectedRuns, config).forEach((run) => {
const runName = run.getOriginalName() || run.getName();
const runConfig = run.getConfig();
runsToExecute.push(executeRun(runName, runConfig));
});
if (!runsToExecute.length) {
fail('Nothing scheduled for execution');
}
// Execute all forks
totalSubprocessCount = runsToExecute.length;
runsToExecute.forEach(runToExecute => runToExecute.call(this));
return childProcessesPromise.then(async () => {
// fire hook
await runHook(config.teardownAll, 'teardownAll');
event.emit(event.multiple.after, null);
});
};
function executeRun(runName, runConfig) {
// clone config
let overriddenConfig = { ...config };
// get configuration
const browserConfig = runConfig.browser;
const browserName = browserConfig.browser;
for (const key in browserConfig) {
overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key]);
}
let outputDir = `${runName}_`;
if (browserConfig.outputName) {
outputDir += typeof browserConfig.outputName === 'function' ? browserConfig.outputName() : browserConfig.outputName;
} else {
const hash = crypto.createHash('md5');
hash.update(JSON.stringify(browserConfig));
outputDir += hash.digest('hex');
}
outputDir += `_${runId}`;
outputDir = clearString(outputDir);
// tweaking default output directories and for mochawesome
overriddenConfig = replaceValueDeep(overriddenConfig, 'output', path.join(config.output, outputDir));
overriddenConfig = replaceValueDeep(overriddenConfig, 'reportDir', path.join(config.output, outputDir));
overriddenConfig = replaceValueDeep(overriddenConfig, 'mochaFile', path.join(config.output, outputDir, `${browserName}_report.xml`));
// override tests configuration
if (overriddenConfig.tests) {
overriddenConfig.tests = runConfig.tests;
}
if (overriddenConfig.gherkin && runConfig.gherkin && runConfig.gherkin.features) {
overriddenConfig.gherkin.features = runConfig.gherkin.features;
}
// override grep param and collect all params
const params = ['run',
'--child', `${runId++}.${runName}:${browserName}`,
'--override', JSON.stringify(overriddenConfig),
];
Object.keys(childOpts).forEach((key) => {
params.push(`--${key}`);
if (childOpts[key] !== true) params.push(childOpts[key]);
});
if (runConfig.grep) {
params.push('--grep');
params.push(runConfig.grep);
}
const onProcessEnd = (errorCode) => {
if (errorCode !== 0) {
process.exitCode = errorCode;
}
subprocessCount += 1;
if (subprocessCount === totalSubprocessCount) {
processesDone();
}
return errorCode;
};
// Return function of fork for later execution
return () => fork(runner, params, { stdio: [0, 1, 2, 'ipc'] })
.on('exit', (code) => {
return onProcessEnd(code);
})
.on('error', () => {
return onProcessEnd(1);
});
}