├── .gitignore ├── README.md ├── api.js ├── apiExample.js ├── cli.js ├── lib ├── SetupRunner.js ├── Test.js ├── TestRunner.js ├── actions │ ├── Run.js │ └── Setup.js ├── display │ ├── RunCli.js │ ├── RunGui.js │ └── RunJson.js └── utils │ ├── Commands.js │ ├── Config.js │ ├── Docker.js │ └── VersionParser.js ├── ndt ├── package-lock.json ├── package.json └── tests ├── helpers ├── DockerStubs.js ├── SpawnStubs.js ├── TestRepos.js ├── cleanConfig.js └── versions.json └── specs ├── options ├── cli-parsing.js ├── config-parsing.js └── defaults.js └── utils ├── Commands.js ├── Docker.js └── VersionParser.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | coverage -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node Docker Test 2 | 3 | Test your node project against multiple node versions using docker. 4 | 5 | ![Demo] 6 | 7 | ## Installation 8 | 9 | npm install -g node-docker-test 10 | 11 | ## Usage 12 | 13 | First, you will need to generate an image. ndt will create a separate image for each repository on your machine. 14 | 15 | cd /path/to/my/project 16 | ndt --setup 17 | 18 | Once the setup is complete, you can run the test. 19 | 20 | ndt 21 | 22 | ----------------- 23 | 24 | ## Configuration 25 | 26 | Configuration can be set via your package.json file or via command line arguments. Command line arguments always 27 | override your package.json. 28 | 29 | - [Commands](#commands) 30 | - [Setup Commands](#setup-commands) 31 | - [Versions](#versions) 32 | - [Concurrency](#concurrency) 33 | - [Setup](#setup) 34 | - [Reset](#reset) 35 | - [Simple Mode](#simple-mode) 36 | - [Base Image](#base-image) 37 | 38 | A minimal package.json file: 39 | 40 | ```json 41 | { 42 | "name": "super-cool-package", 43 | "version": "1.0.0", 44 | "config": { 45 | "ndt": { 46 | "setup-commands": [ 47 | "apt-get install -y curl", 48 | "mkdir -p /some/needed/folder" 49 | ], 50 | "commands": [ 51 | "npm run setup-tests", 52 | "npm test" 53 | ], 54 | "versions": [ 55 | "minor | lts", 56 | "major", 57 | "patch | gte:4.0 | lt:4.1", 58 | "0.12", 59 | "5.1.0" 60 | ], 61 | "concurrency": 2, 62 | "simple": false, 63 | "reset": true 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | A cli invocation with all options set for a setup: 70 | 71 | ```bash 72 | ndt \ 73 | --setup \ 74 | -s "apt-get install -y curl" "mkdir -p /some/needed/folder" \ 75 | -v "minor | lts" "major" "patch | gte:4.0 | lte: 4.1" "0.12" "5.1.0" \ 76 | -b "centos" \ 77 | -p "yum" \ 78 | --yarn \ 79 | --reset 80 | ``` 81 | 82 | A cli invocation with all options set for running tests: 83 | 84 | ```bash 85 | ndt \ 86 | -x "npm run setup-tests" "npm test" \ 87 | -v "minor | lts" "major" "patch | gte:4.0 | lte: 4.1" "0.12" "5.1.0" \ 88 | -c 2 \ 89 | --simple \ 90 | --yarn 91 | ``` 92 | 93 | #### Commands 94 | 95 | > A string, or an array of strings. Each command will be executed during the test. 96 | > 97 | > **Default:** `"npm test"` 98 | > 99 | > - **JSON:** "commands" 100 | > - **CLI:** --commands, -x 101 | 102 | #### Setup Commands 103 | 104 | > A string, or an array of strings. Each command will be executed during the setup. 105 | > 106 | > **Default:** Empty 107 | > 108 | > - **JSON:** "setup-commands" 109 | > - **CLI:** --setup-commands, -s 110 | 111 | #### Versions 112 | 113 | > An array of versions. See [Versions Syntax](#versions-syntax) 114 | > 115 | > **Default:** `["major"]` 116 | > 117 | > - **JSON:** "versions" 118 | > - **CLI:** --versions, -v 119 | 120 | #### Concurrency 121 | 122 | > The number of current tests to run. Only applicable for running tests. 123 | > 124 | > **Default:** # CPUs - 1 125 | > 126 | > - **JSON:** "concurrency" 127 | > - **CLI:** --concurrency, -c 128 | > 129 | > *It is recommended to NOT use the json key for concurrency as users with different systems may prefer to let ndt 130 | > determine the number of concurrent tests to run based on their system.* 131 | 132 | #### Setup 133 | 134 | > Run the setup. Will not run any tests. If the project is already setup, the existing image will be used as the base. 135 | > Run setup anytime to update the image. 136 | > 137 | > **Default:** `false` 138 | > 139 | > - **JSON:** N/A 140 | > - **CLI:** --setup 141 | 142 | #### Reset 143 | 144 | > Do not re-use an existing image during setup. Useful if your setup scripts expect a clean environment. Only applicable 145 | > for setup. 146 | > 147 | > **Default:** `false` 148 | > 149 | > - **JSON:** "reset" 150 | > - **CLI:** --reset 151 | 152 | #### Simple Mode 153 | 154 | > Run the tests in simple mode. This will force a simple line-by-line output. Also in simple mode, the exit code is 155 | > equal to the number of failed tests. Simple mode is most useful for small terminals or scripting. Only applicable for 156 | > running the tests. 157 | > 158 | > **Default:** `false` 159 | > 160 | > - **JSON:** "simple" 161 | > - **CLI:** --simple, -q 162 | 163 | #### JSON Mode 164 | 165 | > Run the tests in JSON mode. This mode will output a json representation of the results to STDOUT. A very small 166 | > amount of status data will be outputted to STDERR. This mode can be used to interface to other programming or 167 | > scripting tools. 168 | > 169 | > **Default** `false` 170 | > 171 | > - **JSON:** "json" 172 | > - **CLI:** --json, -j 173 | 174 | #### Base Image 175 | 176 | > Specify the base image to build the testing image from. (debian, ubuntu, etc). Only applicable during setup. If you 177 | change the base image, remember to use --reset during setup. 178 | > 179 | > **Default:** `"debian:stable"` 180 | > 181 | > - **JSON:** "base-image" 182 | > - **CLI:** --base-image, -b 183 | > 184 | > *ndt by default uses apt-get as the package manager. If you use a yum based distro, like centos or fedora, you will need to update the package-manager options as well* 185 | 186 | #### Package Manager 187 | 188 | > Specify the package manager to use when building the testing image. 189 | > 190 | > **Default:** "apt-get" 191 | > 192 | > **Valid Values:** "apt-get" or "yum" 193 | > 194 | > - **JSON:** "package-manager" 195 | > - **CLI:** --package-manager, -p 196 | 197 | #### Yarn 198 | 199 | > Use yarn instead of npm for installing dependencies. 200 | > 201 | > **Default:** `false` 202 | > 203 | > - **JSON:**: "yarn" 204 | > - **CLI:**: --yarn, -y 205 | > 206 | > *yarn will be installed if --yarn is passed at setup. If yarn was not installed during setup and used during testing, all tests will fail.* 207 | 208 | 209 | ----------------------- 210 | 211 | ## Versions Syntax 212 | 213 | ndt sports a very flexible version syntax. The version syntax follows the following format: 214 | 215 | (version or keyword) | filter | filter | ... 216 | 217 | You must specify one version or one keyword and then any number of filters (or zero). Whitespace is optional. 218 | 219 | ### Possible Versions 220 | 221 | | Version | Example | Description | 222 | |:--------|:--------|:------------------------------------------------| 223 | | X | 6 | Resolves to the greatest single version of X. | 224 | | X.Y | 6.1 | Resolves to the greatest single version of X.Y. | 225 | | X.Y.Z | 6.1.2 | Resolves to exactly X.Y.Z. | 226 | 227 | ### Possible Keywords 228 | 229 | | Keyword | Description | 230 | |:-------:|:-----------------------------------------------------------------------------------------------------| 231 | | major | Resolves to a list of all the latest major versions. Does not include legacy versions. | 232 | | minor | Resolves to a list of all the latest minor versions. Does not include legacy versions. | 233 | | patch | Resolves to a list of all patch versions. Does not include legacy versions. | 234 | | legacy | Resolves to a list of all the legacy versions, which is everything that starts with major version 0. | 235 | | all | Resolves to every single version available. | 236 | 237 | *A note about versions < 0.10: They will not install unless you provide a build environment via the setup commands. See 238 | the nvm documentation* 239 | 240 | ### Possible Filters 241 | 242 | | Filter | Description | 243 | |:-----------:|:------------------------------------------------------------| 244 | | gt:version | Filter to only versions greater than `version`. | 245 | | gte:version | Filter to only versions greater than or equal to `version`. | 246 | | lt:version | Filter to only versions less than `version`. | 247 | | lte:version | Filter to only versions less than or equal to `version`. | 248 | | eq:version | Filter to only versions matching `version`. | 249 | | neq:version | Filter to only versions not matching `version`. | 250 | | lts | Filter to only LTS versions. | 251 | 252 | *`version` can be a partial version.* 253 | 254 | ### Examples 255 | 256 | Minor versions of v6, and major versions of everything else. 257 | 258 | ndt -v "minor | eq:6" -v "major | lt:6" 259 | 260 | All major versions, plus a subset of version 4 which had a bug your project needs to pay attention to. 261 | 262 | ndt -v "major" -v "patch | gte:4.2 | lte:4.6" 263 | 264 | Every single LTS version and the *popular* legacy versions. 265 | 266 | ndt -v "patch | lts" -v "0.12, 0.10" 267 | 268 | *You do not need to worry if multiple versions overlap, as they are de-duplicated before running.* 269 | 270 | 271 | ----------------------------- 272 | 273 | ## API 274 | 275 | ndt also ships with an API for calling the internal functions. 276 | 277 | ndt relies heavily on Promises and Events. 278 | 279 | ### Version Parser 280 | 281 | The Version Parser can parse the [ndt version syntax](#versions-syntax) and return an array of valid node versions. 282 | 283 | `VersionParser(string|string[] version) -> Promise (string[] versions)` 284 | 285 | You can pass either a string, or an array of strings in the ndt version syntax. A promise will resolve with valid nodejs 286 | versions. 287 | 288 | ----------------------------- 289 | 290 | ### Setup Runner 291 | 292 | The Setup Runner will build a docker image for use with the Test Runner. 293 | 294 | `new SetupRunner(object options)` 295 | 296 | **options** 297 | 298 | All options are required. 299 | 300 | - **name** (string) - A name for the image 301 | - **baseImage** (string) - The base image to build from. 302 | - **versions** (string[]) - Valid nodejs versions. 303 | - **commands** (string[]) - Commands to be executed after the nodejs versions are installed. 304 | - **reset** (boolean) - Whether to use an existing image under the same name, or to always use the baseImage. 305 | 306 | **methods** 307 | 308 | `.start() -> Promise()` Start the setup. 309 | 310 | - Will resolve is setup is successful. 311 | - Will reject is setup failed. 312 | 313 | **events** 314 | 315 | `.on('data', function (string){})` Emitted when the setup outputs to STDOUT or STDERR 316 | 317 | ----------------------------- 318 | 319 | ### Test Runner 320 | 321 | The Test Runner will run tests on a project in an existing container. 322 | 323 | `new TestRunner(object options)` 324 | 325 | **options** 326 | 327 | All options are required. 328 | 329 | - **name** (string) - The name of the image to run the tests in. 330 | - **versions** (string[]) - Valid nodejs versions. 331 | - **commands** (string[]) - Commands to execute to run the tests. 332 | - **concurrency** (int) - Number of concurrent tests to run. 333 | 334 | **methods** 335 | 336 | `.start() -> Promise(TestResult[])` Start the tests. 337 | 338 | - Will resolve with an array of TestResults if the tests ran (Passed or Failed). 339 | - Will reject if any tests failed to run. 340 | 341 | `.stop()` Stop the tests. 342 | 343 | - Currently running tests are allowed to finish. Any pending tests are skipped. 344 | 345 | **events** 346 | 347 | `.on('started'), function (){})` Emitted when the tests runner is started. 348 | 349 | `.on('finished'), function (TestResult[]){})` Emitted when the tests runner is finished. 350 | 351 | `.on('testStarted'), function (Test){})` Emitted when a test is started. See [Test](#test-object) 352 | 353 | `.on('testData'), function (TestData){})` Emitted when a test writes to STDOUT or STDERR. See 354 | [TestData](#testdata-object) 355 | 356 | `.on('testFinished'), function (TestResult){})` Emitted when a test is finished. See [TestResult](#testresult-object) 357 | 358 | `.on('runnerStarted'), function (int){})` Emitted when a runner started running tests. The int is the ID of the test 359 | runner (0-indexed). 360 | 361 | `.on('runnerFinished'), function (int){})` Emitted when a runner is finished running tests. The int is the ID of the 362 | test runner (0-indexed). 363 | 364 | 365 | ##### Test Object 366 | 367 | { 368 | runner: int, // The ID of the runner the test was run in. 369 | version: string // The nodejs version the test is running for. 370 | } 371 | 372 | ##### TestData Object 373 | 374 | { 375 | runner: int, // The ID of the runner the test was run on. 376 | version: string, // The nodejs version the test is running for. 377 | data: string // STDOUT or STDERR of the test. Only contains data since the last data event. 378 | } 379 | 380 | ##### TestResult Object 381 | 382 | { 383 | runner: int, // The ID of the runner the test was run on. 384 | version: string, // The nodejs verison the test was run for. 385 | passed: bool, // If the test passed or failed. 386 | data: string // All the STDOUT and STDERR of the test. 387 | } 388 | 389 | #### API Example 390 | 391 | Below is a contrived example of the API. 392 | 393 | ```javascript 394 | var Promise = require('bluebird'); 395 | var ndt = require('node-docker-test'); 396 | 397 | var setupRunner, testRunner; 398 | 399 | Promise 400 | .try(function () { 401 | return ndt.VersionParser(['6', '5.1']); 402 | }) 403 | .then(function (versions) { 404 | setupRunner = new ndt.SetupRunner({ 405 | name: 'custom:runner', 406 | baseImage: 'debian:latest', 407 | versions: versions, 408 | commands: [], 409 | reset: false 410 | }); 411 | 412 | testRunner = new ndt.TestRunner({ 413 | name: 'custom:runner', 414 | versions: versions, 415 | commands: ['npm run t'], 416 | concurrency: 2 417 | }); 418 | 419 | setupRunner.on('data', function (data) { console.log(data); }); 420 | 421 | testRunner.on('started', function () { 422 | console.log('started'); 423 | }); 424 | 425 | testRunner.on('finished', function (results) { 426 | console.log('finished', results); 427 | }); 428 | 429 | testRunner.on('testStarted', function (test) { 430 | console.log('testStarted', test); 431 | }); 432 | 433 | testRunner.on('testData', function (test) { 434 | console.log('testData', test); 435 | }); 436 | 437 | testRunner.on('testFinished', function (test) { 438 | console.log('testFinished', test); 439 | }); 440 | 441 | testRunner.on('runnerStarted', function (runner) { 442 | console.log('runnerStarted', runner); 443 | }); 444 | 445 | testRunner.on('runnerFinished', function (runner) { 446 | console.log('runnerFinished', runner); 447 | }); 448 | }) 449 | .then(function () { 450 | return setupRunner.start(); 451 | }) 452 | .then(function () { 453 | return testRunner.start(); 454 | }) 455 | .then(function (results) { 456 | //results are here as well 457 | }) 458 | .catch(function (error) { 459 | console.error('There was an error', error); 460 | }); 461 | 462 | ``` 463 | 464 | ----------------------- 465 | 466 | ## Other Notes 467 | 468 | - The current working directory is copied into docker to the directory /test-src. Then that directory is rsync'd to 469 | /test excluding the node_modules folder (to ensure a new download of all the dependencies). The test is run from the 470 | /test directory. 471 | - During setup, ndt will pre-download the versions specified during setup. You can re-run setup at any time to update 472 | the image to contain the versions specified at any time. It is recommended to re-run setup whenever there are new 473 | versions which match your config to prevent having to re-download the node binaries every time your run your tests. If 474 | `reset` is false, then the image will be re-used and any existing versions are not re-downloaded. 475 | - ndt uses `debian:stable` as the base image. 476 | - ndt just calls the docker command. You must be able to execute the `docker` application in order to use ndt. Please 477 | see your distribution's documentation for details on how to setup and use docker. 478 | - Each command in `setup-commands` and `commands` will be joined with the "&&" operator. Therefor if any command fails, 479 | the entire setup or test is stopped. 480 | 481 | ## License 482 | 483 | The MIT License (MIT) Copyright (c) 2016 Kevin Gravier 484 | 485 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 486 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 487 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 488 | persons to whom the Software is furnished to do so, subject to the following conditions: 489 | 490 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 491 | Software. 492 | 493 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 494 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 495 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 496 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 497 | 498 | [Demo]: http://i.imgur.com/kNFn9rV.gif 499 | -------------------------------------------------------------------------------- /api.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var ndtApi = {}; 7 | 8 | module.exports = ndtApi; 9 | 10 | ndtApi.VersionParser = require('./lib/utils/VersionParser'); 11 | ndtApi.SetupRunner = require('./lib/SetupRunner'); 12 | ndtApi.TestRunner = require('./lib/TestRunner'); 13 | -------------------------------------------------------------------------------- /apiExample.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise = require('bluebird'); 7 | var ndt = require('./api.js'); 8 | 9 | var setupRunner, testRunner; 10 | 11 | Promise 12 | .try(function () { 13 | return ndt.VersionParser(['6', 'minor | eq:5']); 14 | }) 15 | .then(function (versions) { 16 | setupRunner = new ndt.SetupRunner({ 17 | name: 'custom:runner', 18 | 'base-image': 'debian:latest', 19 | versions: versions, 20 | commands: [], 21 | reset: false 22 | }); 23 | 24 | testRunner = new ndt.TestRunner({ 25 | name: 'custom:runner', 26 | versions: versions, 27 | commands: ['sleep 5', 'echo "Test Running!"', 'sleep 5', 'echo "Test Done"'], 28 | concurrency: 2 29 | }); 30 | 31 | setupRunner.on('data', function (data) { 32 | console.log(data); 33 | }); 34 | 35 | testRunner.on('started', function () { 36 | console.log('started'); 37 | }); 38 | 39 | testRunner.on('finished', function (results) { 40 | console.log('finished', results); 41 | }); 42 | 43 | testRunner.on('testStarted', function (test) { 44 | console.log('testStarted', test); 45 | }); 46 | 47 | testRunner.on('testData', function (test) { 48 | console.log('testData', test); 49 | }); 50 | 51 | testRunner.on('testFinished', function (test) { 52 | console.log('testFinished', test); 53 | }); 54 | 55 | testRunner.on('runnerStarted', function (runner) { 56 | console.log('runnerStarted', runner); 57 | }); 58 | 59 | testRunner.on('runnerFinished', function (runner) { 60 | console.log('runnerFinished', runner); 61 | }); 62 | }) 63 | // .then(function () { 64 | // return setupRunner.start(); 65 | // }) 66 | .then(function () { 67 | setTimeout(function () { 68 | testRunner.stop(); 69 | }, 1000); 70 | return testRunner.start(); 71 | }) 72 | .then(function (results) { 73 | //results are here as well 74 | }); 75 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, Config, Run, Setup; 7 | 8 | Promise = require('bluebird'); 9 | 10 | Config = require('./lib/utils/Config'); 11 | 12 | Run = require('./lib/actions/Run'); 13 | Setup = require('./lib/actions/Setup'); 14 | 15 | return Promise 16 | .try(function () { 17 | var config = Config.parse(); 18 | 19 | if (config.setup) { 20 | return Setup(config); 21 | } 22 | else { 23 | return Run(config); 24 | } 25 | }) 26 | .then(function () { 27 | process.exit(0); 28 | }) 29 | .catch(function (e) { 30 | if (process.env.DEBUG) { 31 | console.error(e); 32 | } 33 | else { 34 | console.error(e.message); 35 | } 36 | process.exit(255); 37 | }); 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lib/SetupRunner.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, Commands, Docker, Util; 7 | 8 | Promise = require('bluebird'); 9 | EventEmitter = require('events'); 10 | Util = require('util'); 11 | 12 | Commands = require('./utils/Commands'); 13 | Docker = require('./utils/Docker'); 14 | 15 | Util.inherits(SetupRunner, EventEmitter); 16 | 17 | module.exports = SetupRunner; 18 | 19 | function SetupRunner(opts) { 20 | this.parseOpts(opts); 21 | } 22 | 23 | SetupRunner.prototype.parseOpts = function parseArgs(opts) { 24 | this._name = opts.name; 25 | this._versions = opts.versions; 26 | this._commands = opts.commands; 27 | this._reset = opts.reset; 28 | this._baseImage = opts.baseImage; 29 | this._packageManager = opts.packageManager; 30 | this._yarn = opts.yarn; 31 | }; 32 | 33 | SetupRunner.prototype.start = function start() { 34 | return Promise 35 | .bind(this) 36 | .return(this._name) 37 | .then(Docker.containerExists) 38 | .then(determineBaseImage) 39 | .then(function (image) { 40 | this._baseImage = image; 41 | }) 42 | .return(this._versions) 43 | .then(doSetup) 44 | }; 45 | 46 | function determineBaseImage(exists) { 47 | var self = this; 48 | 49 | if (!exists) { 50 | return Promise.resolve(this._baseImage); 51 | } 52 | 53 | if (this._reset) { 54 | return Docker.removeContainer(this._name).then(function () { 55 | return self._baseImage; 56 | }); 57 | } 58 | 59 | return Promise.resolve(this._name); 60 | } 61 | 62 | function doSetup(versions) { 63 | var self = this; 64 | 65 | return Docker.makeNew(self._name, Commands.setup(versions, self._commands, self._packageManager, self._yarn), self._baseImage, function (data) { 66 | self.emit('data', data.toString()); 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /lib/Test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, EventEmitter, util, Docker, Commands; 7 | 8 | EventEmitter = require('events'); 9 | Util = require('util'); 10 | Promise = require('bluebird'); 11 | 12 | Docker = require('./utils/Docker'); 13 | Commands = require('./utils/Commands'); 14 | 15 | Util.inherits(Test, EventEmitter); 16 | 17 | function Test(version, name, commands, yarn) { 18 | this.version = version; 19 | this.data = ''; 20 | this.commands = commands; 21 | this.name = name; 22 | this.yarn = yarn; 23 | } 24 | 25 | Test.prototype.run = function run() { 26 | var self = this; 27 | 28 | return Promise 29 | .try(function () { 30 | var promise = Docker.runContainerWithCopy(self.name, Commands.test(self.version, self.commands, self.yarn), function (data) { 31 | self.data += data.toString(); 32 | self.emit('data', data.toString()); 33 | }); 34 | self.process = promise.process; 35 | return promise; 36 | }) 37 | .then(function () { 38 | return { 39 | version: self.version, 40 | passed: true 41 | }; 42 | }) 43 | .catch(function () { 44 | return { 45 | version: self.version, 46 | passed: false 47 | }; 48 | }); 49 | }; 50 | 51 | Test.prototype.stop = function stop() { 52 | var self = this; 53 | 54 | if (self.process) { 55 | self.process.kill('SIGKILL'); 56 | } 57 | }; 58 | 59 | module.exports = Test; 60 | -------------------------------------------------------------------------------- /lib/TestRunner.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Docker, Promise, Test, EventEmitter, Util; 7 | 8 | Promise = require('bluebird'); 9 | Docker = require('./utils/Docker'); 10 | Test = require('./Test'); 11 | EventEmitter = require('events'); 12 | Util = require('util'); 13 | 14 | module.exports = TestRunner; 15 | 16 | Util.inherits(TestRunner, EventEmitter); 17 | 18 | function TestRunner(opts) { 19 | this.parseOpts(opts); 20 | this._running = false; 21 | } 22 | 23 | TestRunner.prototype.parseOpts = function parseOpts(opts) { 24 | this._name = opts.name; 25 | this._versions = opts.versions; 26 | this._commands = opts.commands; 27 | this._yarn = opts.yarn; 28 | this.concurrency = Math.min(this._versions.length, opts.concurrency); 29 | }; 30 | 31 | TestRunner.prototype.start = function run() { 32 | if (this._running) { 33 | return false; 34 | } 35 | 36 | this._cancel_test = false; 37 | this._running = true; 38 | 39 | return Promise 40 | .bind(this) 41 | .return(this._name) 42 | .then(Docker.containerExists) 43 | .then(function (exists) { 44 | if (!exists) { 45 | return Promise.reject("Please run setup first."); 46 | } 47 | }) 48 | .return(this._versions) 49 | .map(makeTest) 50 | .tap(function (tests) { 51 | this.tests = tests; 52 | this.emit('started'); 53 | }) 54 | .then(runTests) 55 | .tap(function (results) { 56 | this.emit('finished', results); 57 | this._running = false; 58 | }); 59 | }; 60 | 61 | TestRunner.prototype.stop = function () { 62 | var self = this; 63 | 64 | self._cancel_test = true; 65 | 66 | if (self.tests) { 67 | self.tests.forEach(function (test) { 68 | test.stop(); 69 | }); 70 | } 71 | }; 72 | 73 | function runTests(tests) { 74 | var self = this; 75 | 76 | var results = []; 77 | var runners = []; 78 | 79 | var currentTest = -1; 80 | 81 | function next(runner_id) { 82 | currentTest++; 83 | 84 | if (self._cancel_test || currentTest >= tests.length) { 85 | self.emit('runnerFinished', runner_id); 86 | return true; 87 | } 88 | 89 | return runTest.bind(self)(runner_id, tests[currentTest]) 90 | .then(function (result) { 91 | results.push(result); 92 | return next(runner_id); 93 | }); 94 | } 95 | 96 | for (var runner_id = 0; runner_id < self.concurrency; runner_id++) { 97 | self.emit('runnerStarted', runner_id); 98 | 99 | runners.push(next(runner_id)); 100 | } 101 | 102 | return Promise.all(runners).then(function () { return results; }); 103 | 104 | } 105 | 106 | function runTest(runner, test) { 107 | var self = this; 108 | return Promise 109 | .try(function () { 110 | self.emit('testStarted', { 111 | runner: runner, 112 | version: test.version 113 | }); 114 | 115 | test.on('data', function (data) { 116 | self.emit('testData', { 117 | runner: runner, 118 | version: test.version, 119 | data: data 120 | }) 121 | }); 122 | 123 | return test.run(); 124 | }) 125 | .then(function (result) { 126 | var r = { 127 | runner: runner, 128 | version: test.version, 129 | passed: result.passed, 130 | data: test.data 131 | }; 132 | 133 | self.emit('testFinished', r); 134 | test.removeAllListeners('data'); 135 | return r; 136 | }); 137 | } 138 | 139 | function makeTest(version) { 140 | return new Test(version, this._name, this._commands, this._yarn); 141 | } 142 | -------------------------------------------------------------------------------- /lib/actions/Run.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, RunCli, RunGui, RunJson, VersionParser, TestRunner; 7 | 8 | Promise = require('bluebird'); 9 | RunCli = require('../display/RunCli'); 10 | RunGui = require('../display/RunGui'); 11 | RunJson = require('../display/RunJson'); 12 | VersionParser = require('../utils/VersionParser'); 13 | TestRunner = require('../TestRunner'); 14 | 15 | module.exports = Run; 16 | 17 | function Run(config) { 18 | var runScreen; 19 | 20 | if (config.json) { 21 | runScreen = new RunJson(config.name); 22 | } else if (config.simple) { 23 | runScreen = new RunCli(config.name); 24 | } else { 25 | runScreen = new RunGui(config.name); 26 | } 27 | 28 | var testRunner; 29 | 30 | return Promise 31 | .resolve(config.versions) 32 | .then(VersionParser) 33 | .then(function (versions) { 34 | var screenFinishedPromise; 35 | 36 | if (versions.length === 0) { 37 | return Promise.reject(new Error("No versions found.")); 38 | } 39 | 40 | testRunner = new TestRunner({ 41 | name: 'ndt:' + config.name, 42 | versions: versions, 43 | commands: config.commands, 44 | concurrency: config.concurrency, 45 | yarn: config.yarn 46 | }); 47 | 48 | connectEvents(testRunner, runScreen); 49 | 50 | screenFinishedPromise = new Promise(function (resolve, reject) { 51 | runScreen.on('finished', resolve); 52 | runScreen.on('errored', reject); 53 | }); 54 | 55 | runScreen.initialize(testRunner.concurrency, versions); 56 | 57 | return Promise.all([testRunner.start(), screenFinishedPromise]); 58 | }) 59 | .catch(function (err) { 60 | if (testRunner) { 61 | testRunner.stop(); 62 | } 63 | 64 | return runScreen.showError(err.message); 65 | }) 66 | .then(function () { 67 | runScreen.destroy(); 68 | }); 69 | } 70 | 71 | function connectEvents(runner, screen) { 72 | runner.on('started', function () { 73 | screen.started(); 74 | }); 75 | 76 | runner.on('finished', function (results) { 77 | screen.finished(results); 78 | }); 79 | 80 | runner.on('testStarted', function (test) { 81 | screen.testStarted(test); 82 | }); 83 | 84 | runner.on('testData', function (data) { 85 | screen.testData(data); 86 | }); 87 | 88 | runner.on('testFinished', function (test) { 89 | screen.testFinished(test); 90 | }); 91 | 92 | runner.on('runnerStarted', function (runner) { 93 | screen.runnerStarted(runner); 94 | }); 95 | 96 | runner.on('runnerFinished', function (runner) { 97 | screen.runnerFinished(runner); 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /lib/actions/Setup.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, SetupRunner, VersionParser; 7 | 8 | Promise = require('bluebird'); 9 | 10 | SetupRunner = require('../SetupRunner'); 11 | VersionParser = require('../utils/VersionParser'); 12 | 13 | module.exports = Setup; 14 | 15 | function Setup(config) { 16 | return Promise 17 | .try(function () { 18 | return VersionParser(config.versions) 19 | }) 20 | .then(function (versions) { 21 | var setup = new SetupRunner({ 22 | name: 'ndt:' + config.name, 23 | baseImage: config['base-image'], 24 | versions: versions, 25 | commands: config['setup-commands'], 26 | reset: config.reset, 27 | packageManager: config['package-manager'], 28 | yarn: config.yarn 29 | }); 30 | 31 | setup.on('data', function (data) { 32 | process.stdout.write(data); 33 | }); 34 | 35 | return setup.start(); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /lib/display/RunCli.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, EventEmitter, extend, pad; 7 | 8 | Promise = require('bluebird'); 9 | EventEmitter = require('events'); 10 | Util = require('util'); 11 | pad = require('pad'); 12 | extend = require('extend'); 13 | 14 | Util.inherits(RunCli, EventEmitter); 15 | 16 | function RunCli(name) { 17 | console.log(name + ' - Node Docker Test'); 18 | this.runnerIdWidth = 1; 19 | } 20 | 21 | RunCli.prototype.started = function started() { 22 | console.log('Starting Tests'); 23 | console.log(''); 24 | this.log('Runner', 'Version', 'Status'); 25 | this.log('--------', '--------', '--------'); 26 | }; 27 | 28 | RunCli.prototype.finished = function finished(results) { 29 | var passed = []; 30 | var failed = []; 31 | 32 | results.forEach(function (result) { 33 | result.passed ? passed.push(result) : failed.push(result); 34 | }); 35 | 36 | console.log(''); 37 | console.log('Passed Tests:\t' + passed.length); 38 | console.log('Failed Tests:\t' + failed.length); 39 | console.log(''); 40 | 41 | if (failed.length) { 42 | console.log('Failed Versions:\t' + failed.map(function (result) { 43 | return 'v' + result.version; 44 | }).join(', ')); 45 | 46 | process.exit(failed.length); 47 | } 48 | else { 49 | this.emit('finished'); 50 | } 51 | }; 52 | 53 | RunCli.prototype.testStarted = function testStarted(test) { 54 | this.log(this._runnerId(test.runner), test.version, 'Started'); 55 | }; 56 | 57 | RunCli.prototype.testData = function testData(test) { 58 | }; 59 | 60 | RunCli.prototype.testFinished = function testFinished(test) { 61 | this.log(this._runnerId(test.runner), test.version, test.passed ? 'Passed' : 'Failed'); 62 | }; 63 | 64 | RunCli.prototype.runnerStarted = function runnerStarted(runner) { 65 | this.log(this._runnerId(runner), '', 'Started'); 66 | }; 67 | 68 | RunCli.prototype.runnerFinished = function runnerFinished(runner) { 69 | this.log(this._runnerId(runner), '', 'Finished'); 70 | }; 71 | 72 | RunCli.prototype.showError = function showError(error) { 73 | console.error(error); 74 | 75 | return Promise.resolve(); 76 | }; 77 | 78 | RunCli.prototype.initialize = function (concurrency) { 79 | this.runnerIdWidth = (concurrency - 1).toString().length; 80 | }; 81 | 82 | RunCli.prototype.destroy = function () { 83 | }; 84 | 85 | RunCli.prototype.log = function log(c1, c2, c3) { 86 | console.log(' ' + pad(c1, 9) + '| ' + pad(c2, 9) + '| ' + pad(c3, 9)); 87 | }; 88 | 89 | RunCli.prototype._runnerId = function _runnerId(id) { 90 | return pad(this.runnerIdWidth, id.toString(), '0'); 91 | }; 92 | 93 | module.exports = RunCli; 94 | -------------------------------------------------------------------------------- /lib/display/RunGui.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var util, EventEmitter, Blessed, extend, Promise; 7 | 8 | Promise = require('bluebird'); 9 | EventEmitter = require('events'); 10 | Util = require('util'); 11 | Blessed = require('blessed'); 12 | 13 | extend = require('extend'); 14 | 15 | Util.inherits(RunGui, EventEmitter); 16 | 17 | function RunGui(name) { 18 | var self = this, 19 | title = name + ' - Node Docker Test', 20 | introMessage = "Please Wait - Initializing Tests"; 21 | 22 | self.Screen = Blessed.screen({ 23 | smartCSR: true 24 | }); 25 | 26 | self.Screen.key(['escape', 'C-c'], function () { 27 | self.emit('errored', new Error("User Canceled")); 28 | }); 29 | 30 | self.Screen.title = title; 31 | 32 | Blessed.text({ 33 | parent: self.Screen, 34 | top: 0, 35 | left: 'center', 36 | width: title.length, 37 | height: 1, 38 | content: title 39 | }); 40 | 41 | self.Body = Blessed.box({ 42 | parent: self.Screen, 43 | top: 1, 44 | left: 0, 45 | right: 0, 46 | bottom: 0 47 | }); 48 | 49 | self.IntroMessage = Blessed.box({ 50 | parent: self.Body, 51 | width: Math.max(introMessage.length + 2), 52 | height: 3, 53 | top: 'center', 54 | left: 'center', 55 | content: introMessage, 56 | padding: 1, 57 | style: { 58 | bg: 'white', 59 | fg: 'black' 60 | } 61 | }); 62 | 63 | self.initialized = false; 64 | self.resultListSize = 20; 65 | self.testBoxes = []; 66 | self.versionRows = []; 67 | 68 | self.Screen.render(); 69 | 70 | self.on('updated', function () { 71 | self.Screen.render(); 72 | }); 73 | } 74 | 75 | RunGui.prototype.initialize = function initialize(number_tests, versions) { 76 | var self = this, percentage; 77 | 78 | self.IntroMessage.destroy(); 79 | 80 | self._addResultList(); 81 | 82 | percentage = Math.floor(100 / number_tests); 83 | 84 | for (var i = 0; i < number_tests; i++) { 85 | self._addTestBox((i * percentage) + '%', percentage + '%') 86 | } 87 | 88 | versions.forEach(function (version, i) { 89 | self._addVersionResultRow(i, version); 90 | }); 91 | 92 | self.emit('updated'); 93 | }; 94 | 95 | RunGui.prototype.started = function started() { 96 | //TODO 97 | }; 98 | 99 | RunGui.prototype.finished = function finished(results) { 100 | var self = this; 101 | 102 | self.on('versionClick', function (version) { 103 | var test = results.filter(function (t) { 104 | return t.version == version; 105 | })[0]; 106 | 107 | self._reviewResult(version, test.data); 108 | }); 109 | 110 | this._showResults(results); 111 | }; 112 | 113 | RunGui.prototype.testStarted = function testStarted(test) { 114 | this._startTest(test.runner, test.version); 115 | }; 116 | 117 | RunGui.prototype.testData = function testData(test) { 118 | this._writeTest(test.runner, test.data); 119 | }; 120 | 121 | RunGui.prototype.testFinished = function testFinished(test) { 122 | this._finishTest(test.runner, test.version, test.passed); 123 | }; 124 | 125 | RunGui.prototype.runnerStarted = function runnerStarted(runner) { 126 | var self = this; 127 | 128 | self.testBoxes[runner].runnerStatus.setContent("Running"); 129 | }; 130 | 131 | RunGui.prototype.runnerFinished = function runnerFinished(runner) { 132 | var self = this; 133 | 134 | self.testBoxes[runner].runnerStatus.setContent("Stopped"); 135 | }; 136 | 137 | 138 | RunGui.prototype.showError = function showError(error) { 139 | var self = this; 140 | 141 | if (typeof error !== 'string') { 142 | error = error.toString(); 143 | } 144 | 145 | self.Body.hide(); 146 | 147 | var continue_message = 'Click this message or press Q to quit'; 148 | 149 | self.errorBox = Blessed.box({ 150 | parent: self.Screen, 151 | width: Math.max(error.length + 2, continue_message.length + 2), 152 | height: 4, 153 | top: 'center', 154 | left: 'center', 155 | mouse: true, 156 | content: error + '\n' + continue_message, 157 | padding: 1, 158 | style: { 159 | bg: '#FF0000', 160 | fg: 'white' 161 | } 162 | }); 163 | 164 | self.emit('updated'); 165 | 166 | return new Promise(function (resolve) { 167 | self.Screen.screen.onceKey(['escape', 'enter', 'q'], function () { 168 | resolve(); 169 | }); 170 | 171 | self.errorBox.on('click', resolve); 172 | }); 173 | 174 | }; 175 | 176 | RunGui.prototype._addResultList = function _addResultList() { 177 | var self = this; 178 | 179 | self.resultList = Blessed.box({ 180 | parent: self.Body, 181 | width: self.resultListSize - 1, 182 | height: '100%', 183 | right: 0, 184 | top: 0, 185 | scrollable: true, 186 | mouse: true, 187 | scrollbar: { 188 | inverse: true 189 | } 190 | }); 191 | 192 | self.resultListLeftBorder = Blessed.line({ 193 | parent: self.Body, 194 | orientation: 'vertical', 195 | inverse: false, 196 | ch: '|', 197 | right: self.resultListSize - 1, 198 | height: '100%' 199 | }); 200 | 201 | self.emit('updated'); 202 | }; 203 | 204 | RunGui.prototype._addTestBox = function _addTestBox(top, height) { 205 | var self = this, testBox = {}; 206 | 207 | testBox.holder = Blessed.box({ 208 | parent: self.Body, 209 | top: top, 210 | left: 0, 211 | height: height, 212 | width: '100%-' + self.resultListSize 213 | }); 214 | 215 | testBox.title = Blessed.text({ 216 | parent: testBox.holder, 217 | width: '100%', 218 | top: 0, 219 | left: 0, 220 | inverse: true 221 | }); 222 | 223 | testBox.content = Blessed.box({ 224 | parent: testBox.holder, 225 | top: 1, 226 | width: '100%', 227 | height: '100%-1', 228 | scrollable: true, 229 | mouse: true, 230 | scrollbar: { 231 | inverse: true 232 | } 233 | }); 234 | 235 | testBox.runnerStatus = Blessed.text({ 236 | parent: testBox.title, 237 | width: 'shrink', 238 | right: 0, 239 | top: 0, 240 | inverse: true 241 | }); 242 | 243 | self.testBoxes.push(testBox); 244 | 245 | self.emit('updated'); 246 | }; 247 | 248 | RunGui.prototype._addVersionResultRow = function _addVersionResultRow(top, version) { 249 | var self = this, versionRow = {}; 250 | 251 | versionRow.version = version; 252 | 253 | versionRow.holder = Blessed.box({ 254 | parent: self.resultList, 255 | top: top, 256 | height: 1, 257 | left: 0, 258 | width: '100%-1', 259 | mouse: true, 260 | style: { 261 | bg: '#aaaaaa', 262 | fg: 'black' 263 | } 264 | }); 265 | 266 | versionRow.name = Blessed.text({ 267 | parent: versionRow.holder, 268 | left: 0, 269 | content: 'v' + version, 270 | style: { 271 | bg: '#aaaaaa', 272 | fg: 'black' 273 | } 274 | }); 275 | 276 | versionRow.status = Blessed.text({ 277 | parent: versionRow.holder, 278 | height: 1, 279 | right: 0, 280 | content: 'Pending', 281 | style: { 282 | bg: '#aaaaaa', 283 | fg: 'black' 284 | } 285 | }); 286 | 287 | versionRow.holder.on('click', function () { 288 | self.emit('versionClick', version); 289 | }); 290 | 291 | self.versionRows.push(versionRow); 292 | 293 | self.emit('updated'); 294 | 295 | }; 296 | 297 | RunGui.prototype._startTest = function _startTest(index, version) { 298 | var self = this, testBox = self.testBoxes[index], versionRow; 299 | 300 | testBox.title.setContent('v' + version); 301 | testBox.content.setContent(''); 302 | 303 | versionRow = self._getVersionRow(version); 304 | 305 | versionRow.status.style.bg = 'yellow'; 306 | versionRow.holder.style.bg = 'yellow'; 307 | versionRow.name.style.bg = 'yellow'; 308 | versionRow.status.setContent('Running'); 309 | 310 | self.emit('updated'); 311 | }; 312 | 313 | RunGui.prototype._writeTest = function _writeTest(index, data) { 314 | var self = this, testBox = self.testBoxes[index]; 315 | 316 | testBox.content.content += data; 317 | testBox.content.scroll(100); // Some random amount to scroll by. 318 | 319 | self.emit('updated'); 320 | }; 321 | 322 | RunGui.prototype._finishTest = function _finishTest(index, version, result) { 323 | var self = this, testBox = self.testBoxes[index], versionRow; 324 | 325 | testBox.content.setContent(''); 326 | testBox.title.setContent(''); 327 | 328 | versionRow = self._getVersionRow(version); 329 | 330 | versionRow.status.style.bg = result ? 'green' : 'red'; 331 | versionRow.holder.style.bg = result ? 'green' : 'red'; 332 | versionRow.name.style.bg = result ? 'green' : 'red'; 333 | versionRow.status.setContent(result ? 'Passed' : 'Failed'); 334 | 335 | self.emit('updated'); 336 | }; 337 | 338 | RunGui.prototype._showResults = function _showResults(results) { 339 | var self = this, passed = [], failed = []; 340 | 341 | results.forEach(function (result) { 342 | result.passed ? passed.push(result) : failed.push(result); 343 | }); 344 | 345 | self.showResultScreenOverlay = Blessed.box({ 346 | parent: self.Body, 347 | width: '100%', 348 | height: '100%', 349 | top: 0, 350 | left: 0 351 | }); 352 | 353 | self.showResultBox = Blessed.box({ 354 | parent: self.Body, 355 | height: 9, 356 | width: '80%', 357 | top: 'center', 358 | left: 'center', 359 | border: { 360 | type: 'line' 361 | } 362 | }); 363 | 364 | Blessed.box({ 365 | parent: self.showResultBox, 366 | left: 'center', 367 | width: 'shrink', 368 | height: 1, 369 | top: 0, 370 | content: 'Results', 371 | underline: true 372 | }); 373 | 374 | Blessed.box({ 375 | parent: self.showResultBox, 376 | top: 2, 377 | left: 'center', 378 | width: 'shrink', 379 | height: 1, 380 | tags: true, 381 | content: '{green-bg}{black-fg} ' + passed.length + ' Passed {/black-fg}{/green-bg} | {red-bg}{black-fg} ' + failed.length + ' Failed {/black-fg}{/red-bg}', 382 | }); 383 | 384 | if (failed.length) { 385 | Blessed.box({ 386 | parent: self.showResultBox, 387 | left: 'center', 388 | width: '100%-2', 389 | top: 3, 390 | height: 1, 391 | content: 'Failed Versions: ' + failed.map(function (r) { 392 | return 'v' + r.version; 393 | }).join(', ') 394 | }); 395 | } 396 | 397 | Blessed.box({ 398 | parent: self.showResultBox, 399 | width: '100%-2', 400 | height: 1, 401 | top: 5, 402 | content: 'Press R to review' 403 | }); 404 | 405 | Blessed.box({ 406 | parent: self.showResultBox, 407 | width: '100%-2', 408 | height: 1, 409 | top: 6, 410 | content: 'Press Enter or Q to exit' 411 | }); 412 | 413 | self.emit('updated'); 414 | 415 | new Promise(function (resolve) { 416 | self.Screen.screen.onceKey(['r'], function () { 417 | self.showResultScreenOverlay.destroy(); 418 | self.showResultBox.destroy(); 419 | 420 | self.reviewTitle = Blessed.text({ 421 | parent: self.Body, 422 | top: 0, 423 | left: 0, 424 | width: '100%-' + self.resultListSize, 425 | inverse: true 426 | }); 427 | 428 | self.reviewContent = Blessed.box({ 429 | parent: self.Body, 430 | top: 1, 431 | left: 0, 432 | height: '100%-1', 433 | width: '100%-' + self.resultListSize, 434 | scrollable: true, 435 | mouse: true, 436 | scrollbar: { 437 | inverse: true 438 | } 439 | }); 440 | 441 | self.emit('updated'); 442 | }); 443 | 444 | self.Screen.screen.onceKey(['enter', 'q'], function () { 445 | resolve(); 446 | }); 447 | }).then(function () { 448 | self.emit('finished'); 449 | }).catch(function (err) { 450 | self.emit('errored', err); 451 | }); 452 | }; 453 | 454 | RunGui.prototype._reviewResult = function _reviewResult(version, content) { 455 | var self = this; 456 | 457 | self.Body.setContent(''); 458 | self.reviewTitle.setContent('v' + version); 459 | self.reviewContent.setContent(content); 460 | self.reviewContent.setScroll(0); 461 | 462 | self.emit('updated'); 463 | }; 464 | 465 | RunGui.prototype._getVersionRow = function _getVersionRow(version) { 466 | return this.versionRows.filter(function (r) { 467 | return r.version == version; 468 | })[0]; 469 | }; 470 | 471 | RunGui.prototype.destroy = function deinit() { 472 | return this.Screen.destroy(); 473 | }; 474 | 475 | module.exports = RunGui; 476 | -------------------------------------------------------------------------------- /lib/display/RunJson.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, EventEmitter, extend, pad; 7 | 8 | Promise = require('bluebird'); 9 | EventEmitter = require('events'); 10 | Util = require('util'); 11 | pad = require('pad'); 12 | extend = require('extend'); 13 | 14 | Util.inherits(RunJson, EventEmitter); 15 | 16 | function RunJson(name) { 17 | console.error(name + ' - Node Docker Test'); 18 | } 19 | 20 | RunJson.prototype.started = function started() { 21 | console.error('Starting tests'); 22 | }; 23 | 24 | RunJson.prototype.finished = function finished(results) { 25 | console.error('Finished Tests'); 26 | 27 | var output = {}; 28 | 29 | results.forEach(function (result) { 30 | output[result.version] = { 31 | passed: result.passed, 32 | data: result.data 33 | }; 34 | }); 35 | 36 | console.log(JSON.stringify(output)); 37 | }; 38 | 39 | RunJson.prototype.testStarted = function testStarted(test) { 40 | console.error(test.version + ' started'); 41 | }; 42 | 43 | RunJson.prototype.testData = function testData(test) { 44 | }; 45 | 46 | RunJson.prototype.testFinished = function testFinished(test) { 47 | console.error(test.version + ' finished'); 48 | }; 49 | 50 | RunJson.prototype.runnerStarted = function runnerStarted(runner) { 51 | }; 52 | 53 | RunJson.prototype.runnerFinished = function runnerFinished(runner) { 54 | }; 55 | 56 | RunJson.prototype.showError = function showError(error) { 57 | }; 58 | 59 | RunJson.prototype.initialize = function (concurrency) { 60 | }; 61 | 62 | RunJson.prototype.destroy = function () { 63 | }; 64 | 65 | module.exports = RunJson; 66 | -------------------------------------------------------------------------------- /lib/utils/Commands.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Commands = {}; 7 | 8 | module.exports = Commands; 9 | 10 | Commands.setup = function (versions, inputSetupCommands, packageManager, yarn) { 11 | if (!Array.isArray(inputSetupCommands)) { 12 | inputSetupCommands = [inputSetupCommands]; 13 | } 14 | 15 | var setupCommands = ['echo "Starting"']; 16 | 17 | switch (packageManager) { 18 | case 'apt-get': 19 | setupCommands = setupCommands.concat([ 20 | 'apt-get update', 21 | 'apt-get upgrade -y', 22 | 'apt-get install -y git rsync wget' 23 | ]); 24 | break; 25 | case 'yum': 26 | setupCommands = setupCommands.concat([ 27 | 'yum update -y', 28 | 'yum clean all', 29 | 'yum install -y git rsync wget' 30 | ]); 31 | break; 32 | default: 33 | throw new Error("Invalid Package Manager Found: " + packageManager); 34 | } 35 | 36 | setupCommands = setupCommands.concat([ 37 | '(mkdir /test || true)', 38 | 'wget https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh -O install.sh', 39 | 'NVM_DIR=\'/nvm\' bash install.sh', 40 | 'source /nvm/nvm.sh' 41 | ]); 42 | 43 | setupCommands = setupCommands 44 | .concat(versions.map(function (v) { 45 | if (yarn) { 46 | return 'nvm install ' + v + ' && npm install -g yarn'; 47 | } 48 | return 'nvm install ' + v; 49 | })) 50 | .concat(inputSetupCommands); 51 | 52 | return setupCommands.join(' && '); 53 | }; 54 | 55 | Commands.test = function (version, inputCommands, yarn) { 56 | if (!Array.isArray(inputCommands)) { 57 | inputCommands = [inputCommands]; 58 | } 59 | 60 | var commands = [ 61 | 'source /nvm/nvm.sh', 62 | 'nvm install ' + version, 63 | 'nvm use ' + version, 64 | 'rsync -aAXx --delete --exclude .git --exclude node_modules /test-src/ /test/', 65 | 'cd /test' 66 | ]; 67 | 68 | if (yarn) commands.push('yarn'); 69 | else commands.push('npm install'); 70 | 71 | commands = commands.concat(inputCommands); 72 | 73 | return commands.join(' && '); 74 | }; 75 | -------------------------------------------------------------------------------- /lib/utils/Config.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var extend, os, pick; 7 | 8 | module.exports = Config; 9 | 10 | extend = require('extend'); 11 | os = require('os'); 12 | pick = require('lodash.pick'); 13 | 14 | 15 | function Config() { 16 | extend(this, { 17 | simple: false, 18 | json: false, 19 | setup: false, 20 | reset: false, 21 | concurrency: os.cpus().length - 1, 22 | commands: ['npm test'], 23 | 'setup-commands': [], 24 | versions: ['major'], 25 | 'base-image': 'debian:stable', 26 | 'package-manager': "apt-get", 27 | yarn: false 28 | }); 29 | } 30 | 31 | Config.parse = function parse() { 32 | return (new Config()).parseJson().parseArgs(); 33 | }; 34 | 35 | Config.prototype.parseArgs = function parseArgs() { 36 | var args = require('yargs') 37 | .usage('Usage: $0 [options]') 38 | .example('$0 -c 3 -v "major" -v "minor | eq 4"', 'Test against all major versions and minor versions of 4. Run 3 concurrently') 39 | .option('versions', { 40 | alias: 'v', 41 | describe: 'Which versions to run.', 42 | array: true, 43 | string: true 44 | }) 45 | .option('concurrency', { 46 | alias: 'c', 47 | describe: 'Number of concurrent tests to run.', 48 | type: 'number' 49 | }) 50 | .option('commands', { 51 | alias: 'x', 52 | describe: 'The commands to run for test.', 53 | type: 'array' 54 | }) 55 | .option('simple', { 56 | alias: 'q', 57 | describe: 'Run in simple mode. Runs the tests against all versions with very little output. Exit code is equal to the number of failed tests.', 58 | type: 'boolean', 59 | default: undefined 60 | }) 61 | .option('json', { 62 | alias: 'j', 63 | describe: 'Run in json mode. Run the tests against all version and outputs the results to STD in json format.', 64 | type: 'boolean', 65 | default: undefined 66 | }) 67 | .option('setup', { 68 | describe: 'Run the setup.', 69 | type: 'boolean', 70 | default: undefined 71 | }) 72 | .option('reset', { 73 | alias: 'r', 74 | describe: 'When running setup, remove the previous image instead of re-using it.', 75 | type: 'version' 76 | }) 77 | .option('setup-commands', { 78 | alias: 's', 79 | describe: 'Extra commands to run during setup.', 80 | type: 'array' 81 | }) 82 | .option('base-image', { 83 | alias: 'b', 84 | describe: 'Base image to build the testing image from. Should be a debian based image (debian, ubuntu, etc).', 85 | type: 'string' 86 | }) 87 | .option('package-manager', { 88 | alias: 'p', 89 | describe: 'Which package manager to use. Valid ./option are "apt-get" or "yum".', 90 | type: 'string' 91 | }) 92 | .option('yarn', { 93 | alias: 'y', 94 | describe: "Use yarn instead of npm to install packages.", 95 | type: 'boolean', 96 | default: undefined 97 | }) 98 | .help('h') 99 | .alias('h', 'help') 100 | .epilog('View the full documentation at https://github.com/mrkmg/node-docker-test') 101 | .argv; 102 | 103 | return extend(this, pick(args, [ 104 | 'base-image', 105 | 'simple', 106 | 'setup', 107 | 'reset', 108 | 'name', 109 | 'concurrency', 110 | 'commands', 111 | 'setup-commands', 112 | 'versions', 113 | 'package-manager', 114 | 'yarn', 115 | 'json' 116 | ])) 117 | }; 118 | 119 | Config.prototype.parseJson = function parseJson() { 120 | var packageJson, ndtConfig; 121 | 122 | try { 123 | packageJson = require(process.cwd() + '/package.json'); 124 | } 125 | catch (e) { 126 | throw new Error('Missing the "package.json" file. Is this a NPM project?'); 127 | } 128 | 129 | try { 130 | ndtConfig = packageJson.config.ndt; 131 | } 132 | catch (e) { 133 | ndtConfig = {}; 134 | } 135 | 136 | ndtConfig.name = packageJson.name; 137 | 138 | return extend(this, ndtConfig); 139 | }; 140 | -------------------------------------------------------------------------------- /lib/utils/Docker.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise = require('bluebird'); 7 | var ChildProcess = require('child_process'); 8 | 9 | var Docker = {}; 10 | 11 | module.exports = Docker; 12 | 13 | Docker.containerExists = function containerExists(name) { 14 | var dockerProcess = ChildProcess.spawn('docker', ['inspect', name]); 15 | 16 | var promise = new Promise(function (resolve) { 17 | dockerProcess.on('close', function (result_code) { 18 | result_code === 0 ? resolve(true) : resolve(false); 19 | }); 20 | }); 21 | 22 | promise.process = dockerProcess; 23 | return promise; 24 | }; 25 | 26 | Docker.getLastContainerSha = function getLastContainerSha() { 27 | var dockerProcess = ChildProcess.spawn('docker', ['ps', '-l', '-q']); 28 | var data = ''; 29 | dockerProcess.stdout.on('data', function (d) { 30 | data += d.toString().trim(); 31 | }); 32 | 33 | var promise = new Promise(function (resolve, reject) { 34 | dockerProcess.on('close', function (result_code) { 35 | result_code === 0 ? resolve(data) : reject(new Error("Failed to get last SHA")); 36 | }); 37 | }); 38 | 39 | promise.process = dockerProcess; 40 | return promise; 41 | }; 42 | 43 | Docker.removeContainer = function removeContainer(name) { 44 | var dockerProcess = ChildProcess.spawn('docker', ['rmi', '--force', name]); 45 | 46 | var promise = new Promise(function (resolve, reject) { 47 | dockerProcess.on('close', function (result_code) { 48 | result_code === 0 ? resolve() : reject(new Error("Failed to remove container")); 49 | }); 50 | }); 51 | 52 | promise.process = dockerProcess; 53 | return promise; 54 | }; 55 | 56 | Docker.runContainer = function runContainer(name, cmd, outputCallback) { 57 | var args = ['run', '-v', 'ndt-npm:/root/.npm', '-i', name, '/bin/bash', '-c', cmd]; 58 | var dockerProcess = ChildProcess.spawn('docker', args); 59 | var kill; 60 | 61 | dockerProcess.stdout.on('data', outputCallback); 62 | dockerProcess.stderr.on('data', outputCallback); 63 | 64 | process.on('exit', kill = function () { 65 | dockerProcess.kill('SIGKILL'); 66 | }); 67 | 68 | var promise = new Promise(function (resolve, reject) { 69 | dockerProcess.on('close', function (result_code) { 70 | process.removeListener('exit', kill); 71 | result_code === 0 ? resolve() : reject(new Error("Failed to run container")); 72 | }); 73 | }); 74 | 75 | promise.process = dockerProcess; 76 | return promise; 77 | }; 78 | 79 | Docker.runContainerWithCopy = function runContainerWithCopy(name, cmd, outputCallback) { 80 | var args = ['run', '-v', 'ndt-npm:/root/.npm', '-i', '--rm', '-v', process.cwd() + ':/test-src/:ro', name, '/bin/bash', '-c', cmd]; 81 | var dockerProcess = ChildProcess.spawn('docker', args); 82 | var kill; 83 | 84 | dockerProcess.stdout.on('data', outputCallback); 85 | dockerProcess.stderr.on('data', outputCallback); 86 | 87 | process.on('exit', kill = function () { 88 | dockerProcess.kill('SIGKILL'); 89 | }); 90 | 91 | var promise = new Promise(function (resolve, reject) { 92 | dockerProcess.on('close', function (result_code) { 93 | process.removeListener('exit', kill); 94 | result_code === 0 ? resolve() : reject(new Error("Failed to run container")); 95 | }); 96 | }); 97 | 98 | promise.process = dockerProcess; 99 | return promise; 100 | }; 101 | 102 | Docker.commitContainer = function commitContainer(sha, name) { 103 | 104 | var dockerProcess = ChildProcess.spawn('docker', ['commit', sha, name]); 105 | 106 | var promise = new Promise(function (resolve, reject) { 107 | dockerProcess.on('close', function (result_code) { 108 | result_code === 0 ? resolve() : reject("Failed to commit container"); 109 | }); 110 | }); 111 | 112 | promise.process = dockerProcess; 113 | return promise; 114 | }; 115 | 116 | Docker.makeNew = function makeNew(name, cmd, baseImage, outputCallback) { 117 | return Promise.try(function () { 118 | return Docker.containerExists(name); 119 | }).then(function (exists) { 120 | return exists ? name : baseImage; 121 | }).then(function (base) { 122 | return Docker.runContainer(base, cmd, outputCallback); 123 | }).then(function () { 124 | return Docker._wait(); 125 | }).then(function () { 126 | return Docker.getLastContainerSha(); 127 | }).then(function (sha) { 128 | outputCallback("\n\nCompleted Setup.\nSaving Container. Please Wait"); 129 | return Docker.commitContainer(sha, name); 130 | }); 131 | }; 132 | 133 | Docker._wait = function () { 134 | return new Promise(function (resolve) { 135 | setTimeout(resolve, 2000); 136 | }); 137 | }; 138 | -------------------------------------------------------------------------------- /lib/utils/VersionParser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise, Semver, SemverLoose; 7 | 8 | Promise = require('bluebird'); 9 | Semver = require('semver'); 10 | SemverLoose = require('semver-loose'); 11 | 12 | module.exports = VersionParser; 13 | 14 | VersionParser.NODEJS_MIRROR = "https://nodejs.org/dist/"; 15 | VersionParser.versionListCache = undefined; 16 | 17 | function VersionParser(version) { 18 | var testVersion, filters; 19 | 20 | if (Array.isArray(version)) { 21 | return Promise 22 | .map(version, VersionParser) 23 | .then(flattenArrays) 24 | .then(Semver.sort) 25 | .then(dedupArray) 26 | .then(removeInvalid); 27 | } 28 | else { 29 | filters = version.split('|').map(trim); 30 | testVersion = filters.shift(); 31 | 32 | 33 | return Promise 34 | .resolve(testVersion) 35 | .then(resolveKeyword) 36 | .map(resolveVersion) 37 | .filter(function (version) { 38 | return Promise.reduce(filters, function (lastResult, filter) { 39 | return Promise.try(function () { 40 | return filterVersion(version, filter); 41 | }).then(function (result) { 42 | return result && lastResult; 43 | }) 44 | }, true); 45 | }) 46 | .then(Semver.sort); 47 | } 48 | } 49 | 50 | function resolveKeyword(version) { 51 | switch (version) { 52 | case 'major': 53 | return major(); 54 | case 'minor': 55 | return minor(); 56 | case 'patch': 57 | return patch(); 58 | case 'legacy': 59 | return legacy(); 60 | case 'all': 61 | return all(); 62 | default: 63 | return [version] 64 | } 65 | } 66 | 67 | function filterVersion(version, filterString) { 68 | var t, type, test; 69 | 70 | t = filterString.split(':').map(trim); 71 | 72 | type = t[0]; 73 | test = t[1]; 74 | 75 | switch (type) { 76 | case 'gt': 77 | return Promise 78 | .resolve(test) 79 | .then(resolveGreatestVersion) 80 | .then(function (t) { 81 | return Semver.gt(version, t); 82 | }); 83 | case 'gte': 84 | return Promise 85 | .resolve(test) 86 | .then(resolveLeastVersion) 87 | .then(function (t) { 88 | return Semver.gte(version, t); 89 | }); 90 | case 'lt': 91 | return Promise 92 | .resolve(test) 93 | .then(resolveLeastVersion) 94 | .then(function (t) { 95 | return Semver.lt(version, t); 96 | }); 97 | case 'lte': 98 | return Promise 99 | .resolve(test) 100 | .then(resolveGreatestVersion) 101 | .then(function (t) { 102 | return Semver.lte(version, t); 103 | }); 104 | case 'eq': 105 | return Promise.resolve(SemverLoose.match(test, version)); 106 | case 'neq': 107 | return Promise.resolve(!SemverLoose.match(test, version)); 108 | case 'lts': 109 | return Promise.resolve(version).then(isLts); 110 | default: 111 | throw new Error('Unknown filter type: ' + type); 112 | } 113 | } 114 | 115 | function resolveVersion(version) { 116 | return Promise 117 | .try(getVersionList) 118 | .map(extractVersion) 119 | .reduce(function (bestVersion, testVersion) { 120 | return SemverLoose.match(version, testVersion) && Semver.gt(testVersion, bestVersion) 121 | ? testVersion 122 | : bestVersion; 123 | }, '0.0.0'); 124 | } 125 | 126 | function resolveGreatestVersion(version) { 127 | return Promise 128 | .try(getVersionList) 129 | .map(extractVersion) 130 | .reduce(function (bestVersion, testVersion) { 131 | return SemverLoose.match(version, testVersion) && Semver.gt(testVersion, bestVersion) 132 | ? testVersion 133 | : bestVersion; 134 | }, '0.0.0'); 135 | } 136 | 137 | function resolveLeastVersion(version) { 138 | return Promise 139 | .try(getVersionList) 140 | .map(extractVersion) 141 | .reduce(function (bestVersion, testVersion) { 142 | return SemverLoose.match(version, testVersion) && Semver.lt(testVersion, bestVersion) 143 | ? testVersion 144 | : bestVersion; 145 | }, '9999999.9999999.9999999'); 146 | } 147 | 148 | function major() { 149 | return Promise 150 | .try(notLegacy) 151 | .then(function (remoteVersions) { 152 | var versions = [], majorVersion; 153 | remoteVersions.forEach(function (version) { 154 | majorVersion = Semver.major(version).toString() + '.x'; 155 | if (versions.indexOf(majorVersion) === -1) { 156 | versions.push(majorVersion); 157 | } 158 | }); 159 | 160 | return versions; 161 | }); 162 | } 163 | 164 | function minor() { 165 | return Promise 166 | .try(notLegacy) 167 | .then(function (remoteVersions) { 168 | var versions = [], minorVersion; 169 | remoteVersions.forEach(function (version) { 170 | minorVersion = Semver.major(version) + '.' + Semver.minor(version); 171 | if (versions.indexOf(minorVersion) === -1) { 172 | versions.push(minorVersion); 173 | } 174 | }); 175 | 176 | return versions; 177 | }); 178 | } 179 | 180 | function patch() { 181 | return Promise 182 | .try(notLegacy) 183 | .then(function (remoteVersions) { 184 | var versions = [], patchVersion; 185 | remoteVersions.forEach(function (version) { 186 | patchVersion = Semver.major(version) + '.' + Semver.minor(version) + '.' + Semver.patch(version); 187 | if (versions.indexOf(patchVersion) === -1) { 188 | versions.push(patchVersion); 189 | } 190 | }); 191 | 192 | return versions; 193 | }); 194 | } 195 | 196 | function legacy() { 197 | return Promise 198 | .try(getVersionList) 199 | .map(extractVersion) 200 | .filter(isLegacy); 201 | } 202 | 203 | function notLegacy() { 204 | return Promise 205 | .try(getVersionList) 206 | .map(extractVersion) 207 | .filter(isNotLegacy) 208 | } 209 | 210 | function all() { 211 | return Promise 212 | .try(getVersionList) 213 | .map(extractVersion); 214 | } 215 | 216 | function getVersionList() { 217 | if (VersionParser.versionListCache) { 218 | return Promise.resolve(VersionParser.versionListCache); 219 | } 220 | else { 221 | return new Promise(function (resolve, reject) { 222 | var lib, body; 223 | lib = VersionParser.NODEJS_MIRROR.substr(0, 5) == 'https' ? require('https') : require('http'); 224 | request = lib.get(VersionParser.NODEJS_MIRROR + '/index.json', function (response) { 225 | if (response < 200 || response > 299) { 226 | return reject('Failed to load nodejs version list from: ' + VersionParser.NODEJS_MIRROR); 227 | } 228 | 229 | body = []; 230 | 231 | response.on('data', function (data) { 232 | body.push(data.toString()); 233 | }); 234 | response.on('end', function () { 235 | try { 236 | versionListCache = JSON.parse(body.join('')); 237 | } catch (e) { 238 | reject(e); 239 | } 240 | resolve(versionListCache); 241 | }); 242 | }); 243 | 244 | request.on('error', reject); 245 | }); 246 | } 247 | } 248 | 249 | function isLegacy(version) { 250 | return Semver.major(version) == 0; 251 | } 252 | 253 | function isNotLegacy(version) { 254 | return !isLegacy(version); 255 | } 256 | 257 | function isLts(version) { 258 | return Promise 259 | .try(getVersionList) 260 | .reduce(function (passed, remoteVersion) { 261 | return passed || (Semver.eq(version, remoteVersion.version) && !!remoteVersion.lts); 262 | }, false); 263 | } 264 | 265 | function extractVersion(version) { 266 | return Semver.clean(version.version); 267 | } 268 | 269 | function trim(input) { 270 | return input.toString().trim(); 271 | } 272 | 273 | function flattenArrays(arrays) { 274 | return [].concat.apply([], arrays); 275 | } 276 | 277 | function dedupArray(array) { 278 | return array.filter(function (i, p, s) { 279 | return s.indexOf(i) == p; 280 | }); 281 | } 282 | 283 | function removeInvalid(array) { 284 | return array.filter(function (version) { 285 | return version != '0.0.0'; 286 | }) 287 | } 288 | -------------------------------------------------------------------------------- /ndt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | Written by Kevin Gravier 4 | Part of the node-docker-test project. 5 | MIT Licence 6 | */ 7 | 8 | require('./cli.js'); 9 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-docker-test", 3 | "version": "1.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.0.9", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", 10 | "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", 11 | "dev": true 12 | }, 13 | "align-text": { 14 | "version": "0.1.4", 15 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 16 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 17 | "dev": true, 18 | "requires": { 19 | "kind-of": "3.2.2", 20 | "longest": "1.0.1", 21 | "repeat-string": "1.6.1" 22 | } 23 | }, 24 | "amdefine": { 25 | "version": "1.0.1", 26 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 27 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 28 | "dev": true 29 | }, 30 | "ansi-regex": { 31 | "version": "2.1.1", 32 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 33 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 34 | }, 35 | "argparse": { 36 | "version": "1.0.9", 37 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 38 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 39 | "dev": true, 40 | "requires": { 41 | "sprintf-js": "1.0.3" 42 | } 43 | }, 44 | "assertion-error": { 45 | "version": "1.0.2", 46 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", 47 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", 48 | "dev": true 49 | }, 50 | "async": { 51 | "version": "1.5.2", 52 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 53 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 54 | "dev": true 55 | }, 56 | "balanced-match": { 57 | "version": "1.0.0", 58 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 59 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 60 | "dev": true 61 | }, 62 | "blessed": { 63 | "version": "0.1.81", 64 | "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", 65 | "integrity": "sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk=" 66 | }, 67 | "bluebird": { 68 | "version": "3.5.0", 69 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", 70 | "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" 71 | }, 72 | "brace-expansion": { 73 | "version": "1.1.8", 74 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 75 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 76 | "dev": true, 77 | "requires": { 78 | "balanced-match": "1.0.0", 79 | "concat-map": "0.0.1" 80 | } 81 | }, 82 | "browser-stdout": { 83 | "version": "1.3.0", 84 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 85 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 86 | "dev": true 87 | }, 88 | "builtin-modules": { 89 | "version": "1.1.1", 90 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 91 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" 92 | }, 93 | "camelcase": { 94 | "version": "4.1.0", 95 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 96 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" 97 | }, 98 | "center-align": { 99 | "version": "0.1.3", 100 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 101 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 102 | "dev": true, 103 | "optional": true, 104 | "requires": { 105 | "align-text": "0.1.4", 106 | "lazy-cache": "1.0.4" 107 | } 108 | }, 109 | "chai": { 110 | "version": "4.1.2", 111 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 112 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 113 | "dev": true, 114 | "requires": { 115 | "assertion-error": "1.0.2", 116 | "check-error": "1.0.2", 117 | "deep-eql": "3.0.1", 118 | "get-func-name": "2.0.0", 119 | "pathval": "1.1.0", 120 | "type-detect": "4.0.3" 121 | } 122 | }, 123 | "chai-as-promised": { 124 | "version": "7.1.1", 125 | "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", 126 | "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", 127 | "dev": true, 128 | "requires": { 129 | "check-error": "1.0.2" 130 | } 131 | }, 132 | "check-error": { 133 | "version": "1.0.2", 134 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 135 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 136 | "dev": true 137 | }, 138 | "cliui": { 139 | "version": "3.2.0", 140 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 141 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 142 | "requires": { 143 | "string-width": "1.0.2", 144 | "strip-ansi": "3.0.1", 145 | "wrap-ansi": "2.1.0" 146 | }, 147 | "dependencies": { 148 | "string-width": { 149 | "version": "1.0.2", 150 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 151 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 152 | "requires": { 153 | "code-point-at": "1.1.0", 154 | "is-fullwidth-code-point": "1.0.0", 155 | "strip-ansi": "3.0.1" 156 | } 157 | } 158 | } 159 | }, 160 | "code-point-at": { 161 | "version": "1.1.0", 162 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 163 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 164 | }, 165 | "commander": { 166 | "version": "2.9.0", 167 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 168 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 169 | "dev": true, 170 | "requires": { 171 | "graceful-readlink": "1.0.1" 172 | } 173 | }, 174 | "concat-map": { 175 | "version": "0.0.1", 176 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 177 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 178 | "dev": true 179 | }, 180 | "cross-spawn": { 181 | "version": "5.1.0", 182 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 183 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 184 | "requires": { 185 | "lru-cache": "4.1.1", 186 | "shebang-command": "1.2.0", 187 | "which": "1.3.0" 188 | } 189 | }, 190 | "debug": { 191 | "version": "2.6.8", 192 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", 193 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", 194 | "dev": true, 195 | "requires": { 196 | "ms": "2.0.0" 197 | } 198 | }, 199 | "decamelize": { 200 | "version": "1.2.0", 201 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 202 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 203 | }, 204 | "deep-eql": { 205 | "version": "3.0.1", 206 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 207 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 208 | "dev": true, 209 | "requires": { 210 | "type-detect": "4.0.3" 211 | } 212 | }, 213 | "deep-is": { 214 | "version": "0.1.3", 215 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 216 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 217 | "dev": true 218 | }, 219 | "diff": { 220 | "version": "3.2.0", 221 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", 222 | "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", 223 | "dev": true 224 | }, 225 | "error-ex": { 226 | "version": "1.3.1", 227 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", 228 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", 229 | "requires": { 230 | "is-arrayish": "0.2.1" 231 | } 232 | }, 233 | "escape-string-regexp": { 234 | "version": "1.0.5", 235 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 236 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 237 | "dev": true 238 | }, 239 | "escodegen": { 240 | "version": "1.8.1", 241 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", 242 | "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", 243 | "dev": true, 244 | "requires": { 245 | "esprima": "2.7.3", 246 | "estraverse": "1.9.3", 247 | "esutils": "2.0.2", 248 | "optionator": "0.8.2", 249 | "source-map": "0.2.0" 250 | } 251 | }, 252 | "esprima": { 253 | "version": "2.7.3", 254 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", 255 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 256 | "dev": true 257 | }, 258 | "estraverse": { 259 | "version": "1.9.3", 260 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", 261 | "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", 262 | "dev": true 263 | }, 264 | "esutils": { 265 | "version": "2.0.2", 266 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 267 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 268 | "dev": true 269 | }, 270 | "execa": { 271 | "version": "0.7.0", 272 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 273 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 274 | "requires": { 275 | "cross-spawn": "5.1.0", 276 | "get-stream": "3.0.0", 277 | "is-stream": "1.1.0", 278 | "npm-run-path": "2.0.2", 279 | "p-finally": "1.0.0", 280 | "signal-exit": "3.0.2", 281 | "strip-eof": "1.0.0" 282 | } 283 | }, 284 | "extend": { 285 | "version": "3.0.1", 286 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 287 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" 288 | }, 289 | "fast-levenshtein": { 290 | "version": "2.0.6", 291 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 292 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 293 | "dev": true 294 | }, 295 | "find-up": { 296 | "version": "2.1.0", 297 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 298 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 299 | "requires": { 300 | "locate-path": "2.0.0" 301 | } 302 | }, 303 | "formatio": { 304 | "version": "1.2.0", 305 | "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", 306 | "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", 307 | "dev": true, 308 | "requires": { 309 | "samsam": "1.2.1" 310 | } 311 | }, 312 | "fs.realpath": { 313 | "version": "1.0.0", 314 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 315 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 316 | "dev": true 317 | }, 318 | "get-caller-file": { 319 | "version": "1.0.2", 320 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", 321 | "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" 322 | }, 323 | "get-func-name": { 324 | "version": "2.0.0", 325 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 326 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 327 | "dev": true 328 | }, 329 | "get-stream": { 330 | "version": "3.0.0", 331 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 332 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 333 | }, 334 | "glob": { 335 | "version": "5.0.15", 336 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 337 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 338 | "dev": true, 339 | "requires": { 340 | "inflight": "1.0.6", 341 | "inherits": "2.0.3", 342 | "minimatch": "3.0.4", 343 | "once": "1.4.0", 344 | "path-is-absolute": "1.0.1" 345 | } 346 | }, 347 | "graceful-fs": { 348 | "version": "4.1.11", 349 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 350 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 351 | }, 352 | "graceful-readlink": { 353 | "version": "1.0.1", 354 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 355 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 356 | "dev": true 357 | }, 358 | "growl": { 359 | "version": "1.9.2", 360 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 361 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 362 | "dev": true 363 | }, 364 | "handlebars": { 365 | "version": "4.0.10", 366 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", 367 | "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", 368 | "dev": true, 369 | "requires": { 370 | "async": "1.5.2", 371 | "optimist": "0.6.1", 372 | "source-map": "0.4.4", 373 | "uglify-js": "2.8.29" 374 | }, 375 | "dependencies": { 376 | "source-map": { 377 | "version": "0.4.4", 378 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", 379 | "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", 380 | "dev": true, 381 | "requires": { 382 | "amdefine": "1.0.1" 383 | } 384 | } 385 | } 386 | }, 387 | "has-flag": { 388 | "version": "1.0.0", 389 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 390 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 391 | "dev": true 392 | }, 393 | "he": { 394 | "version": "1.1.1", 395 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 396 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 397 | "dev": true 398 | }, 399 | "hosted-git-info": { 400 | "version": "2.5.0", 401 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", 402 | "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" 403 | }, 404 | "inflight": { 405 | "version": "1.0.6", 406 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 407 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 408 | "dev": true, 409 | "requires": { 410 | "once": "1.4.0", 411 | "wrappy": "1.0.2" 412 | } 413 | }, 414 | "inherits": { 415 | "version": "2.0.3", 416 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 417 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 418 | "dev": true 419 | }, 420 | "invert-kv": { 421 | "version": "1.0.0", 422 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 423 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 424 | }, 425 | "is-arrayish": { 426 | "version": "0.2.1", 427 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 428 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 429 | }, 430 | "is-buffer": { 431 | "version": "1.1.5", 432 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", 433 | "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", 434 | "dev": true 435 | }, 436 | "is-builtin-module": { 437 | "version": "1.0.0", 438 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 439 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 440 | "requires": { 441 | "builtin-modules": "1.1.1" 442 | } 443 | }, 444 | "is-fullwidth-code-point": { 445 | "version": "1.0.0", 446 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 447 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 448 | "requires": { 449 | "number-is-nan": "1.0.1" 450 | } 451 | }, 452 | "is-stream": { 453 | "version": "1.1.0", 454 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 455 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 456 | }, 457 | "isarray": { 458 | "version": "0.0.1", 459 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 460 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 461 | "dev": true 462 | }, 463 | "isexe": { 464 | "version": "2.0.0", 465 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 466 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 467 | }, 468 | "istanbul": { 469 | "version": "0.4.5", 470 | "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", 471 | "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", 472 | "dev": true, 473 | "requires": { 474 | "abbrev": "1.0.9", 475 | "async": "1.5.2", 476 | "escodegen": "1.8.1", 477 | "esprima": "2.7.3", 478 | "glob": "5.0.15", 479 | "handlebars": "4.0.10", 480 | "js-yaml": "3.10.0", 481 | "mkdirp": "0.5.1", 482 | "nopt": "3.0.6", 483 | "once": "1.4.0", 484 | "resolve": "1.1.7", 485 | "supports-color": "3.2.3", 486 | "which": "1.3.0", 487 | "wordwrap": "1.0.0" 488 | } 489 | }, 490 | "js-yaml": { 491 | "version": "3.10.0", 492 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", 493 | "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", 494 | "dev": true, 495 | "requires": { 496 | "argparse": "1.0.9", 497 | "esprima": "4.0.0" 498 | }, 499 | "dependencies": { 500 | "esprima": { 501 | "version": "4.0.0", 502 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", 503 | "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", 504 | "dev": true 505 | } 506 | } 507 | }, 508 | "json3": { 509 | "version": "3.3.2", 510 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", 511 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", 512 | "dev": true 513 | }, 514 | "just-extend": { 515 | "version": "1.1.22", 516 | "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz", 517 | "integrity": "sha1-MzCvdWyralQnAMZLLk5KoGLVL/8=", 518 | "dev": true 519 | }, 520 | "kind-of": { 521 | "version": "3.2.2", 522 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 523 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 524 | "dev": true, 525 | "requires": { 526 | "is-buffer": "1.1.5" 527 | } 528 | }, 529 | "lazy-cache": { 530 | "version": "1.0.4", 531 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 532 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 533 | "dev": true, 534 | "optional": true 535 | }, 536 | "lcid": { 537 | "version": "1.0.0", 538 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 539 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 540 | "requires": { 541 | "invert-kv": "1.0.0" 542 | } 543 | }, 544 | "levn": { 545 | "version": "0.3.0", 546 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 547 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 548 | "dev": true, 549 | "requires": { 550 | "prelude-ls": "1.1.2", 551 | "type-check": "0.3.2" 552 | } 553 | }, 554 | "load-json-file": { 555 | "version": "2.0.0", 556 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", 557 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", 558 | "requires": { 559 | "graceful-fs": "4.1.11", 560 | "parse-json": "2.2.0", 561 | "pify": "2.3.0", 562 | "strip-bom": "3.0.0" 563 | } 564 | }, 565 | "locate-path": { 566 | "version": "2.0.0", 567 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 568 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 569 | "requires": { 570 | "p-locate": "2.0.0", 571 | "path-exists": "3.0.0" 572 | } 573 | }, 574 | "lodash._baseassign": { 575 | "version": "3.2.0", 576 | "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", 577 | "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", 578 | "dev": true, 579 | "requires": { 580 | "lodash._basecopy": "3.0.1", 581 | "lodash.keys": "3.1.2" 582 | } 583 | }, 584 | "lodash._basecopy": { 585 | "version": "3.0.1", 586 | "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", 587 | "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", 588 | "dev": true 589 | }, 590 | "lodash._basecreate": { 591 | "version": "3.0.3", 592 | "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", 593 | "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", 594 | "dev": true 595 | }, 596 | "lodash._getnative": { 597 | "version": "3.9.1", 598 | "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", 599 | "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", 600 | "dev": true 601 | }, 602 | "lodash._isiterateecall": { 603 | "version": "3.0.9", 604 | "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", 605 | "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", 606 | "dev": true 607 | }, 608 | "lodash.create": { 609 | "version": "3.1.1", 610 | "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", 611 | "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", 612 | "dev": true, 613 | "requires": { 614 | "lodash._baseassign": "3.2.0", 615 | "lodash._basecreate": "3.0.3", 616 | "lodash._isiterateecall": "3.0.9" 617 | } 618 | }, 619 | "lodash.get": { 620 | "version": "4.4.2", 621 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 622 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", 623 | "dev": true 624 | }, 625 | "lodash.isarguments": { 626 | "version": "3.1.0", 627 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 628 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", 629 | "dev": true 630 | }, 631 | "lodash.isarray": { 632 | "version": "3.0.4", 633 | "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", 634 | "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", 635 | "dev": true 636 | }, 637 | "lodash.keys": { 638 | "version": "3.1.2", 639 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", 640 | "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", 641 | "dev": true, 642 | "requires": { 643 | "lodash._getnative": "3.9.1", 644 | "lodash.isarguments": "3.1.0", 645 | "lodash.isarray": "3.0.4" 646 | } 647 | }, 648 | "lodash.pick": { 649 | "version": "4.4.0", 650 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", 651 | "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" 652 | }, 653 | "lolex": { 654 | "version": "2.1.2", 655 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.1.2.tgz", 656 | "integrity": "sha1-JpS5U8nqTQE+W4v7qJHJkQJbJik=", 657 | "dev": true 658 | }, 659 | "longest": { 660 | "version": "1.0.1", 661 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 662 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", 663 | "dev": true 664 | }, 665 | "lru-cache": { 666 | "version": "4.1.1", 667 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", 668 | "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", 669 | "requires": { 670 | "pseudomap": "1.0.2", 671 | "yallist": "2.1.2" 672 | } 673 | }, 674 | "mem": { 675 | "version": "1.1.0", 676 | "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", 677 | "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", 678 | "requires": { 679 | "mimic-fn": "1.1.0" 680 | } 681 | }, 682 | "mimic-fn": { 683 | "version": "1.1.0", 684 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", 685 | "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=" 686 | }, 687 | "minimatch": { 688 | "version": "3.0.4", 689 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 690 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 691 | "dev": true, 692 | "requires": { 693 | "brace-expansion": "1.1.8" 694 | } 695 | }, 696 | "minimist": { 697 | "version": "0.0.10", 698 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 699 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", 700 | "dev": true 701 | }, 702 | "mkdirp": { 703 | "version": "0.5.1", 704 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 705 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 706 | "dev": true, 707 | "requires": { 708 | "minimist": "0.0.8" 709 | }, 710 | "dependencies": { 711 | "minimist": { 712 | "version": "0.0.8", 713 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 714 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 715 | "dev": true 716 | } 717 | } 718 | }, 719 | "mocha": { 720 | "version": "3.5.3", 721 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", 722 | "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", 723 | "dev": true, 724 | "requires": { 725 | "browser-stdout": "1.3.0", 726 | "commander": "2.9.0", 727 | "debug": "2.6.8", 728 | "diff": "3.2.0", 729 | "escape-string-regexp": "1.0.5", 730 | "glob": "7.1.1", 731 | "growl": "1.9.2", 732 | "he": "1.1.1", 733 | "json3": "3.3.2", 734 | "lodash.create": "3.1.1", 735 | "mkdirp": "0.5.1", 736 | "supports-color": "3.1.2" 737 | }, 738 | "dependencies": { 739 | "glob": { 740 | "version": "7.1.1", 741 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 742 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 743 | "dev": true, 744 | "requires": { 745 | "fs.realpath": "1.0.0", 746 | "inflight": "1.0.6", 747 | "inherits": "2.0.3", 748 | "minimatch": "3.0.4", 749 | "once": "1.4.0", 750 | "path-is-absolute": "1.0.1" 751 | } 752 | }, 753 | "supports-color": { 754 | "version": "3.1.2", 755 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", 756 | "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", 757 | "dev": true, 758 | "requires": { 759 | "has-flag": "1.0.0" 760 | } 761 | } 762 | } 763 | }, 764 | "ms": { 765 | "version": "2.0.0", 766 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 767 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 768 | "dev": true 769 | }, 770 | "native-promise-only": { 771 | "version": "0.8.1", 772 | "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", 773 | "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", 774 | "dev": true 775 | }, 776 | "nise": { 777 | "version": "1.1.0", 778 | "resolved": "https://registry.npmjs.org/nise/-/nise-1.1.0.tgz", 779 | "integrity": "sha512-lIFidCxB0mJGyq1i33tLRNojtMoYX95EAI7WQEU+/ees0w6hvXZQHZ7WD130Tjeh5+YJAUVLfQ3k/s9EA8jj+w==", 780 | "dev": true, 781 | "requires": { 782 | "formatio": "1.2.0", 783 | "just-extend": "1.1.22", 784 | "lolex": "1.6.0", 785 | "path-to-regexp": "1.7.0", 786 | "text-encoding": "0.6.4" 787 | }, 788 | "dependencies": { 789 | "lolex": { 790 | "version": "1.6.0", 791 | "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", 792 | "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=", 793 | "dev": true 794 | } 795 | } 796 | }, 797 | "nopt": { 798 | "version": "3.0.6", 799 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", 800 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 801 | "dev": true, 802 | "requires": { 803 | "abbrev": "1.0.9" 804 | } 805 | }, 806 | "normalize-package-data": { 807 | "version": "2.4.0", 808 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 809 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 810 | "requires": { 811 | "hosted-git-info": "2.5.0", 812 | "is-builtin-module": "1.0.0", 813 | "semver": "5.4.1", 814 | "validate-npm-package-license": "3.0.1" 815 | } 816 | }, 817 | "npm-run-path": { 818 | "version": "2.0.2", 819 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 820 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 821 | "requires": { 822 | "path-key": "2.0.1" 823 | } 824 | }, 825 | "number-is-nan": { 826 | "version": "1.0.1", 827 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 828 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 829 | }, 830 | "once": { 831 | "version": "1.4.0", 832 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 833 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 834 | "dev": true, 835 | "requires": { 836 | "wrappy": "1.0.2" 837 | } 838 | }, 839 | "optimist": { 840 | "version": "0.6.1", 841 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 842 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 843 | "dev": true, 844 | "requires": { 845 | "minimist": "0.0.10", 846 | "wordwrap": "0.0.3" 847 | }, 848 | "dependencies": { 849 | "wordwrap": { 850 | "version": "0.0.3", 851 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 852 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 853 | "dev": true 854 | } 855 | } 856 | }, 857 | "optionator": { 858 | "version": "0.8.2", 859 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 860 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 861 | "dev": true, 862 | "requires": { 863 | "deep-is": "0.1.3", 864 | "fast-levenshtein": "2.0.6", 865 | "levn": "0.3.0", 866 | "prelude-ls": "1.1.2", 867 | "type-check": "0.3.2", 868 | "wordwrap": "1.0.0" 869 | } 870 | }, 871 | "os-locale": { 872 | "version": "2.1.0", 873 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", 874 | "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", 875 | "requires": { 876 | "execa": "0.7.0", 877 | "lcid": "1.0.0", 878 | "mem": "1.1.0" 879 | } 880 | }, 881 | "os-tmpdir": { 882 | "version": "1.0.2", 883 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 884 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", 885 | "dev": true 886 | }, 887 | "p-finally": { 888 | "version": "1.0.0", 889 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 890 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 891 | }, 892 | "p-limit": { 893 | "version": "1.1.0", 894 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", 895 | "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=" 896 | }, 897 | "p-locate": { 898 | "version": "2.0.0", 899 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 900 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 901 | "requires": { 902 | "p-limit": "1.1.0" 903 | } 904 | }, 905 | "pad": { 906 | "version": "1.1.0", 907 | "resolved": "https://registry.npmjs.org/pad/-/pad-1.1.0.tgz", 908 | "integrity": "sha1-en0YUgDrrDL58S7nVsOh0IezGQs=" 909 | }, 910 | "parse-json": { 911 | "version": "2.2.0", 912 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 913 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 914 | "requires": { 915 | "error-ex": "1.3.1" 916 | } 917 | }, 918 | "path-exists": { 919 | "version": "3.0.0", 920 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 921 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 922 | }, 923 | "path-is-absolute": { 924 | "version": "1.0.1", 925 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 926 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 927 | "dev": true 928 | }, 929 | "path-key": { 930 | "version": "2.0.1", 931 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 932 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 933 | }, 934 | "path-to-regexp": { 935 | "version": "1.7.0", 936 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", 937 | "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", 938 | "dev": true, 939 | "requires": { 940 | "isarray": "0.0.1" 941 | } 942 | }, 943 | "path-type": { 944 | "version": "2.0.0", 945 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", 946 | "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", 947 | "requires": { 948 | "pify": "2.3.0" 949 | } 950 | }, 951 | "pathval": { 952 | "version": "1.1.0", 953 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 954 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 955 | "dev": true 956 | }, 957 | "pify": { 958 | "version": "2.3.0", 959 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 960 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 961 | }, 962 | "prelude-ls": { 963 | "version": "1.1.2", 964 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 965 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 966 | "dev": true 967 | }, 968 | "pseudomap": { 969 | "version": "1.0.2", 970 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 971 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 972 | }, 973 | "read-pkg": { 974 | "version": "2.0.0", 975 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", 976 | "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", 977 | "requires": { 978 | "load-json-file": "2.0.0", 979 | "normalize-package-data": "2.4.0", 980 | "path-type": "2.0.0" 981 | } 982 | }, 983 | "read-pkg-up": { 984 | "version": "2.0.0", 985 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", 986 | "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", 987 | "requires": { 988 | "find-up": "2.1.0", 989 | "read-pkg": "2.0.0" 990 | } 991 | }, 992 | "repeat-string": { 993 | "version": "1.6.1", 994 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 995 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 996 | "dev": true 997 | }, 998 | "require-directory": { 999 | "version": "2.1.1", 1000 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1001 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 1002 | }, 1003 | "require-main-filename": { 1004 | "version": "1.0.1", 1005 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 1006 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" 1007 | }, 1008 | "resolve": { 1009 | "version": "1.1.7", 1010 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", 1011 | "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", 1012 | "dev": true 1013 | }, 1014 | "right-align": { 1015 | "version": "0.1.3", 1016 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 1017 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 1018 | "dev": true, 1019 | "optional": true, 1020 | "requires": { 1021 | "align-text": "0.1.4" 1022 | } 1023 | }, 1024 | "samsam": { 1025 | "version": "1.2.1", 1026 | "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz", 1027 | "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=", 1028 | "dev": true 1029 | }, 1030 | "semver": { 1031 | "version": "5.4.1", 1032 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", 1033 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" 1034 | }, 1035 | "semver-loose": { 1036 | "version": "0.2.0", 1037 | "resolved": "https://registry.npmjs.org/semver-loose/-/semver-loose-0.2.0.tgz", 1038 | "integrity": "sha1-V9pE+wj6m28V9M0U1txTtMG5cAk=" 1039 | }, 1040 | "set-blocking": { 1041 | "version": "2.0.0", 1042 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1043 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1044 | }, 1045 | "shebang-command": { 1046 | "version": "1.2.0", 1047 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1048 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1049 | "requires": { 1050 | "shebang-regex": "1.0.0" 1051 | } 1052 | }, 1053 | "shebang-regex": { 1054 | "version": "1.0.0", 1055 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1056 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 1057 | }, 1058 | "signal-exit": { 1059 | "version": "3.0.2", 1060 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1061 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1062 | }, 1063 | "sinon": { 1064 | "version": "4.0.0", 1065 | "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.0.0.tgz", 1066 | "integrity": "sha1-pUpfAjeqHdIhXl6ByJtCtQxP22s=", 1067 | "dev": true, 1068 | "requires": { 1069 | "diff": "3.2.0", 1070 | "formatio": "1.2.0", 1071 | "lodash.get": "4.4.2", 1072 | "lolex": "2.1.2", 1073 | "native-promise-only": "0.8.1", 1074 | "nise": "1.1.0", 1075 | "path-to-regexp": "1.7.0", 1076 | "samsam": "1.2.1", 1077 | "text-encoding": "0.6.4", 1078 | "type-detect": "4.0.3" 1079 | } 1080 | }, 1081 | "sinon-chai": { 1082 | "version": "2.13.0", 1083 | "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.13.0.tgz", 1084 | "integrity": "sha512-hRNu/TlYEp4Rw5IbzO8ykGoZMSG489PGUx1rvePpHGrtl20cXivRBgtr/EWYxIwL9EOO9+on04nd9k3tW8tVww==", 1085 | "dev": true 1086 | }, 1087 | "source-map": { 1088 | "version": "0.2.0", 1089 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", 1090 | "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", 1091 | "dev": true, 1092 | "optional": true, 1093 | "requires": { 1094 | "amdefine": "1.0.1" 1095 | } 1096 | }, 1097 | "spdx-correct": { 1098 | "version": "1.0.2", 1099 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", 1100 | "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", 1101 | "requires": { 1102 | "spdx-license-ids": "1.2.2" 1103 | } 1104 | }, 1105 | "spdx-expression-parse": { 1106 | "version": "1.0.4", 1107 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", 1108 | "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" 1109 | }, 1110 | "spdx-license-ids": { 1111 | "version": "1.2.2", 1112 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", 1113 | "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" 1114 | }, 1115 | "sprintf-js": { 1116 | "version": "1.0.3", 1117 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1118 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1119 | "dev": true 1120 | }, 1121 | "string-width": { 1122 | "version": "2.1.1", 1123 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1124 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1125 | "requires": { 1126 | "is-fullwidth-code-point": "2.0.0", 1127 | "strip-ansi": "4.0.0" 1128 | }, 1129 | "dependencies": { 1130 | "ansi-regex": { 1131 | "version": "3.0.0", 1132 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1133 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 1134 | }, 1135 | "is-fullwidth-code-point": { 1136 | "version": "2.0.0", 1137 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1138 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 1139 | }, 1140 | "strip-ansi": { 1141 | "version": "4.0.0", 1142 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1143 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1144 | "requires": { 1145 | "ansi-regex": "3.0.0" 1146 | } 1147 | } 1148 | } 1149 | }, 1150 | "strip-ansi": { 1151 | "version": "3.0.1", 1152 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1153 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1154 | "requires": { 1155 | "ansi-regex": "2.1.1" 1156 | } 1157 | }, 1158 | "strip-bom": { 1159 | "version": "3.0.0", 1160 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1161 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" 1162 | }, 1163 | "strip-eof": { 1164 | "version": "1.0.0", 1165 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1166 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 1167 | }, 1168 | "supports-color": { 1169 | "version": "3.2.3", 1170 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", 1171 | "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", 1172 | "dev": true, 1173 | "requires": { 1174 | "has-flag": "1.0.0" 1175 | } 1176 | }, 1177 | "text-encoding": { 1178 | "version": "0.6.4", 1179 | "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", 1180 | "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", 1181 | "dev": true 1182 | }, 1183 | "tmp": { 1184 | "version": "0.0.33", 1185 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1186 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1187 | "dev": true, 1188 | "requires": { 1189 | "os-tmpdir": "1.0.2" 1190 | } 1191 | }, 1192 | "type-check": { 1193 | "version": "0.3.2", 1194 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1195 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1196 | "dev": true, 1197 | "requires": { 1198 | "prelude-ls": "1.1.2" 1199 | } 1200 | }, 1201 | "type-detect": { 1202 | "version": "4.0.3", 1203 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", 1204 | "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", 1205 | "dev": true 1206 | }, 1207 | "uglify-js": { 1208 | "version": "2.8.29", 1209 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 1210 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 1211 | "dev": true, 1212 | "optional": true, 1213 | "requires": { 1214 | "source-map": "0.5.7", 1215 | "uglify-to-browserify": "1.0.2", 1216 | "yargs": "3.10.0" 1217 | }, 1218 | "dependencies": { 1219 | "camelcase": { 1220 | "version": "1.2.1", 1221 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 1222 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 1223 | "dev": true, 1224 | "optional": true 1225 | }, 1226 | "cliui": { 1227 | "version": "2.1.0", 1228 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 1229 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 1230 | "dev": true, 1231 | "optional": true, 1232 | "requires": { 1233 | "center-align": "0.1.3", 1234 | "right-align": "0.1.3", 1235 | "wordwrap": "0.0.2" 1236 | } 1237 | }, 1238 | "source-map": { 1239 | "version": "0.5.7", 1240 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1241 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1242 | "dev": true, 1243 | "optional": true 1244 | }, 1245 | "wordwrap": { 1246 | "version": "0.0.2", 1247 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 1248 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 1249 | "dev": true, 1250 | "optional": true 1251 | }, 1252 | "yargs": { 1253 | "version": "3.10.0", 1254 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 1255 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 1256 | "dev": true, 1257 | "optional": true, 1258 | "requires": { 1259 | "camelcase": "1.2.1", 1260 | "cliui": "2.1.0", 1261 | "decamelize": "1.2.0", 1262 | "window-size": "0.1.0" 1263 | } 1264 | } 1265 | } 1266 | }, 1267 | "uglify-to-browserify": { 1268 | "version": "1.0.2", 1269 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 1270 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 1271 | "dev": true, 1272 | "optional": true 1273 | }, 1274 | "validate-npm-package-license": { 1275 | "version": "3.0.1", 1276 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", 1277 | "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", 1278 | "requires": { 1279 | "spdx-correct": "1.0.2", 1280 | "spdx-expression-parse": "1.0.4" 1281 | } 1282 | }, 1283 | "which": { 1284 | "version": "1.3.0", 1285 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", 1286 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", 1287 | "requires": { 1288 | "isexe": "2.0.0" 1289 | } 1290 | }, 1291 | "which-module": { 1292 | "version": "2.0.0", 1293 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1294 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 1295 | }, 1296 | "window-size": { 1297 | "version": "0.1.0", 1298 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 1299 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 1300 | "dev": true, 1301 | "optional": true 1302 | }, 1303 | "wordwrap": { 1304 | "version": "1.0.0", 1305 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1306 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1307 | "dev": true 1308 | }, 1309 | "wrap-ansi": { 1310 | "version": "2.1.0", 1311 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1312 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1313 | "requires": { 1314 | "string-width": "1.0.2", 1315 | "strip-ansi": "3.0.1" 1316 | }, 1317 | "dependencies": { 1318 | "string-width": { 1319 | "version": "1.0.2", 1320 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1321 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1322 | "requires": { 1323 | "code-point-at": "1.1.0", 1324 | "is-fullwidth-code-point": "1.0.0", 1325 | "strip-ansi": "3.0.1" 1326 | } 1327 | } 1328 | } 1329 | }, 1330 | "wrappy": { 1331 | "version": "1.0.2", 1332 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1333 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1334 | "dev": true 1335 | }, 1336 | "y18n": { 1337 | "version": "3.2.1", 1338 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 1339 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 1340 | }, 1341 | "yallist": { 1342 | "version": "2.1.2", 1343 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1344 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 1345 | }, 1346 | "yargs": { 1347 | "version": "9.0.1", 1348 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", 1349 | "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", 1350 | "requires": { 1351 | "camelcase": "4.1.0", 1352 | "cliui": "3.2.0", 1353 | "decamelize": "1.2.0", 1354 | "get-caller-file": "1.0.2", 1355 | "os-locale": "2.1.0", 1356 | "read-pkg-up": "2.0.0", 1357 | "require-directory": "2.1.1", 1358 | "require-main-filename": "1.0.1", 1359 | "set-blocking": "2.0.0", 1360 | "string-width": "2.1.1", 1361 | "which-module": "2.0.0", 1362 | "y18n": "3.2.1", 1363 | "yargs-parser": "7.0.0" 1364 | } 1365 | }, 1366 | "yargs-parser": { 1367 | "version": "7.0.0", 1368 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", 1369 | "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", 1370 | "requires": { 1371 | "camelcase": "4.1.0" 1372 | } 1373 | } 1374 | } 1375 | } 1376 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-docker-test", 3 | "version": "1.1.2", 4 | "description": "Test your node project against multiple node versions using docker.", 5 | "repository": "https://github.com/mrkmg/node-docker-test.git", 6 | "main": "api.js", 7 | "scripts": { 8 | "test": "mocha tests/specs/**/*.js", 9 | "cover": "istanbul cover _mocha 'tests/specs/**/*.js'" 10 | }, 11 | "bin": { 12 | "ndt": "./ndt" 13 | }, 14 | "author": { 15 | "name": "Kevin Gravier", 16 | "email": "kevin@mrkmg.com", 17 | "url": "https://github.com/mrkmg" 18 | }, 19 | "license": "MIT", 20 | "dependencies": { 21 | "blessed": "^0.1.81", 22 | "bluebird": "^3.5.1", 23 | "extend": "^3.0.1", 24 | "lodash.pick": "^4.4.0", 25 | "pad": "^2.0.3", 26 | "semver": "^5.4.1", 27 | "semver-loose": "^0.2.0", 28 | "yargs": "^10.0.3" 29 | }, 30 | "devDependencies": { 31 | "chai": "^4.1.2", 32 | "chai-as-promised": "^7.1.1", 33 | "istanbul": "^0.4.5", 34 | "mocha": "^4.0.1", 35 | "sinon": "^4.1.2", 36 | "sinon-chai": "^2.14.0", 37 | "tmp": "^0.0.33" 38 | }, 39 | "config": { 40 | "ndt": { 41 | "versions": [ 42 | "minor" 43 | ] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/helpers/DockerStubs.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var Promise = require('bluebird'); 7 | 8 | module.exports = { 9 | containerExistsTrue: function (name) { 10 | return Promise.resolve(true) 11 | }, 12 | containerExistsFalse: function (name) { 13 | return Promise.resolve(false); 14 | }, 15 | runContainer: function (base, cmd, outputCallback) { 16 | return Promise.resolve() 17 | }, 18 | getLastContainerSha: function () { 19 | return Promise.resolve('testsha'); 20 | }, 21 | commitContainer: function (sha, name) { 22 | return Promise.resolve() 23 | }, 24 | _wait: function () { 25 | return Promise.resolve(); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /tests/helpers/SpawnStubs.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | module.exports = { 7 | containerExists: function (app, args) { 8 | return { 9 | on: function (type, cb) { 10 | if (args[1] == 'good') { 11 | cb(0); 12 | } else { 13 | cb(1); 14 | } 15 | } 16 | } 17 | }, 18 | runContainer: function (app, args) { 19 | return { 20 | on: function (type, cb) { 21 | if (args[4] == 'good') { 22 | cb(0); 23 | } else { 24 | cb(1); 25 | } 26 | }, 27 | stdout: { 28 | on: function (type, cb) { 29 | if (args[4] == 'good') { 30 | cb('stdout'); 31 | } 32 | } 33 | }, 34 | stderr: { 35 | on: function (type, cb) { 36 | if (args[4] != 'good') { 37 | cb('stderr'); 38 | } 39 | } 40 | } 41 | } 42 | }, 43 | runContainerWithCopy: function (app, args) { 44 | return { 45 | on: function (type, cb) { 46 | if (args[7] == 'good') { 47 | cb(0); 48 | } else { 49 | cb(1); 50 | } 51 | }, 52 | stdout: { 53 | on: function (type, cb) { 54 | if (args[7] == 'good') { 55 | cb('stdout'); 56 | } 57 | } 58 | }, 59 | stderr: { 60 | on: function (type, cb) { 61 | if (args[7] != 'good') { 62 | cb('stderr'); 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | lastContainerShaGood: function (app, args) { 69 | return { 70 | on: function (type, cb) { 71 | cb(0); 72 | }, 73 | stdout: { 74 | on: function (type, cb) { 75 | cb('stdout'); 76 | } 77 | }, 78 | stderr: { 79 | on: function (type, cb) { 80 | cb('stderr'); 81 | } 82 | } 83 | } 84 | }, 85 | lastContainerShaBad: function (app, args) { 86 | return { 87 | on: function (type, cb) { 88 | cb(1); 89 | }, 90 | stdout: { 91 | on: function (type, cb) { 92 | cb('stdout'); 93 | } 94 | }, 95 | stderr: { 96 | on: function (type, cb) { 97 | cb('stderr'); 98 | } 99 | } 100 | } 101 | }, 102 | commitContainer: function (app, args) { 103 | return { 104 | on: function (type, cb) { 105 | if (args[2] == 'good') { 106 | cb(0); 107 | } else { 108 | cb(1); 109 | } 110 | } 111 | } 112 | }, 113 | }; 114 | -------------------------------------------------------------------------------- /tests/helpers/TestRepos.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var fs, tmp; 7 | 8 | fs = require('fs'); 9 | tmp = require('tmp'); 10 | 11 | module.exports = { 12 | 13 | defaults: function defaults() { 14 | return this._createSimpleJson('default', {}); 15 | }, 16 | 17 | allString: function allString() { 18 | return this._createSimpleJson('string', { 19 | commands: "commands-test", 20 | 'setup-commands': 'setup-commands-test', 21 | 'base-image': 'base-image-test', 22 | 'package-manager': "package-manager-test" 23 | }); 24 | }, 25 | 26 | allArray: function allArray() { 27 | return this._createSimpleJson('array', { 28 | commands: ['commands-1', 'commands-2'], 29 | 'setup-commands': ['setup-commands-1', 'setup-commands-2'], 30 | versions: ['4', 5, 'minor'] 31 | }); 32 | }, 33 | 34 | allNumber: function allNumber() { 35 | return this._createSimpleJson('number', { 36 | concurrency: 99 37 | }); 38 | }, 39 | 40 | allBoolean: function allBoolean() { 41 | return this._createSimpleJson('boolean', { 42 | reset: true, 43 | simple: true 44 | }); 45 | }, 46 | 47 | all: function () { 48 | return this._createSimpleJson('all', { 49 | commands: 'commands-test', 50 | 'setup-commands': 'setup-commands-test', 51 | 'base-image': 'base-image-test', 52 | 'package-manager': "package-manager-test", 53 | versions: ['4', 5], 54 | concurrency: 99, 55 | simple: false 56 | }); 57 | }, 58 | 59 | cleanup: function cleanup() { 60 | this._tmp_directories.forEach(function (tmp_directory) { 61 | tmp_directory.removeCallback(); 62 | }); 63 | }, 64 | 65 | 66 | _tmp_directories: [], 67 | 68 | _createSimpleJson: function _createSimpleJson(name, ndt) { 69 | var dir = this._createDirectory(); 70 | 71 | this._writeFileJson(dir + '/package.json', { 72 | name: 'ndt-testing' + name, 73 | config: { 74 | ndt: ndt 75 | } 76 | }); 77 | 78 | return dir; 79 | }, 80 | 81 | _createDirectory: function _createDirectory() { 82 | var dir = tmp.dirSync({ 83 | unsafeCleanup: true 84 | }); 85 | this._tmp_directories.push(dir); 86 | return dir.name; 87 | }, 88 | 89 | _writeFile: function (file, content) { 90 | return fs.writeFileSync(file, content); 91 | }, 92 | 93 | _writeFileJson: function (file, object) { 94 | return this._writeFile(file, JSON.stringify(object)); 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /tests/helpers/cleanConfig.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | module.exports = function cleanConfig() { 7 | delete require.cache[require.resolve('../../lib/utils/Config')]; 8 | delete require.cache[require.resolve('yargs')]; 9 | } 10 | -------------------------------------------------------------------------------- /tests/specs/options/cli-parsing.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var expect, TestRepos, cleanConfig; 7 | 8 | expect = require('chai').expect; 9 | 10 | TestRepos = require('../../helpers/TestRepos'); 11 | cleanConfig = require('../../helpers/cleanConfig'); 12 | 13 | describe('cli-parsing', function () { 14 | describe('no-config', function () { 15 | before(function () { 16 | this.previous_cwd = process.cwd(); 17 | this.previous_argv = process.argv; 18 | 19 | process.argv = [ 20 | 'node', 'ndt', 21 | '-x', 'command1', 'command2', 22 | '-s', 'setup-command1', 'setup-command2', 23 | '-v', 'version1', 'version2', 24 | '-c', '99', 25 | '-b', 'image:tag', 26 | "-p", "pkgmgr", 27 | '--setup', 28 | '--reset' 29 | ]; 30 | process.chdir(TestRepos.defaults()); 31 | 32 | this.config = require('../../../lib/utils/Config').parse(); 33 | }); 34 | 35 | after(function () { 36 | process.chdir(this.previous_cwd); 37 | process.argv = this.previous_argv; 38 | cleanConfig(); 39 | TestRepos.cleanup(); 40 | }); 41 | 42 | it('commands', function () { 43 | expect(this.config.commands).to.eql(['command1', 'command2']); 44 | }); 45 | 46 | it('setup-commands', function () { 47 | expect(this.config['setup-commands']).to.eql(['setup-command1', 'setup-command2']); 48 | }); 49 | 50 | it('versions', function () { 51 | expect(this.config.versions).to.eql(['version1', 'version2']); 52 | }); 53 | 54 | it('concurrency', function () { 55 | expect(this.config.concurrency).to.equal(99); 56 | }); 57 | 58 | it('setup', function () { 59 | expect(this.config.setup).to.equal(true); 60 | }); 61 | 62 | it('reset', function () { 63 | expect(this.config.reset).to.equal(true); 64 | }); 65 | 66 | it('base-image', function () { 67 | expect(this.config['base-image']).to.equal('image:tag'); 68 | }); 69 | 70 | it('package-manager', function () { 71 | expect(this.config['package-manager']).to.equal('pkgmgr'); 72 | }); 73 | }); 74 | 75 | describe('with-config', function () { 76 | before(function () { 77 | this.previous_cwd = process.cwd(); 78 | this.previous_argv = process.argv; 79 | 80 | process.argv = [ 81 | 'node', 'ndt', 82 | '-x', 'command1', 'command2', 83 | '-s', 'setup-command1', 'setup-command2', 84 | '-v', 'version1', 'version2', 85 | '-c', '80', 86 | '-b', 'image:tag', 87 | "-p", "pkgmgr", 88 | '--setup', 89 | '--reset' 90 | ]; 91 | process.chdir(TestRepos.all()); 92 | 93 | this.config = require('../../../lib/utils/Config').parse(); 94 | }); 95 | 96 | after(function () { 97 | process.chdir(this.previous_cwd); 98 | process.argv = this.previous_argv; 99 | cleanConfig(); 100 | TestRepos.cleanup(); 101 | }); 102 | 103 | it('commands', function () { 104 | expect(this.config.commands).to.eql(['command1', 'command2']); 105 | }); 106 | 107 | it('setup-commands', function () { 108 | expect(this.config['setup-commands']).to.eql(['setup-command1', 'setup-command2']); 109 | }); 110 | 111 | it('versions', function () { 112 | expect(this.config.versions).to.eql(['version1', 'version2']); 113 | }); 114 | 115 | it('concurrency', function () { 116 | expect(this.config.concurrency).to.equal(80); 117 | }); 118 | 119 | it('setup', function () { 120 | expect(this.config.setup).to.equal(true); 121 | }); 122 | 123 | it('reset', function () { 124 | expect(this.config.reset).to.equal(true); 125 | }); 126 | 127 | it('base-image', function () { 128 | expect(this.config['base-image']).to.equal('image:tag'); 129 | }); 130 | 131 | it('package-manager', function () { 132 | expect(this.config['package-manager']).to.equal('pkgmgr'); 133 | }); 134 | }) 135 | }); 136 | -------------------------------------------------------------------------------- /tests/specs/options/config-parsing.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var expect, TestRepos, cleanConfig; 7 | 8 | expect = require('chai').expect; 9 | 10 | TestRepos = require('../../helpers/TestRepos'); 11 | cleanConfig = require('../../helpers/cleanConfig'); 12 | 13 | describe('config-parsing', function () { 14 | describe('arrays', function () { 15 | before(function () { 16 | this.previous_cwd = process.cwd(); 17 | this.previous_argv = process.argv; 18 | 19 | process.argv = ['node', 'ndt']; 20 | process.chdir(TestRepos.allArray()); 21 | 22 | this.config = require('../../../lib/utils/Config').parse(); 23 | }); 24 | 25 | after(function () { 26 | process.chdir(this.previous_cwd); 27 | process.argv = this.previous_argv; 28 | cleanConfig(); 29 | TestRepos.cleanup(); 30 | }); 31 | 32 | it('commands', function () { 33 | expect(this.config['commands']).to.eql(['commands-1', 'commands-2']); 34 | }); 35 | 36 | it('setup-commands', function () { 37 | expect(this.config['setup-commands']).to.eql(['setup-commands-1', 'setup-commands-2']); 38 | }); 39 | 40 | it('versions', function () { 41 | expect(this.config.versions).to.eql(['4', 5, 'minor']); 42 | }); 43 | }); 44 | 45 | describe('strings', function () { 46 | before(function () { 47 | this.previous_cwd = process.cwd(); 48 | this.previous_argv = process.argv; 49 | 50 | process.argv = ['node', 'ndt']; 51 | process.chdir(TestRepos.allString()); 52 | 53 | this.config = require('../../../lib/utils/Config').parse(); 54 | }); 55 | 56 | after(function () { 57 | process.chdir(this.previous_cwd); 58 | process.argv = this.previous_argv; 59 | cleanConfig(); 60 | TestRepos.cleanup(); 61 | }); 62 | 63 | it('commands', function () { 64 | expect(this.config['commands']).to.eql('commands-test'); 65 | }); 66 | 67 | it('setup-commands', function () { 68 | expect(this.config['setup-commands']).to.eql('setup-commands-test'); 69 | }); 70 | 71 | it('base-image', function () { 72 | expect(this.config['base-image']).to.equal('base-image-test'); 73 | }); 74 | 75 | it('package-manager', function () { 76 | expect(this.config['package-manager']).to.equal('package-manager-test'); 77 | }); 78 | }); 79 | 80 | describe('integers', function () { 81 | before(function () { 82 | this.previous_cwd = process.cwd(); 83 | this.previous_argv = process.argv; 84 | 85 | process.argv = ['node', 'ndt']; 86 | process.chdir(TestRepos.allNumber()); 87 | 88 | this.config = require('../../../lib/utils/Config').parse(); 89 | }); 90 | 91 | after(function () { 92 | process.chdir(this.previous_cwd); 93 | process.argv = this.previous_argv; 94 | cleanConfig(); 95 | TestRepos.cleanup(); 96 | }); 97 | 98 | it('concurrency', function () { 99 | expect(this.config['concurrency']).to.equal(99); 100 | }); 101 | }); 102 | 103 | describe('boolean', function () { 104 | before(function () { 105 | this.previous_cwd = process.cwd(); 106 | this.previous_argv = process.argv; 107 | 108 | process.argv = ['node', 'ndt']; 109 | process.chdir(TestRepos.allBoolean()); 110 | 111 | this.config = require('../../../lib/utils/Config').parse(); 112 | }); 113 | 114 | after(function () { 115 | process.chdir(this.previous_cwd); 116 | process.argv = this.previous_argv; 117 | cleanConfig(); 118 | TestRepos.cleanup(); 119 | }); 120 | 121 | it('reset', function () { 122 | expect(this.config['reset']).to.equal(true); 123 | }); 124 | 125 | it('simple', function () { 126 | expect(this.config['simple']).to.equal(true); 127 | }); 128 | }); 129 | }); 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /tests/specs/options/defaults.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var expect, TestRepos, cleanConfig; 7 | 8 | expect = require('chai').expect; 9 | 10 | TestRepos = require('../../helpers/TestRepos'); 11 | cleanConfig = require('../../helpers/cleanConfig'); 12 | 13 | describe('defaults', function () { 14 | describe('no-config', function () { 15 | before(function () { 16 | this.previous_cwd = process.cwd(); 17 | this.previous_argv = process.argv; 18 | 19 | process.argv = [ 20 | 'node', 'ndt' 21 | ]; 22 | process.chdir(TestRepos.defaults()); 23 | 24 | this.config = require('../../../lib/utils/Config').parse(); 25 | }); 26 | 27 | after(function () { 28 | process.chdir(this.previous_cwd); 29 | process.argv = this.previous_argv; 30 | cleanConfig(); 31 | TestRepos.cleanup(); 32 | }); 33 | 34 | it('commands', function () { 35 | expect(this.config.commands).to.eql(['npm test']); 36 | }); 37 | 38 | it('setup-commands', function () { 39 | expect(this.config['setup-commands']).to.eql([]) 40 | }); 41 | 42 | it('versions', function () { 43 | expect(this.config.versions).to.eql(['major']); 44 | }); 45 | 46 | it('concurrency', function () { 47 | expect(this.config.concurrency).to.equal(require('os').cpus().length - 1); 48 | }); 49 | 50 | it('setup', function () { 51 | expect(this.config.setup).to.equal(false); 52 | }); 53 | 54 | it('reset', function () { 55 | expect(this.config.reset).to.equal(false); 56 | }); 57 | 58 | it('base-image', function () { 59 | expect(this.config['base-image']).to.equal('debian:stable'); 60 | }); 61 | 62 | it('package-manager', function () { 63 | expect(this.config['package-manager']).to.equal('apt-get'); 64 | }) 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /tests/specs/utils/Commands.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var expect, Commands; 7 | 8 | expect = require('chai').expect; 9 | 10 | Commands = require('../../../lib/utils/Commands'); 11 | 12 | describe('Commands', function () { 13 | describe('setup', function () { 14 | it('apt-get', function () { 15 | this.setupCommands = Commands.setup(['1.2.3'], 'command1', 'apt-get'); 16 | 17 | expect(this.setupCommands).to.equal('echo "Starting" && apt-get update && apt-get upgrade -y && apt-get install -y git rsync wget && (mkdir /test || true) && wget https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh -O install.sh && NVM_DIR=\'/nvm\' bash install.sh && source /nvm/nvm.sh && nvm install 1.2.3 && command1'); 18 | }); 19 | 20 | 21 | it('yum', function () { 22 | this.setupCommands = Commands.setup(['1.2.3'], 'command1', 'yum'); 23 | 24 | expect(this.setupCommands).to.equal('echo "Starting" && yum update -y && yum clean all && yum install -y git rsync wget && (mkdir /test || true) && wget https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh -O install.sh && NVM_DIR=\'/nvm\' bash install.sh && source /nvm/nvm.sh && nvm install 1.2.3 && command1'); 25 | }); 26 | }); 27 | 28 | describe('both: string', function () { 29 | before(function () { 30 | this.setupCommands = Commands.setup(['1.2.3'], 'command1', 'apt-get'); 31 | this.testCommands = Commands.test('1.2.3', 'command1'); 32 | }); 33 | 34 | it('setup', function () { 35 | expect(this.setupCommands).to.equal('echo "Starting" && apt-get update && apt-get upgrade -y && apt-get install -y git rsync wget && (mkdir /test || true) && wget https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh -O install.sh && NVM_DIR=\'/nvm\' bash install.sh && source /nvm/nvm.sh && nvm install 1.2.3 && command1'); 36 | }); 37 | 38 | it('test', function () { 39 | expect(this.testCommands).to.equal('source /nvm/nvm.sh && nvm install 1.2.3 && nvm use 1.2.3 && rsync -aAXx --delete --exclude .git --exclude node_modules /test-src/ /test/ && cd /test && npm install && command1'); 40 | }); 41 | }); 42 | 43 | describe('both: arrays', function () { 44 | before(function () { 45 | this.setupCommands = Commands.setup(['1.2.3'], ['command1', 'command2'], 'apt-get'); 46 | this.testCommands = Commands.test('1.2.3', ['command1', 'command2']); 47 | }); 48 | 49 | it('setup', function () { 50 | expect(this.setupCommands).to.equal('echo "Starting" && apt-get update && apt-get upgrade -y && apt-get install -y git rsync wget && (mkdir /test || true) && wget https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh -O install.sh && NVM_DIR=\'/nvm\' bash install.sh && source /nvm/nvm.sh && nvm install 1.2.3 && command1 && command2'); 51 | }); 52 | 53 | it('test', function () { 54 | expect(this.testCommands).to.equal('source /nvm/nvm.sh && nvm install 1.2.3 && nvm use 1.2.3 && rsync -aAXx --delete --exclude .git --exclude node_modules /test-src/ /test/ && cd /test && npm install && command1 && command2'); 55 | }); 56 | }) 57 | }); 58 | -------------------------------------------------------------------------------- /tests/specs/utils/Docker.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var chai, expect, sinon, Docker, ChildProcess, SpawnStubs, DockerStubs; 7 | 8 | chai = require('chai'); 9 | expect = chai.expect; 10 | sinon = require('sinon'); 11 | chai.use(require('sinon-chai')); 12 | chai.use(require('chai-as-promised')); 13 | 14 | Docker = require('../../../lib/utils/Docker'); 15 | ChildProcess = require('child_process'); 16 | 17 | SpawnStubs = require('../../helpers/SpawnStubs'); 18 | DockerStubs = require('../../helpers/DockerStubs'); 19 | 20 | describe('Docker', function () { 21 | describe('containerExists', function () { 22 | beforeEach(function () { 23 | this.spawnStub = sinon.stub(ChildProcess, 'spawn').callsFake(SpawnStubs.containerExists); 24 | }); 25 | 26 | afterEach(function () { 27 | this.spawnStub.restore(); 28 | }); 29 | 30 | it('spawn call', function () { 31 | Docker.containerExists('good'); 32 | return expect(this.spawnStub).to.have.been.calledWith('docker', ['inspect', 'good']); 33 | }); 34 | 35 | it('does exists', function () { 36 | return expect(Docker.containerExists('good')).to.eventually.equal(true); 37 | }); 38 | 39 | it('does not exist', function () { 40 | return expect(Docker.containerExists('bad')).to.eventually.equal(false); 41 | }); 42 | }); 43 | 44 | describe('getLastContainerSha', function () { 45 | afterEach(function () { 46 | this.spawnStub.restore(); 47 | }); 48 | 49 | it('success', function () { 50 | this.spawnStub = sinon.stub(ChildProcess, 'spawn').callsFake(SpawnStubs.lastContainerShaGood); 51 | return expect(Docker.getLastContainerSha()).to.eventually.equal('stdout'); 52 | }); 53 | 54 | it('failed', function () { 55 | this.spawnStub = sinon.stub(ChildProcess, 'spawn').callsFake(SpawnStubs.lastContainerShaBad); 56 | return expect(Docker.getLastContainerSha()).to.be.rejectedWith(Error, 'Failed to get last SHA'); 57 | }) 58 | }); 59 | 60 | describe('runContainer', function () { 61 | beforeEach(function () { 62 | this.spawnStub = sinon.stub(ChildProcess, 'spawn').callsFake(SpawnStubs.runContainer); 63 | }); 64 | 65 | afterEach(function () { 66 | this.spawnStub.restore(); 67 | }); 68 | 69 | it('spawn call', function () { 70 | var outputSpy = sinon.spy(); 71 | Docker.runContainer('good', ['command1', 'command2'], outputSpy); 72 | return expect(this.spawnStub).to.have.been.calledWith('docker', ['run', '-v', 'ndt-npm:/root/.npm', '-i', 'good', '/bin/bash', '-c', ['command1', 'command2']]); 73 | }); 74 | 75 | it('resolves', function () { 76 | var outputSpy = sinon.spy(); 77 | return expect(Docker.runContainer('good', ['command1', 'command2'], outputSpy)).to.be.fulfilled; 78 | }); 79 | 80 | it('rejects', function () { 81 | var outputSpy = sinon.spy(); 82 | return expect(Docker.runContainer('bad', ['command1', 'command2'], outputSpy)).to.be.rejected; 83 | }); 84 | 85 | it('stdout', function () { 86 | var outputSpy = sinon.spy(); 87 | Docker.runContainer('good', ['command1', 'command2'], outputSpy); 88 | expect(outputSpy).to.have.been.calledWith('stdout'); 89 | }); 90 | 91 | it('stderr', function () { 92 | var outputSpy = sinon.spy(); 93 | Docker.runContainer('bad', ['command1', 'command2'], outputSpy).catch(function () { 94 | }); 95 | expect(outputSpy).to.have.been.calledWith('stderr'); 96 | }); 97 | }); 98 | 99 | describe('runContainerWithCopy', function () { 100 | beforeEach(function () { 101 | this.spawnStub = sinon.stub(ChildProcess, 'spawn').callsFake(SpawnStubs.runContainerWithCopy); 102 | }); 103 | 104 | afterEach(function () { 105 | this.spawnStub.restore(); 106 | }); 107 | 108 | it('spawn call', function () { 109 | var outputSpy = sinon.spy(); 110 | Docker.runContainerWithCopy('good', ['command1', 'command2'], outputSpy) 111 | return expect(this.spawnStub).to.have.been.calledWith('docker', ['run', '-v', 'ndt-npm:/root/.npm', '-i', '--rm', '-v', process.cwd() + ':/test-src/:ro', 'good', '/bin/bash', '-c', ['command1', 'command2']]); 112 | }); 113 | 114 | it('resolves', function () { 115 | var outputSpy = sinon.spy(); 116 | return expect(Docker.runContainerWithCopy('good', ['command1', 'command2'], outputSpy)).to.be.fulfilled; 117 | }); 118 | 119 | it('rejects', function () { 120 | var outputSpy = sinon.spy(); 121 | return expect(Docker.runContainerWithCopy('bad', ['command1', 'command2'], outputSpy)).to.be.rejected; 122 | }); 123 | 124 | it('stdout', function () { 125 | var outputSpy = sinon.spy(); 126 | Docker.runContainerWithCopy('good', ['command1', 'command2'], outputSpy); 127 | return expect(outputSpy).to.have.been.calledWith('stdout'); 128 | }); 129 | 130 | it('stderr', function () { 131 | var outputSpy = sinon.spy(); 132 | Docker.runContainerWithCopy('bad', ['command1', 'command2'], outputSpy).catch(function () { 133 | }); 134 | return expect(outputSpy).to.have.been.calledWith('stderr'); 135 | }); 136 | }); 137 | 138 | describe('commitContainer', function () { 139 | beforeEach(function () { 140 | this.spawnStub = sinon.stub(ChildProcess, 'spawn').callsFake(SpawnStubs.commitContainer); 141 | }); 142 | 143 | afterEach(function () { 144 | this.spawnStub.restore(); 145 | }); 146 | 147 | it('spawn call', function () { 148 | Docker.commitContainer('sha', 'good') 149 | return expect(this.spawnStub).to.have.been.calledWith('docker', ["commit", "sha", "good"]); 150 | }); 151 | 152 | it('resolves', function () { 153 | return expect(Docker.commitContainer('sha', 'good')).to.be.fulfilled; 154 | }); 155 | 156 | it('rejects', function () { 157 | return expect(Docker.commitContainer('sha', 'bad')).to.be.rejected; 158 | }); 159 | }); 160 | 161 | describe('makeNew', function () { 162 | beforeEach(function () { 163 | this.runContainerStub = sinon.stub(Docker, 'runContainer').callsFake(DockerStubs.runContainer); 164 | this.getLastContainerShaStub = sinon.stub(Docker, 'getLastContainerSha').callsFake(DockerStubs.getLastContainerSha); 165 | this.commitContainerStub = sinon.stub(Docker, 'commitContainer').callsFake(DockerStubs.commitContainer); 166 | this._waitStub = sinon.stub(Docker, '_wait').callsFake(DockerStubs._wait); 167 | this.callbackSpy = sinon.spy(); 168 | }); 169 | 170 | afterEach(function () { 171 | this.runContainerStub.restore(); 172 | this.getLastContainerShaStub.restore(); 173 | this.commitContainerStub.restore(); 174 | this._waitStub.restore(); 175 | this.callbackSpy.reset(); 176 | }); 177 | 178 | describe('exists', function () { 179 | beforeEach(function () { 180 | this.outputCallback = function () { 181 | }; 182 | this.containerExistsStub = sinon.stub(Docker, 'containerExists').callsFake(DockerStubs.containerExistsTrue); 183 | this.runPromise = Docker.makeNew('new', 'command', 'old', this.outputCallback); 184 | return this.runPromise; 185 | }); 186 | 187 | afterEach(function () { 188 | delete this.runPromise; 189 | this.containerExistsStub.restore(); 190 | }); 191 | 192 | it('fulfills', function () { 193 | return expect(this.runPromise).to.be.fulfilled; 194 | }); 195 | 196 | it('call:containerExists', function () { 197 | return expect(this.containerExistsStub).to.have.been.calledWith('new') 198 | }); 199 | 200 | it('call:runContainer', function () { 201 | return expect(this.runContainerStub).to.have.been.calledWith('new', 'command', this.outputCallback); 202 | }); 203 | 204 | it('call:commitContainer', function () { 205 | return expect(this.commitContainerStub).to.have.been.calledWith('testsha', 'new'); 206 | }) 207 | }); 208 | 209 | 210 | describe('doesnt exists', function () { 211 | beforeEach(function () { 212 | this.outputCallback = function () { 213 | }; 214 | this.containerExistsStub = sinon.stub(Docker, 'containerExists').callsFake(DockerStubs.containerExistsFalse); 215 | this.runPromise = Docker.makeNew('new', 'command', 'old', this.outputCallback); 216 | return this.runPromise; 217 | }); 218 | 219 | afterEach(function () { 220 | delete this.runPromise; 221 | this.containerExistsStub.restore(); 222 | }); 223 | 224 | it('fulfills', function () { 225 | return expect(this.runPromise).to.be.fulfilled; 226 | }); 227 | 228 | it('call:containerExists', function () { 229 | return expect(this.containerExistsStub).to.have.been.calledWith('new') 230 | }); 231 | 232 | it('call:runContainer', function () { 233 | return expect(this.runContainerStub).to.have.been.calledWith('old', 'command', this.outputCallback); 234 | }); 235 | 236 | it('call:commitContainer', function () { 237 | return expect(this.commitContainerStub).to.have.been.calledWith('testsha', 'new'); 238 | }) 239 | }); 240 | 241 | 242 | }) 243 | }); 244 | -------------------------------------------------------------------------------- /tests/specs/utils/VersionParser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Written by Kevin Gravier 3 | Part of the node-docker-test project. 4 | MIT Licence 5 | */ 6 | var expect, VersionParser; 7 | 8 | expect = require('chai').expect; 9 | 10 | VersionParser = require('../../../lib/utils/VersionParser'); 11 | 12 | 13 | describe('VersionParser', function () { 14 | before(function () { 15 | VersionParser.versionListCache = require('../../helpers/versions.json'); 16 | }); 17 | 18 | after(function () { 19 | VersionParser.versionListCache = undefined; 20 | }); 21 | 22 | describe('retrieve remote', function () { 23 | before(function () { 24 | VersionParser.versionListCache = undefined; 25 | }); 26 | 27 | after(function () { 28 | VersionParser.versionListCache = require('../../helpers/versions.json'); 29 | }); 30 | 31 | it('should download remote list', function () { 32 | return VersionParser('4.3.1') 33 | .then(function (versions) { 34 | expect(versions).to.eql([ 35 | '4.3.1' 36 | ]); 37 | }); 38 | }); 39 | }); 40 | 41 | describe('invoke', function () { 42 | it('string', function () { 43 | return VersionParser('4.3.1') 44 | .then(function (versions) { 45 | expect(versions).to.eql([ 46 | '4.3.1' 47 | ]); 48 | }); 49 | }); 50 | 51 | it('array', function () { 52 | return VersionParser(['4.3.0', '4.3.1']) 53 | .then(function (versions) { 54 | expect(versions).to.eql([ 55 | '4.3.0', 56 | '4.3.1' 57 | ]); 58 | }); 59 | }) 60 | }); 61 | 62 | describe('versions', function () { 63 | it('patch', function () { 64 | return VersionParser('4.3.1') 65 | .then(function (versions) { 66 | expect(versions).to.eql([ 67 | '4.3.1' 68 | ]); 69 | }); 70 | }); 71 | 72 | it('minor', function () { 73 | return VersionParser('4.3') 74 | .then(function (versions) { 75 | expect(versions).to.eql([ 76 | '4.3.2' 77 | ]); 78 | }); 79 | }); 80 | 81 | it('major', function () { 82 | return VersionParser('4') 83 | .then(function (versions) { 84 | expect(versions).to.eql([ 85 | '4.4.7' 86 | ]); 87 | }); 88 | }); 89 | }); 90 | 91 | describe('keywords', function () { 92 | it('major', function () { 93 | return VersionParser('major') 94 | .then(function (versions) { 95 | expect(versions).to.eql([ 96 | '4.4.7', '5.12.0', '6.3.1' 97 | ]); 98 | }); 99 | }); 100 | 101 | it('minor', function () { 102 | return VersionParser('minor') 103 | .then(function (versions) { 104 | expect(versions).to.eql([ 105 | "4.0.0", "4.1.2", "4.2.6", "4.3.2", "4.4.7", "5.0.0", "5.1.1", "5.2.0", "5.3.0", "5.4.1", "5.5.0", "5.6.0", "5.7.1", "5.8.0", "5.9.1", "5.10.1", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.2", "6.3.1" 106 | ]); 107 | }); 108 | }); 109 | 110 | it('patch', function () { 111 | return VersionParser('patch') 112 | .then(function (versions) { 113 | expect(versions).to.eql([ 114 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7", "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 115 | ]); 116 | }); 117 | }); 118 | 119 | it('legacy', function () { 120 | return VersionParser('legacy') 121 | .then(function (versions) { 122 | expect(versions).to.eql([ 123 | "0.1.14", "0.1.15", "0.1.16", "0.1.17", "0.1.18", "0.1.19", "0.1.20", "0.1.21", "0.1.22", "0.1.23", "0.1.24", "0.1.25", "0.1.26", "0.1.27", "0.1.28", "0.1.29", "0.1.30", "0.1.31", "0.1.32", "0.1.33", "0.1.90", "0.1.91", "0.1.92", "0.1.93", "0.1.94", "0.1.95", "0.1.96", "0.1.97", "0.1.98", "0.1.99", "0.1.100", "0.1.101", "0.1.102", "0.1.103", "0.1.104", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.3.0", "0.3.1", "0.3.2", "0.3.3", "0.3.4", "0.3.5", "0.3.6", "0.3.7", "0.3.8", "0.4.0", "0.4.1", "0.4.2", "0.4.3", "0.4.4", "0.4.5", "0.4.6", "0.4.7", "0.4.8", "0.4.9", "0.4.10", "0.4.11", "0.4.12", "0.5.0", "0.5.1", "0.5.2", "0.5.3", "0.5.4", "0.5.5", "0.5.6", "0.5.7", "0.5.8", "0.5.9", "0.5.10", "0.6.0", "0.6.1", "0.6.2", "0.6.3", "0.6.4", "0.6.5", "0.6.6", "0.6.7", "0.6.8", "0.6.9", "0.6.10", "0.6.11", "0.6.12", "0.6.13", "0.6.14", "0.6.15", "0.6.16", "0.6.17", "0.6.18", "0.6.19", "0.6.20", "0.6.21", "0.7.0", "0.7.1", "0.7.2", "0.7.3", "0.7.4", "0.7.5", "0.7.6", "0.7.7", "0.7.8", "0.7.9", "0.7.10", "0.7.11", "0.7.12", "0.8.0", "0.8.1", "0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10", "0.8.11", "0.8.12", "0.8.13", "0.8.14", "0.8.15", "0.8.16", "0.8.17", "0.8.18", "0.8.19", "0.8.20", "0.8.21", "0.8.22", "0.8.23", "0.8.24", "0.8.25", "0.8.26", "0.8.27", "0.8.28", "0.9.0", "0.9.1", "0.9.2", "0.9.3", "0.9.4", "0.9.5", "0.9.6", "0.9.7", "0.9.8", "0.9.9", "0.9.10", "0.9.11", "0.9.12", "0.10.0", "0.10.1", "0.10.2", "0.10.3", "0.10.4", "0.10.5", "0.10.6", "0.10.7", "0.10.8", "0.10.9", "0.10.10", "0.10.11", "0.10.12", "0.10.13", "0.10.14", "0.10.15", "0.10.16", "0.10.17", "0.10.18", "0.10.19", "0.10.20", "0.10.21", "0.10.22", "0.10.23", "0.10.24", "0.10.25", "0.10.26", "0.10.27", "0.10.28", "0.10.29", "0.10.30", "0.10.31", "0.10.32", "0.10.33", "0.10.34", "0.10.35", "0.10.36", "0.10.37", "0.10.38", "0.10.39", "0.10.40", "0.10.41", "0.10.42", "0.10.43", "0.10.44", "0.10.45", "0.10.46", "0.11.0", "0.11.1", "0.11.2", "0.11.3", "0.11.4", "0.11.5", "0.11.6", "0.11.7", "0.11.8", "0.11.9", "0.11.10", "0.11.11", "0.11.12", "0.11.13", "0.11.14", "0.11.15", "0.11.16", "0.12.0", "0.12.1", "0.12.2", "0.12.3", "0.12.4", "0.12.5", "0.12.6", "0.12.7", "0.12.8", "0.12.9", "0.12.10", "0.12.11", "0.12.12", "0.12.13", "0.12.14", "0.12.15" 124 | ]); 125 | }); 126 | }); 127 | 128 | it('all', function () { 129 | return VersionParser('all') 130 | .then(function (versions) { 131 | expect(versions).to.eql([ 132 | "0.1.14", "0.1.15", "0.1.16", "0.1.17", "0.1.18", "0.1.19", "0.1.20", "0.1.21", "0.1.22", "0.1.23", "0.1.24", "0.1.25", "0.1.26", "0.1.27", "0.1.28", "0.1.29", "0.1.30", "0.1.31", "0.1.32", "0.1.33", "0.1.90", "0.1.91", "0.1.92", "0.1.93", "0.1.94", "0.1.95", "0.1.96", "0.1.97", "0.1.98", "0.1.99", "0.1.100", "0.1.101", "0.1.102", "0.1.103", "0.1.104", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.3.0", "0.3.1", "0.3.2", "0.3.3", "0.3.4", "0.3.5", "0.3.6", "0.3.7", "0.3.8", "0.4.0", "0.4.1", "0.4.2", "0.4.3", "0.4.4", "0.4.5", "0.4.6", "0.4.7", "0.4.8", "0.4.9", "0.4.10", "0.4.11", "0.4.12", "0.5.0", "0.5.1", "0.5.2", "0.5.3", "0.5.4", "0.5.5", "0.5.6", "0.5.7", "0.5.8", "0.5.9", "0.5.10", "0.6.0", "0.6.1", "0.6.2", "0.6.3", "0.6.4", "0.6.5", "0.6.6", "0.6.7", "0.6.8", "0.6.9", "0.6.10", "0.6.11", "0.6.12", "0.6.13", "0.6.14", "0.6.15", "0.6.16", "0.6.17", "0.6.18", "0.6.19", "0.6.20", "0.6.21", "0.7.0", "0.7.1", "0.7.2", "0.7.3", "0.7.4", "0.7.5", "0.7.6", "0.7.7", "0.7.8", "0.7.9", "0.7.10", "0.7.11", "0.7.12", "0.8.0", "0.8.1", "0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8", "0.8.9", "0.8.10", "0.8.11", "0.8.12", "0.8.13", "0.8.14", "0.8.15", "0.8.16", "0.8.17", "0.8.18", "0.8.19", "0.8.20", "0.8.21", "0.8.22", "0.8.23", "0.8.24", "0.8.25", "0.8.26", "0.8.27", "0.8.28", "0.9.0", "0.9.1", "0.9.2", "0.9.3", "0.9.4", "0.9.5", "0.9.6", "0.9.7", "0.9.8", "0.9.9", "0.9.10", "0.9.11", "0.9.12", "0.10.0", "0.10.1", "0.10.2", "0.10.3", "0.10.4", "0.10.5", "0.10.6", "0.10.7", "0.10.8", "0.10.9", "0.10.10", "0.10.11", "0.10.12", "0.10.13", "0.10.14", "0.10.15", "0.10.16", "0.10.17", "0.10.18", "0.10.19", "0.10.20", "0.10.21", "0.10.22", "0.10.23", "0.10.24", "0.10.25", "0.10.26", "0.10.27", "0.10.28", "0.10.29", "0.10.30", "0.10.31", "0.10.32", "0.10.33", "0.10.34", "0.10.35", "0.10.36", "0.10.37", "0.10.38", "0.10.39", "0.10.40", "0.10.41", "0.10.42", "0.10.43", "0.10.44", "0.10.45", "0.10.46", "0.11.0", "0.11.1", "0.11.2", "0.11.3", "0.11.4", "0.11.5", "0.11.6", "0.11.7", "0.11.8", "0.11.9", "0.11.10", "0.11.11", "0.11.12", "0.11.13", "0.11.14", "0.11.15", "0.11.16", "0.12.0", "0.12.1", "0.12.2", "0.12.3", "0.12.4", "0.12.5", "0.12.6", "0.12.7", "0.12.8", "0.12.9", "0.12.10", "0.12.11", "0.12.12", "0.12.13", "0.12.14", "0.12.15", "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7", "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 133 | ]); 134 | }); 135 | }) 136 | }); 137 | 138 | describe('filters', function () { 139 | describe('eq', function () { 140 | it('major', function () { 141 | return VersionParser('patch | eq:4') 142 | .then(function (versions) { 143 | expect(versions).to.eql([ 144 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7" 145 | ]); 146 | }); 147 | }); 148 | 149 | it('minor', function () { 150 | return VersionParser('patch | eq:4.1') 151 | .then(function (versions) { 152 | expect(versions).to.eql([ 153 | "4.1.0", "4.1.1", "4.1.2" 154 | ]); 155 | }); 156 | }); 157 | 158 | it('patch', function () { 159 | return VersionParser('patch | eq:4.1.2') 160 | .then(function (versions) { 161 | expect(versions).to.eql([ 162 | "4.1.2" 163 | ]); 164 | }); 165 | }); 166 | }); 167 | 168 | describe('neq', function () { 169 | it('major', function () { 170 | return VersionParser('patch | neq:4') 171 | .then(function (versions) { 172 | expect(versions).to.eql([ 173 | "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 174 | ]); 175 | }); 176 | }); 177 | 178 | it('minor', function () { 179 | return VersionParser('patch | neq:4.1') 180 | .then(function (versions) { 181 | expect(versions).to.eql([ 182 | "4.0.0", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7", "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 183 | ]); 184 | }); 185 | }); 186 | 187 | it('patch', function () { 188 | return VersionParser('patch | neq:4.1.2') 189 | .then(function (versions) { 190 | expect(versions).to.eql([ 191 | "4.0.0", "4.1.0", "4.1.1", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7", "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 192 | ]); 193 | }); 194 | }); 195 | }); 196 | 197 | describe('lt', function () { 198 | it('major', function () { 199 | return VersionParser('patch | lt:5') 200 | .then(function (versions) { 201 | expect(versions).to.eql([ 202 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7" 203 | ]); 204 | }); 205 | }); 206 | 207 | it('minor', function () { 208 | return VersionParser('patch | lt:4.3') 209 | .then(function (versions) { 210 | expect(versions).to.eql([ 211 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6" 212 | ]); 213 | }); 214 | }); 215 | 216 | it('patch', function () { 217 | return VersionParser('patch | lt:4.2.1') 218 | .then(function (versions) { 219 | expect(versions).to.eql([ 220 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0" 221 | ]); 222 | }); 223 | }); 224 | }); 225 | 226 | describe('lte', function () { 227 | it('major', function () { 228 | return VersionParser('patch | lte:5') 229 | .then(function (versions) { 230 | expect(versions).to.eql([ 231 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2", "4.4.0", "4.4.1", "4.4.2", "4.4.3", "4.4.4", "4.4.5", "4.4.6", "4.4.7", "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0" 232 | ]); 233 | }); 234 | }); 235 | 236 | it('minor', function () { 237 | return VersionParser('patch | lte:4.3') 238 | .then(function (versions) { 239 | expect(versions).to.eql([ 240 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1", "4.2.2", "4.2.3", "4.2.4", "4.2.5", "4.2.6", "4.3.0", "4.3.1", "4.3.2" 241 | ]); 242 | }); 243 | }); 244 | 245 | it('patch', function () { 246 | return VersionParser('patch | lte:4.2.1') 247 | .then(function (versions) { 248 | expect(versions).to.eql([ 249 | "4.0.0", "4.1.0", "4.1.1", "4.1.2", "4.2.0", "4.2.1" 250 | ]); 251 | }); 252 | }); 253 | }); 254 | 255 | describe('gt', function () { 256 | it('major', function () { 257 | return VersionParser('patch | gt:5') 258 | .then(function (versions) { 259 | expect(versions).to.eql([ 260 | "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 261 | ]); 262 | }); 263 | }); 264 | 265 | it('minor', function () { 266 | return VersionParser('patch | gt:5.10') 267 | .then(function (versions) { 268 | expect(versions).to.eql([ 269 | "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 270 | ]); 271 | }); 272 | }); 273 | 274 | it('patch', function () { 275 | return VersionParser('patch | gt:5.11.0') 276 | .then(function (versions) { 277 | expect(versions).to.eql([ 278 | "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 279 | ]); 280 | }); 281 | }); 282 | }); 283 | 284 | describe('gte', function () { 285 | it('major', function () { 286 | return VersionParser('patch | gte:5') 287 | .then(function (versions) { 288 | expect(versions).to.eql([ 289 | "5.0.0", "5.1.0", "5.1.1", "5.2.0", "5.3.0", "5.4.0", "5.4.1", "5.5.0", "5.6.0", "5.7.0", "5.7.1", "5.8.0", "5.9.0", "5.9.1", "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 290 | ]); 291 | }); 292 | }); 293 | 294 | it('minor', function () { 295 | return VersionParser('patch | gte:5.10') 296 | .then(function (versions) { 297 | expect(versions).to.eql([ 298 | "5.10.0", "5.10.1", "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 299 | ]); 300 | }); 301 | }); 302 | 303 | it('patch', function () { 304 | return VersionParser('patch | gte:5.11.0') 305 | .then(function (versions) { 306 | expect(versions).to.eql([ 307 | "5.11.0", "5.11.1", "5.12.0", "6.0.0", "6.1.0", "6.2.0", "6.2.1", "6.2.2", "6.3.0", "6.3.1" 308 | ]); 309 | }); 310 | }); 311 | }); 312 | 313 | it('lts', function () { 314 | return VersionParser('major | lts') 315 | .then(function (versions) { 316 | expect(versions).to.eql([ 317 | '4.4.7' 318 | ]); 319 | }); 320 | }); 321 | 322 | it('invalid filter', function () { 323 | return VersionParser('major | invalid') 324 | .then(function (versions) { 325 | expect(true).to.be.false(); 326 | }) 327 | .catch(function (error) { 328 | expect(error).to.have.property('message'); 329 | expect(error.message).to.equal('Unknown filter type: invalid'); 330 | }); 331 | }) 332 | }); 333 | 334 | }); 335 | --------------------------------------------------------------------------------