diff --git a/.gitignore b/.gitignore index 683edb13e..145aed740 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ bower_components .DS_Store .idea -coverage \ No newline at end of file +coverage + +test/tmp_* +test/fixtures/samples/*.actual.* diff --git a/lib/cli.js b/lib/cli.js index db28e62e4..0b389dc85 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -21,6 +21,7 @@ function cli(options) { help: 'h' , png: 'p' , outputDir: 'o' + , outputSuffix: 'O' , svg: 's' , verbose: 'v' , phantomPath: 'e' @@ -30,7 +31,7 @@ function cli(options) { , width: 'w' } , 'boolean': ['help', 'png', 'svg', 'verbose'] - , 'string': ['outputDir'] + , 'string': ['outputDir', 'outputSuffix'] } this.errors = [] @@ -45,6 +46,7 @@ function cli(options) { , " -s --svg Output SVG instead of PNG (experimental)" , " -p --png If SVG was selected, and you also want PNG, set this flag" , " -o --outputDir Directory to save files, will be created automatically, defaults to `cwd`" + , " -O --outputSuffix Suffix to output filenames in front of '.svg' or '.png', defaults to ''" , " -e --phantomPath Specify the path to the phantomjs executable" , " -t --css Specify the path to a CSS file to be included when processing output" , " -c --sequenceConfig Specify the path to the file with the configuration to be applied in the sequence diagram" @@ -79,7 +81,7 @@ cli.prototype.parse = function(argv, next) { } // ensure that parameter-expecting options have parameters - ;['outputDir', 'phantomPath', 'sequenceConfig', 'ganttConfig', 'css'].forEach(function(i) { + ;['outputDir', 'outputSuffix', 'phantomPath', 'sequenceConfig', 'ganttConfig', 'css'].forEach(function(i) { if(typeof options[i] !== 'undefined') { if (typeof options[i] !== 'string' || options[i].length < 1) { this.errors.push(new Error(i + " expects a value.")) diff --git a/lib/index.js b/lib/index.js index 5da3683b2..edf2b3702 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,6 +12,7 @@ module.exports = { process: processMermaid } function processMermaid(files, _options, _next) { var options = _options || {} , outputDir = options.outputDir || process.cwd() + , outputSuffix = options.outputSuffix || '' , next = _next || function() {} , phantomArgs = [ phantomscript @@ -23,6 +24,7 @@ function processMermaid(files, _options, _next) { , options.ganttConfig , options.verbose , options.width + , outputSuffix ]; files.forEach(function(file) { diff --git a/lib/phantomscript.js b/lib/phantomscript.js index 1e0edf1f0..12a73c625 100644 --- a/lib/phantomscript.js +++ b/lib/phantomscript.js @@ -29,7 +29,7 @@ var system = require('system') , webpage = require('webpage') var page = webpage.create() - , files = system.args.slice(9, system.args.length) + , files = system.args.slice(10, system.args.length) , width = system.args[8] if(typeof width === 'undefined' || width==='undefined'){ @@ -44,6 +44,7 @@ var options = { , ganttConfig: system.args[6] !== 'null' ? JSON.parse(fs.read(system.args[6])) : {} , verbose: system.args[7] === 'true' ? true : false , width: width + , outputSuffix: system.args[9] } , log = logger(options.verbose) options.sequenceConfig.useMaxWidth = false; @@ -98,21 +99,21 @@ files.forEach(function(file) { for (var i = 0, len = allElements.length; i < len; i++) { resolveForeignObjects(allElements[i]) } - + + var outputPath=options.outputDir + fs.separator + filename + options.outputSuffix; if (options.png) { page.viewportSize = { width: ~~oDOM.documentElement.attributes.getNamedItem('width').value , height: ~~oDOM.documentElement.attributes.getNamedItem('height').value } - page.render(options.outputDir + fs.separator + filename + '.png') + page.render(outputPath+'.png') console.log('saved png: ' + filename + '.png') } if (options.svg) { var serialize = new XMLSerializer(); - fs.write( - options.outputDir + fs.separator + filename + '.svg' + fs.write(outputPath+'.svg' , serialize.serializeToString(oDOM)+'\n' , 'w' ) diff --git a/src/diagrams/sequenceDiagram/svgDraw.js b/src/diagrams/sequenceDiagram/svgDraw.js index defd454f1..16204c763 100644 --- a/src/diagrams/sequenceDiagram/svgDraw.js +++ b/src/diagrams/sequenceDiagram/svgDraw.js @@ -271,16 +271,14 @@ var _drawTextCandidateFunc = (function() { .attr('x', x + width / 2).attr('y', y + height / 2 + 5) .style('text-anchor', 'middle') .text(content); - for (var key in textAttrs) { - text.attr(key, textAttrs[key]); - } - }; + _setTextAttrs(text, textAttrs); + } function byTspan(content, g, x, y, width, height, textAttrs) { var text = g.append('text') .attr('x', x + width / 2).attr('y', y) .style('text-anchor', 'middle'); - var tspan = text.append('tspan') + text.append('tspan') .attr('x', x + width / 2).attr('dy', '0') .text(content); @@ -293,16 +291,13 @@ var _drawTextCandidateFunc = (function() { if (tspans.length > 0 && tspans[0].length > 0) { tspans = tspans[0]; //set y of to the mid y of the first line - text.attr('y', y + (height/2.- text[0][0].getBBox().height*(1 - 1.0/tspans.length)/2.)) + text.attr('y', y + (height/2.0 - text[0][0].getBBox().height*(1 - 1.0/tspans.length)/2.0)) .attr("dominant-baseline", "central") - .attr("alignment-baseline", "central") + .attr("alignment-baseline", "central"); } } - - for (var key in textAttrs) { - text.attr(key, textAttrs[key]); - } - }; + _setTextAttrs(text, textAttrs); + } function byFo(content, g, x, y, width, height, textAttrs) { var s = g.append('switch'); @@ -315,14 +310,19 @@ var _drawTextCandidateFunc = (function() { text.append('div').style('display', 'table-cell') .style('text-align', 'center').style('vertical-align', 'middle') - .text(content) + .text(content); byTspan(content, s, x, y, width, height, textAttrs); + _setTextAttrs(text, textAttrs); + } - for (var key in textAttrs) { - text.attr(key, textAttrs[key]); + function _setTextAttrs(toText, fromTextAttrsDict) { + for (var key in fromTextAttrsDict) { + if (fromTextAttrsDict.hasOwnProperty(key)) { + toText.attr(key, fromTextAttrsDict[key]); } - }; + } + } return function(conf) { return conf.textPlacement==='fo' ? byFo : ( diff --git a/test/cli_test-output.js b/test/cli_test-output.js index b85355655..f57d8be60 100644 --- a/test/cli_test-output.js +++ b/test/cli_test-output.js @@ -47,7 +47,7 @@ test('output of single png', function(t) { var expected = ['test.mermaid.png'] - opt = clone(singleFile) + var opt = clone(singleFile) opt.outputDir += '_png' opt.png = true @@ -64,7 +64,7 @@ test('output of multiple png', function(t) { var expected = ['test.mermaid.png', 'test2.mermaid.png', 'gantt.mermaid.png', 'sequence.mermaid.png'] - opt = clone(multiFile) + var opt = clone(multiFile) opt.outputDir += '_png' opt.png = true @@ -80,7 +80,7 @@ test('output of single svg', function(t) { var expected = ['test.mermaid.svg'] - opt = clone(singleFile) + var opt = clone(singleFile) opt.outputDir += '_svg' opt.svg = true @@ -97,7 +97,7 @@ test('output of multiple svg', function(t) { var expected = ['test.mermaid.svg', 'test2.mermaid.svg', 'gantt.mermaid.svg', 'sequence.mermaid.svg'] - opt = clone(multiFile) + var opt = clone(multiFile) opt.outputDir += '_svg' opt.svg = true diff --git a/test/cli_test-samples.js b/test/cli_test-samples.js new file mode 100644 index 000000000..c043f01b9 --- /dev/null +++ b/test/cli_test-samples.js @@ -0,0 +1,93 @@ +'use strict'; +var exec = require('child_process').exec; +var path = require('path') + + var test = require('tape') + , rimraf = require('rimraf') + +var test_dir = 'test/fixtures/samples/'.replace('/',path.sep) + +rimraf.sync(test_dir+'*.actual.*'); + +function exec_mermaid(args, verify) { + exec('bin/mermaid.js ' + args, + {env: {PATH: './node_modules/.bin'+path.delimiter+process.env.PATH}}, + function(error, stdout, stderr) { + console.log('error:',error,'\nstdout:\n',stdout,'\nstderr:\n',stderr); + verify(error, stdout, stderr); + }); +} + +test('mermaid cli help', function(t) { + t.plan(1); + var args = [ '--help' ] + exec_mermaid(args.join(' '), function(error, stdout, stderr) { + t.notOk(stderr, 'no error') + t.end() + }); +}); + +test('mermaid cli help', function(t) { + t.plan(1); + var args = [ '--badopt' ] + exec_mermaid(args.join(' '), function(error, stdout, stderr) { + t.ok(stderr, 'should get error') + t.end() + }); +}); + +//todo +test.skip('sequence syntax error', function(t) { + t.plan(1); + var args = [ '--svg', + '--outputDir=' + test_dir, + '--outputSuffix=.actual', + test_dir+'sequence_err.mmd' + ] + exec_mermaid(args.join(' '), function(error, stdout, stderr) { + t.ok(stderr, 'should get error') + t.end() + }); +}); + +['', 'fo', 'tspan', 'old'].forEach(function(textPlacement) { + test('sequence svg text placelment: '+textPlacement, function(t) { + t.plan(1); + var args = [ '--svg', + '--outputDir=' + test_dir, + '--outputSuffix='+(textPlacement ? '_'+textPlacement : '')+'.actual', + textPlacement ? '--sequenceConfig='+test_dir+'sequence_text_'+textPlacement+'.cfg' : '', + test_dir+'sequence_text.mmd' + ] + exec_mermaid(args.join(' '), function(error, stdout, stderr) { + t.notOk(stderr, 'no error') + t.end() + }); + }) +}); + +test('sequence png', function(t) { + t.plan(1); + var args = [ '--png', + '--outputDir=' + test_dir, + '--outputSuffix=.actual', + test_dir+'sequence_text.mmd' + ] + exec_mermaid(args.join(' '), function(error, stdout, stderr) { + t.notOk(stderr, 'no error') + t.end() + }); +}) + +test('flowchart svg text', function(t) { + t.plan(1); + var args = [ '--svg', + '--outputDir=' + test_dir, + '--outputSuffix=.actual', + test_dir+'flowchart_text.mmd' + ] + exec_mermaid(args.join(' '), function(error, stdout, stderr) { + t.notOk(stderr, 'no error') + t.end() + }); +}) diff --git a/test/fixtures/samples/flowchart_text.mmd b/test/fixtures/samples/flowchart_text.mmd new file mode 100644 index 000000000..0d82cee4e --- /dev/null +++ b/test/fixtures/samples/flowchart_text.mmd @@ -0,0 +1,4 @@ +graph TD + A[label] + B[very very very long long long long-long-long text] + A--test-->B diff --git a/test/fixtures/samples/sequence_err.mmd b/test/fixtures/samples/sequence_err.mmd new file mode 100644 index 000000000..fe2e16f20 --- /dev/null +++ b/test/fixtures/samples/sequence_err.mmd @@ -0,0 +1,2 @@ +sequenceDiagram + bad diff --git a/test/fixtures/samples/sequence_text.mmd b/test/fixtures/samples/sequence_text.mmd new file mode 100644 index 000000000..c36663fd6 --- /dev/null +++ b/test/fixtures/samples/sequence_text.mmd @@ -0,0 +1,6 @@ +sequenceDiagram + participant A as actor + participant B as very very very long long long long-long-long text + A->>B: hi + B-->A: + B->>A: hello diff --git a/test/fixtures/samples/sequence_text_fo.cfg b/test/fixtures/samples/sequence_text_fo.cfg new file mode 100644 index 000000000..f4667e30f --- /dev/null +++ b/test/fixtures/samples/sequence_text_fo.cfg @@ -0,0 +1,3 @@ +{ + "textPlacement": "fo" +} diff --git a/test/fixtures/samples/sequence_text_old.cfg b/test/fixtures/samples/sequence_text_old.cfg new file mode 100644 index 000000000..f95a87978 --- /dev/null +++ b/test/fixtures/samples/sequence_text_old.cfg @@ -0,0 +1,3 @@ +{ + "textPlacement": "old" +} diff --git a/test/fixtures/samples/sequence_text_tspan.cfg b/test/fixtures/samples/sequence_text_tspan.cfg new file mode 100644 index 000000000..10fa0ec4b --- /dev/null +++ b/test/fixtures/samples/sequence_text_tspan.cfg @@ -0,0 +1,3 @@ +{ + "textPlacement": "tspan" +} diff --git a/test/fixtures/sequence.mermaid b/test/fixtures/sequence.mermaid index e0f8a5b57..9774e6551 100644 --- a/test/fixtures/sequence.mermaid +++ b/test/fixtures/sequence.mermaid @@ -1,8 +1,8 @@ sequenceDiagram - Alice->Bob: Hello Bob, how are you? + Alice->>Bob: Hello Bob, how are you? Note right of Bob: Bob thinks - Bob-->Alice: I am good thanks! - Bob-->John the Long: How about you John? - Bob-->Alice: Checking with John... - Alice->John the Long: Yes... John, how are you? + Bob-->>Alice: I am good thanks! + Bob-->>John the Long: How about you John? + Bob-->>Alice: Checking with John... + Alice->>John the Long: Yes... John, how are you? John the Long-->Alice: Better than you!