From 1aaeaeed0b6547a9e5237eca1c90df7b09c4a88d Mon Sep 17 00:00:00 2001 From: Marcelo Bernardes <17261643+marcelobern@users.noreply.github.com> Date: Sun, 11 Nov 2018 08:25:41 -0700 Subject: [PATCH] Added StagingWorkflow (e.g. for streamlined use in gulp/grunt) --- CHANGELOG.md | 2 + README.md | 79 +++++++++++++++- docker-compose.yml | 13 +++ index.js | 1 + lib/StagingWorkflow.js | 80 +++++++++++++++++ package-lock.json | 147 +++++++++++++++++++++++++++++- package.json | 8 +- test/data/mock-bin.tar | Bin 0 -> 10240 bytes test/test-StagingBin.js | 33 ++----- test/test-StagingWorkflow.js | 169 +++++++++++++++++++++++++++++++++++ test/utils/fsUtils.js | 45 ++++++++++ 11 files changed, 547 insertions(+), 30 deletions(-) create mode 100644 docker-compose.yml create mode 100644 lib/StagingWorkflow.js create mode 100644 test/data/mock-bin.tar create mode 100644 test/test-StagingWorkflow.js create mode 100644 test/utils/fsUtils.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 511f328..97dc64f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,5 +2,7 @@ ### 0.1.0 (Next) +* [#1](https://github.com/botbits/bin-minify/issues/1): Fixed issue with trash (actually just a test issue with mock-fs) - [@marcelobern](https://github.com/marcelobern). +* Added staging workflow (e.g. to streamline use of `bin-minify` under gulp/grunt) and updated documentation - [@marcelobern](https://github.com/marcelobern). * Test cases now using disposable temporary folders - [@marcelobern](https://github.com/marcelobern). * Improved documentation readability - [@marcelobern](https://github.com/marcelobern). diff --git a/README.md b/README.md index 4ee9ff5..15ef3db 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Stable Release -You are reading the documentation for the next release of bin-minify, which should be 0.1.0. Please see [CHANGELOG](CHANGELOG.md) and make sure to read [UPGRADING](UPGRADING.md) when upgrading from a previous version. The current stable release is [0.0.0](https://github.com/botbits/bin-minify/tree/v0.0.0). +You are reading the documentation for the stable release of bin-minify, 0.1.0. Please see [CHANGELOG](CHANGELOG.md) and make sure to read [UPGRADING](UPGRADING.md) when upgrading from a previous version. ## Overview @@ -35,6 +35,30 @@ $ npm install --save bin-minify ## Usage +### Streamlined Staging + +It is typically preferred to use the streamlined staging in conjunction with `gulp` or `grunt`. + +The following `gulp` task will analyze all binaries under `./bin/my-bin`, save the resulting ***minPack*** to `./.bin-minify/my-bin.json`, and remove any redundant binaries from `./bin/my-bin`. + +```js +const gulp = require('gulp'); +const file = require('gulp-file'); +const path = require('path'); +const stagingWorkflow = require('bin-minify').stagingWorkflow; + +gulp.task('default', async (done) => { + stagingWorkflow(path.resolve(__dirname, path.join('bin', 'my-bin'))).then(minPack => { + file('my-bin.json', JSON.stringify(minPack, null, ' '), {src: true}) + .pipe(gulp.dest('.bin-minify')); + done(); + }, error => { + console.error(`Could not create minPack: ${error}`); + done(); + }); +}); +``` + ### Staging Before using bin-minify the binaries (e.g. from a tar file or a build from source (maybe from [`bin-build`](https://www.npmjs.com/package/bin-build)) should be placed in the desired destination (typically `vendor` or `bin` folders). @@ -290,6 +314,59 @@ Resolved Promise: `{ loaded: true or false }`. `loaded` will be: Rejected Promise: `{ error }`. +## Promise stagingWorkflow (targetPath, minifyBinOptions) + +### targetPath + +- Type: `string` +- Default: `./bin/bin-minify` + +Location of the actual binaries. + +**Note**: Typically the binaries under `targetPath` are source controlled (and should be included in the npm module or Lambda package). + +### minifyBinOptions + +- Type: `Object` +- Optional + +The following are supported keys in the `minifyBinOptions` JSON object. Any other keys are ignored. + +#### dryRun + +- Type: `boolean` +- Default: `false` + +If `true`, will not remove redundant files under `targetPath`. + +If `false`, redundant files under `targetPath` will be handled according to `minifyBinOptions.sendToTrash`. + +#### strict + +- Type: `boolean` +- Default: `false` + +If `true`, will only remove redundant files under `targetPath` if no difference exists between original and reconstructed binaries. + +If `false`, will only remove redundant files under `targetPath` if only difference between original and reconstructed binaries are empty folders. + +#### sendToTrash + +- Type: `boolean` +- Default: `false` + +If `true`, all redundant files under `targetPath` will be removed by sending them to the trash. + +If `false`, all redundant files under `targetPath` will be permanently removed. + +### returns Promise + +Resolved Promise: ***minPack*** JSON. + +**Note**: It is recommended to store this ***minPack*** JSON to a source controlled file for future use. + +Rejected Promise: `{ error }`. + ## Performance diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..38ca26c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: "2" +services: + node: + image: "node:10" + user: "node" + working_dir: /home/node/app + environment: + - NODE_ENV=production + volumes: + - ./:/home/node/app + expose: + - "8081" + command: "npm run test" diff --git a/index.js b/index.js index ab82a8d..e61144d 100644 --- a/index.js +++ b/index.js @@ -4,4 +4,5 @@ module.exports = { RuntimeBin: require('./lib/RuntimeBin'), StagingBin: require('./lib/StagingBin'), + stagingWorkflow: require('./lib/StagingWorkflow').stagingWorkflow, }; diff --git a/lib/StagingWorkflow.js b/lib/StagingWorkflow.js new file mode 100644 index 0000000..cffb4a4 --- /dev/null +++ b/lib/StagingWorkflow.js @@ -0,0 +1,80 @@ +/* eslint-env mocha */ +'use strict'; + +const async = require('async'); +const path = require('path'); +const tmp = require('tmp'); + +const StagingBin = require('./StagingBin'); +const RuntimeBin = require('./RuntimeBin'); + +const BIN_PATH = 'bin-minify'; + +const onlyFolders = (diffTreeResult) => { + for (var entry of diffTreeResult) { + if (!['mkdir', 'rmdir'].includes(entry[0])) return false; + } + return true; +}; + +exports.stagingWorkflow = function (targetPath, options) { + return new Promise((resolve, reject) => { + + targetPath = targetPath || path.resolve(__dirname, '..', 'bin', 'bin-minify'); + options = undefined !== options ? options : {}; + const sendToTrash = options.sendToTrash || false; + const dryRun = options.dryRun || false; + const strict = options.strict || false; + + var stagingBin + , runtimeBin + , tmpFromBase; + + async.waterfall([ + (callback) => callback(null, new StagingBin({ targetPath })), + (newStagingBin, callback) => { + stagingBin = newStagingBin; + return callback(null, stagingBin.createMinPack()); + }, + (createMinPackResult, callback) => { + createMinPackResult.then(minPack => { + runtimeBin = new RuntimeBin({ + targetPath, + minPack, + }); + + tmp.dir({ unsafeCleanup: true }, (err, tmpBasePath) => { + if (err) return callback(new Error(`Could not create temp dir: ${err}`)); + else { + tmpFromBase = path.join(tmpBasePath, BIN_PATH); + return callback(null, runtimeBin.applyMinPack(tmpFromBase)); + } + }); + }, err => callback(new Error(`createMinPack() failed: ${err}`))); + }, + (applyMinPackResult, callback) => { + applyMinPackResult.then(result => { + if (result.loaded) return callback(null, stagingBin.checkMinPack(tmpFromBase)); + else return callback(new Error(`Folder ${tmpFromBase} already exists`)); + }, err => callback(new Error(`applyMinPack() failed: ${err}`)) + ); + }, + (checkMinPackResult, callback) => { + checkMinPackResult.then(result => { + if (result.length > 0 && (strict || !onlyFolders(result))) { + return callback(new Error(`Folders differ ${JSON.stringify(result, null, ' ')}`)); + } + if (dryRun) return callback(null, new Promise(resolve => resolve({ delCount: 0 }))); + else return callback(null, stagingBin.minifyBin(sendToTrash)); + }, err => callback(new Error(`checkMinPack() failed: ${err}`)) + ); + }, + ], (err, minifyBinResult) => { + if (err) reject(err); + else minifyBinResult.then( + () => resolve(stagingBin.minPack), + err => reject(new Error(`minifyBin() failed: ${err}`)) + ); + }); + }); +}; diff --git a/package-lock.json b/package-lock.json index e11410f..1bbfe6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -301,7 +301,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, "requires": { "lodash": "^4.17.10" } @@ -338,6 +337,16 @@ "tweetnacl": "^0.14.3" } }, + "bl": { + "version": "1.2.2", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "dev": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -353,6 +362,28 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, "bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", @@ -425,6 +456,12 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", @@ -621,6 +658,15 @@ "safer-buffer": "^2.1.0" } }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, "ensure-posix-path": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.0.2.tgz", @@ -977,6 +1023,12 @@ "mime-types": "^2.1.12" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", @@ -2934,6 +2986,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, "progress": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", @@ -2951,6 +3009,16 @@ "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", "dev": true }, + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -2963,6 +3031,29 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -3167,6 +3258,15 @@ "strip-ansi": "^4.0.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -3239,6 +3339,33 @@ } } }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "dev": true, + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, "text-encoding": { "version": "0.6.4", "resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", @@ -3265,6 +3392,12 @@ "os-tmpdir": "~1.0.2" } }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -3396,6 +3529,12 @@ "os-homedir": "^1.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", @@ -3476,6 +3615,12 @@ } } }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", diff --git a/package.json b/package.json index 589aec9..4662a98 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,12 @@ "test": "mocha", "test-integration": "TEST_TYPE=integration npm test", "test-capture": "TEST_TYPE=capture npm test", - "test-with-coverage": "nyc npm test && nyc report --reporter=text-lcov > ./lcov.info" + "test-with-coverage": "nyc npm test && nyc report --reporter=text-lcov > ./lcov.info", + "test-node-10": "docker-compose up", + "cleanup-node-10": "docker-compose rm" }, "dependencies": { + "async": "^2.6.1", "del": "^3.0.0", "file-dedupe": "^0.1.0", "filecompare": "^1.0.4", @@ -57,6 +60,7 @@ "mocha-lcov-reporter": "^1.3.0", "mock-fs": "^4.7.0", "nyc": "^13.0.1", - "sinon": "^7.1.0" + "sinon": "^7.1.0", + "tar-fs": "^1.16.3" } } diff --git a/test/data/mock-bin.tar b/test/data/mock-bin.tar new file mode 100644 index 0000000000000000000000000000000000000000..25590d68c9f36631fc386a015ebc665b99825755 GIT binary patch literal 10240 zcmeI0QBK4l5Qcg76g`15P~bSGZd+|^c9kv3>gi>SP3Y5ZOd;L$=LHC4ARqG&18w*i zheI=UkJDumd5Vn1*0qQgIV01WhhnJ(Y3&r%mb4O*l$KglGS7CFR>OH}d{v2X?)-9O z*}FEroP9UF=H>8kH=D&qS0~V)#C=lcIU|8N{WL#{i35BP@oZ;evMDvtk}$Rhsda{1KmqKLxV0_FLoIaX7xK zZRCH((r^B!h@k%xu891v1m*3nG3!60{5kKt<^JdQ->iR94*DkOD3vA>2-*8o_62WQb i|38~`Ol%AUKmY_l00ck)1V8`;KmY_l00cl_H3C1rbf0Vh literal 0 HcmV?d00001 diff --git a/test/test-StagingBin.js b/test/test-StagingBin.js index d0ba723..98af4d1 100644 --- a/test/test-StagingBin.js +++ b/test/test-StagingBin.js @@ -2,20 +2,21 @@ 'use strict'; const fs = require('fs'); -const mockFs = require('mock-fs'); const path = require('path'); const tmp = require('tmp'); +const prepareFs = require('./utils/fsUtils').prepareFs; var responses = require('./utils/responses'); -const RESULTS_PATH = path.resolve(__dirname, path.join('data', 'mock-bin.json')); +const RESULTS_PATH = path.resolve(__dirname, 'data', 'mock-bin.json'); const RESULTS = require(RESULTS_PATH); var shouldCapture = (testType) => ['capture'].includes(testType); +var shouldMock = (testType) => ['unit'].includes(testType); /* * Configurable test suite parameters */ -const TEST_TYPE = ['unit', 'integration', 'capture'].includes(process.env.TEST_TYPE) ? process.env.TEST_TYPE : 'unit'; +const TEST_TYPE = ['unit', 'integration', 'capture'].includes(process.env.TEST_TYPE) ? process.env.TEST_TYPE : 'integration'; // TEST_TYPE = 'unit' will run unit tests locally (completes in milliseconds). This is the default value. // TEST_TYPE = 'integration' will run integration tests against local filesystem (completes in milliseconds). // TEST_TYPE = 'capture' same as integration plus will capture the responses for future unit tests. @@ -33,7 +34,6 @@ const RuntimeBin = require('../index').RuntimeBin; const BIN_PATH = 'bin-minify'; const TARGET_PATH = path.resolve(__dirname, path.join('data', 'mock-bin')); const MIN_PACK = require(`${TARGET_PATH}.json`); -//const TASKS = require(path.resolve(__dirname, path.join('data', 'result.js'))).tasks; describe('StagingBin', () => { var stagingBin; @@ -183,26 +183,7 @@ describe('StagingBin', () => { }); describe('#minifyBin()', () => { - beforeEach(() => { - mockFs({ - [path.join(__dirname, 'data', 'mock-bin')]: { - 'duplicate.txt': 'abc', - 'hardlink.txt': 'abc', - 'hardlink2.txt': 'abc', - 'regular_file.txt': 'abc', - 'softlink.txt': mockFs.symlink({ - path: 'regular_file.txt' - }), - 'folder': { - 'unique.txt': 'xyz', - }, - }, - }); - }); - - after(() => { - mockFs.restore(); - }); + prepareFs(shouldMock(TEST_TYPE), TARGET_PATH); it('successfully removes the original files', () => { return expect(stagingBin.minifyBin(false)).to.eventually.eql({ @@ -219,7 +200,7 @@ describe('StagingBin', () => { }); }); - it.skip('successfully send the original files to the trash', () => { + it('successfully send the original files to the trash', () => { return expect(stagingBin.minifyBin(true)).to.eventually.have.property('delCount', 4); }); @@ -238,7 +219,7 @@ describe('StagingBin', () => { }); }); - it.skip('rejects promise if Promise.all() rejects', (done) => { + it('rejects promise if Promise.all() rejects', (done) => { const stub = sinon.stub(Promise, 'all').rejects(); expect(stagingBin.minifyBin(true)).to.be.rejected.notify(done); stub.restore(); diff --git a/test/test-StagingWorkflow.js b/test/test-StagingWorkflow.js new file mode 100644 index 0000000..4991e7c --- /dev/null +++ b/test/test-StagingWorkflow.js @@ -0,0 +1,169 @@ +/* eslint-env mocha */ +'use strict'; + +const path = require('path'); +const tmp = require('tmp'); + +const prepareFs = require('./utils/fsUtils').prepareFs; +const RESULTS_PATH = path.resolve(__dirname, path.join('data', 'mock-bin.json')); +const RESULTS = require(RESULTS_PATH); + +var shouldMock = (testType) => ['unit'].includes(testType); + +/* + * Configurable test suite parameters + */ +const TEST_TYPE = ['unit', 'integration', 'capture'].includes(process.env.TEST_TYPE) ? process.env.TEST_TYPE : 'integration'; + +const sinon = require('sinon'); +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const expect = chai.expect; +chai.config.includeStack = true; + +const StagingBin = require('../index').StagingBin; +const RuntimeBin = require('../index').RuntimeBin; +const stagingWorkflow = require('../index').stagingWorkflow; + +const TARGET_PATH = path.resolve(__dirname, path.join('data', 'mock-bin')); + +describe('stagingWorkflow', () => { + prepareFs(shouldMock(TEST_TYPE), TARGET_PATH); + + describe('works with', () => { + prepareFs(shouldMock(TEST_TYPE), path.resolve(__dirname, '..', 'bin', 'bin-minify')); + + it('default values', () => { + return expect(stagingWorkflow()).to.eventually.eql(RESULTS); + }); + }); + + it('works when all options are false', () => { + const options = { + sendToTrash: false, + dryRun: false, + strict: false, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when strict is true', () => { + const options = { + sendToTrash: false, + dryRun: false, + strict: true, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when dryRun is true', () => { + const options = { + sendToTrash: false, + dryRun: true, + strict: false, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when dryRun & strict are true', () => { + const options = { + sendToTrash: false, + dryRun: true, + strict: true, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when sendToTrash is true', () => { + const options = { + sendToTrash: true, + dryRun: false, + strict: false, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when sendToTrash & strict are true', () => { + const options = { + sendToTrash: true, + dryRun: false, + strict: true, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when sendToTrash & dryRun are true', () => { + const options = { + sendToTrash: true, + dryRun: true, + strict: false, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + it('works when all options are true', () => { + const options = { + sendToTrash: true, + dryRun: true, + strict: true, + }; + return expect(stagingWorkflow(TARGET_PATH, options)).to.eventually.eql(RESULTS); + }); + + describe('# test exception scenarios', () => { + var stub; + + afterEach(() => { + stub.restore(); + }); + + it('is rejected if tmp.dir() errs', (done) => { + stub = sinon.stub(tmp, 'dir').callsArgWith(1, new Error('dummy')); + expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected.notify(done); + }); + + it('is rejected when when applyMinPack() resolves to { loaded: false }', (done) => { + stub = sinon.stub(RuntimeBin.prototype, 'applyMinPack').resolves({ loaded: false }); + expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected.notify(done); + }); + + it('is rejected when when checkMinPack() resolves to non-empty Array', (done) => { + stub = sinon.stub(StagingBin.prototype, 'checkMinPack').resolves([['dummy']]); + expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected.notify(done); + }); + + it('works when checkMinPack() resolves to non-empty Array (dirs only)', (done) => { + stub = sinon.stub(StagingBin.prototype, 'checkMinPack').resolves([['mkdir'], ['rmdir']]); + expect(stagingWorkflow(TARGET_PATH)).to.eventually.eql(RESULTS).notify(done); + }); + }); + + describe('# test reject scenarios', () => { + var stub; + + afterEach(() => { + stub.restore(); + }); + + it('is rejected when createMinPack() rejects', () => { + stub = sinon.stub(StagingBin.prototype, 'createMinPack').rejects({}); + return expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected; + }); + + it('is rejected when applyMinPack() rejects', () => { + stub = sinon.stub(RuntimeBin.prototype, 'applyMinPack').rejects({}); + return expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected; + }); + + it('is rejected when checkMinPack() rejects', () => { + stub = sinon.stub(StagingBin.prototype, 'checkMinPack').rejects({}); + return expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected; + }); + + it('is rejected when minifyBin() rejects', () => { + stub = sinon.stub(StagingBin.prototype, 'minifyBin').rejects({}); + return expect(stagingWorkflow(TARGET_PATH)).to.eventually.be.rejected; + }); + }); +}); diff --git a/test/utils/fsUtils.js b/test/utils/fsUtils.js new file mode 100644 index 0000000..170e0fb --- /dev/null +++ b/test/utils/fsUtils.js @@ -0,0 +1,45 @@ +/* eslint-env mocha */ +'use strict'; + +const path = require('path'); + +exports.prepareFs = (shouldMock, myPath) => { + if (shouldMock) { + const mockFs = require('mock-fs'); + + beforeEach(() => { + mockFs({ + [myPath]: { + 'duplicate.txt': 'abc', + 'hardlink.txt': 'abc', + 'hardlink2.txt': 'abc', + 'regular_file.txt': 'abc', + 'softlink.txt': mockFs.symlink({ + path: 'regular_file.txt' + }), + 'folder': { + 'unique.txt': 'xyz', + }, + }, + }); + }); + + after(() => { + mockFs.restore(); + }); + } else { + beforeEach((done) => { + const fs = require('fs'); + const fse = require('fs-extra'); + const tar = require('tar-fs'); + + const TAR_FILENAME = path.resolve(__dirname, '..', 'data', 'mock-bin.tar'); + + fse.ensureDirSync(myPath); + + fs.createReadStream(TAR_FILENAME) + .pipe(tar.extract(myPath)) + .on('finish', done); + }); + } +};