Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions bin/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ program.command('generate:helper [path]')
.description('Generates a new helper')
.action(require('../lib/command/generate').helper);

program.command('run [suite] [test]')
program.command('run [test]')
.description('Executes tests')

// codecept-only options
Expand All @@ -59,11 +59,11 @@ program.command('run [suite] [test]')
.option('--verbose', 'output internal logging information')
.option('-o, --override [value]', 'override current config options')
.option('--profile [value]', 'configuration profile to be used')
.option('--config [file]', 'configuration file to be used')
.option('-c, --config [file]', 'configuration file to be used')

// mocha options
.option('-c, --colors', 'force enabling of colors')
.option('-C, --no-colors', 'force disabling of colors')
.option('--colors', 'force enabling of colors')
.option('--no-colors', 'force disabling of colors')
.option('-G, --growl', 'enable growl notification support')
.option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
.option('-R, --reporter <name>', 'specify the reporter to use')
Expand All @@ -84,18 +84,19 @@ program.command('run [suite] [test]')

.action(require('../lib/command/run'));

program.command('run:multiple [suite] [test]')
program.command('run-multiple [suites...]')
.description('Executes tests multiple')
.option('-c, --config [file]', 'configuration file to be used')
.option('--all', 'run all suites')
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
.option('-f, --fgrep <string>', 'only run tests containing <string>')
.option('--steps', 'show step-by-step execution')
.option('--debug', 'output additional information')
.option('--verbose', 'output internal logging information')
.option('-o, --override [value]', 'override current config options')
.option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
.option('-R, --reporter <name>', 'specify the reporter to use')
.option('--recursive', 'include sub directories')

.option('--multiple <string>', 'run several instanses of codeceptjs to parallel test execution')

.action(require('../lib/command/run-multiple'));

