codeceptjs
Advanced tools
Comparing version 0.3.5 to 0.4.0
@@ -57,2 +57,3 @@ #!/usr/bin/env node | ||
.option('--debug', 'output additional information') | ||
.option('--verbose', 'output internal logging information') | ||
@@ -59,0 +60,0 @@ // mocha options |
@@ -0,1 +1,11 @@ | ||
## 0.4.0 | ||
* **Nightmare Helper** added [http://codecept.io/nightmare] for faster web testing. | ||
* [Protractor][SeleniumWebdriver][WebDriverIO] added `restart: false` option to reuse one browser between tests | ||
* **Protractor 4.0** compatibility. Please upgrade Protractor library. | ||
* Added `--verbose` option for `run` command to log and print global promise and events. | ||
* Fixed errors with shutting down and cleanup. | ||
* Fixed starting interactive shell with `codeceptjs shell`. | ||
* Fixed handling of failures inside within block | ||
## 0.3.5 | ||
@@ -2,0 +12,0 @@ |
@@ -13,3 +13,3 @@ 'use strict'; | ||
let fileExists = require('./utils').fileExists; | ||
const scenarioUi = fsPath.join(__dirname, './interfaces/codeceptjs.js'); | ||
const scenarioUi = fsPath.join(__dirname, './interfaces/bdd.js'); | ||
@@ -23,3 +23,3 @@ class Codecept { | ||
this.mocha.ui(scenarioUi); | ||
if (opts.reporter) { | ||
if (opts.reporter) { | ||
// custom reporters | ||
@@ -68,5 +68,5 @@ this.mocha.reporter(opts.reporter, opts.reporterOptions); | ||
this.mocha.run(() => { | ||
event.dispatcher.emit(event.all.result, this); | ||
event.emit(event.all.result, this); | ||
}); | ||
} | ||
@@ -73,0 +73,0 @@ |
@@ -18,6 +18,6 @@ 'use strict'; | ||
recorder.start(); | ||
recorder.finishHandler(() => event.dispatcher.emit(event.test.teardown)); | ||
event.dispatcher.emit(event.test.setup); | ||
recorder.finishHandler(() => event.emit(event.test.teardown)); | ||
event.emit(event.test.setup); | ||
require('../pause')(); | ||
recorder.finalize(); | ||
}; |
@@ -19,9 +19,16 @@ 'use strict'; | ||
module.exports.getConfig = function (testsPath) { | ||
let configFile = path.join(testsPath, 'codecept.json'); | ||
if (!fileExists(configFile)) { | ||
output.error(`Can not load config from ${configFile}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`); | ||
module.exports.getConfig = function (testRoot) { | ||
let config, | ||
jsConfigFile = path.join(testRoot, 'codecept.js'), | ||
jsonConfigFile = path.join(testRoot, 'codecept.json'); | ||
if (fileExists(jsConfigFile)) { | ||
config = require(jsConfigFile).config; | ||
} else if (fileExists(jsonConfigFile)) { | ||
config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8')); | ||
} else { | ||
output.error(`Can not load config from ${jsConfigFile} or ${jsonConfigFile}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`); | ||
process.exit(1); | ||
} | ||
let config = JSON.parse(fs.readFileSync(configFile, 'utf8')); | ||
if (!config.include) config.include = {}; | ||
@@ -28,0 +35,0 @@ if (!config.helpers) config.helpers = {}; |
var events = require('events'); | ||
var dispatcher = new events.EventEmitter(); | ||
var log = require('./output').log; | ||
@@ -26,2 +27,11 @@ module.exports = { | ||
}, | ||
emit: function(event, param) { | ||
var msg = 'Emitted | '+event; | ||
if (param && param.toString()) { | ||
msg += ` (${param.toString()})`; | ||
} | ||
log(msg); | ||
this.dispatcher.emit.apply(this.dispatcher, arguments); | ||
}, | ||
// for testing only! | ||
@@ -28,0 +38,0 @@ cleanDispatcher: () => { |
@@ -58,2 +58,11 @@ 'use strict'; | ||
/** | ||
* Hook executed after each failed test | ||
* | ||
* @param test | ||
*/ | ||
_failed() { | ||
} | ||
/** | ||
* Hook executed before each step | ||
@@ -77,2 +86,20 @@ * | ||
/** | ||
* Hook executed before each suite | ||
* | ||
* @param suite | ||
*/ | ||
_beforeSuite() { | ||
} | ||
/** | ||
* Hook executed after each suite | ||
* | ||
* @param suite | ||
*/ | ||
_afterSuite() { | ||
} | ||
/** | ||
* Access another configured helper: `this.helpers['AnotherHelper']` | ||
@@ -79,0 +106,0 @@ */ |
@@ -42,2 +42,3 @@ 'use strict'; | ||
* * `driver` - which protrator driver to use (local, direct, session, hosted, sauce, browserstack). By default set to 'hosted' which requires selenium server to be started. | ||
* * `restart` - restart browser between tests (default: true), if set to false cookies will be cleaned but browser window will be kept. | ||
* * `seleniumAddress` - Selenium address to connect (default: http://localhost:4444/wd/hub) | ||
@@ -85,7 +86,6 @@ * * `rootElement` - Root element of AngularJS application (default: body) | ||
} | ||
protractorWrapper = requireg('protractor').wrapDriver; | ||
EC = requireg('protractor').ExpectedConditions; | ||
protractorWrapper = requireg('protractor').Browser.wrapDriver; | ||
EC = requireg('protractor').Browser.ExpectedConditions; | ||
global.by = requireg('protractor').Browser.By; | ||
global.by = requireg('protractor').By; | ||
global.element = requireg('protractor').element; | ||
let driverProviderModule = requireg('protractor/built/driverProviders/'+this.options.driver); | ||
@@ -101,5 +101,6 @@ let className = Object.keys(driverProviderModule)[0]; | ||
requireg("protractor"); | ||
require('assert').ok(requireg("protractor").Browser); | ||
require('assert').ok(requireg("protractor/built/driverProviders/hosted").Hosted); | ||
} catch(e) { | ||
return ["protractor@^3.3.0"]; | ||
return ["protractor@^4.0.0"]; | ||
} | ||
@@ -117,4 +118,10 @@ } | ||
_startBrowser() { | ||
this.browser = this.driverProvider.getNewDriver(); | ||
global.element = this.browser.element; | ||
return this.browser; | ||
} | ||
_before() { | ||
this.browser = this.driverProvider.getNewDriver(); | ||
super._before(); | ||
this.amInsideAngularApp(); | ||
@@ -125,6 +132,2 @@ this.context = this.options.rootElement; | ||
_after() { | ||
return this.browser.quit(); | ||
} | ||
_withinBegin(locator) { | ||
@@ -140,5 +143,5 @@ withinStore.elFn = this.browser.findElement; | ||
this.browser.findElements = (l) => context.all(l).getWebElements(); | ||
return; | ||
return context; | ||
} | ||
super._withinBegin(locator); | ||
return super._withinBegin(locator); | ||
} | ||
@@ -170,2 +173,3 @@ | ||
if (this.browser.driver && this.insideAngular) { | ||
console.log(this.browser.driver); | ||
this.browser = this.browser.driver; | ||
@@ -172,0 +176,0 @@ this.insideAngular = false; |
@@ -44,2 +44,3 @@ 'use strict'; | ||
* * `driver` - which protrator driver to use (local, direct, session, hosted, sauce, browserstack). By default set to 'hosted' which requires selenium server to be started. | ||
* * `restart` - restart browser between tests (default: true), if set to false cookies will be cleaned but browser window will be kept. | ||
* * `seleniumAddress` - Selenium address to connect (default: http://localhost:4444/wd/hub) | ||
@@ -67,2 +68,3 @@ * * `waitForTimeout`: (optional) sets default wait time in _ms_ for all `wait*` functions. 1000 by default; | ||
seleniumAddress: 'http://localhost:4444/wd/hub', | ||
restart: true, | ||
waitforTimeout: 1000, // ms | ||
@@ -106,10 +108,31 @@ capabilities: {} | ||
_startBrowser() { | ||
this.browser = this.browserBuilder.build(); | ||
return this.browser; | ||
} | ||
_beforeSuite() { | ||
if (!this.options.restart) { | ||
this.debugSection('Session','Starting singleton browser session'); | ||
return this._startBrowser(); | ||
} | ||
} | ||
_before() { | ||
return this.browser = this.browserBuilder.build(); | ||
if (this.options.restart) return this._startBrowser(); | ||
} | ||
_after() { | ||
return this.browser.quit(); | ||
if (this.options.restart) return this.browser.quit(); | ||
// if browser should not be restarted | ||
this.debugSection('Session', 'cleaning cookies and localStorage'); | ||
return this.browser.executeScript('localStorage.clear();').then(() => { | ||
return this.browser.manage().deleteAllCookies(); | ||
}) | ||
} | ||
_afterSuite() { | ||
if (!this.options.restart) return this.browser.quit(); | ||
} | ||
_failed(test) { | ||
@@ -129,2 +152,3 @@ let fileName = test.title.replace(/ /g, '_') + '.failed.png'; | ||
this.browser.findElements = (l) => context.findElements(l); | ||
return context; | ||
} | ||
@@ -325,4 +349,3 @@ | ||
*/ | ||
seeCheckboxIsChecked(field) | ||
{ | ||
seeCheckboxIsChecked(field) { | ||
return co.wrap(proceedIsChecked).call(this, 'assert', field); | ||
@@ -540,3 +563,3 @@ } | ||
clearCookie(cookie) { | ||
if (cookie) { | ||
if (!cookie) { | ||
return this.browser.manage().deleteAllCookies(); | ||
@@ -543,0 +566,0 @@ } |
@@ -41,5 +41,6 @@ 'use strict'; | ||
* * `browser` - browser in which perform testing | ||
* * `restart` - restart browser between tests (default: true), if set to false cookies will be cleaned but browser window will be kept. | ||
* * `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; | ||
* * `desiredCapabilities`: | ||
* * `desiredCapabilities`: Selenium capabilities | ||
* | ||
@@ -174,3 +175,4 @@ * | ||
waitforTimeout: 1000, // ms | ||
desiredCapabilities: {} | ||
desiredCapabilities: {}, | ||
restart: true | ||
}; | ||
@@ -218,4 +220,10 @@ | ||
_before() { | ||
this.failedTestName = null; | ||
_beforeSuite() { | ||
if (!this.options.restart) { | ||
this.debugSection('Session','Starting singleton browser session'); | ||
return this._startBrowser(); | ||
} | ||
} | ||
_startBrowser() { | ||
if (this.options.multiremote) { | ||
@@ -234,2 +242,8 @@ this.browser = webdriverio.multiremote(this.options.multiremote); | ||
} | ||
return this.browser; | ||
} | ||
_before() { | ||
if (this.options.restart) this._startBrowser(); | ||
this.failedTestName = null; | ||
this.context = 'body'; | ||
@@ -240,5 +254,13 @@ return this.browser; | ||
_after() { | ||
return this.browser.end(); | ||
if (this.options.restart) return this.browser.end(); | ||
this.debugSection('Session', 'cleaning cookies and localStorage'); | ||
return this.browser.execute('localStorage.clear();').then(() => { | ||
return this.browser.deleteCookie(); | ||
}); | ||
} | ||
_afterSuite() { | ||
if (!this.options.restart) return this.browser.end(); | ||
} | ||
_failed(test) { | ||
@@ -282,2 +304,41 @@ let fileName = test.title.replace(/ /g, '_') + '.failed.png'; | ||
/** | ||
* Find a checkbox by providing human readable text: | ||
* | ||
* ```js | ||
* this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... | ||
* ``` | ||
*/ | ||
_locateCheckable(locator) { | ||
return findCheckable(this.browser, locator).then(function(res){ | ||
return res.value; | ||
}) | ||
} | ||
/** | ||
* Find a clickable element by providing human readable text: | ||
* | ||
* ```js | ||
* this.helpers['WebDriverIO']._locateClickable('Next page').then // ... | ||
* ``` | ||
*/ | ||
_locateClickable(locator) { | ||
return findClickable(this.browser, locator).then(function(res){ | ||
return res.value; | ||
}) | ||
} | ||
/** | ||
* Find field elements by providing human readable text: | ||
* | ||
* ```js | ||
* this.helpers['WebDriverIO']._locateFields('Your email').then // ... | ||
* ``` | ||
*/ | ||
_locateFields(locator) { | ||
return findFields(this.browser, locator).then(function(res){ | ||
return res.value; | ||
}) | ||
} | ||
/** | ||
* {{> ../webapi/amOnPage }} | ||
@@ -837,3 +898,3 @@ */ | ||
* Drag an item to a destination element. | ||
* | ||
* ```js | ||
@@ -879,4 +940,3 @@ * I.dragAndDrop('#dragHandle', '#container'); | ||
context = context || 'body'; | ||
return this.browser | ||
.waitUntil(function () { | ||
return this.browser.waitUntil(function () { | ||
return this.getText(context).then(function (source) { | ||
@@ -890,3 +950,3 @@ if (Array.isArray(source)) { | ||
.catch((e) => { | ||
if (e.type === 'CommandError') { | ||
if (e.type === 'WaitUntilTimeoutError') { | ||
return proceedSee.call(this, 'assert', text, context); | ||
@@ -1102,3 +1162,3 @@ } else { | ||
case 'id': return '#' + value; | ||
case 'name': return `[name="value"]`; | ||
case 'name': return `[name="${value}"]`; | ||
} | ||
@@ -1105,0 +1165,0 @@ } |
@@ -19,3 +19,3 @@ 'use strict'; | ||
debug(`${key}.${hook}`); | ||
recorder.add(() => helpers[key][hook](param), force); | ||
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force); | ||
}); | ||
@@ -28,2 +28,10 @@ }; | ||
event.dispatcher.on(event.suite.before, function () { | ||
runHelpersHook('_beforeSuite'); | ||
}); | ||
event.dispatcher.on(event.suite.after, function () { | ||
runAsyncHelpersHook('_afterSuite'); | ||
}); | ||
event.dispatcher.on(event.test.before, function () { | ||
@@ -35,2 +43,4 @@ runAsyncHelpersHook('_before'); | ||
runAsyncHelpersHook('_failed', test, true); | ||
// should not fail test execution, so errors should be catched | ||
recorder.catch((e) => debug(e)); | ||
}); | ||
@@ -37,0 +47,0 @@ |
@@ -18,2 +18,8 @@ 'use strict'; | ||
event.dispatcher.on(event.test.failed, function () { | ||
// last step is failing step | ||
if (!steps.length) return; | ||
steps.slice(-1)[0].status = 'failed'; | ||
}); | ||
event.dispatcher.on(event.step.before, function (step) { | ||
@@ -20,0 +26,0 @@ if (!currentTest || !currentTest.steps) return; |
@@ -12,3 +12,4 @@ 'use strict'; | ||
basic: colors.white, | ||
debug: colors.cyan | ||
debug: colors.cyan, | ||
log: colors.grey | ||
}; | ||
@@ -29,5 +30,10 @@ | ||
debug: (msg) => { | ||
if (outputLevel === 2) print(' '.repeat(this.stepShift), styles.debug("> " + msg)); | ||
if (outputLevel >= 2) print(' '.repeat(this.stepShift), styles.debug("> " + msg)); | ||
}, | ||
log: (msg) => { | ||
if (outputLevel >= 3) print(' '.repeat(this.stepShift), styles.log(" " + msg)); | ||
}, | ||
error: (msg) => { | ||
@@ -43,2 +49,3 @@ print(styles.error(msg)); | ||
if (outputLevel === 0) return; | ||
if (!step) return; | ||
let sym = process.platform === 'win32' ? '*' : '•'; | ||
@@ -45,0 +52,0 @@ print(' '.repeat(this.stepShift), `${sym} ${step.toString()}`); |
@@ -17,4 +17,4 @@ 'use strict'; | ||
module.exports = function () { | ||
recorder.add(function () { | ||
recorder.session.start(); | ||
recorder.add('Start new session' ,function () { | ||
recorder.session.start('pause'); | ||
output.print(colors.yellow(" Interative debug session started")); | ||
@@ -54,3 +54,3 @@ output.print(colors.yellow(" Use JavaScript syntax to try steps in action")); | ||
}); | ||
recorder.add(askForStep); | ||
recorder.add('ask for next step', askForStep); | ||
nextStep(); | ||
@@ -57,0 +57,0 @@ } |
@@ -7,3 +7,8 @@ 'use strict'; | ||
let next; | ||
let queueId = 0; | ||
let sessionId = null; | ||
let log = require('./output').log; | ||
let tasks = []; | ||
/** | ||
@@ -13,2 +18,3 @@ * Singlton object to record all test steps as promises and run them in chain. | ||
module.exports = { | ||
start() { | ||
@@ -26,8 +32,20 @@ running = true; | ||
if (promise && running) this.catch(); | ||
queueId++; | ||
sessionId = null; | ||
log(currentQueue() + `Starting recording promises`); | ||
promise = Promise.resolve(); | ||
oldpromise = null; | ||
tasks = []; | ||
this.session.running = false; | ||
}, | ||
session: { | ||
start(errFn) { | ||
running: false, | ||
start(name) { | ||
log(currentQueue() + `Starting <${name}> session`); | ||
tasks.push('--->'); | ||
oldpromise = promise; | ||
this.running = true; | ||
sessionId = name; | ||
promise = Promise.resolve(); | ||
@@ -37,2 +55,6 @@ }, | ||
restore() { | ||
tasks.push('<---'); | ||
log(currentQueue() + `Starting <${this.name}> session`); | ||
this.running = false; | ||
sessionId = null; | ||
promise = promise.then(() => oldpromise); | ||
@@ -47,6 +69,12 @@ }, | ||
add(fn, force) { | ||
add(taskName, fn, force) { | ||
if (typeof taskName === "function") { | ||
fn = taskName; | ||
taskName = fn.toString(); | ||
} | ||
if (!running && !force) { | ||
return; | ||
} | ||
tasks.push(taskName); | ||
log(currentQueue() + `Queued | ${taskName}`); | ||
return promise = promise.then(fn); | ||
@@ -56,8 +84,15 @@ }, | ||
addStep(step, args) { | ||
this.add(() => step.run.apply(step, args)); | ||
step.status = 'queued'; | ||
let task = `${step.name}: ${Object.keys(args).map(key => args[key]).join(', ')}`; | ||
this.add(task, () => step.run.apply(step, args)); | ||
}, | ||
catch() { | ||
catch(customErrFn) { | ||
return promise = promise.catch((err) => { | ||
if (errFn) errFn(err); | ||
log(currentQueue() + `Error | ${err}`); | ||
if (customErrFn) { | ||
customErrFn(err); | ||
} else if (errFn) { | ||
errFn(err); | ||
} | ||
this.stop(); | ||
@@ -68,3 +103,3 @@ }); | ||
throw(err) { | ||
return this.add(function () { | ||
return this.add('throw error ' + err,function () { | ||
throw err; | ||
@@ -74,3 +109,5 @@ }); | ||
stop() { | ||
stop() { | ||
log(currentQueue() + `Stopping recording promises`); | ||
var err = new Error(); | ||
running = false; | ||
@@ -81,3 +118,19 @@ }, | ||
return promise; | ||
}, | ||
scheduled() { | ||
return tasks.join("\n"); | ||
}, | ||
toString() { | ||
return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`; | ||
} | ||
}; | ||
function currentQueue() { | ||
let session = ''; | ||
if (sessionId) session = `<${sessionId}> `; | ||
return `[${queueId}] ${session}`; | ||
} | ||
@@ -18,4 +18,6 @@ 'use strict'; | ||
let level = 0; | ||
if (opts.steps) level = 1; | ||
if (opts.debug) level = 2; | ||
if (opts.steps) level = 1; | ||
if (opts.verbose) level = 3; | ||
output.level(level); | ||
@@ -29,3 +31,2 @@ let showSteps = level >= 1; | ||
runner.on('start', function () { | ||
@@ -32,0 +33,0 @@ console.log(); |
@@ -20,9 +20,10 @@ 'use strict'; | ||
test.fn = function (done) { | ||
recorder.errHandler(function (err){ | ||
event.dispatcher.emit(event.test.failed, test, err); | ||
recorder.add(() => done(err), true); | ||
recorder.errHandler(function (err) { | ||
recorder.session.start('teardown'); | ||
event.emit(event.test.failed, test, err); | ||
recorder.add(() => done(err)); | ||
}); | ||
try { | ||
event.dispatcher.emit(event.test.started, test); | ||
event.emit(event.test.started, test); | ||
let res = testFn.apply(test, getInjectedArguments(testFn)); | ||
@@ -38,8 +39,8 @@ if (isGenerator(testFn)) { | ||
let resumeTest = function () { | ||
recorder.add(function (data) { | ||
recorder.reset(); // creating a new promise chain | ||
recorder.add('create new promises queue for generator',function (data) { | ||
recorder.session.start('generator'); // creating a new promise chain | ||
try { | ||
let resume = res.next(data); | ||
if (resume.done) { | ||
recorder.add(() => done()); // finish him | ||
recorder.add('finish generator with no error', () => done()); // finish him | ||
} else { | ||
@@ -60,3 +61,3 @@ resumeTest(); | ||
if (!isGenerator(testFn)) { | ||
recorder.add(() => done()); | ||
recorder.add('finish test', () => done()); | ||
recorder.catch(); | ||
@@ -88,8 +89,7 @@ } | ||
recorder.start(); | ||
event.dispatcher.emit(event.test.before); | ||
event.emit(event.test.before); | ||
}; | ||
module.exports.teardown = function () { | ||
// recorder.reset(); | ||
event.dispatcher.emit(event.test.after); | ||
event.emit(event.test.after); | ||
}; | ||
@@ -101,4 +101,3 @@ | ||
function getInjectedArguments(fn) | ||
{ | ||
function getInjectedArguments(fn) { | ||
let testArguments = []; | ||
@@ -105,0 +104,0 @@ let params = getParamNames(fn) || []; |
@@ -20,5 +20,3 @@ 'use strict'; | ||
this.args = Array.prototype.slice.call(arguments); | ||
event.dispatcher.emit(event.step.init, this); | ||
this.status = 'queued'; | ||
event.dispatcher.emit(event.step.before, this); | ||
event.emit(event.step.before, this); | ||
let result; | ||
@@ -32,3 +30,3 @@ try { | ||
} finally { | ||
event.dispatcher.emit(event.step.after, this); | ||
event.emit(event.step.after, this); | ||
} | ||
@@ -35,0 +33,0 @@ return result; |
@@ -8,5 +8,5 @@ 'use strict'; | ||
function within(context, fn) { | ||
recorder.add(function () { | ||
recorder.add('register within wrapper', function () { | ||
recorder.session.start(); | ||
recorder.session.start('within'); | ||
let helpers = container.helpers(); | ||
@@ -22,3 +22,3 @@ let locator = typeof context === 'object' ? JSON.parse(context) : context; | ||
Object.keys(helpers).forEach((helper) => { | ||
if (helpers[helper]._withinBegin) recorder.add(() => helpers[helper]._withinBegin(context)); | ||
if (helpers[helper]._withinBegin) recorder.add('start within block', () => helpers[helper]._withinBegin(context)); | ||
}); | ||
@@ -29,8 +29,10 @@ | ||
Object.keys(helpers).forEach((helper) => { | ||
if (helpers[helper]._withinEnd) recorder.add(() => helpers[helper]._withinEnd()); | ||
if (helpers[helper]._withinEnd) recorder.add('finish within block', () => helpers[helper]._withinEnd()); | ||
}); | ||
recorder.add(() => recorder.session.restore()); | ||
recorder.add(() => event.dispatcher.removeListener(event.step.after, addContextToStep)); | ||
recorder.add(() => output.stepShift = 1); | ||
recorder.add('restore session', () => { | ||
recorder.session.restore(); | ||
output.stepShift = 1; | ||
event.dispatcher.removeListener(event.step.after, addContextToStep) | ||
}); | ||
return recorder.promise(); | ||
@@ -37,0 +39,0 @@ }); |
{ | ||
"name": "codeceptjs", | ||
"version": "0.3.5", | ||
"version": "0.4.0", | ||
"description": "Modern Era Aceptance Testing Framework for NodeJS", | ||
@@ -55,3 +55,5 @@ "homepage": "http://codecept.io", | ||
"guppy-pre-commit": "^0.3.0", | ||
"protractor": "^3.3.0", | ||
"nightmare": "^2.5.2", | ||
"nightmare-upload": "^0.1.1", | ||
"protractor": "^4.0.0", | ||
"selenium-webdriver": "^2.53.1", | ||
@@ -58,0 +60,0 @@ "sinon": "^1.17.2", |
@@ -47,2 +47,3 @@ # 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) | ||
* Uses ES6 natively without transpiler. | ||
* Also plays nice with TypeScript. | ||
* Selenium WebDriver integration using [webdriverio](http://webdriver.io). | ||
@@ -81,2 +82,10 @@ * Smart locators: use names, labels, matching text, CSS or XPath to locate elements. | ||
If you want to write your tests using TypeScript just generate standard Type Definitions by executing: | ||
``` | ||
codeceptjs def . | ||
``` | ||
Later you can even automagically update Type Definitions to include your own custom [helpers methods](docs/helpers.md). | ||
## Usage | ||
@@ -83,0 +92,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
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
185228
42
5387
286
23
18