From 84e9a459d14a123db0829fc7ea56c51e10e0f48a Mon Sep 17 00:00:00 2001 From: vseventer Date: Thu, 5 Nov 2015 17:33:02 +1100 Subject: [PATCH] Implemented tests, improved error handling. --- .gitignore | 2 +- CHANGELOG.md | 2 +- README.md | 22 +- cli.js | 24 +- help.txt | 22 +- lib/runner.js | 47 ++-- test/fixtures/input.png | Bin 0 -> 68 bytes test/mocha.opts | 1 + test/runner.test.js | 572 +++++++++++++++++++++++++++++++++++----- 9 files changed, 590 insertions(+), 102 deletions(-) create mode 100644 test/fixtures/input.png diff --git a/.gitignore b/.gitignore index edf4621..1d8267f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules/ -test/fixtures/output.jpg +test/fixtures/output*.jpg .DS_Store npm_debug.log \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c52ee..381a685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ # Changelog -## 0.1.0 (November 4, 2015) +## 0.1.0 (November 5, 2015) Initial version. \ No newline at end of file diff --git a/README.md b/README.md index de3e72f..7e35da9 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,17 @@ $ sharp --help --blur [number] Blur the output image, optionally specifying the blur radius. --compressionLevel zlib compression level, between 0 and 9, of the lossless PNG output format. Defaults to 6. - --crop [string] Crop the resized image, optionally specifying the gravity - (north, east, south, west, center). Defaults to center. + --crop Crop the resized image based on the specified gravity (north, east, + south, west, center). --embed Resize the image, then embed on a background. - --extractHeight Height of the region to be extracted. - --extractLeft Left offset of the region to be extracted. - --extractTop Top offset of the region to be extracted. - --extractWidth Width of the region to be extracted. + --extractHeight Height of the region to be extracted. Use in conjunction with + --extractLeft, --extractTop, and --extractWidth. + --extractLeft Left offset of the region to be extracted. Use in conjunction with + --extractHeight, --extractTop, and --extractWidth. + --extractTop Top offset of the region to be extracted. Use in conjunction with + --extractHeight, --extractLeft, and --extractWidth. + --extractWidth Width of the region to be extracted. Use in conjunction with + --extractHeight, --extractLeft, and --extractTop. -f, --format Output format (jpeg, png, webp, raw). Defaults to input format. --flatten Merge alpha transparency channel, if any, with --background. --flip Flip the image about the vertical Y axis. @@ -69,14 +73,16 @@ $ sharp --help --sequentialRead Switches the libvips access method to VIPS_ACCESS_SEQUENTIAL. --sharpen [number] Sharpen the output image, optionally specifying the sharpen radius. --sharpenFlat Specify the level of sharpeness to apply to "flat" areas. Defaults to - 1.0. + 1.0. Use in conjunction with --sharpen. --sharpenJagged Specify the level of sharpeness to apply to "jagged" areas. Defaults - to 2.0. + to 2.0. Use in conjunction with --sharpen. --tile [number] Applies square image pyramid tiles over the image, optionally specifying a tile size between 1 and 8192. Defaults to 256 pixels. --tileOverlap Specifies the tile overlap, between 0 and 8192. Defaults to 0 pixels. + Use in conjunction with --tile. --trellisQuantization, --trellesQuantisation Apply the use of trellis quantization with JPEG output. + -v, --verbose Output image processing information. -w, --width Scale output to width. --withoutAdaptiveFiltering Disable adaptive row filtering for the lossless PNG output format. --withoutChromaSubsampling Disable the use of chroma subsampling with JPEG output (4:4:4). diff --git a/cli.js b/cli.js index ce69e50..c14e206 100755 --- a/cli.js +++ b/cli.js @@ -26,7 +26,8 @@ 'use strict'; // Standard lib. -var fs = require('fs'); +var fs = require('fs'), + path = require('path'); // Package modules. var glob = require('glob'), @@ -38,9 +39,11 @@ var glob = require('glob'), var runner = require('./lib/runner'); // Configure. -var cli = meow({ - help: fs.readFileSync('./help.txt', 'utf8') -}, { +var helpFile = path.join(__dirname, './help.txt'), + help = fs.readFileSync(helpFile, 'utf8'); + +// Run. +var cli = meow(help, { boolean: [ 'embed', 'flatten', @@ -94,6 +97,7 @@ var cli = meow({ h : 'height', o : 'output', q : 'quality', + v : 'verbose', w : 'width', // American vs. UK English. @@ -128,14 +132,20 @@ else { }, [ ]); // Ensure output flag is set. - if(1 < src.length && !cli.flags.output) { - console.error('No output.'); + if(1 < src.length && false === cli.flags.output) { + console.error('Batch operation requires the use of --output.'); process.exit(1); // Exit with failure. } // Invoke runner for each src. var promises = src.map(function(file) { - return runner.run(file, cli.flags); + return runner.run(file, cli.flags).then(function(m) { + if(cli.flags.verbose) { + var size = Math.round(m.size / 1024); + console.log(' %s -> %s (%d×%d, %dkb)', file, m.src, m.width, m.height, size); + } + return m; // Continue. + }); }); // Run. diff --git a/help.txt b/help.txt index 35f7775..f9631a7 100644 --- a/help.txt +++ b/help.txt @@ -13,13 +13,17 @@ Options --blur [number] Blur the output image, optionally specifying the blur radius. --compressionLevel zlib compression level, between 0 and 9, of the lossless PNG output format. Defaults to 6. - --crop [string] Crop the resized image, optionally specifying the gravity - (north, east, south, west, center). Defaults to center. + --crop Crop the resized image based on the specified gravity (north, east, + south, west, center). --embed Resize the image, then embed on a background. - --extractHeight Height of the region to be extracted. - --extractLeft Left offset of the region to be extracted. - --extractTop Top offset of the region to be extracted. - --extractWidth Width of the region to be extracted. + --extractHeight Height of the region to be extracted. Use in conjunction with + --extractLeft, --extractTop, and --extractWidth. + --extractLeft Left offset of the region to be extracted. Use in conjunction with + --extractHeight, --extractTop, and --extractWidth. + --extractTop Top offset of the region to be extracted. Use in conjunction with + --extractHeight, --extractLeft, and --extractWidth. + --extractWidth Width of the region to be extracted. Use in conjunction with + --extractHeight, --extractLeft, and --extractTop. -f, --format Output format (jpeg, png, webp, raw). Defaults to input format. --flatten Merge alpha transparency channel, if any, with --background. --flip Flip the image about the vertical Y axis. @@ -55,14 +59,16 @@ Options --sequentialRead Switches the libvips access method to VIPS_ACCESS_SEQUENTIAL. --sharpen [number] Sharpen the output image, optionally specifying the sharpen radius. --sharpenFlat Specify the level of sharpeness to apply to "flat" areas. Defaults to - 1.0. + 1.0. Use in conjunction with --sharpen. --sharpenJagged Specify the level of sharpeness to apply to "jagged" areas. Defaults - to 2.0. + to 2.0. Use in conjunction with --sharpen. --tile [number] Applies square image pyramid tiles over the image, optionally specifying a tile size between 1 and 8192. Defaults to 256 pixels. --tileOverlap Specifies the tile overlap, between 0 and 8192. Defaults to 0 pixels. + Use in conjunction with --tile. --trellisQuantization, --trellesQuantisation Apply the use of trellis quantization with JPEG output. + -v, --verbose Output image processing information. -w, --width Scale output to width. --withoutAdaptiveFiltering Disable adaptive row filtering for the lossless PNG output format. --withoutChromaSubsampling Disable the use of chroma subsampling with JPEG output (4:4:4). diff --git a/lib/runner.js b/lib/runner.js index 1023412..04560c0 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -46,12 +46,6 @@ var run = function(src, flags) { var isStream = src instanceof stream.Readable, image = isStream ? src.pipe(sharp()) : sharp(src); - // Add error listener. - image.on('error', function(err) { - console.error('%s', err); - process.exit(1); // Exit with failure. - }); - // Input options. // ============== @@ -62,7 +56,8 @@ var run = function(src, flags) { // @see http://sharp.dimens.io/en/stable/api/#limitinputpixelspixels if(flags.limitInputPixels) { - image.limitInputPixels(flags.limitInputPixels); + var pixels = parseInt(flags.limitInputPixels, 10); + image.limitInputPixels(pixels); } // Resize options. @@ -115,7 +110,7 @@ var run = function(src, flags) { // ============ // @see http://sharp.dimens.io/en/stable/api/#extracttop-left-width-height - if(flags.extractTop && flags.extractLeft && flags.extractWidth && flags.extractHeight) { + if(flags.extractTop || flags.extractLeft || flags.extractWidth || flags.extractHeight) { var top = parseInt(flags.extractTop, 10), left = parseInt(flags.extractLeft, 10), eWidth = parseInt(flags.extractWidth, 10), @@ -150,21 +145,27 @@ var run = function(src, flags) { } // @see http://sharp.dimens.io/en/stable/api/#blursigma - if(flags.blur) { + if(true === flags.blur) { + image.blur(); + } + else if(flags.blur) { var sigma = parseFloat(flags.blur); image.blur(sigma); } // @see http://sharp.dimens.io/en/stable/api/#sharpenradius-flat-jagged - if(flags.sharpen) { - var radius = parseInt(flags.sharpen, 10), - flat = parseInt(flags.sharpenFlat, 10), - jagged = parseInt(flags.sharpenJagged, 10); + if(flags.sharpen || flags.sharpenFlat || flags.sharpenJagged) { + var radius = true === flags.sharpen ? undefined : parseInt(flags.sharpen, 10), + flat = flags.sharpenFlat ? parseFloat(flags.sharpenFlat) : undefined, + jagged = flags.sharpenJagged ? parseFloat(flags.sharpenJagged) : undefined; image.sharpen(radius, flat, jagged); } // @see http://sharp.dimens.io/en/stable/api/#gammagamma - if(flags.gamma) { + if(true === flags.gamma) { + image.gamma(); + } + else if(flags.gamma) { var gamma = parseFloat(flags.gamma); image.gamma(gamma); } @@ -209,9 +210,9 @@ var run = function(src, flags) { } // @see http://sharp.dimens.io/en/stable/api/#tilesize-overlap - if(flags.tile) { - var tile = parseInt(flags.tile, 10), - overlap = parseInt(flags.tileOverlap, 10); + if(flags.tile || flags.tileOverlap) { + var tile = true === flags.tile ? undefined : parseInt(flags.tile, 10), + overlap = flags.tileOverlap ? parseInt(flags.tileOverlap, 10) : undefined; image.tile(tile, overlap); } @@ -256,6 +257,9 @@ var run = function(src, flags) { // Ensure the destination directory exists. return mkdirSafe(dir).then(function() { return image.toFile(dest); + }).then(function(metadata) { + metadata.src = dest; + return metadata; }); } @@ -267,4 +271,11 @@ var run = function(src, flags) { }; // Exports. -module.exports = { run: run }; \ No newline at end of file +module.exports = { + run: function(src, flags) { + // Wrap in Promise so exceptions turn into proper promise rejections. + return new Promise(function(resolve) { + return resolve(run(src, flags)); + }); + } +}; \ No newline at end of file diff --git a/test/fixtures/input.png b/test/fixtures/input.png new file mode 100644 index 0000000000000000000000000000000000000000..89e7a3fb18befd14143d478d96741c9cd380eca5 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6UNh{bH7&;i58rS{W Q2oz@UboFyt=akR{0DXTBRsaA1 literal 0 HcmV?d00001 diff --git a/test/mocha.opts b/test/mocha.opts index 2b7a2da..b72ef3e 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,5 +1,6 @@ --check-leaks --no-exit --reporter spec +--slow 50 --timeout 100 --ui bdd \ No newline at end of file diff --git a/test/runner.test.js b/test/runner.test.js index 8c3a3cf..5b22d50 100644 --- a/test/runner.test.js +++ b/test/runner.test.js @@ -25,7 +25,8 @@ 'use strict'; // Standard lib. -var path = require('path'); +var fs = require('fs'), + path = require('path'); // Package modules. var chai = require('chai'), @@ -43,30 +44,46 @@ var expect = chai.expect; // Fixtures. var fixturePath = path.join(__dirname, './fixtures'), input = path.join(fixturePath, 'input.jpg'), - output = path.join(fixturePath, 'output.jpg'); + inputPixel = path.join(fixturePath, 'input.png'), + output = path.join(fixturePath, 'output.jpg'), + outputTest = path.join(fixturePath, 'output.test.jpg'); // Test suite. describe('runner', function() { + // Prepopulate flags. + beforeEach('flags', function() { + this.flags = { output: output }; + }); + afterEach('flags', function() { + delete this.flags; // Cleanup. + }); + // Tests. - it('should run.', function() { - expect(runner.run).to.exist; + it('should accept a filename.', function() { + return runner.run(input, this.flags); + }); + it('should accept a buffer.', function() { + var buffer = fs.readFileSync(input); + return runner.run(buffer, this.flags); + }); + it('should accept a stream.', function() { + var stream = fs.createReadStream(input); + return runner.run(stream, this.flags); + }); + it('should return output metadata.', function() { + return runner.run(input, this.flags).then(function(metadata) { + expect(metadata).to.have.keys([ 'format', 'height', 'width', 'size', 'src' ]); + expect(metadata).to.have.property('src', output); + }); }); // Options. describe('options', function() { - // Prepopulate flags. - beforeEach('flags', function() { - this.flags = { output: output }; - }); - afterEach('flags', function() { - delete this.flags; // Cleanup. - }); - // Manage spies. before('spy', function() { this.spies = [ ]; this.spyOn = function(method) { - // Re-use existing spy. + // Re-use existing spies. if(sharp.prototype[method].isSinonProxy) { return sharp.prototype[method]; } @@ -89,7 +106,7 @@ describe('runner', function() { delete this.spies; // Cleanup. }); - // Helper method to test simple boolean flags. + // Helper to test boolean options. var testBoolean = function(name) { return /* mocha:it */ function() { var spy = this.spyOn(name); @@ -102,24 +119,198 @@ describe('runner', function() { }; // Tests. - it('--background '); - it('--background (invalid value)'); + it('--background ', function() { + var spy = this.spyOn('background'); + this.flags.background = '#FFFFFF'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ '#FFFFFF' ]); + }); + }); + it('--background (invalid value)', function() { + var spy = this.spyOn('background'); + this.flags.background = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 'foo' ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('unable to parse color'); + }); + }); + it('--blur', testBoolean('blur')); - it('--blur [number] (invalid value)'); - it('--compressionLevel '); - it('--compressionLevel (invalid value)'); - it('--crop', testBoolean('crop')); - it('--crop [string]'); - it('--crop [string] (invalid value)'); + it('--blur [number]', function() { + var spy = this.spyOn('blur'); + this.flags.blur = '1.5'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1.5 ]); + }); + }); + it('--blur [number] (invalid value)', function() { + var spy = this.spyOn('blur'); + this.flags.blur = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid blur sigma'); + }); + }); + + it('--compressionLevel ', function() { + var spy = this.spyOn('compressionLevel'); + this.flags.compressionLevel = '7'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 7 ]); + }); + }); + it('--compressionLevel (invalid value)', function() { + var spy = this.spyOn('compressionLevel'); + this.flags.compressionLevel = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid compressionlevel'); + }); + }); + + it('--crop ', function() { + var spy = this.spyOn('crop'); + this.flags.crop = 'north'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 'north' ]); + }); + }); + it('--crop (invalid value)', function() { + var spy = this.spyOn('crop'); + this.flags.crop = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 'foo' ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('unsupported crop gravity'); + }); + }); + it('--embed', testBoolean('embed')); - it('--extractHeight '); - it('--extractHeight (invalid value)'); - it('--extractLeft '); - it('--extractLeft (invalid value)'); - it('--extractTop '); - it('--extractTop (invalid value)'); - it('--extractWidth '); - it('--extractWidth (invalid value)'); + + it('--extractTop ', function() { + var spy = this.spyOn('extract'); + this.flags.extractTop = '10'; + this.flags.extractLeft = '1'; + this.flags.extractWidth = '1'; + this.flags.extractHeight = '1'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 10, 1, 1, 1 ]); + }); + }); + it('--extractTop (invalid value)', function() { + var spy = this.spyOn('extract'); + this.flags.extractTop = 'foo'; + this.flags.extractLeft = '1'; + this.flags.extractWidth = '1'; + this.flags.extractHeight = '1'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN, 1, 1, 1 ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('non-integer value for top'); + }); + }); + it('--extractLeft ', function() { + var spy = this.spyOn('extract'); + this.flags.extractTop = '1'; + this.flags.extractLeft = '10'; + this.flags.extractWidth = '1'; + this.flags.extractHeight = '1'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, 10, 1, 1 ]); + }); + }); + it('--extractLeft (invalid value)', function() { + var spy = this.spyOn('extract'); + this.flags.extractTop = '1'; + this.flags.extractLeft = 'foo'; + this.flags.extractWidth = '1'; + this.flags.extractHeight = '1'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, NaN, 1, 1 ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('non-integer value for left'); + }); + }); + it('--extractWidth ', function() { + var spy = this.spyOn('extract'); + this.flags.extractTop = '1'; + this.flags.extractLeft = '1'; + this.flags.extractWidth = '10'; + this.flags.extractHeight = '1'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, 1, 10, 1 ]); + }); + }); + it('--extractWidth (invalid value)', function() { + var spy = this.spyOn('extract'); + this.flags.extractTop = '1'; + this.flags.extractLeft = '1'; + this.flags.extractWidth = 'foo'; + this.flags.extractHeight = '1'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, 1, NaN, 1 ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('non-integer value for width'); + }); + }); + it('--extractHeight ', function() { + var spy = this.spyOn('extract'); + this.flags.extractHeight = '10'; + this.flags.extractLeft = '1'; + this.flags.extractTop = '1'; + this.flags.extractWidth = '1'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, 1, 1, 10 ]); + }); + }); + it('--extractHeight (invalid value)', function() { + var spy = this.spyOn('extract'); + this.flags.extractHeight = 'foo'; + this.flags.extractLeft = '1'; + this.flags.extractTop = '1'; + this.flags.extractWidth = '1'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, 1, 1, NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('non-integer value for height'); + }); + }); + it('--format ', function() { var spy = this.spyOn('toFormat'); this.flags.format = 'jpeg'; @@ -136,16 +327,39 @@ describe('runner', function() { }).catch(function(err) { expect(spy).to.be.calledOnce; expect(spy.args[0]).to.eql([ 'foo' ]); - expect(err).to.have.property('message', 'Unsupported format foo'); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('unsupported format'); }); }); + it('--flatten', testBoolean('flatten')); it('--flip', testBoolean('flip')); it('--flop', testBoolean('flop')); - it('--gamma', testBoolean('gamma')); - it('--gamma '); - it('--gamma (invalid value)'); + + it('--gamma', testBoolean('gamma')); + it('--gamma ', function() { + var spy = this.spyOn('gamma'); + this.flags.gamma = '2.1'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 2.1 ]); + }); + }); + it('--gamma (invalid value)', function() { + var spy = this.spyOn('gamma'); + this.flags.gamma = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid gamma correction'); + }); + }); + it('--grayscale', testBoolean('grayscale')); + it('--height ', function() { var spy = this.spyOn('resize'); this.flags.height = 100; @@ -162,39 +376,271 @@ describe('runner', function() { expect(spy.args[0]).to.eql([ NaN, NaN ]); }); }); + it('--ignoreAspectRatio', testBoolean('ignoreAspectRatio')); - it('--interpolateWith '); - it('--interpolateWith (invalid value)'); - it('--limitInputPixels '); - it('--limitInputPixels (invalid value)'); + + it('--interpolateWith ', function() { + var spy = this.spyOn('interpolateWith'); + this.flags.interpolateWith = 'bilinear'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 'bilinear' ]); + }); + }); + it('--interpolateWith (invalid value)', function() { + var spy = this.spyOn('interpolateWith'); + this.flags.interpolateWith = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 'foo' ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid interpolator'); + }); + }); + + it('--limitInputPixels ', function() { + var spy = this.spyOn('limitInputPixels'); + this.flags.limitInputPixels = '100000'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 100000 ]); + }); + }); + it('--limitInputPixels (invalid value)', function() { + var spy = this.spyOn('limitInputPixels'); + this.flags.limitInputPixels = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid pixel limit'); + }); + }); + it('--max', testBoolean('max')); it('--max', testBoolean('min')); it('--max', testBoolean('normalize')); - it('--output '); - it('--output (invalid value).'); + + it('--output ', function() { + var spy = this.spyOn('toFile'); + this.flags.output = outputTest; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ outputTest ]); + }); + }); + it('--output (invalid value).', function() { + var spy = this.spyOn('toFile'); + this.flags.output = true; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).not.to.be.called; // Error occurs in mkdir. + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('path must be a string'); + }); + }); + it('--optimizeScans', testBoolean('optimizeScans')); - it('--overlay '); - it('--overlay (invalid value)'); + + it('--overlay ', function() { + var spy = this.spyOn('overlayWith'); + this.flags.overlay = inputPixel; + return runner.run(inputPixel, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ inputPixel ]); + }); + }); + it('--overlay (invalid value)', function() { + var spy = this.spyOn('overlayWith'); + this.flags.overlay = true; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ true ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('overlay path must be a string'); + }); + }); + it('--overshootDeringing', testBoolean('overshootDeringing')); it('--progressive', testBoolean('progressive')); - it('--quality '); - it('--quality (invalid value)'); - it('--rotate '); - it('--rotate (invalid value)'); + + it('--quality ', function() { + var spy = this.spyOn('quality'); + this.flags.quality = '70'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 70 ]); + }); + }); + it('--quality (invalid value)', function() { + var spy = this.spyOn('quality'); + this.flags.quality = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid quality'); + }); + }); + + it('--rotate ', function() { + var spy = this.spyOn('rotate'); + this.flags.rotate = '180'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 180 ]); + }); + }); + it('--rotate (invalid value)', function() { + var spy = this.spyOn('rotate'); + this.flags.rotate = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('unsupported angle'); + }); + }); + it('--sequentialRead', testBoolean('sequentialRead')); - it('--sharpen', testBoolean('sharpen')); - it('--sharpen [number]'); - it('--sharpen [number] (invalid value)'); - it('--sharpenFlat '); - it('--sharpenFlat (invalid value)'); - it('--sharpenJagged '); - it('--sharpenJagged (invalud value)'); - it('--tile', testBoolean('tile')); - it('--tile [number]'); - it('--tile [number] (invalid value)'); - it('--tileOverlap '); - it('--tileOverlap (invalid value)'); + + it('--sharpen', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = true; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ undefined, undefined, undefined ]); + }); + }); + it('--sharpen [number]', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = '10'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 10, undefined, undefined ]); + }); + }); + it('--sharpen [number] (invalid value)', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN, undefined, undefined ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid sharpen radius'); + }); + }); + it('--sharpenFlat ', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = '1'; + this.flags.sharpenFlat = '2.5'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, 2.5, undefined ]); + }); + }); + it('--sharpenFlat (invalid value)', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = '1'; + this.flags.sharpenFlat = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, NaN, undefined ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid sharpen level for flat areas'); + }); + }); + it('--sharpenJagged ', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = '1'; + this.flags.sharpenJagged = '2.5'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, undefined, 2.5 ]); + }); + }); + it('--sharpenJagged (invalid value)', function() { + var spy = this.spyOn('sharpen'); + this.flags.sharpen = '1'; + this.flags.sharpenJagged = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 1, undefined, NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid sharpen level for jagged areas'); + }); + }); + + it('--tile', function() { + var spy = this.spyOn('tile'); + this.flags.tile = true; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ undefined, undefined ]); + }); + }); + it('--tile [number]', function() { + var spy = this.spyOn('tile'); + this.flags.tile = '128'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ 128, undefined ]); + }); + }); + it('--tile [number] (invalid value)', function() { + var spy = this.spyOn('tile'); + this.flags.tile = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ NaN, undefined ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid tile size'); + }); + }); + it('--tileOverlap ', function() { + var spy = this.spyOn('tile'); + this.flags.tile = true; + this.flags.tileOverlap = '32'; + return runner.run(input, this.flags).then(function() { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ undefined, 32 ]); + }); + }); + it('--tileOverlap (invalid value)', function() { + var spy = this.spyOn('tile'); + this.flags.tile = true; + this.flags.tileOverlap = 'foo'; + return runner.run(input, this.flags).then(function() { + throw new Error('TRIGGER REJECTION'); + }).catch(function(err) { + expect(spy).to.be.calledOnce; + expect(spy.args[0]).to.eql([ undefined, NaN ]); + expect(err).to.have.property('message'); + expect(err.message.toLowerCase()).to.contain('invalid tile overlap'); + }); + }); + it('--trellisQuantization', testBoolean('trellisQuantization')); + it('--width ', function() { var spy = this.spyOn('resize'); this.flags.width = 100; @@ -211,9 +657,17 @@ describe('runner', function() { expect(spy.args[0]).to.eql([ NaN, NaN ]); }); }); + it('--withoutAdaptiveFiltering', testBoolean('withoutAdaptiveFiltering')); it('--withoutChromaSubsampling', testBoolean('withoutChromaSubsampling')); it('--withoutEnlargement', testBoolean('withoutEnlargement')); - it('--withoutMetadata'); + + it('--withoutMetadata', function() { + var spy = this.spyOn('withMetadata'); + this.flags.withoutMetadata = true; + return runner.run(input, this.flags).then(function() { + expect(spy).not.to.be.called; + }); + }); }); }); \ No newline at end of file