diff --git a/.jshintrc b/.jshintrc
index 7a26e42f8..cfe3116ff 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -3,7 +3,8 @@
"phantom": false,
"describe": false,
"before": false,
- "it": false
+ "it": false,
+ "beforeEach": false
},
"node": true,
"strict": "global",
diff --git a/lib/svgo/coa.js b/lib/svgo/coa.js
index d66b900c7..8a5ebda46 100644
--- a/lib/svgo/coa.js
+++ b/lib/svgo/coa.js
@@ -142,7 +142,7 @@ module.exports = require('coa').Cmd()
// --show-plugins
if (opts['show-plugins']) {
showAvailablePlugins();
- process.exit(0);
+ return;
}
// w/o anything
@@ -243,48 +243,56 @@ module.exports = require('coa').Cmd()
// --folder
if (opts.folder) {
- optimizeFolder(opts.folder, config, output);
-
- return;
+ return optimizeFolder(opts.folder, config, output);
}
// --input
if (input) {
- // STDIN
- if (input === '-') {
- var data = '';
-
- process.stdin.pause();
-
- process.stdin
- .on('data', function(chunk) {
- data += chunk;
- })
- .once('end', function() {
- optimizeFromString(data, config, opts.datauri, input, output);
- })
- .resume();
- // file
- } else {
- FS.readFile(input, 'utf8', function(err, data) {
- if (err) {
- if (err.code === 'EISDIR')
- optimizeFolder(input, config, output);
- else if (err.code === 'ENOENT')
- console.error(`Error: no such file or directory '${input}'.`);
- else
- console.error(err);
- return;
- }
- optimizeFromString(data, config, opts.datauri, input, output);
- });
- }
+ return new Promise((resolve, reject) => {
+ // STDIN
+ if (input === '-') {
+ var data = '';
+
+ process.stdin.pause();
+
+ process.stdin
+ .on('data', function(chunk) {
+ data += chunk;
+ })
+ .once('end', function() {
+ optimizeFromString(data, config, opts.datauri, input, output)
+ .then(resolve)
+ .catch(reject);
+ })
+ .resume();
+ // file
+ } else {
+ FS.readFile(input, 'utf8', function(err, data) {
+ if (err) {
+ switch(err.code) {
+ case 'EISDIR':
+ return optimizeFolder(input, config, output)
+ .then(resolve)
+ .catch(reject);
+ case 'ENOENT':
+ return reject(`Error: no such file or directory '${input}'.`);
+ default:
+ return reject(err);
+ }
+ }
+
+ optimizeFromString(data, config, opts.datauri, input, output)
+ .then(resolve)
+ .catch(reject);
+ });
+ }
+ });
// --string
} else if (opts.string) {
opts.string = decodeSVGDatauri(opts.string);
- optimizeFromString(opts.string, config, opts.datauri, input, output);
+ return optimizeFromString(opts.string, config, opts.datauri, input, output);
}
});
@@ -295,7 +303,7 @@ function optimizeFromString(svgstr, config, datauri, input, output) {
outBytes,
svgo = new SVGO(config);
- svgo.optimize(svgstr).then(function(result) {
+ return svgo.optimize(svgstr).then(function(result) {
if (datauri) {
result.data = encodeSVGDatauri(result.data, datauri);
}
@@ -305,7 +313,7 @@ function optimizeFromString(svgstr, config, datauri, input, output) {
// stdout
if (output === '-' || (!input || input === '-') && !output) {
- process.stdout.write(result.data + '\n');
+ console.log(result.data + '\n');
// file
} else {
// overwrite input file if there is no output
@@ -317,23 +325,26 @@ function optimizeFromString(svgstr, config, datauri, input, output) {
console.log('\r');
}
- saveFileAndPrintInfo(config, result.data, output, inBytes, outBytes, time);
+ return saveFileAndPrintInfo(config, result.data, output, inBytes, outBytes, time);
}
- }, error => console.error(error));
+ }).catch(console.error);
}
function saveFileAndPrintInfo(config, data, path, inBytes, outBytes, time) {
- FS.writeFile(path, data, 'utf8', function() {
- if (config.quiet) {
- return;
- }
+ return new Promise((resolve, reject) => {
+ FS.writeFile(path, data, 'utf8', function(err) {
+ if (err) return reject(err);
- // print time info
- printTimeInfo(time);
+ if (!config.quiet) {
+ // print time info
+ printTimeInfo(time);
- // print optimization profit info
- printProfitInfo(inBytes, outBytes);
+ // print optimization profit info
+ printProfitInfo(inBytes, outBytes);
+ }
+ resolve();
+ });
});
}
@@ -411,94 +422,96 @@ function optimizeFolder(dir, config, output) {
var path = PATH.resolve(dir);
// list folder content
- FS.readdir(path, function(err, files) {
- if (err) {
- console.error(err);
- return;
- }
-
- if (!files.length) {
- console.log(`Directory '${dir}' is empty.`);
- return;
- }
-
- var i = 0,
- found = false;
-
- function optimizeFile(file) {
- // absoluted file path
- var filepath = PATH.resolve(path, file);
- var fileStat = FS.lstatSync(filepath);
- var outfilepath = output ? PATH.resolve(output, file) : filepath;
-
- if (config.recursive && fileStat.isDirectory()) {
- optimizeFolder(filepath, config, output);
+ return new Promise((resolve, reject) => {
+ FS.readdir(path, function(err, files) {
+ if (err) {
+ return reject(err);
}
- // check if file name matches *.svg
- if (regSVGFile.test(filepath)) {
- found = true;
- FS.readFile(filepath, 'utf8', function(err, data) {
- if (err) {
- console.error(err);
- return;
- }
-
- var startTime = Date.now(),
- time,
- inBytes = Buffer.byteLength(data, 'utf8'),
- outBytes;
+ if (!files.length) {
+ console.log(`Directory '${dir}' is empty.`);
+ return resolve();
+ }
- svgo.optimize(data).then(function(result) {
- outBytes = Buffer.byteLength(result.data, 'utf8');
- time = Date.now() - startTime;
+ var found = false;
- writeOutput();
+ Promise.all(files.map(optimizeFile))
+ .then(() => {
+ if (!found) {
+ console.log('No SVG files have been found.');
+ }
+ resolve();
+ })
+ .catch(reject);
+
+ function optimizeFile(file) {
+ // absoluted file path
+ var filepath = PATH.resolve(path, file);
+ var fileStat = FS.lstatSync(filepath);
+ var outfilepath = output ? PATH.resolve(output, file) : filepath;
+
+ if (config.recursive && fileStat.isDirectory()) {
+ return optimizeFolder(filepath, config, output);
+ }
- function writeOutput() {
- FS.writeFile(outfilepath, result.data, 'utf8', report);
- }
+ // check if file name matches *.svg
+ if (regSVGFile.test(file)) {
+ found = true;
- function report(err) {
+ return new Promise((resolve, reject) => {
+ FS.readFile(filepath, 'utf8', function(err, data) {
if (err) {
- if (err.code === 'ENOENT') {
- mkdirp(output, writeOutput);
- return;
- } else if (err.code === 'ENOTDIR') {
- console.error(`Error: output '${output}' is not a directory.`);
- return;
- }
- console.error(err);
+ reject(err);
return;
}
- if (!config.quiet) {
- console.log(file + ':');
-
- // print time info
- printTimeInfo(time);
-
- // print optimization profit info
- printProfitInfo(inBytes, outBytes);
- }
-
- //move on to the next file
- if (++i < files.length) {
- optimizeFile(files[i]);
- }
- }
- }, error => console.error(error));
- });
- }
- //move on to the next file
- else if (++i < files.length) {
- optimizeFile(files[i]);
- } else if (!found) {
- console.log('No SVG files have been found.');
+ var startTime = Date.now(),
+ time,
+ inBytes = Buffer.byteLength(data, 'utf8'),
+ outBytes;
+
+ svgo.optimize(data)
+ .then(function(result) {
+ outBytes = Buffer.byteLength(result.data, 'utf8');
+ time = Date.now() - startTime;
+
+ writeOutput();
+
+ function writeOutput() {
+ FS.writeFile(outfilepath, result.data, 'utf8', report);
+ }
+
+ function report(err) {
+ if (err) {
+ switch(err.code) {
+ case 'ENOENT':
+ return mkdirp(output, writeOutput);
+ case 'ENOTDIR':
+ return reject(`Error: output '${output}' is not a directory.`);
+ default:
+ return reject(err);
+ }
+ }
+
+ if (!config.quiet) {
+ console.log(file + ':');
+
+ // print time info
+ printTimeInfo(time);
+
+ // print optimization profit info
+ printProfitInfo(inBytes, outBytes);
+ }
+
+ resolve();
+ }
+ })
+ .catch(reject);
+ });
+ });
+ }
}
- }
-
- optimizeFile(files[i]);
+ });
});
}
diff --git a/package.json b/package.json
index 3839ed584..ef31e48ae 100644
--- a/package.json
+++ b/package.json
@@ -48,12 +48,14 @@
"jshint": "jshint --show-non-errors ."
},
"dependencies": {
- "sax": "~1.2.1",
"coa": "~1.0.1",
- "js-yaml": "~3.7.0",
"colors": "~1.1.2",
+ "csso": "~2.3.1",
+ "fs-extra": "^4.0.1",
+ "js-yaml": "~3.7.0",
"mkdirp": "~0.5.1",
- "csso": "~2.3.1"
+ "mock-stdin": "^0.3.1",
+ "sax": "~1.2.1"
},
"devDependencies": {
"mocha": "~3.2.0",
diff --git a/test/coa/_index.js b/test/coa/_index.js
new file mode 100644
index 000000000..9621cbaf5
--- /dev/null
+++ b/test/coa/_index.js
@@ -0,0 +1,169 @@
+'use strict';
+
+const fs = require('fs'),
+ svgo = require(process.env.COVERAGE ?
+ '../../lib-cov/svgo/coa.js' :
+ '../../lib/svgo/coa.js').api,
+ path = require('path'),
+ svgPath = path.resolve(__dirname, 'test.svg'),
+ svgFolderPath = path.resolve(__dirname, 'testSvg'),
+ fse = require('fs-extra');
+
+describe('coa', function() {
+ let output;
+
+ beforeEach(function() {
+ output = '';
+
+ if(fs.existsSync('temp')) {
+ fse.removeSync('temp');
+ }
+
+ fs.mkdirSync('temp');
+ });
+
+ function replaceConsoleLog() {
+ const initialConsoleLog = global.console.log;
+
+ global.console.log = function() {
+ output += arguments[0];
+
+ initialConsoleLog.apply(console, arguments);
+ };
+ }
+
+ function replaceConsoleError() {
+ const initialConsoleError = global.console.error;
+
+ global.console.error = function() {
+ output += arguments[0];
+
+ initialConsoleError.apply(console, arguments);
+ };
+ }
+
+ function calcFolderSvgWeight(folderPath) {
+ return fs.readdirSync(folderPath).reduce((initWeight, fileName) => {
+ return initWeight +
+ (/.svg/.test(fileName) ? fs.statSync(folderPath + '/' + fileName).size : 0);
+ }, 0);
+ }
+
+ it('should throw an error if "config" can not be parsed', function(done) {
+ replaceConsoleError();
+
+ svgo({ input: svgPath, config: '{' })
+ .then(() => done( /Error: Couldn't parse config JSON/.test(output) ? null : 'Error was not thrown' ));
+ });
+
+ it('should work properly with string input', function(done) {
+ svgo({ string: fs.readFileSync(svgPath, 'utf8'), output: 'temp.svg' })
+ .then(done);
+ });
+
+ it('should optimize folder', function(done) {
+ const initWeight = calcFolderSvgWeight(svgFolderPath);
+
+ svgo({ folder: svgFolderPath })
+ .then(() => {
+ const optimizedWeight = calcFolderSvgWeight(svgFolderPath);
+
+ done(initWeight <= optimizedWeight ? null : 'Folder was not optimized');
+ });
+ });
+
+ it('should optimize file', function(done) {
+ const initialFileLength = fs.readFileSync(path.resolve(__dirname, 'test.svg')).length;
+
+ svgo({ input: svgPath, output: 'temp.svg' })
+ .then(() => {
+ const optimizedFileLength = fs.readFileSync('temp.svg').length;
+
+ done(optimizedFileLength <= initialFileLength ? null : 'File was not optimized');
+ });
+ });
+
+ it('should optimize file from process.stdin', function(done) {
+ const initialFile = fs.readFileSync(path.resolve(__dirname, 'test.svg'));
+
+ const stdin = require('mock-stdin').stdin();
+
+ setTimeout(() => { stdin.send(initialFile, 'ascii').end(); }, 0);
+
+ svgo({ input: '-', output: 'temp.svg', string: fs.readFileSync(svgPath, 'utf8') })
+ .then(() => {
+ const optimizedFileLength = fs.readFileSync('temp.svg').length;
+
+ done(optimizedFileLength <= initialFile.length ? null : 'Files were not optimized');
+ });
+ });
+
+ it('should optimize folder, when it stated in input', function(done) {
+ const initWeight = calcFolderSvgWeight(svgFolderPath);
+
+ svgo({ input: svgFolderPath, output: 'temp' })
+ .then(() => {
+ let optimizedWeight = calcFolderSvgWeight(svgFolderPath);
+
+ done(initWeight <= optimizedWeight ? null : 'Files were not optimized');
+ });
+ });
+
+ it('should throw error when stated in input folder does not exist', function(done) {
+ svgo({ input: svgFolderPath + 'temp', output: 'temp' })
+ .catch(err => done(/no such file or directory/.test(err) ? null : 'Error was not thrown'));
+ });
+
+ describe('stdout', function() {
+ it('should show file content when no output set', function(done) {
+ replaceConsoleLog();
+
+ svgo({ string: fs.readFileSync(svgPath, 'utf8'), output: '-', datauri: 'unenc' })
+ .then(() => done(/www.w3.org\/2000\/svg/.test(output) ? null : 'File content was not shown'));
+ });
+
+ it('should show message when the folder is empty', function(done) {
+ const emptyFolderPath = path.resolve(__dirname, 'testSvgEmpty');
+ if(!fs.existsSync(emptyFolderPath))
+ fs.mkdirSync(emptyFolderPath);
+
+ replaceConsoleLog();
+
+ svgo({ folder: emptyFolderPath })
+ .then(() => done(/is empty/.test(output) ? null : 'Empty folder message was not shown'));
+ });
+
+ it('should show message when folder does not consists any svg files', function(done) {
+ replaceConsoleLog();
+
+ svgo({ folder: path.resolve(__dirname, 'testFolderWithNoSvg') })
+ .then(() => done(/No SVG files have been found/.test(output) ?
+ null : 'Error "No SVG files have been found" was not shown'));
+ });
+
+ it('should create output directory when it does not exist', function(done) {
+ const initWeight = calcFolderSvgWeight(svgFolderPath);
+ const outputFolder = path.resolve(__dirname, 'temp');
+
+ replaceConsoleLog();
+
+ if(fs.existsSync(outputFolder)) {
+ fse.removeSync(outputFolder);
+ }
+
+ svgo({ folder: svgFolderPath, output: outputFolder })
+ .then(() => {
+ const optimizedWeight = calcFolderSvgWeight(outputFolder);
+
+ done(initWeight <= optimizedWeight ? null : 'Files were not optimized');
+ });
+ });
+
+ it('should show plugins', function(done) {
+ replaceConsoleLog();
+
+ svgo({ 'show-plugins': true })
+ .then(() => done(/Currently available plugins:/.test(output) ? null : 'List of plugins was not shown'));
+ });
+ });
+});
diff --git a/test/coa/test.svg b/test/coa/test.svg
new file mode 100644
index 000000000..de7975de7
--- /dev/null
+++ b/test/coa/test.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/coa/testFolderWithNoSvg/test b/test/coa/testFolderWithNoSvg/test
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/coa/testSvg/test.1.svg b/test/coa/testSvg/test.1.svg
new file mode 100644
index 000000000..de7975de7
--- /dev/null
+++ b/test/coa/testSvg/test.1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/coa/testSvg/test.svg b/test/coa/testSvg/test.svg
new file mode 100644
index 000000000..de7975de7
--- /dev/null
+++ b/test/coa/testSvg/test.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/mocha.opts b/test/mocha.opts
index 3b56cd1ac..b4f63a92f 100644
--- a/test/mocha.opts
+++ b/test/mocha.opts
@@ -5,3 +5,4 @@ test/svg2js
test/plugins
test/jsapi
test/svgo
+test/coa