if (process.argv.length <= 2) {
Expand Down
6 changes: 6 additions & 0 deletions examples/codecept.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
"mochaFile": "./output/result.xml"
}
},
"multiple": {
"default": {
"grep": "signin",
"browsers": ["chrome", "firefox"]
}
},
"name": "tests",
"bootstrap": "./bootstrap.js",
"include": {
Expand Down
1 change: 0 additions & 1 deletion lib/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ class Codecept {
init(dir, callback) {
// preparing globals
global.codecept_dir = dir;
global.suiteDetails = this.opts.child;
global.output_dir = fsPath.resolve(dir, this.config.output);
global.actor = global.codecept_actor = require('./actor');
global.Helper = global.codecept_helper = require('./helper');
Expand Down
219 changes: 102 additions & 117 deletions lib/command/run-multiple.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,141 +3,126 @@ let getConfig = require('./utils').getConfig;
let getTestRoot = require('./utils').getTestRoot;
let deepMerge = require('./utils').deepMerge;
let Codecept = require('../codecept');
var fork = require('child_process').fork
let fork = require('child_process').fork;
let output = require('../output');
const path = require('path');
const runner = path.join(__dirname, '/../../bin/codecept');
let config, childOpts = {};
const copyOptions = ['steps', 'reporter', 'verbose', 'config', 'reporter-options', 'grep', 'fgrep'];

module.exports = function (suite, test, options) {
// codeceptjs run:multiple smoke:chrome regression:firefox - will launch smoke suite in chrome and regression in firefox
// codeceptjs run:multiple smoke:chrome regression - will launch smoke suite in chrome and regression in firefox and chrome
// codeceptjs run:multiple all - will launch all suites
// codeceptjs run:multiple smoke regression'

module.exports = function (suites, options) {
// registering options globally to use in config
process.profile = options.profile;
let configFile = options.config;
let codecept;

let testRoot = getTestRoot(suite);
let config = getConfig(testRoot, configFile);
let testRoot = getTestRoot(configFile);
config = getConfig(configFile);

let configMultiple = config.multiple;

// override config with options
if (options.override) {
config = deepMerge(config, JSON.parse(options.override));
if (!configMultiple) {
throw new Error(`Multiple suites not configured, add "multiple": { /../ } section to config`);
}

let outputDir = config.output;
let defaultWindowSize = searchValueInObj('windowSize', config.helpers)

// run multiple codeceptjs with options
if (options.multiple) {
// get list of multiple options
let multipleOptions = options.multiple.replace(/ /g, "").split(';');
let browserList = ['chrome', 'firefox', 'ie', 'safari', 'edge'];
//iterate all multipleOptions
multipleOptions.forEach(function (multipleOption) {
// get suites
let browserSuites = multipleOption.split(':');
// if key in browser list, then seach suites for this browser
if (browserList.indexOf(browserSuites[0]) > -1) {
let browser = browserSuites[0];
let suites = browserSuites[1].split(',')
// iterate suites for browser
suites.forEach(function (suite) {
runnerHelper(browser, suite, config)
})
} else if (browserSuites[0] == 'all' && browserSuites.length == 1) {
let browsersConfig = Object.keys(config.multiple);
browsersConfig.forEach(function (browser) {
let suites = Object.keys(config.multiple[browser]);
suites.forEach(function (suite) {
runnerHelper(browser, suite, config)
})
});
} else if (browserSuites.length == 1 && browserSuites[0] != '') {
//run suite in all browsers
let suites = browserSuites[0].split(',');
suites.forEach(function (suite) {
let browserConfiguration = Object.keys(config.multiple)
browserConfiguration.forEach(function (browser) {
let configuration = config.multiple[browser];
if (configuration[suite]) {
runnerHelper(browser, suite, config)
}
})
})
} else throw new Error('Error while running codeceptjs multiply. Suites list:' + multipleOptions);
});
// copy opts to run
Object.keys(options)
.filter((key) => copyOptions.indexOf(key) > -1)
.forEach((key) => childOpts[key] = options[key]);

if (options.all) {
suites = Object.keys(configMultiple);
}

//iterate options
suites.forEach(function (suite) {

function runnerHelper(browser, suite, config) {
let suiteConf = config.multiple[browser][suite];
let sizes = suiteConf.windowSizes || ['default']
// override browser
let overriddenConfig = replaceValue(new getConfig(testRoot, configFile), 'browser',
(browser === 'ie') ? 'internet explorer' : browser)

sizes.forEach(function (size) {
if (size != 'default') {
//override windowSize
overriddenConfig = replaceValue(overriddenConfig, 'windowSize', size)
overriddenConfig =
replaceValue(overriddenConfig, 'output', outputDir + browser + '_' + suite + '_' + size + '/')
overriddenConfig =
replaceValue(overriddenConfig, 'reportDir', outputDir + browser + '_' + suite + '_' + size + '/')
} else {
overriddenConfig = replaceValue(overriddenConfig, 'windowSize', defaultWindowSize)
overriddenConfig = replaceValue(overriddenConfig, 'output', outputDir + browser + '_' + suite + '/')
overriddenConfig = replaceValue(overriddenConfig, 'reportDir', outputDir + browser + '_' + suite + '/')
}
// get suites
suite = suite.split(':');

//override grep param and collect all params
let params = ['run', '--child', browser + '_' + suite + '_' + size, '--grep', suiteConf.grep,
'--override', JSON.stringify(overriddenConfig)];
let suiteName = suite[0];
let browser = suite[1];
let suiteConf = configMultiple[suiteName];

for (let i = 3; i < process.argv.length; i++) {
if (process.argv[i].indexOf('--multiple=') < 0)
if (process.argv[i].indexOf('--multiple') < 0) {
params.push(process.argv[i]);
} else i++
}
fork(__dirname.replace('/lib/command', '/bin/codecept'), params, {
stdio: [0, 1, 2, 'ipc']
}).on('exit', (code) => {
if (code != 0) process.exitCode = 1;
}).on('error', (err) => {
process.exitCode = 1;
});
})
if (!suiteConf) {
throw new Error(`Suite ${suiteName} was not configured in "multiple" section of config`);
}

runSuite(suiteName, suiteConf, browser);
});
};

function runSuite(suite, suiteConf, browser) {
let suiteBrowsers = suiteConf.browsers.map((b) => typeof b === 'object' ? b.browser : b);

if (!browser) {
// run all browsers in this suite
suiteBrowsers.forEach((b) => runSuite(suite, suiteConf, b));
return;
}

function replaceValue(obj, key, value) {
/* search key in object recursive and replace value in it
*/
if (obj instanceof Array) {
for (var i in obj) {
replaceValue(obj[i], key, value);
}
}
if (obj[key]) obj[key] = value;
if ((typeof obj == "object") && (obj !== null)) {
var children = Object.keys(obj);
if (children.length > 0) {
for (i = 0; i < children.length; i++) {
replaceValue(obj[children[i]], key, value);
}
}
let overriddenConfig = config;

if (suiteBrowsers.indexOf(browser) === -1) {
throw new Error(`Browser ${browser} not found in multiple suite "${suite}" config`);
}

let browserConfig = suiteConf.browsers
.map((b) => typeof b === 'object' ? b : { browser: b })
.filter((b) => b.browser == browser)[0];

for (let key in browserConfig) {
overriddenConfig.helpers = replaceValue(overriddenConfig.helpers, key, browserConfig[key]);
}

// tweaking default output directories and for mochawesome
overriddenConfig = replaceValue(overriddenConfig, 'output', `${config.output}/${suite}_${browser}/`);
overriddenConfig = replaceValue(overriddenConfig, 'reportDir', `${config.output}/${suite}_${browser}/`);

// override grep param and collect all params
let params = ['run',
'--child', `${suite}:${browser}`,
'--override', JSON.stringify(overriddenConfig)
];

Object.keys(childOpts).forEach((key) => {
params.push('--' + key);
if (childOpts[key] !== true) params.push(childOpts[key]);
});

if (suiteConf.grep) {
params.push('--grep');
params.push(suiteConf.grep);
}

fork(runner, params, {stdio: [0, 1, 2, 'ipc']})
.on('exit', (code) => code === 0 ? null : process.exitCode = 1)
.on('error', (err) => process.exitCode = 1);
}


/**
* search key in object recursive and replace value in it
*/
function replaceValue(obj, key, value) {
if (obj instanceof Array) {
for (var i in obj) {
replaceValue(obj[i], key, value);
}
return obj;
};

function searchValueInObj(_for, _in) {
var r;
for (var p in _in) {
if (p === _for) {
return _in[p];
}
if (typeof _in[p] === 'object') {
if ((r = searchValueInObj(_for, _in[p])) !== null) {
return r;
}
}
if (obj[key]) obj[key] = value;
if (typeof obj === "object" && obj !== null) {
var children = Object.keys(obj);
if (children.length > 0) {
for (i = 0; i < children.length; i++) {
replaceValue(obj[children[i]], key, value);
}
}
return null;
}
};
return obj;
}
6 changes: 3 additions & 3 deletions lib/command/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ let deepMerge = require('./utils').deepMerge;
let Codecept = require('../codecept');
let output = require('../output');

module.exports = function (suite, test, options) {
module.exports = function (test, options) {
// registering options globally to use in config
process.profile = options.profile;
let configFile = options.config;
let codecept;

let testRoot = getTestRoot(suite);
let config = getConfig(testRoot, configFile);
let testRoot = getTestRoot(configFile);
let config = getConfig(configFile);

// override config with options
if (options.override) {
Expand Down
22 changes: 11 additions & 11 deletions lib/command/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,9 @@ let output = require("../output");
let fs = require('fs');
let path = require('path');

module.exports.getTestRoot = function (currentPath) {
let testsPath = path.resolve(currentPath || '.');
return testsPath;
};

module.exports.getConfig = function (testRoot, configFile) {
module.exports.getConfig = function (configFile) {

// using relative config
if (configFile && !path.isAbsolute(configFile)) {
configFile = path.join(testRoot, configFile);
}
let testRoot = getTestRoot(configFile);

let config,
manualConfigFile = configFile && path.resolve(configFile),
Expand Down Expand Up @@ -63,9 +55,17 @@ function deepMerge(target, source) {
}
return target;
}

module.exports.deepMerge = deepMerge;


function getTestRoot(currentPath) {
if (!currentPath) currentPath = '.';
if (!path.isAbsolute(currentPath)) currentPath = path.join(process.cwd(), currentPath);
return currentPath = !path.extname(currentPath) ? currentPath : path.dirname(currentPath);
}

module.exports.getTestRoot = getTestRoot;

function configWithDefaults(config) {
if (!config.include) config.include = {};
if (!config.helpers) config.helpers = {};
Expand Down
Loading