Skip to content

Commit

Permalink
Replace runInContext with node-eval with exporter option (#16)
Browse files Browse the repository at this point in the history
* replace runInContext with node-eval
* add test for plugin itself
  • Loading branch information
Vittly authored and dima117 committed Jul 26, 2017
1 parent f5df64a commit eff66b9
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 17 deletions.
33 changes: 26 additions & 7 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
'use strict';

const loader = require('./loader'),
const nodeEval = require('node-eval'),
loader = require('./loader'),
validate = require('./validator'),
format = require('./formatter');

module.exports = {

// For tests
_utils: {
loadSchema: arg => loader.load(arg),
validate,
format,
eval: nodeEval
},

/**
* Returns default plugin configuration
*
Expand All @@ -20,19 +29,29 @@ module.exports = {
* @param {Entity} entity
* @param {Config} config
*/
forEachTech: (tech, entity, config) => {
forEachTech: function(tech, entity, config) {

const locator = config.getConfig().locator;
const locator = config.getConfig().locator,
schema = this._utils.loadSchema(config);

function addError(title, error) {
const addError = (title, error) => {
entity.addError({
msg: title,
tech: tech.name,
value: typeof error === 'object' ? format(error) : error,
location: locator && locator(tech.content, error.dataPath)
value: typeof error === 'object' ? this._utils.format(error) : error,
location: typeof error === 'object' && locator ? locator(tech.content, error.dataPath) : undefined
});
};

let value;

try {
value = this._utils.eval(tech.content || '');
} catch (e) {
addError('Invalid content in source file', e.message);
return;
}

tech.content && validate(loader.load(config), tech.content, addError);
value && this._utils.validate(schema, value, addError);
}
};
9 changes: 4 additions & 5 deletions lib/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ const vm = require('vm'),
* Validates content with json-schema in async way
*
* @param {Object} schema
* @param {String} content
* @param {*} value
* @param {Function} errCallback
*/
module.exports = (schema, content, errCallback) => {
module.exports = (schema, value, errCallback) => {
try {
const data = vm.runInThisContext(content),
ajv = Ajv({ allErrors: true, verbose: true, v5: true });
const ajv = Ajv({ allErrors: true, verbose: true, v5: true });

ajv.validate(schema, data) || ajv.errors.forEach(error => {
ajv.validate(schema, value) || ajv.errors.forEach(error => {
errCallback('.deps.js schema error', error);
});

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
},
"homepage": "https://github.com/bemhint/bemhint-deps-schema#readme",
"dependencies": {
"ajv": "^4.8.2"
"ajv": "^4.8.2",
"node-eval": "^1.1.0"
},
"devDependencies": {
"chai": "^3.5.0",
Expand Down
145 changes: 145 additions & 0 deletions test/cases/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
'use strict';

const plugin = require('../../lib');

describe('plugin', () => {
let makeConfig = pluginConfig => ({ getConfig: () => pluginConfig }),
tech, entity, sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();

sandbox.stub(plugin._utils, 'loadSchema').returns('test-schema');
sandbox.stub(plugin._utils, 'format').returns('test-format');
sandbox.stub(plugin._utils, 'eval').returns('test-value');
sandbox.stub(plugin._utils, 'validate');

entity = { addError: sinon.stub() };

tech = { content: 'test-content', name: 'test' };
});

afterEach(() => {
sandbox.restore();
});

it('should run validation', () => {
const config = makeConfig({});

plugin.forEachTech(tech, entity, config);

assert.calledOnce(plugin._utils.loadSchema);
assert.calledWith(plugin._utils.loadSchema, config);

assert.calledOnce(plugin._utils.eval);
assert.calledWith(plugin._utils.eval, 'test-content');

assert.calledOnce(plugin._utils.validate);
assert.calledWith(plugin._utils.validate, 'test-schema', 'test-value', sinon.match.func);

assert.notCalled(plugin._utils.format);
});

it('should pass formatted error object into entity.addError', () => {
const config = makeConfig({});

plugin.forEachTech(tech, entity, config);

const validateAddError = plugin._utils.validate.firstCall.args[2];

// fake error during validation
validateAddError('test-error', { error: true });

assert.calledOnce(entity.addError);
assert.calledWith(entity.addError, {
msg: 'test-error',
tech: 'test',
value: 'test-format',
location: undefined
});

assert.calledOnce(plugin._utils.format);
assert.calledWith(plugin._utils.format, { error: true });
});

it('should pass plain string error into entity.addError', () => {
const config = makeConfig({});

plugin.forEachTech(tech, entity, config);

const validateAddError = plugin._utils.validate.firstCall.args[2];

// fake error during validation
validateAddError('test-error', 'plain-error');

assert.calledOnce(entity.addError);
assert.calledWith(entity.addError, {
msg: 'test-error',
tech: 'test',
value: 'plain-error',
location: undefined
});

assert.notCalled(plugin._utils.format);
});

it('should pass location for error object if locator defined', () => {
const locator = sinon.stub().returns('test-location'),
config = makeConfig({ locator });

plugin.forEachTech(tech, entity, config);

const validateAddError = plugin._utils.validate.firstCall.args[2];

// fake error during validation
validateAddError('test-error', { dataPath: 'foo.bar.0.baz' });

assert.calledWith(entity.addError, {
msg: 'test-error',
tech: 'test',
value: 'test-format',
location: 'test-location'
});

assert.calledOnce(locator);
assert.calledWith(locator, 'test-content', 'foo.bar.0.baz');
});

it('should throw with syntax error', () => {
const config = makeConfig({});

plugin._utils.eval.throws(new SyntaxError('bad-syntax'));

plugin.forEachTech(tech, entity, config);

assert.notCalled(plugin._utils.validate);

assert.calledOnce(entity.addError);
assert.calledWith(entity.addError, {
msg: 'Invalid content in source file',
tech: 'test',
value: 'bad-syntax',
location: undefined
});
});

it('should ignore falsy evaluated values', () => {
const config = makeConfig({});

plugin._utils.eval.returns();

plugin.forEachTech(tech, entity, config);

assert.notCalled(plugin._utils.validate);
assert.notCalled(entity.addError);
});

it('should deal with empty tech content', () => {
const config = makeConfig({});

plugin.forEachTech({}, entity, config);

assert.calledOnce(plugin._utils.eval);
assert.calledWith(plugin._utils.eval, '');
});
});
7 changes: 3 additions & 4 deletions test/cases/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,13 @@ const testCases = rootWrapper.getCases();

dump('testcases.json', testCases);

describe.only('base-schema', () => {
describe('base-schema', () => {

testCases.forEach(data => {
it(data.title, () => {
const content = '(' + JSON.stringify(data.obj) + ')',
errorCallback = sinon.spy();
const errorCallback = sinon.spy();

validate(baseSchema, content, errorCallback);
validate(baseSchema, data.obj, errorCallback);

if (data.error) {
assert.calledOnce(errorCallback);
Expand Down

0 comments on commit eff66b9

Please sign in to comment.