codeceptjs
Advanced tools
Comparing version 0.6.1 to 0.6.2
@@ -0,1 +1,17 @@ | ||
## 0.6.2 | ||
* Added `config` object to [public API](http://codecept.io/hooks/#api) | ||
* Extended `index.js` to include `actor` and `helpers`, so they could be required: | ||
```js | ||
const actor = require('codeceptjs').actor; | ||
``` | ||
* Added [example for creating custom runner](http://codecept.io/hooks/#custom-runner) with public API. | ||
* run command to create `output` directory if it doesn't exist | ||
* [Protractor] fixed loading globally installed Protractor | ||
* run-multiple command improvements: | ||
* create output directories for each process | ||
* print process ids in output | ||
## 0.6.1 | ||
@@ -2,0 +18,0 @@ |
'use strict'; | ||
let Mocha = require('mocha/lib/mocha'); | ||
let fsPath = require('path'); | ||
@@ -8,4 +7,4 @@ let readFileSync = require('fs').readFileSync; | ||
let statSync = require('fs').statSync; | ||
let container = require('./container'); | ||
let reporter = require('./reporter/cli'); | ||
let Container = require('./container'); | ||
let Config = require('./config'); | ||
let event = require('../lib/event'); | ||
@@ -16,21 +15,28 @@ let glob = require('glob'); | ||
const scenarioUi = fsPath.join(__dirname, './interfaces/bdd.js'); | ||
/** | ||
* CodeceptJS runner | ||
*/ | ||
class Codecept { | ||
/** | ||
* Create CodeceptJS runner. | ||
* Config and options should be passed | ||
* | ||
* @param {*} config | ||
* @param {*} opts | ||
*/ | ||
constructor(config, opts) { | ||
this.config = config; | ||
this.opts = opts; | ||
this.config = config; | ||
this.mocha = new Mocha(Object.assign(config.mocha, opts)); | ||
this.mocha.ui(scenarioUi); | ||
if (opts.reporter) { | ||
// custom reporters | ||
this.mocha.reporter(opts.reporter, this.reporterOptions()); | ||
} else { | ||
// standard reporter. TODO: make them run side by side | ||
this.mocha.reporter(reporter, opts); | ||
} | ||
this.testFiles = []; | ||
} | ||
/** | ||
* Initialize CodeceptJS at specific directory. | ||
* If async initialization is required pass callbacke as second parameter. | ||
* | ||
* @param {*} dir | ||
* @param {*} callback | ||
*/ | ||
init(dir, callback) { | ||
@@ -46,7 +52,12 @@ // preparing globals | ||
// initializing listeners | ||
container.create(this.config); | ||
Container.create(this.config, this.opts); | ||
this.bootstrap(callback); | ||
} | ||
// loading bootstrap | ||
/** | ||
* Executes hooks and bootstrap. | ||
* If bootstrap is async second parameter is required. | ||
* | ||
* @param {*} done | ||
*/ | ||
bootstrap(done) { | ||
@@ -61,3 +72,3 @@ | ||
// custom hooks | ||
(this.config.hooks || []).forEach((hook) => runHook(hook)); | ||
this.config.hooks.forEach((hook) => runHook(hook)); | ||
@@ -68,3 +79,8 @@ // bootstrap | ||
// loading teardown | ||
/** | ||
* Executes teardown. | ||
* If teardown is async a parameter is provided. | ||
* | ||
* @param {*} done | ||
*/ | ||
teardown(done) { | ||
@@ -74,2 +90,7 @@ runHook(this.config.teardown, done, 'teardown'); | ||
/** | ||
* Loads tests by pattern or by config.tests | ||
* | ||
* @param {optional} pattern | ||
*/ | ||
loadTests(pattern) { | ||
@@ -82,26 +103,14 @@ pattern = pattern || this.config.tests; | ||
reporterOptions() { | ||
var reporterOptions = Object.assign(this.config.mocha.reporterOptions || {}); | ||
if (this.opts.reporterOptions !== undefined) { | ||
this.opts.reporterOptions.split(",").forEach(function (opt) { | ||
var L = opt.split("="); | ||
if (L.length > 2 || L.length === 0) { | ||
throw new Error("invalid reporter option '" + opt + "'"); | ||
} else if (L.length === 2) { | ||
reporterOptions[L[0]] = L[1]; | ||
} else { | ||
reporterOptions[L[0]] = true; | ||
} | ||
}); | ||
} | ||
return reporterOptions; | ||
} | ||
/** | ||
* Run a specific test or all loaded tests. | ||
* | ||
* @param {optional} test | ||
*/ | ||
run(test) { | ||
this.mocha.files = this.testFiles; | ||
let mocha = Container.mocha(); | ||
mocha.files = this.testFiles; | ||
if (test) { | ||
this.mocha.files = this.mocha.files.filter((t) => fsPath.basename(t, '_test.js') === test || fsPath.basename(t, '.js') === test || fsPath.basename(t) === test); | ||
mocha.files = mocha.files.filter((t) => fsPath.basename(t, '_test.js') === test || fsPath.basename(t, '.js') === test || fsPath.basename(t) === test); | ||
} | ||
this.mocha.run().on('end', () => { | ||
mocha.run().on('end', () => { | ||
let done = () => { | ||
@@ -114,2 +123,3 @@ event.emit(event.all.result, this); | ||
static version() { | ||
@@ -116,0 +126,0 @@ return JSON.parse(readFileSync(__dirname + '/../package.json', 'utf8')).version; |
'use strict'; | ||
let getConfig = require('./utils').getConfig; | ||
let getTestRoot = require('./utils').getTestRoot; | ||
let fail = require('./utils').fail; | ||
let deepMerge = require('./utils').deepMerge; | ||
let Codecept = require('../codecept'); | ||
let Config = require('../config'); | ||
let fork = require('child_process').fork; | ||
@@ -18,2 +20,4 @@ let output = require('../output'); | ||
let suiteId = 1; | ||
module.exports = function (suites, options) { | ||
@@ -31,3 +35,3 @@ // registering options globally to use in config | ||
if (!configMultiple) { | ||
throw new Error(`Multiple suites not configured, add "multiple": { /../ } section to config`); | ||
fail(`Multiple suites not configured, add "multiple": { /../ } section to config`); | ||
} | ||
@@ -43,2 +47,5 @@ | ||
} | ||
if (!suites.length) { | ||
fail('No suites provided. Use --all option to run all configured suites'); | ||
} | ||
@@ -72,3 +79,4 @@ //iterate options | ||
let overriddenConfig = config; | ||
// clone config | ||
let overriddenConfig = Object.assign({}, config); | ||
@@ -87,9 +95,11 @@ if (suiteBrowsers.indexOf(browser) === -1) { | ||
let outputDir = suite + JSON.stringify(browserConfig).replace(/[^\d\w]+/g, '_') + suiteId; | ||
// tweaking default output directories and for mochawesome | ||
overriddenConfig = replaceValue(overriddenConfig, 'output', `${config.output}/${suite}_${browser}/`); | ||
overriddenConfig = replaceValue(overriddenConfig, 'reportDir', `${config.output}/${suite}_${browser}/`); | ||
overriddenConfig = replaceValue(overriddenConfig, 'output', path.join(config.output, outputDir)); | ||
overriddenConfig = replaceValue(overriddenConfig, 'reportDir', path.join(config.output, outputDir)); | ||
// override grep param and collect all params | ||
let params = ['run', | ||
'--child', `${suite}:${browser}`, | ||
'--child', `${suiteId++}.${suite}:${browser}`, | ||
'--override', JSON.stringify(overriddenConfig) | ||
@@ -118,2 +128,3 @@ ]; | ||
function replaceValue(obj, key, value) { | ||
if (!obj) return; | ||
if (obj instanceof Array) { | ||
@@ -127,6 +138,4 @@ for (var i in obj) { | ||
var children = Object.keys(obj); | ||
if (children.length > 0) { | ||
for (i = 0; i < children.length; i++) { | ||
replaceValue(obj[children[i]], key, value); | ||
} | ||
for (i = 0; i < children.length; i++) { | ||
replaceValue(obj[children[i]], key, value); | ||
} | ||
@@ -133,0 +142,0 @@ } |
'use strict'; | ||
let getConfig = require('./utils').getConfig; | ||
let getTestRoot = require('./utils').getTestRoot; | ||
let deepMerge = require('./utils').deepMerge; | ||
let Codecept = require('../codecept'); | ||
let output = require('../output'); | ||
const getConfig = require('./utils').getConfig; | ||
const getTestRoot = require('./utils').getTestRoot; | ||
const deepMerge = require('./utils').deepMerge; | ||
const fileExists = require('../utils').fileExists; | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const Config = require('../config'); | ||
const Codecept = require('../codecept'); | ||
const output = require('../output'); | ||
@@ -12,11 +16,15 @@ module.exports = function (test, options) { | ||
let configFile = options.config; | ||
let codecept; | ||
let codecept, outputDir; | ||
let testRoot = getTestRoot(configFile); | ||
let config = getConfig(configFile); | ||
// override config with options | ||
if (options.override) { | ||
config = deepMerge(config, JSON.parse(options.override)); | ||
config = Config.append(JSON.parse(options.override)); | ||
} | ||
if (!fileExists(outputDir = path.join(testRoot, config.output))) { | ||
output.print('creating output directory: ' + outputDir); | ||
fs.mkdirSync(outputDir); | ||
} | ||
try { | ||
@@ -23,0 +31,0 @@ codecept = new Codecept(config, options); |
'use strict'; | ||
let fileExists = require('../utils').fileExists; | ||
let isFile = require('../utils').isFile; | ||
let output = require("../output"); | ||
let fs = require('fs'); | ||
let path = require('path'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const output = require('../output'); | ||
// alias to deep merge | ||
module.exports.deepMerge = require('../utils').deepMerge; | ||
module.exports.getConfig = function (configFile) { | ||
let testRoot = getTestRoot(configFile); | ||
let config, | ||
manualConfigFile = configFile && path.resolve(configFile), | ||
jsConfigFile = path.join(testRoot, 'codecept.conf.js'), | ||
jsConfigFileDeprecated = path.join(testRoot, 'codecept.js'), | ||
jsonConfigFile = path.join(testRoot, 'codecept.json'); | ||
if (isFile(manualConfigFile)) { // --config option provided | ||
if (path.extname(manualConfigFile) === '.js') { | ||
return configWithDefaults(require(manualConfigFile).config); | ||
} | ||
return configWithDefaults(JSON.parse(fs.readFileSync(manualConfigFile, 'utf8'))); | ||
try { | ||
return require('../config').load(configFile); | ||
} catch (err) { | ||
fail(err.message); | ||
} | ||
if (isFile(jsConfigFile)) { // js config file | ||
return configWithDefaults(require(jsConfigFile).config); | ||
} | ||
if (isFile(jsConfigFileDeprecated)) { // deprecated js config file | ||
console.log('Using codecept.js as configuration is deprecated, please rename it to codecept.conf.js'); | ||
return configWithDefaults(require(jsConfigFileDeprecated).config); | ||
} | ||
if (isFile(jsonConfigFile)) { // json config provided | ||
return configWithDefaults(JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'))); | ||
} | ||
output.error(`Can not load config from ${jsConfigFile}, ${jsonConfigFile} or ${manualConfigFile || 'manual config'}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`); | ||
process.exit(1); | ||
}; | ||
// alias to deep merge | ||
module.exports.deepMerge = require('../utils').deepMerge; | ||
function getTestRoot(currentPath) { | ||
@@ -51,15 +23,7 @@ if (!currentPath) currentPath = '.'; | ||
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); | ||
function fail(msg) { | ||
output.error(msg); | ||
process.exit(1); | ||
} | ||
module.exports.getTestRoot = getTestRoot; | ||
function configWithDefaults(config) { | ||
if (!config.include) config.include = {}; | ||
if (!config.helpers) config.helpers = {}; | ||
return config; | ||
} | ||
module.exports.fail = fail; |
'use strict'; | ||
let fs = require('fs'); | ||
let path = require('path'); | ||
let isFile = require('./utils').isFile; | ||
let fileExists = require('./utils').fileExists; | ||
const deepMerge = require('./utils').deepMerge; | ||
@@ -9,3 +13,5 @@ let defaultConfig = { | ||
mocha: {}, | ||
bootstrap: null | ||
bootstrap: null, | ||
teardown: null, | ||
hooks: [] | ||
}; | ||
@@ -15,12 +21,76 @@ | ||
/** | ||
* Current configuration | ||
*/ | ||
class Config { | ||
static load(configFile, force) { | ||
if (Object.keys(config).length > 0 && !force) { | ||
return config; | ||
/** | ||
* Create a config with default options | ||
* | ||
* @param {*} newConfig | ||
*/ | ||
static create(newConfig) { | ||
return config = deepMerge(defaultConfig, newConfig); | ||
} | ||
/** | ||
* Load config from a file. | ||
* If js file provided: require it and get .config key | ||
* If json file provided: load and parse JSON | ||
* If directory provided: | ||
* * try to load `codecept.conf.js` from it | ||
* * try to load `codecept.json` from it | ||
* If none of above: fail. | ||
* | ||
* @param {*} configFile | ||
*/ | ||
static load(configFile) { | ||
configFile = path.resolve(configFile || '.'); | ||
if (!fileExists(configFile)) { | ||
throw new Error(`Config file ${configFile} does not exist. Execute 'codeceptjs init' to create config`); | ||
} | ||
config = JSON.parse(fs.readFileSync(configFile, 'utf8')); | ||
config = Object.assign(defaultConfig, config); | ||
// is config file | ||
if (isFile(configFile)) { | ||
return loadConfigFile(configFile); | ||
} | ||
// is path to directory | ||
let jsConfig = path.join(configFile, 'codecept.conf.js'); | ||
if (isFile(jsConfig)) { | ||
return loadConfigFile(jsConfig); | ||
} | ||
let jsonConfig = path.join(configFile, 'codecept.json'); | ||
if (isFile(jsonConfig)) { | ||
return loadConfigFile(jsonConfig); | ||
} | ||
throw new Error(`Can not load config from ${jsConfig} or ${jsonConfig}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`); | ||
} | ||
/** | ||
* Get current config. | ||
*/ | ||
static get() { | ||
return config; | ||
} | ||
/** | ||
* Appends values to current config | ||
* | ||
* @param {*} additionalConfig | ||
*/ | ||
static append(additionalConfig) { | ||
return config = deepMerge(config, additionalConfig); | ||
} | ||
/** | ||
* Resets config to default | ||
*/ | ||
static reset() { | ||
return config = defaultConfig; | ||
} | ||
} | ||
@@ -30,1 +100,14 @@ | ||
function loadConfigFile(configFile) { | ||
// .conf.js config file | ||
if (path.extname(configFile) == '.js') { | ||
return Config.create(require(configFile).config); | ||
} | ||
// json config provided | ||
if (path.extname(configFile) == '.json') { | ||
return Config.create(JSON.parse(fs.readFileSync(configFile, 'utf8'))); | ||
} | ||
throw new Error(`Config file ${configFile} can't be loaded`); | ||
} | ||
@@ -5,2 +5,3 @@ 'use strict'; | ||
let Translation = require('./translation'); | ||
let MochaFactory = require('./mocha_factory'); | ||
@@ -10,14 +11,32 @@ let container = { | ||
support: {}, | ||
translation: {} | ||
mocha: {}, | ||
translation: {}, | ||
}; | ||
module.exports = { | ||
/** | ||
* Dependency Injection Container | ||
*/ | ||
class Container { | ||
create: (config) => { | ||
/** | ||
* Create container with all required helpers and support objects | ||
* | ||
* @api | ||
* @param {*} config | ||
* @param {*} opts | ||
*/ | ||
static create(config, opts) { | ||
container.mocha = MochaFactory.create(config.mocha || {}, opts || {}); | ||
container.helpers = createHelpers(config.helpers || {}); | ||
container.translation = loadTranslation(config.translation || null); | ||
container.support = createSupportObjects(config.include || {}); | ||
}, | ||
} | ||
support: (name) => { | ||
/** | ||
* Get all support objects or get support object by name | ||
* | ||
* @api | ||
* @param {optional} name | ||
*/ | ||
static support(name) { | ||
if (!name) { | ||
@@ -27,5 +46,11 @@ return container.support; | ||
return container.support[name]; | ||
}, | ||
} | ||
helpers: (name) => { | ||
/** | ||
* Get all helpers or get a helper by name | ||
* | ||
* @api | ||
* @param {optional} name | ||
*/ | ||
static helpers(name) { | ||
if (!name) { | ||
@@ -35,14 +60,39 @@ return container.helpers; | ||
return container.helpers[name]; | ||
}, | ||
} | ||
translation: () => { | ||
/** | ||
* Get translation | ||
* | ||
* @api | ||
*/ | ||
static translation() { | ||
return container.translation; | ||
}, | ||
} | ||
append: (newContainer) => { | ||
/** | ||
* Get Mocha instance | ||
* | ||
* @api | ||
*/ | ||
static mocha() { | ||
return container.mocha; | ||
} | ||
/** | ||
* Append new services to container | ||
* | ||
* @api | ||
*/ | ||
static append(newContainer) { | ||
const deepMerge = require('./utils').deepMerge; | ||
container = deepMerge(container, newContainer); | ||
}, | ||
} | ||
clear: (newHelpers, newSupport) => { | ||
/** | ||
* Clear container | ||
* | ||
* @param {*} newHelpers | ||
* @param {*} newSupport | ||
*/ | ||
static clear(newHelpers, newSupport) { | ||
container.helpers = newHelpers || {}; | ||
@@ -52,4 +102,6 @@ container.support = newSupport || {}; | ||
} | ||
}; | ||
} | ||
module.exports = Container; | ||
function createHelpers(config) { | ||
@@ -56,0 +108,0 @@ let helpers = {}; |
@@ -28,2 +28,3 @@ var events = require('events'); | ||
}, | ||
emit: function (event, param) { | ||
@@ -30,0 +31,0 @@ var msg = 'Emitted | ' + event; |
@@ -88,5 +88,5 @@ 'use strict'; | ||
_init() { | ||
Runner = require('protractor/built/runner').Runner; | ||
By = require('protractor').ProtractorBy; | ||
this.isProtractor5 = !require('protractor').wrapDriver; | ||
Runner = requireg('protractor/built/runner').Runner; | ||
By = requireg('protractor').ProtractorBy; | ||
this.isProtractor5 = !requireg('protractor').wrapDriver; | ||
@@ -93,0 +93,0 @@ try { |
@@ -8,7 +8,12 @@ 'use strict'; | ||
module.exports = { | ||
Codecept: require('./codecept'), | ||
codecept: require('./codecept'), | ||
output: require('./output'), | ||
container: require('./container'), | ||
event: require('./event'), | ||
recorder: require('./recorder') | ||
recorder: require('./recorder'), | ||
config: require('./config'), | ||
actor: require('./actor'), | ||
helper: require('./helper'), | ||
pause: require('./pause'), | ||
within: require('./within') | ||
}; |
@@ -23,2 +23,6 @@ 'use strict'; | ||
stepShift: 0, | ||
/** | ||
* Set or return current verbosity level | ||
*/ | ||
level: (level) => { | ||
@@ -29,2 +33,6 @@ if (level) outputLevel = level; | ||
/** | ||
* Print information for a process | ||
* Used in multiple-run | ||
*/ | ||
process: (process) => { | ||
@@ -35,2 +43,5 @@ if (process) outputProcess = `[${process}]`; | ||
/** | ||
* Print information in --debug mode | ||
*/ | ||
debug: (msg) => { | ||
@@ -40,2 +51,5 @@ if (outputLevel >= 2) print(' '.repeat(this.stepShift), styles.debug("> " + msg)); | ||
/** | ||
* Print information in --verbose mode | ||
*/ | ||
log: (msg) => { | ||
@@ -45,2 +59,5 @@ if (outputLevel >= 3) print(' '.repeat(this.stepShift), styles.log(" " + msg)); | ||
/** | ||
* Print error | ||
*/ | ||
error: (msg) => { | ||
@@ -50,3 +67,5 @@ print(styles.error(msg)); | ||
/** | ||
* Print a successful message | ||
*/ | ||
success: (msg) => { | ||
@@ -56,7 +75,9 @@ print(styles.success(msg)); | ||
step: function (step, suiteDetails) { | ||
/** | ||
* Print a step | ||
*/ | ||
step: function (step) { | ||
if (outputLevel === 0) return; | ||
if (!step) return; | ||
let sym = process.platform === 'win32' ? '*' : '•'; | ||
if (suiteDetails) process.stdout.write('[ ' + suiteDetails + ' ] '); | ||
print(' '.repeat(this.stepShift), `${sym} ${step.toString()}`); | ||
@@ -63,0 +84,0 @@ }, |
@@ -18,2 +18,7 @@ 'use strict'; | ||
/** | ||
* Start recording promises | ||
* | ||
* @api | ||
*/ | ||
start() { | ||
@@ -25,2 +30,8 @@ running = true; | ||
/** | ||
* Add error handler to catch rejected promises | ||
* | ||
* @api | ||
* @param {*} fn | ||
*/ | ||
errHandler(fn) { | ||
@@ -30,2 +41,8 @@ errFn = fn; | ||
/** | ||
* Stops current promise chain, calls `catch`. | ||
* Resets recorder to initial state. | ||
* | ||
* @api | ||
*/ | ||
reset() { | ||
@@ -68,2 +85,10 @@ if (promise && running) this.catch(); | ||
/** | ||
* Adds a promise to a chain. | ||
* Promise description should be passed as first parameter. | ||
* | ||
* @param {*} taskName | ||
* @param {*} fn | ||
* @param {*} force | ||
*/ | ||
add(taskName, fn, force) { | ||
@@ -97,2 +122,8 @@ if (typeof taskName === "function") { | ||
/** | ||
* Adds a promise which throws an error into a chain | ||
* | ||
* @api | ||
* @param {*} err | ||
*/ | ||
throw(err) { | ||
@@ -104,2 +135,6 @@ return this.add('throw error ' + err, function () { | ||
/** | ||
* Stops recording promises | ||
* @api | ||
*/ | ||
stop() { | ||
@@ -111,2 +146,7 @@ log(currentQueue() + `Stopping recording promises`); | ||
/** | ||
* Get latest promise in chain. | ||
* | ||
* @api | ||
*/ | ||
promise() { | ||
@@ -116,2 +156,5 @@ return promise; | ||
/** | ||
* Get a list of all chained tasks | ||
*/ | ||
scheduled() { | ||
@@ -121,2 +164,5 @@ return tasks.join("\n"); | ||
/** | ||
* Get a state of current queue and tasks | ||
*/ | ||
toString() { | ||
@@ -123,0 +169,0 @@ return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`; |
{ | ||
"name": "codeceptjs", | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"description": "Modern Era Aceptance Testing Framework for NodeJS", | ||
@@ -5,0 +5,0 @@ "homepage": "http://codecept.io", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
240825
53
6674
20