Skip to content
Closed
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
13 changes: 12 additions & 1 deletion bin/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ program.command('run [test]')
.option('-o, --override [value]', 'override current config options')
.option('--profile [value]', 'configuration profile to be used')
.option('-c, --config [file]', 'configuration file to be used')
.option('--transpile', 'configure codecept to understand JSX syntax and transpilers')

// mocha options
.option('--colors', 'force enabling of colors')
Expand All @@ -72,7 +73,8 @@ program.command('run [test]')
.option('-f, --fgrep <string>', 'only run tests containing <string>')
.option('-i, --invert', 'inverts --grep and --fgrep matches')
.option('--full-trace', 'display the full stack trace')
.option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files')
.option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
.option('-r, --require <name>,...', 'require the given module', list, [])
.option('--debug-brk', "enable node's debugger breaking on the first line")
.option('--inline-diffs', 'display actual/expected differences inline within each string')
.option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
Expand Down Expand Up @@ -103,3 +105,12 @@ if (process.argv.length <= 2) {
program.outputHelp();
}
program.parse(process.argv);


/**
* Parse list.
*/

function list(str) {
return str.split(/ *, */);
}
23 changes: 23 additions & 0 deletions lib/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Config = require('./config');
const event = require('../lib/event');
const glob = require('glob');
const runHook = require('./hooks');
const transpile = require('./transpile');

