codeceptjs
Advanced tools
Comparing version 0.5.1 to 0.6.0
@@ -53,3 +53,3 @@ #!/usr/bin/env node | ||
program.command('run [suite] [test]') | ||
program.command('run [test]') | ||
.description('Executes tests') | ||
@@ -63,7 +63,7 @@ | ||
.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') | ||
@@ -85,5 +85,21 @@ .option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options') | ||
.option('--trace', 'trace function calls') | ||
.option('--child <string>', 'option for child processes') | ||
.action(require('../lib/command/run')); | ||
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('--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') | ||
.action(require('../lib/command/run-multiple')); | ||
if (process.argv.length <= 2) { | ||
@@ -90,0 +106,0 @@ console.log('CodeceptJS v' + Codecept.version()); |
@@ -0,1 +1,37 @@ | ||
## 0.6.0 | ||
Major release with extension API and parallel execution. | ||
* **Breaking** Removed path argument from `run`. To specify path other than current directory use `--config` or `-c` option: | ||
Instead of: `codeceptjs run tests` use: | ||
``` | ||
# load config and run from tests directory | ||
codeceptjs run -c tests/ | ||
# or load codecept.json from tests directory | ||
codeceptjs run -c tests/codecept.json | ||
# run users_test.js inside tests directory | ||
codeceptjs run users_test.js -c tests | ||
``` | ||
* **Command `multiple-run` added**, to execute tests in several browsers in parallel by @APshenkin and @davertmik. [See documentation](http://codecept.io/advanced/#multiple-execution). | ||
* **Hooks API added to extend CodeceptJS** with custom listeners and plugins. [See documentation](http://codecept.io/hooks/#hooks_1). | ||
* [Nightmare][WebDriverIO] `within` can work with iframes by @imvetri. [See documentation](http://codecept.io/acceptance/#iframes). | ||
* [WebDriverIO][SeleniumWebdriver][Protractor] Default browser changed to `chrome` | ||
* [Nightmare] Fixed globally locating `nightmare-upload`. | ||
* [WebDriverIO] added `seeNumberOfVisibleElements` method by @elarouche. | ||
* Exit with non-zero code if init throws an error by @rincedd | ||
* New guides published: | ||
* [Installation](http://codecept.io/installation/) | ||
* [Hooks](http://codecept.io/hooks/) | ||
* [Advanced Usage](http://codecept.io/advanced/) | ||
* Meta packages published: | ||
* [codecept-webdriverio](https://www.npmjs.com/package/codecept-webdriverio) | ||
* [codecept-protractor](https://www.npmjs.com/package/codecept-protractor) | ||
* [codecept-nightmare](https://www.npmjs.com/package/codecept-nightmare) | ||
## 0.5.1 | ||
@@ -2,0 +38,0 @@ |
@@ -14,2 +14,3 @@ 'use strict'; | ||
let runHook = require('./hooks'); | ||
const scenarioUi = fsPath.join(__dirname, './interfaces/bdd.js'); | ||
@@ -45,7 +46,2 @@ | ||
container.create(this.config); | ||
require('./listener/steps'); | ||
require('./listener/helpers'); | ||
require('./listener/exit'); | ||
require('./listener/trace'); | ||
this.bootstrap(callback); | ||
@@ -56,3 +52,14 @@ } | ||
bootstrap(done) { | ||
runHook('bootstrap', this.config, done); | ||
// default hooks | ||
runHook(require('./listener/steps')); | ||
runHook(require('./listener/helpers')); | ||
runHook(require('./listener/exit')); | ||
runHook(require('./listener/trace')); | ||
// bootstrap | ||
runHook(this.config.bootstrap, done, 'bootstrap'); | ||
// custom hooks | ||
(this.config.hooks || []).forEach((hook) => runHook(hook, done, 'bootstrap')); | ||
} | ||
@@ -62,3 +69,3 @@ | ||
teardown(done) { | ||
runHook('teardown', this.config, done); | ||
runHook(this.config.teardown, done, 'teardown'); | ||
} | ||
@@ -65,0 +72,0 @@ |
@@ -8,3 +8,3 @@ 'use strict'; | ||
module.exports = function (suite, test, options) { | ||
module.exports = function (test, options) { | ||
// registering options globally to use in config | ||
@@ -15,4 +15,4 @@ process.profile = options.profile; | ||
let testRoot = getTestRoot(suite); | ||
let config = getConfig(testRoot, configFile); | ||
let testRoot = getTestRoot(configFile); | ||
let config = getConfig(configFile); | ||
@@ -35,3 +35,4 @@ // override config with options | ||
output.print(output.colors.grey(err.stack.replace(err.message, ''))); | ||
process.exit(1); | ||
} | ||
}; |
@@ -8,14 +8,6 @@ 'use strict'; | ||
module.exports.getTestRoot = function (currentPath) { | ||
let testsPath = path.resolve(currentPath || '.'); | ||
return testsPath; | ||
}; | ||
module.exports.getConfig = function (configFile) { | ||
module.exports.getConfig = function (testRoot, configFile) { | ||
let testRoot = getTestRoot(configFile); | ||
// using relative config | ||
if (configFile && !path.isAbsolute(configFile)) { | ||
configFile = path.join(testRoot, configFile); | ||
} | ||
let config, | ||
@@ -50,21 +42,19 @@ manualConfigFile = configFile && path.resolve(configFile), | ||
function isObject(item) { | ||
return item && typeof item === 'object' && !Array.isArray(item); | ||
// alias to deep merge | ||
module.exports.deepMerge = require('../utils').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 deepMerge(target, source) { | ||
if (isObject(target) && isObject(source)) { | ||
for (const key in source) { | ||
if (isObject(source[key])) { | ||
if (!target[key]) Object.assign(target, { [key]: {} }); | ||
deepMerge(target[key], source[key]); | ||
} else { | ||
Object.assign(target, { [key]: source[key] }); | ||
} | ||
} | ||
} | ||
return target; | ||
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.deepMerge = deepMerge; | ||
module.exports.getTestRoot = getTestRoot; | ||
@@ -71,0 +61,0 @@ function configWithDefaults(config) { |
@@ -6,2 +6,46 @@ 'use strict'; | ||
let container = { | ||
helpers: {}, | ||
support: {}, | ||
translation: {} | ||
}; | ||
module.exports = { | ||
create: (config) => { | ||
container.helpers = createHelpers(config.helpers || {}); | ||
container.translation = loadTranslation(config.translation || null); | ||
container.support = createSupportObjects(config.include || {}); | ||
}, | ||
support: (name) => { | ||
if (!name) { | ||
return container.support; | ||
} | ||
return container.support[name]; | ||
}, | ||
helpers: (name) => { | ||
if (!name) { | ||
return container.helpers; | ||
} | ||
return container.helpers[name]; | ||
}, | ||
translation: () => { | ||
return container.translation; | ||
}, | ||
append: (newContainer) => { | ||
const deepMerge = require('./utils').deepMerge; | ||
container = deepMerge(container, newContainer); | ||
}, | ||
clear: (newHelpers, newSupport) => { | ||
container.helpers = newHelpers || {}; | ||
container.support = newSupport || {}; | ||
container.translation = loadTranslation(); | ||
} | ||
}; | ||
function createHelpers(config) { | ||
@@ -65,4 +109,4 @@ let helpers = {}; | ||
if (translation.I != 'I') { | ||
objects[translation.I] = objects.I; | ||
if (container.translation.I != 'I') { | ||
objects[container.translation.I] = objects.I; | ||
} | ||
@@ -96,39 +140,1 @@ } | ||
} | ||
let helpers = {}; | ||
let support = {}; | ||
let translation = {}; | ||
module.exports = { | ||
create: (config) => { | ||
helpers = createHelpers(config.helpers || {}); | ||
translation = loadTranslation(config.translation || null); | ||
support = createSupportObjects(config.include || {}); | ||
}, | ||
support: (name) => { | ||
if (!name) { | ||
return support; | ||
} | ||
return support[name]; | ||
}, | ||
helpers: (name) => { | ||
if (!name) { | ||
return helpers; | ||
} | ||
return helpers[name]; | ||
}, | ||
translation: () => { | ||
return translation; | ||
}, | ||
clear: (newHelpers, newSupport) => { | ||
helpers = newHelpers || {}; | ||
support = newSupport || {}; | ||
translation = loadTranslation(); | ||
} | ||
}; |
@@ -73,2 +73,7 @@ if (!window.codeceptjs) { | ||
} | ||
if (by == 'frame') { | ||
if (!Array.isArray(locator)) locator = [locator]; | ||
return [locator.reduce((parent, child)=>parent.querySelector(child).contentDocument, window.document).querySelector('body')]; | ||
} | ||
return []; | ||
@@ -75,0 +80,0 @@ }; |
@@ -25,3 +25,4 @@ 'use strict'; | ||
* 1. Download [Selenium Server](http://docs.seleniumhq.org/download/) | ||
* 2. Launch the daemon: `java -jar selenium-server-standalone-2.xx.xxx.jar` | ||
* 2. For Chrome browser install [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/getting-started), for Firefox browser install [GeckoDriver](https://github.com/mozilla/geckodriver). | ||
* 3. Launch the daemon: `java -jar selenium-server-standalone-3.xx.xxx.jar` | ||
* | ||
@@ -68,3 +69,3 @@ * #### PhantomJS Installation | ||
this.options = { | ||
browser: 'firefox', | ||
browser: 'chrome', | ||
url: 'http://localhost', | ||
@@ -115,3 +116,3 @@ seleniumAddress: 'http://localhost:4444/wd/hub', | ||
{ name: 'driver', message: "Protractor driver (local, direct, session, hosted, sauce, browserstack)", default: 'hosted' }, | ||
{ name: 'browser', message: 'Browser in which testing will be performed', default: 'firefox' }, | ||
{ name: 'browser', message: 'Browser in which testing will be performed', default: 'chrome' }, | ||
{ name: 'rootElement', message: "Root element of AngularJS application", default: 'body' }, | ||
@@ -118,0 +119,0 @@ ]; |
@@ -26,3 +26,4 @@ 'use strict'; | ||
* 1. Download [Selenium Server](http://docs.seleniumhq.org/download/) | ||
* 2. Launch the daemon: `java -jar selenium-server-standalone-2.xx.xxx.jar` | ||
* 2. For Chrome browser install [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/getting-started), for Firefox browser install [GeckoDriver](https://github.com/mozilla/geckodriver). | ||
* 3. Launch the daemon: `java -jar selenium-server-standalone-3.xx.xxx.jar` | ||
* | ||
@@ -114,3 +115,3 @@ * | ||
{ name: 'url', message: "Base url of site to be tested", default: 'http://localhost' }, | ||
{ name: 'browser', message: 'Browser in which testing will be performed', default: 'firefox' }, | ||
{ name: 'browser', message: 'Browser in which testing will be performed', default: 'chrome' }, | ||
]; | ||
@@ -117,0 +118,0 @@ } |
@@ -25,5 +25,5 @@ 'use strict'; | ||
* 1. Download [Selenium Server](http://docs.seleniumhq.org/download/) | ||
* 2. Launch the daemon: `java -jar selenium-server-standalone-2.xx.xxx.jar` | ||
* 2. For Chrome browser install [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/getting-started), for Firefox browser install [GeckoDriver](https://github.com/mozilla/geckodriver). | ||
* 3. Launch the daemon: `java -jar selenium-server-standalone-3.xx.xxx.jar` | ||
* | ||
* | ||
* #### PhantomJS Installation | ||
@@ -46,3 +46,3 @@ * | ||
* * `windowSize`: (optional) default window size. Set to `maximize` or a dimension in the format `640x480`. | ||
* * `waitForTimeout`: (optional) sets default wait time in *ms* for all `wait*` functions. 1000 by default; | ||
* * `waitForTimeout`: (option) sets default wait time in *ms* for all `wait*` functions. 1000 by default; | ||
* * `desiredCapabilities`: Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) | ||
@@ -100,3 +100,2 @@ * * `manualStart` (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriverIO"]._startBrowser()` | ||
* ``` | ||
* | ||
* For example, | ||
@@ -219,3 +218,3 @@ * | ||
throw new Error(` | ||
WebDriverIO requires at least these parameters | ||
WebDriverIO requires ares.valuet least these parameters | ||
Check your codeceptjs config file to ensure these are set properly | ||
@@ -250,3 +249,3 @@ { | ||
message: 'Browser in which testing will be performed', | ||
default: 'firefox' | ||
default: 'chrome' | ||
}]; | ||
@@ -317,2 +316,7 @@ } | ||
_withinBegin(locator) { | ||
let frame = isFrameLocator(locator); | ||
if (frame) { | ||
withinStore.frame = frame; | ||
return this.browser.element(frame).then((res) => this.browser.frame(res.value)); | ||
} | ||
withinStore.elFn = this.browser.element; | ||
@@ -332,5 +336,10 @@ withinStore.elsFn = this.browser.elements; | ||
_withinEnd() { | ||
if (withinStore.frame) { | ||
withinStore = {}; | ||
return this.browser.frame(null); | ||
} | ||
this.context = 'body'; | ||
this.browser.element = withinStore.elFn; | ||
this.browser.elements = withinStore.elsFn; | ||
withinStore = {}; | ||
} | ||
@@ -769,2 +778,19 @@ | ||
/** | ||
* asserts that an element is visible a given number of times | ||
* Element is located by CSS or XPath. | ||
* | ||
* ```js | ||
* I.seeNumberOfVisibleElements('.buttons', 3); | ||
* ``` | ||
*/ | ||
seeNumberOfVisibleElements(selector, num) { | ||
return this.browser.isVisible(withStrictLocator(selector)) | ||
.then(function (res) { | ||
if(!Array.isArray(res)) res = [res]; | ||
res = res.filter((val) => val == true); | ||
return truth(`elements of ${locator}`, 'to be seen').assert.equal(res.length, num); | ||
}); | ||
} | ||
/** | ||
* {{> ../webapi/seeInCurrentUrl }} | ||
@@ -1264,2 +1290,9 @@ */ | ||
function isFrameLocator(locator) { | ||
if (typeof locator !== 'object') return false; | ||
let key = Object.keys(locator)[0]; | ||
if (key !== 'frame') return false; | ||
return locator[key]; | ||
} | ||
module.exports = WebDriverIO; |
@@ -7,5 +7,6 @@ 'use strict'; | ||
module.exports = function (hook, config, done) { | ||
if (typeof config[hook] === 'string' && fileExists(fsPath.join(codecept_dir, config[hook]))) { | ||
var callable = require(fsPath.join(codecept_dir, config[hook])); | ||
module.exports = function (hook, done, stage) { | ||
stage = stage || 'bootstrap'; | ||
if (typeof hook === 'string' && fileExists(fsPath.join(codecept_dir, hook))) { | ||
var callable = require(fsPath.join(codecept_dir, hook)); | ||
if (typeof callable === 'function') { | ||
@@ -15,8 +16,8 @@ callSync(callable, done); | ||
} | ||
if (typeof callable === 'object' && callable[hook]) { | ||
callSync(callable[hook], done); | ||
if (typeof callable === 'object' && callable[stage]) { | ||
callSync(callable[stage], done); | ||
return; | ||
} | ||
} else if (typeof config[hook] === 'function') { | ||
callSync(config[hook], done); | ||
} else if (typeof hook === 'function') { | ||
callSync(hook, done); | ||
return; | ||
@@ -33,3 +34,3 @@ } | ||
callable(); | ||
done(); | ||
if (done) done(); | ||
} | ||
@@ -36,0 +37,0 @@ } |
'use strict'; | ||
let event = require('../event'); | ||
const event = require('../event'); | ||
let failed = false; | ||
module.exports = function () { | ||
let failedTests = []; | ||
let failed = false; | ||
event.dispatcher.on(event.test.failed, function (testOrSuite) { | ||
// NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object | ||
// is a suite and not a test | ||
failedTests.push(testOrSuite.fullTitle()); | ||
}); | ||
let failedTests = []; | ||
// if test was successful after retries | ||
event.dispatcher.on(event.test.passed, function (testOrSuite) { | ||
// NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object | ||
// is a suite and not a test | ||
failedTests = failedTests.filter((failed) => testOrSuite.fullTitle() !== failed); | ||
}); | ||
event.dispatcher.on(event.test.failed, function (testOrSuite) { | ||
// NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object | ||
// is a suite and not a test | ||
failedTests.push(testOrSuite.fullTitle()); | ||
}); | ||
event.dispatcher.on(event.all.result, function () { | ||
if (failedTests.length) { | ||
process.on('exit', function () { | ||
process.exit(1); // exit with non-zero status if there were failures | ||
}); | ||
} | ||
}); | ||
// if test was successful after retries | ||
event.dispatcher.on(event.test.passed, function (testOrSuite) { | ||
// NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object | ||
// is a suite and not a test | ||
failedTests = failedTests.filter((failed) => testOrSuite.fullTitle() !== failed); | ||
}); | ||
event.dispatcher.on(event.all.result, function () { | ||
if (failedTests.length) { | ||
process.exitCode = 1; | ||
} | ||
}); | ||
}; |
'use strict'; | ||
let container = require('../container'); | ||
let event = require('../event'); | ||
let recorder = require('../recorder'); | ||
let debug = require('../output').debug; | ||
const event = require('../event'); | ||
const container = require('../container'); | ||
const recorder = require('../recorder'); | ||
const debug = require('../output').debug; | ||
let helpers = container.helpers(); | ||
/** | ||
* Enable Helpers to listen to test events | ||
*/ | ||
module.exports = function () { | ||
let helpers = container.helpers(); | ||
let runHelpersHook = (hook, param) => { | ||
Object.keys(helpers).forEach((key) => { | ||
helpers[key][hook](param); | ||
const runHelpersHook = (hook, param) => { | ||
Object.keys(helpers).forEach((key) => { | ||
helpers[key][hook](param); | ||
}); | ||
}; | ||
const runAsyncHelpersHook = (hook, param, force) => { | ||
Object.keys(helpers).forEach((key) => { | ||
if (!helpers[key][hook]) return; | ||
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force); | ||
}); | ||
}; | ||
event.dispatcher.on(event.all.before, function () { | ||
runHelpersHook('_init'); | ||
}); | ||
}; | ||
let runAsyncHelpersHook = (hook, param, force) => { | ||
Object.keys(helpers).forEach((key) => { | ||
if (!helpers[key][hook]) return; | ||
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force); | ||
event.dispatcher.on(event.suite.before, function (suite) { | ||
runAsyncHelpersHook('_beforeSuite', null, true); | ||
}); | ||
}; | ||
event.dispatcher.on(event.all.before, function () { | ||
runHelpersHook('_init'); | ||
}); | ||
event.dispatcher.on(event.suite.after, function (suite) { | ||
runAsyncHelpersHook('_afterSuite', null, true); | ||
}); | ||
event.dispatcher.on(event.suite.before, function (suite) { | ||
runAsyncHelpersHook('_beforeSuite', null, true); | ||
}); | ||
event.dispatcher.on(event.test.started, function (test) { | ||
runHelpersHook('_test', test); | ||
recorder.catch((e) => debug(e)); | ||
}); | ||
event.dispatcher.on(event.suite.after, function (suite) { | ||
runAsyncHelpersHook('_afterSuite', null, true); | ||
}); | ||
event.dispatcher.on(event.test.before, function () { | ||
runAsyncHelpersHook('_before'); | ||
}); | ||
event.dispatcher.on(event.test.started, function (test) { | ||
runHelpersHook('_test', test); | ||
recorder.catch((e) => debug(e)); | ||
}); | ||
event.dispatcher.on(event.test.failed, function (test) { | ||
runAsyncHelpersHook('_failed', test, true); | ||
// should not fail test execution, so errors should be catched | ||
recorder.catch((e) => debug(e)); | ||
}); | ||
event.dispatcher.on(event.test.before, function () { | ||
runAsyncHelpersHook('_before'); | ||
}); | ||
event.dispatcher.on(event.test.after, function (test) { | ||
runAsyncHelpersHook('_after', {}, true); | ||
}); | ||
event.dispatcher.on(event.test.failed, function (test) { | ||
runAsyncHelpersHook('_failed', test, true); | ||
// should not fail test execution, so errors should be catched | ||
recorder.catch((e) => debug(e)); | ||
}); | ||
event.dispatcher.on(event.step.before, function (step) { | ||
runAsyncHelpersHook('_beforeStep', step); | ||
}); | ||
event.dispatcher.on(event.test.after, function (test) { | ||
runAsyncHelpersHook('_after', {}, true); | ||
}); | ||
event.dispatcher.on(event.step.before, function (step) { | ||
runAsyncHelpersHook('_beforeStep', step); | ||
}); | ||
event.dispatcher.on(event.step.after, function (step) { | ||
runAsyncHelpersHook('_afterStep', step); | ||
}); | ||
event.dispatcher.on(event.step.after, function (step) { | ||
runAsyncHelpersHook('_afterStep', step); | ||
}); | ||
}; |
'use strict'; | ||
const event = require('../event'); | ||
let event = require('../event'); | ||
let currentTest; | ||
let steps; | ||
event.dispatcher.on(event.test.started, function (test) { | ||
currentTest = test; | ||
currentTest.steps = []; | ||
}); | ||
/** | ||
* Register steps inside tests | ||
*/ | ||
module.exports = function () { | ||
event.dispatcher.on(event.test.after, function () { | ||
currentTest = null; | ||
}); | ||
event.dispatcher.on(event.test.started, function (test) { | ||
currentTest = test; | ||
currentTest.steps = []; | ||
}); | ||
event.dispatcher.on(event.test.failed, function (test) { | ||
if (!currentTest) return; | ||
// last step is failing step | ||
if (!currentTest.steps.length) return; | ||
currentTest.steps.slice(-1)[0].status = 'failed'; | ||
}); | ||
event.dispatcher.on(event.test.after, function () { | ||
currentTest = null; | ||
}); | ||
event.dispatcher.on(event.step.before, function (step) { | ||
if (!currentTest || !currentTest.steps) return; | ||
currentTest.steps.push(step); | ||
}); | ||
event.dispatcher.on(event.test.failed, function (test) { | ||
if (!currentTest) return; | ||
// last step is failing step | ||
if (!currentTest.steps.length) return; | ||
currentTest.steps.slice(-1)[0].status = 'failed'; | ||
}); | ||
event.dispatcher.on(event.step.before, function (step) { | ||
if (!currentTest || !currentTest.steps) return; | ||
currentTest.steps.push(step); | ||
}); | ||
}; | ||
'use strict'; | ||
let event = require('../event'); | ||
let AssertionFailedError = require('../assert/error'); | ||
let output = require('../output'); | ||
let ucfirst = require('../utils').ucfirst; | ||
const output = require('../output'); | ||
const event = require('../event'); | ||
const AssertionFailedError = require('../assert/error'); | ||
const ucfirst = require('../utils').ucfirst; | ||
event.dispatcher.on(event.test.failed, function (test, err) { | ||
let msg = err.message; | ||
if (err instanceof AssertionFailedError) { | ||
msg = err.message = err.inspect(); | ||
} | ||
let steps = test.steps; | ||
if (steps && steps.length) { | ||
let scenarioTrace = ""; | ||
steps.reverse().forEach((step, i) => { | ||
let line = `- ${step.toCode()} ${step.line()}`; | ||
// if (step.status === 'failed') line = '' + line; | ||
scenarioTrace += "\n" + line; | ||
}); | ||
msg += `\n\nScenario Steps:\n${scenarioTrace}\n\n`; | ||
} | ||
/** | ||
* Register stack trace for scenarios | ||
*/ | ||
module.exports = function () { | ||
if (output.level() < 3) { | ||
err.stack = ''; // hide internal error stack trace in non-verbose mode | ||
} | ||
event.dispatcher.on(event.test.failed, function (test, err) { | ||
let msg = err.message; | ||
if (err instanceof AssertionFailedError) { | ||
msg = err.message = err.inspect(); | ||
} | ||
let steps = test.steps; | ||
if (steps && steps.length) { | ||
let scenarioTrace = ""; | ||
steps.reverse().forEach((step, i) => { | ||
let line = `- ${step.toCode()} ${step.line()}`; | ||
// if (step.status === 'failed') line = '' + line; | ||
scenarioTrace += "\n" + line; | ||
}); | ||
msg += `\n\nScenario Steps:\n${scenarioTrace}\n\n`; | ||
} | ||
err.stack = msg + err.stack; | ||
test.err = err; | ||
}); | ||
if (output.level() < 3) { | ||
err.stack = ''; // hide internal error stack trace in non-verbose mode | ||
} | ||
err.stack = msg + err.stack; | ||
test.err = err; | ||
}); | ||
}; | ||
'use strict'; | ||
let colors = require('chalk'); | ||
let print = console.log; | ||
let symbols = require('mocha/lib/reporters/base').symbols; | ||
@@ -17,2 +16,3 @@ | ||
let outputLevel = 0; | ||
let outputProcess = ''; | ||
@@ -29,2 +29,7 @@ module.exports = { | ||
process: (process) => { | ||
if (process) outputProcess = `[${process}]`; | ||
return outputProcess; | ||
}, | ||
debug: (msg) => { | ||
@@ -42,2 +47,3 @@ if (outputLevel >= 2) print(' '.repeat(this.stepShift), styles.debug("> " + msg)); | ||
success: (msg) => { | ||
@@ -47,6 +53,7 @@ print(styles.success(msg)); | ||
step: function (step) { | ||
step: function (step, suiteDetails) { | ||
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()}`); | ||
@@ -89,8 +96,6 @@ }, | ||
} | ||
}, | ||
say: (message) => { | ||
print(` ${colors.cyan.bold(message)}`); | ||
if (outputLevel >= 1) print(` ${colors.cyan.bold(message)}`); | ||
}, | ||
@@ -118,2 +123,8 @@ | ||
function print(...msg) { | ||
if (outputProcess) { | ||
msg.unshift(outputProcess); | ||
} | ||
console.log.apply(this, msg); | ||
} | ||
@@ -120,0 +131,0 @@ function ind() { |
@@ -20,4 +20,5 @@ 'use strict'; | ||
if (opts.verbose) level = 3; | ||
output.process(opts.child); | ||
output.level(level); | ||
output.print('CodeceptJS v' + require('../codecept').version()); | ||
@@ -70,3 +71,3 @@ output.print(`Using test root "${global.codecept_dir}"`); | ||
event.dispatcher.on(event.step.started, function (step) { | ||
output.step(step); | ||
output.step(step, global.suiteDetails); | ||
}); | ||
@@ -73,0 +74,0 @@ } |
var fs = require('fs'); | ||
var path = require('path'); | ||
var getParameterNames = require('get-parameter-names'); | ||
function isObject(item) { | ||
return item && typeof item === 'object' && !Array.isArray(item); | ||
} | ||
function deepMerge(target, source) { | ||
if (isObject(target) && isObject(source)) { | ||
for (const key in source) { | ||
if (isObject(source[key])) { | ||
if (!target[key]) Object.assign(target, { [key]: {} }); | ||
deepMerge(target[key], source[key]); | ||
} else { | ||
Object.assign(target, { [key]: source[key] }); | ||
} | ||
} | ||
} | ||
return target; | ||
} | ||
module.exports.deepMerge = deepMerge; | ||
module.exports.fileExists = function (filePath) { | ||
@@ -26,4 +47,3 @@ try { | ||
if (fn.isSinonProxy) return []; | ||
var funStr = fn.toString(); | ||
return funStr.slice(funStr.indexOf('(') + 1, funStr.indexOf(')')).match(/([^\s,]+)/g); | ||
return getParameterNames(fn); | ||
}; | ||
@@ -30,0 +50,0 @@ |
@@ -12,3 +12,3 @@ 'use strict'; | ||
let helpers = container.helpers(); | ||
let locator = typeof context === 'object' ? JSON.parse(context) : context; | ||
let locator = typeof context === 'object' ? JSON.stringify(context) : context; | ||
let addContextToStep = function (step) { | ||
@@ -15,0 +15,0 @@ step.prefix = `Within ${locator}: `; |
{ | ||
"name": "codeceptjs", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "Modern Era Aceptance Testing Framework for NodeJS", | ||
@@ -21,3 +21,3 @@ "homepage": "http://codecept.io", | ||
], | ||
"main": "lib/codecept.js", | ||
"main": "lib/index.js", | ||
"keywords": [ | ||
@@ -34,2 +34,3 @@ "tdd", | ||
"escape-string-regexp": "^1.0.3", | ||
"get-parameter-names": "^0.3.0", | ||
"glob": "^6.0.1", | ||
@@ -36,0 +37,0 @@ "inquirer": "^0.11.0", |
@@ -1,2 +0,2 @@ | ||
# CodeceptJS [![NPM version][npm-image]][npm-url] [![Build Status](https://travis-ci.org/Codeception/CodeceptJS.svg)](https://travis-ci.org/Codeception/CodeceptJS) [![Join the chat at https://gitter.im/Codeception/CodeceptJS](https://badges.gitter.im/Codeception/CodeceptJS.svg)](https://gitter.im/Codeception/CodeceptJS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
# CodeceptJS [![NPM version][npm-image]][npm-url] [![Build Status](https://travis-ci.org/Codeception/CodeceptJS.svg?branch=master)](https://travis-ci.org/Codeception/CodeceptJS) [![Join the chat at https://gitter.im/Codeception/CodeceptJS](https://badges.gitter.im/Codeception/CodeceptJS.svg)](https://gitter.im/Codeception/CodeceptJS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
@@ -17,3 +17,3 @@ Reference: [Helpers API](https://github.com/Codeception/CodeceptJS/blob/master/docs) | [Demo](https://github.com/Codeception/codeceptjs-demo) | ||
I.see('Welcome'); | ||
} | ||
}); | ||
``` | ||
@@ -20,0 +20,0 @@ |
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
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
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
235976
52
6449
10
2
+ Addedget-parameter-names@^0.3.0
+ Addedget-parameter-names@0.3.0(transitive)