spec-detective
Advanced tools
Comparing version 0.0.9 to 0.1.0
/*jslint node: true */ | ||
'use strict'; | ||
var colors = require('colors'); | ||
/** | ||
* "buildConfig" | ||
* | ||
* Collects input options from console or a config file. | ||
*/ | ||
function buildConfig(args) { | ||
var fs = require('fs'), | ||
config = {}, | ||
content; | ||
argv = require('minimist')(args.slice(2)), | ||
config, | ||
content, | ||
configFile; | ||
config.specFile = 'spec-file.json'; | ||
config.resultsFile = 'results-file.json'; | ||
/** | ||
* Below are some of the example inputs. | ||
* | ||
* --config=file.conf | ||
* --specs=.feature.md | ||
* --results=output.json,test.xml | ||
* --watch | ||
* --junit-out=junit-output.json | ||
* --specs-out=spec-file.json, | ||
* --results-out=results-file.json | ||
*/ | ||
if (fs.existsSync("spec-detective.conf.json")) { | ||
content = fs.readFileSync("spec-detective.conf.json", {encoding:"utf8"}); | ||
config = JSON.parse(content); | ||
/** | ||
* Default config. | ||
*/ | ||
config = { | ||
config: (argv.config !== undefined)? argv.config : 'spec-detective.conf.json', | ||
watch: false, | ||
specs: false, | ||
results: false, | ||
tags: [], | ||
'junit-out': 'junit-output.xml', | ||
'specs-out': 'spec-file.json', | ||
'results-out': 'results-file.json' | ||
} | ||
if (args[2] !== undefined) { | ||
config.specGlob = args[2]; | ||
/** | ||
* Overrides from config file. | ||
*/ | ||
if (fs.existsSync(config.config)) { | ||
content = fs.readFileSync(config.config, {encoding:"utf8"}); | ||
configFile = JSON.parse(content); | ||
for (var key in configFile) { | ||
if (configFile.hasOwnProperty(key)) { | ||
config[key] = configFile[key]; | ||
} | ||
} | ||
} | ||
if (args[3] !== undefined) { | ||
config.resultsGlob = args[3]; | ||
/** | ||
* Overrides from input. | ||
*/ | ||
for (var key in argv) { | ||
if (argv.hasOwnProperty(key)) { | ||
config[key] = argv[key]; | ||
} | ||
} | ||
if (config.specGlob === undefined) { | ||
throw new Error("A specs glob such as path/*.feature must be specified"); | ||
/** | ||
* Help. | ||
*/ | ||
if (argv['h'] || argv['help'] || args.length === 2) { | ||
console.log( | ||
'\nSpec Detective: A BDD tool minus the pain\n\n'.green + | ||
'Usage:\n' + | ||
' spec-detective <arguments>\n\n' + | ||
'Arguments:\n' + | ||
' --start=<configFile> Set a config file location.\n' + | ||
' --watch Start in watch mode.\n' + | ||
' --specs="<glob>" Feature files to read (eg "*/**/*.feature.md").\n' + | ||
' --results="<glob>" Comma-deliminated list of test output (eg "tests/*.xml,tests/*.json").\n' + | ||
' --tags=<tags> Comma-delinated list of tagged features to read.\n' + | ||
' --junit-out=<file> File name for junit output (default: junit-output.xml).\n' + | ||
' --specs-out=<file> File name for spec json output (default: spec-file.json).\n' + | ||
' --results-out=<file> File name for results json output (default: results-file.json).\n' | ||
); | ||
process.exit(); | ||
} | ||
if (config.resultsGlob === undefined) { | ||
throw new Error("A results file must be specified"); | ||
/** | ||
* Enrichment. | ||
*/ | ||
if (config.tags && config.tags.indexOf(',') !== -1) { | ||
config.tags = config.tags.split(','); | ||
} | ||
if (config.results && config.results.indexOf(',') !== -1) { | ||
config.results = config.results.split(','); | ||
} | ||
if (config.results && typeof config.results === 'string') { | ||
config.results = [config.results]; | ||
} | ||
/** | ||
* We ALWAYS need spec files and tests results. | ||
*/ | ||
if (!config.specs) { | ||
print("A specs glob such as path/*.feature must be specified.", "red"); | ||
} | ||
if (!config.results) { | ||
print("A results file must be specified.", "red"); | ||
} | ||
/** | ||
* Abstract print for tests. | ||
*/ | ||
function print (message, color) { | ||
if (args[1].indexOf('spec-detective') !== -1 || args[1].indexOf('shouldit') !== -1) { | ||
if (color === undefined) { | ||
console.log(message); | ||
return; | ||
} | ||
console.log(message[color]); | ||
} else { | ||
config.message = message; | ||
} | ||
} | ||
return config; | ||
} | ||
}; | ||
module.exports = buildConfig; |
@@ -1,6 +0,6 @@ | ||
function transformFeature(input, callback) { | ||
function transformFeature(input, tags, callback) { | ||
var parser = require('./markdownParser'), | ||
output = {}; | ||
parser(input, function(spec){ | ||
parser(input, tags, function(spec){ | ||
handleSpec(spec, output, callback); | ||
@@ -12,4 +12,5 @@ }); | ||
var current = getCurrentOutputPointer(spec.suite, output); | ||
var current; | ||
if(spec.description) { | ||
current = getCurrentOutputPointer(spec.suite, output); | ||
current[spec.description] = spec.line; | ||
@@ -16,0 +17,0 @@ } |
var lineReader = require('line-reader'); | ||
function parseMarkdown(file, callback) { | ||
var output = {suite: [], last: false}; | ||
function parseMarkdown(file, tags, callback) { | ||
var output = {suite: [], last: false, currentTag: false, currentTagLevel: false}; | ||
//TODO: Refactor these into modules to by DRY | ||
/** | ||
* TODO: Refactor these into modules to be more DRY | ||
* | ||
* We also need to make this more performant by not instantiating new | ||
* Regex's on each pass. | ||
* | ||
* We can also grab the content by matching it | ||
*/ | ||
var lineTypes = { | ||
@@ -53,2 +60,18 @@ 'SPEC' : { | ||
} | ||
}, | ||
'TAG' : { | ||
tokens: ['> @', '>@'], | ||
match: function(line) { | ||
for(var i = 0; i < this.tokens.length; i++) { | ||
var regex = new RegExp('\\' + this.tokens[i] + '([^\n]*)'); | ||
if(line.match(regex)) { | ||
return line.match(regex); | ||
} | ||
} | ||
return false; | ||
}, | ||
strip: function(line) { | ||
var regex = new RegExp('(> @ )', 'gi' ); | ||
return this.match(line)[1].replace(regex, ''); | ||
} | ||
} | ||
@@ -64,2 +87,3 @@ }; | ||
} | ||
output.description = false; | ||
output.line = getDescriptiveLine(file, lineCount); | ||
@@ -76,6 +100,19 @@ handleLine(line, callback); | ||
case 'SPEC': | ||
output.description = typeObject.strip(line); | ||
if ( | ||
(tags.length < 1) || | ||
tags.indexOf(output.currentTag) !== -1 | ||
) { | ||
output.description = typeObject.strip(line); | ||
} | ||
callback(output); | ||
return; | ||
case 'DESCRIBE': | ||
if (output.currentTagLevel && output.currentTagLevel == typeObject.level(line)) { | ||
output.currentTagLevel = false; | ||
output.currentTag = false; | ||
} | ||
if (output.currentTag && !output.currentTagLevel) { | ||
output.currentTagLevel = typeObject.level(line); | ||
} | ||
//place the describe at the correct level | ||
@@ -85,2 +122,3 @@ output.suite[typeObject.level(line) -1] = typeObject.strip(line); | ||
output.suite = output.suite.splice(0, typeObject.level(line)); | ||
return; | ||
@@ -90,2 +128,7 @@ case 'SKIP' : | ||
output.last = true; | ||
break; | ||
case 'TAG' : | ||
output.currentTag = typeObject.strip(line); | ||
output.currentTagLevel = false; | ||
break; | ||
} | ||
@@ -92,0 +135,0 @@ if(output.last) { |
@@ -14,4 +14,2 @@ /** | ||
resultsGlob = resultsGlob.split(","); | ||
for (var i = 0; i < resultsGlob.length; i++) { | ||
@@ -18,0 +16,0 @@ files = files.concat(glob.sync(resultsGlob[i])); |
/** | ||
* Get tht specs from a glob and merges the specs | ||
* Get the specs from a glob and merges the specs | ||
*/ | ||
function collectSpecs(specsGlob, callback) { | ||
function collectSpecs(specsGlob, tags, callback) { | ||
var featureTransformer = require('./featureTransformer'), | ||
@@ -18,3 +18,3 @@ glob = require("glob"), | ||
for (i = 0; i < files.length; i++) { | ||
featureTransformer(files[i], fileTransformerCallback); | ||
featureTransformer(files[i], tags, fileTransformerCallback); | ||
} | ||
@@ -21,0 +21,0 @@ }); |
{ | ||
"name": "spec-detective", | ||
"version": "0.0.9", | ||
"version": "0.1.0", | ||
"description": "A cool way to find out if your spec's in feature files and the like have met expectations.", | ||
@@ -47,3 +47,4 @@ "main": "index.js", | ||
"mocha-spec-json-reporter": "*", | ||
"grunt-contrib-jshint": "^0.10.0" | ||
"grunt-contrib-jshint": "^0.10.0", | ||
"minimist": "*" | ||
}, | ||
@@ -50,0 +51,0 @@ "dependencies": { |
@@ -16,9 +16,8 @@ { | ||
}, | ||
"Results1": { | ||
"should be able to have tests": "PASSED", | ||
"should be able to merge tests": "PASSED" | ||
"describe something": { | ||
"should do something": "PASSED" | ||
}, | ||
"Results2": { | ||
"should be able to other context based tests": "PASSED" | ||
"Feature Subfeature": { | ||
"has one spec": "FAILED" | ||
} | ||
} |
@@ -8,3 +8,3 @@ var buildConfig = require("../lib/configBuilder"), | ||
beforeEach(function(){ | ||
args = ["", "", "", ""]; | ||
args = ['node', '/mocha', '--specs=exampleSpec.feature', '--results=results.json,results.xml']; | ||
}); | ||
@@ -14,4 +14,4 @@ | ||
var glob = "tests/fixtures/spec/exampleSpec.feature"; | ||
args[2] = glob; | ||
assert.equal(buildConfig(args).specGlob, glob); | ||
args[2] = '--specs=' + glob; | ||
assert.equal(buildConfig(args).specs, glob); | ||
}); | ||
@@ -21,4 +21,4 @@ | ||
var comparisonFile = "tests/fixtures/spec/exampleSpec.feature"; | ||
args[3] = comparisonFile; | ||
assert.equal(buildConfig(args).resultsGlob, comparisonFile); | ||
args[3] = '--results=' + comparisonFile; | ||
assert.deepEqual(buildConfig(args).results, [comparisonFile]); | ||
}); | ||
@@ -28,3 +28,3 @@ | ||
args[2] = undefined; | ||
assert.throws(function() {buildConfig(args)}, Error, "A glob must be specified"); | ||
assert.equal(buildConfig(args).message, "A specs glob such as path/*.feature must be specified."); | ||
}); | ||
@@ -34,5 +34,15 @@ | ||
args[3] = undefined; | ||
assert.throws(function() {buildConfig(args)}, Error, "A comparison file must be specified"); | ||
assert.equal(buildConfig(args).message, "A results file must be specified."); | ||
}); | ||
it("should be able to add a comma deliminated tag", function() { | ||
args[4] = "--tags=richard,john"; | ||
assert.deepEqual(buildConfig(args).tags, ['richard', 'john']); | ||
}); | ||
it("should be turn results into an array", function() { | ||
args[3] = "--results=richard,john"; | ||
assert.deepEqual(buildConfig(args).results, ['richard', 'john']); | ||
}); | ||
}); |
@@ -47,3 +47,3 @@ var featureTransformer = require('../lib/featureTransformer'), | ||
featureTransformer(inputFile, function(data) { | ||
featureTransformer(inputFile, [], function(data) { | ||
assert.like(JSON.parse(data), expectedOutput); | ||
@@ -50,0 +50,0 @@ done(); |
@@ -8,3 +8,3 @@ var markdownParser = require('../lib/markdownParser'), | ||
var inputFile = "test/fixtures/markdown/exampleSpec.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.description, "should do something"); | ||
@@ -17,3 +17,3 @@ done(); | ||
var inputFile = "test/fixtures/markdown/describe.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.description, "should do something"); | ||
@@ -29,3 +29,3 @@ assert.equal(spec.suite.length, 1); | ||
var count = 0; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
count++; | ||
@@ -49,3 +49,3 @@ if(count == 2) { | ||
var count = 0; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
count++; | ||
@@ -64,3 +64,3 @@ if(count == 2) { | ||
var inputFile = "test/fixtures/markdown/skipStep.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.description, "a second spec"); | ||
@@ -77,3 +77,3 @@ assert.equal(spec.suite.length, 1); | ||
var count = 0; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
count++; | ||
@@ -90,3 +90,3 @@ if(count == 2) { | ||
var inputFile = "test/fixtures/markdown/skipFile.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.last, true); | ||
@@ -101,3 +101,3 @@ assert.equal(spec.skip, true); | ||
var inputFile = "test/fixtures/markdown/upperSkip.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.last, true); | ||
@@ -112,3 +112,3 @@ assert.equal(spec.skip, true); | ||
var inputFile = "test/fixtures/markdown/skipNoSpace.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.last, true); | ||
@@ -124,3 +124,3 @@ assert.equal(spec.skip, true); | ||
var count = 0; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
count++; | ||
@@ -136,3 +136,3 @@ if(count >= 2) { | ||
var inputFile = "test/fixtures/markdown/upperIT.md"; | ||
markdownParser(inputFile, function(spec) { | ||
markdownParser(inputFile, [], function(spec) { | ||
assert.equal(spec.description, "should do something"); | ||
@@ -143,3 +143,29 @@ done(); | ||
it("matches markers and returns only those specs when a marker is supplied", function(done) { | ||
var inputFile = "test/fixtures/markdown/markered.md", | ||
count = 0; | ||
markdownParser(inputFile, ["mymark"], function(spec) { | ||
count++; | ||
switch (count) { | ||
case 1: | ||
assert.equal(spec.currentTag, "ignoremark"); | ||
assert.equal(spec.description, false); | ||
break; | ||
case 2: | ||
assert.equal(spec.currentTag, "mymark"); | ||
assert.equal(spec.description, "also has a spec on the sub-sub feature"); | ||
break; | ||
case 3: | ||
assert.equal(spec.currentTag, "mymark"); | ||
assert.equal(spec.description, "also has a spec on another sub-sub feature"); | ||
break; | ||
case 4: | ||
assert.equal(spec.currentTag, false); | ||
assert.equal(spec.description, false); | ||
done(); | ||
} | ||
}); | ||
}); | ||
}); |
@@ -30,3 +30,3 @@ var assert = require('assert'), | ||
it("should fire a callback passing in an object made up of merged objects", function(done) { | ||
resultCollector('test/fixtures/multiple_results/*.json', function(data) { | ||
resultCollector(['test/fixtures/multiple_results/*.json'], function(data) { | ||
assert.equal(JSON.stringify(data), JSON.stringify({ | ||
@@ -33,0 +33,0 @@ "Results1": { |
@@ -17,3 +17,3 @@ var assert = require('assert'), | ||
var object, | ||
transformer = function (file, callback) { | ||
transformer = function (file, tags, callback) { | ||
i++; | ||
@@ -46,3 +46,3 @@ object = {}; | ||
specCollector('test/fixtures/markdown/*.md', function(){}); | ||
specCollector('test/fixtures/markdown/*.md', [], function(){}); | ||
assert.equal(transformerSpy.callCount, 3); | ||
@@ -52,3 +52,3 @@ }); | ||
it("should fire a callback passing in an object made up of merged objects", function(done) { | ||
specCollector('test/fixtures/markdown/*.md', function(data) { | ||
specCollector('test/fixtures/markdown/*.md', [], function(data) { | ||
assert.equal(JSON.stringify(data), JSON.stringify({ | ||
@@ -55,0 +55,0 @@ 'some data1': 'some value', |
Sorry, the diff of this file is not supported yet
74361
56
1646
14
10