/**
* CodeceptJS runner
Expand Down Expand Up @@ -96,6 +97,28 @@ class Codecept {
* @param {string} [test]
*/
run(test) {
this.opts.compilers = typeof this.opts.compilers === 'string' ? [this.opts.compilers] : this.opts.compilers;
this.opts.compilers.forEach((c) => {
const idx = c.indexOf(':');
const ext = c.slice(0, idx);
let mod = c.slice(idx + 1);

if (mod[0] === '.') {
mod = fsPath.join(process.cwd(), mod);
}
require(mod);
});
this.opts.require = typeof this.opts.requires === 'string' ? [this.opts.require] : this.opts.require;
if (this.opts.transpile) {
transpile.enable();
this.opts.require.push(fsPath.join(__dirname, './react/jsDom'));
}
this.opts.require.forEach((mod) => {
if (mod[0] === '.') {
mod = fsPath.join(process.cwd(), mod);
}
require(mod);
});
const mocha = Container.mocha();
mocha.files = this.testFiles;
if (test) {
Expand Down
49 changes: 49 additions & 0 deletions lib/helper/Enzyme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const requireg = require('requireg');
const Helper = require('../helper');

const enzyme = requireg('enzyme');
const Adapter = requireg('enzyme-adapter-react-16');

class Enzyme extends Helper {
constructor(config) {
super(config);
this._validateConfig(config);
}

_validateConfig(config) {
this.options = {};

enzyme.configure({ adapter: new Adapter() });
// override defaults with config
Object.assign(this.options, config);
}

static _checkRequirements() {
try {
requireg('enzyme');
} catch (e) {
return ['enzyme'];
}
}

mountComponent(component) {
this.wrapper = enzyme.mount(component);
return this.wrapper;
}

shallowComponent(component) {
this.wrapper = enzyme.shallow(component);
return this.wrapper;
}

_after() {
if (this.wrapper) {
return this.wrapper.unmount();
}
}

_failed() {
}
}

module.exports = Enzyme;
11 changes: 11 additions & 0 deletions lib/react/jsDom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const baseDOM = '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body></body></html>';
const JSDOM = require('jsdom').JSDOM;

global.window = (new JSDOM(baseDOM)).window;
global.document = global.window.document;
if (global.self === null) {
global.self = global.this;
}
global.navigator = {
userAgent: 'node.js',
};
89 changes: 75 additions & 14 deletions lib/scenario.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const event = require('./event');
const container = require('./container');
const recorder = require('./recorder');
const { getParamNames, isGenerator, isAsyncFunction } = require('./utils');
const transpile = require('./transpile');

const resumeTest = function (res, done, error, returnStatement) {
recorder.add('create new promises queue for generator', (data) => {
Expand Down Expand Up @@ -122,7 +123,7 @@ module.exports.injected = function (fn, suite, hookName) {
event.emit(event.hook.started, suite);
recorder.startUnlessRunning();
this.test.body = fn.toString();
fn.apply(this, getInjectedArguments(fn)).then(() => {
fn.apply(this, getInjectedArguments(fn, suite, hookName)).then(() => {
recorder.add('fire hook.passed', () => event.emit(event.hook.passed, suite));
recorder.add(`finish ${hookName} hook`, () => done());
recorder.catch();
Expand All @@ -142,7 +143,7 @@ module.exports.injected = function (fn, suite, hookName) {
event.emit(event.hook.started, suite);
recorder.startUnlessRunning();
this.test.body = fn.toString();
const res = fn.apply(this, getInjectedArguments(fn));
const res = fn.apply(this, getInjectedArguments(fn, suite, hookName));
if (isGenerator(fn)) {
try {
res.next(); // running test
Expand Down Expand Up @@ -199,21 +200,81 @@ module.exports.suiteTeardown = function (suite) {
}, suite);
};

function getInjectedArguments(fn, test) {
function getInjectedArguments(fn, test, hookName) {
const testArguments = [];
const params = getParamNames(fn) || [];
const objects = container.support();
for (const key in params) {
const obj = params[key];
if (test && test.inject && test.inject[obj]) {
testArguments.push(test.inject[obj]);
continue;
if (transpile.status()) {
const esprima = require('esprima');
const escodegen = require('escodegen');
const babel = require('babel-core');
const fs = require('fs');
const data = fs.readFileSync(test.file, 'utf8');
const transformReact = babel.transform(data, {
presets: ['es2015'],
plugins: ['transform-react-jsx'],
});
const parsed = esprima.parse(transformReact.code);
if (hookName) {
parsed.body.forEach((parse) => {
if (parse.type === 'ExpressionStatement' && parse.expression && parse.expression.callee &&
parse.expression.callee.name && parse.expression.callee.name.toLowerCase() === hookName) {
const testFunction = parse.expression.arguments[0];
const testCode = escodegen.generate(testFunction);
test.ctx.test.body = testCode.toString();
const args = testFunction.params ? testFunction.params : testFunction.arguments[0].params;
args.forEach((arg) => {
testArguments.push(container.support(arg.name));
});
}
});
} else {
const testName = test.title;
parsed.body.forEach((parse) => {
if (parse.type === 'ExpressionStatement' && parse.expression && parse.expression.callee &&
parse.expression.callee.name && parse.expression.callee.name === 'Scenario' &&
parse.expression.arguments[0].value === testName) {
const testFunction = parse.expression.arguments[1].type === 'FunctionExpression' ?
parse.expression.arguments[1] : parse.expression.arguments[2];
const testCode = escodegen.generate(testFunction);
test.body = testCode.toString();
const args = testFunction.params ? testFunction.params : testFunction.arguments[0].params;
args.forEach((arg) => {
testArguments.push(container.support(arg.name));
});
}
if (parse.type === 'ExpressionStatement' && parse.expression && parse.expression.callee &&
parse.expression.callee.property && parse.expression.callee.property.name === 'Scenario' &&
parse.expression.callee.object && parse.expression.callee.object.callee &&
parse.expression.callee.object.callee.name === 'Data' &&
testName.indexOf(`${parse.expression.arguments[0].value} |`) === 0) {
const testFunction = parse.expression.arguments[1].type === 'FunctionExpression' ?
parse.expression.arguments[1] : parse.expression.arguments[1].arguments[0];
const testCode = escodegen.generate(testFunction);
test.body = testCode.toString();
const args = testFunction.params ? testFunction.params : testFunction.arguments[0].params;
args.forEach((arg) => {
if (arg.name !== 'current') {
testArguments.push(container.support(arg.name));
} else {
testArguments.push(test.inject.current);
}
});
}
});
}
if (!objects[obj]) {
throw new Error(`Object of type ${obj} is not defined in container`);
} else {
const params = getParamNames(fn) || [];
const objects = container.support();
for (const key in params) {
const obj = params[key];
if (test && test.inject && test.inject[obj]) {
testArguments.push(test.inject[obj]);
continue;
}
if (!objects[obj]) {
throw new Error(`Object of type ${obj} is not defined in container`);
}
testArguments.push(container.support(obj));
}
testArguments.push(container.support(obj));
}

return testArguments;
}
5 changes: 4 additions & 1 deletion lib/step.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const transpile = require('./transpile');
/**
* Each command in test executed through `I.` object is wrapped in Step.
* Step allows logging executed commands and triggers hook before and after step execution.
Expand Down Expand Up @@ -44,7 +45,9 @@ class Step {

humanizeArgs() {
return this.args.map((arg) => {
if (typeof arg === 'string') {
if (transpile.status() && require('react').isValidElement(arg)) {
return require('react-element-to-string')(arg);
} else if (typeof arg === 'string') {
return `"${arg}"`;
} else if (Array.isArray(arg)) {
return `[${arg.toString()}]`;
Expand Down
12 changes: 12 additions & 0 deletions lib/transpile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
let transpile = false;

module.exports = {
enable: () => {
transpile = true;
return transpile;
},

status: () => {
return transpile;
},
};
Loading