├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── Gruntfile.js ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── test ├── index.spec.js └── template.ejs └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | #Temporary data 6 | .tmp 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # Deployed apps should consider commenting this line out: 27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "camelcase": true, 5 | "curly": true, 6 | "eqeqeq": true, 7 | "esnext": true, 8 | "immed": true, 9 | "latedef": true, 10 | "newcap": true, 11 | "noarg": true, 12 | "node": true, 13 | "proto": true, 14 | "mocha": true, 15 | "quotmark": "single", 16 | "strict": true, 17 | "undef": true, 18 | "unused": true, 19 | "expr": true, 20 | "ignore": true 21 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ssl 3 | .DS_STORE 4 | *~ 5 | .idea 6 | nbproject 7 | test 8 | .git 9 | .gitignore 10 | .tmp 11 | *.swo 12 | *.swp 13 | *.swn 14 | *.swm 15 | *.log 16 | .jshintrc 17 | .editorconfig 18 | docs 19 | doc.html 20 | .travis.yml 21 | Gruntfile.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - npm install -g grunt-cli -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | // add grunt tasks. 6 | grunt.loadNpmTasks('grunt-mocha-test'); 7 | grunt.loadNpmTasks('grunt-contrib-jshint'); 8 | grunt.loadNpmTasks('grunt-contrib-watch'); 9 | 10 | grunt.initConfig({ 11 | // Configure a mochaTest task 12 | mochaTest: { 13 | test: { 14 | options: { 15 | reporter: 'spec', 16 | timeout: 20000 17 | }, 18 | src: ['test/**/*.js'] 19 | } 20 | }, 21 | jshint: { 22 | options: { 23 | reporter: require('jshint-stylish'), 24 | jshintrc: '.jshintrc' 25 | }, 26 | all: [ 27 | 'Gruntfile.js', 28 | 'index.js', 29 | 'test/**/*.js' 30 | ] 31 | }, 32 | watch: { 33 | all: { 34 | files: [ 35 | 'Gruntfile.js', 36 | 'index.js', 37 | 'test/**/*.js' 38 | ], 39 | tasks: ['default'] 40 | } 41 | } 42 | }); 43 | 44 | //custom tasks 45 | grunt.registerTask('default', ['jshint', 'mochaTest', 'watch']); 46 | grunt.registerTask('test', ['jshint', 'mochaTest']); 47 | 48 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mock-express-response 2 | 3 | [](https://travis-ci.org/lykmapipo/mock-express-response) 4 | 5 | Nodejs library to mock [expressjs](https://github.com/strongloop/express/) http response based on [mock-res](https://github.com/diachedelic/mock-res) 6 | 7 | See [mock-express-request](https://github.com/lykmapipo/mock-express-request) to mock express http request. 8 | 9 | *Note: The mocked response instance have the same properties and methods as an instance of express http response* 10 | 11 | ## Installation 12 | ```sh 13 | $ npm install --save-dev mock-express-response 14 | ``` 15 | 16 | ## Usage 17 | ```js 18 | var ejs = require('ejs'); 19 | var MockExpressRequest = require('mock-express-request'); 20 | var MockExpressResponse = require('mock-express-response'); 21 | 22 | // Basic usage 23 | var response = new MockExpressResponse(); 24 | 25 | // With options 26 | var response = new MockExpressResponse({ 27 | render: ejs.renderFile, //use ejs as template engine 28 | request: new MockExpressRequest({ 29 | //request options 30 | ... 31 | }); 32 | ... 33 | }); 34 | 35 | //express response methods 36 | //and properties 37 | 38 | //send json response 39 | response.json({user:{active:true}}); 40 | 41 | //render a template 42 | response.render('user.ejs',{user:{active:true}}); 43 | 44 | //send a response 45 | response.send('
Hi
'); 46 | 47 | ... 48 | 49 | //to obtain json response 50 | var result = response._getJSON(); 51 | 52 | //to obtain text/html response 53 | var result = response._getString(); 54 | 55 | ... 56 | 57 | ``` 58 | 59 | ## TODO 60 | - [ ] File send mock 61 | 62 | 63 | ## Testing 64 | * Clone this repository 65 | 66 | * Install all development dependencies 67 | ```sh 68 | $ npm install 69 | ``` 70 | 71 | * Then run test 72 | ```sh 73 | $ npm test 74 | ``` 75 | 76 | 77 | ## Contribute 78 | It will be nice, if you open an issue first so that we can know what is going on, then, fork this repo and push in your ideas. Do not forget to add a bit of test(s) of what value you adding. 79 | 80 | 81 | ## Licence 82 | The MIT License (MIT) 83 | 84 | Copyright (c) 2015 lykmapipo & Contributors 85 | 86 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 89 | 90 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | //dependencies 5 | var path = require('path'); 6 | var contentDisposition = require('content-disposition'); 7 | var onFinished = require('on-finished'); 8 | var escapeHtml = require('escape-html'); 9 | var merge = require('utils-merge'); 10 | var Utils = require('./utils'); 11 | var MockResponse = require('mock-res'); 12 | var MockExpressRequest = require('mock-express-request'); 13 | var util = require('util'); 14 | var send = require('send'); 15 | var mime = send.mime; 16 | var STATUS_CODES = require('http').STATUS_CODES; 17 | var deprecate = require('depd')('mock-express-response'); 18 | var setCharset = Utils.setCharset; 19 | var normalizeType = Utils.normalizeType; 20 | var normalizeTypes = Utils.normalizeTypes; 21 | var isAbsolute = Utils.isAbsolute; 22 | var vary = require('vary'); 23 | var sign = require('cookie-signature').sign; 24 | var cookie = require('cookie'); 25 | var extname = path.extname; 26 | 27 | 28 | /** 29 | * @constructor 30 | * @description Express response mock 31 | * @public 32 | */ 33 | function MockExpressResponse(options) { 34 | options = options || {}; 35 | 36 | MockResponse.call(this, options.finish); 37 | 38 | this.app = { 39 | 'jsonp callback name': 'callback', 40 | render: function(view, data, fn) { 41 | //default implementation is 42 | //to return uncompiled view 43 | // 44 | //this must me ovveriden by view engine 45 | //of choice 46 | if ('function' === typeof options.render) { 47 | options.render(view, data, fn); 48 | } else { 49 | fn(null, view); 50 | } 51 | } 52 | }; 53 | 54 | this.locals = options.locals || {}; 55 | 56 | this.req = options.request || new MockExpressRequest({ 57 | query: {} 58 | }); 59 | } 60 | util.inherits(MockExpressResponse, MockResponse); 61 | 62 | 63 | //------------------------------------------------------------------------------ 64 | // Express respnse methods 65 | //------------------------------------------------------------------------------ 66 | 67 | /** 68 | * Set status `code`. 69 | * 70 | * @param {Number} code 71 | * @return {ServerResponse} 72 | * @api public 73 | */ 74 | MockExpressResponse.prototype.status = function(code) { 75 | this.statusCode = code; 76 | this.statusMessage = STATUS_CODES[this.statusCode]; 77 | return this; 78 | }; 79 | 80 | 81 | /** 82 | * Set Link header field with the given `links`. 83 | * 84 | * Examples: 85 | * 86 | * res.links({ 87 | * next: 'http://api.example.com/users?page=2', 88 | * last: 'http://api.example.com/users?page=5' 89 | * }); 90 | * 91 | * @param {Object} links 92 | * @return {ServerResponse} 93 | * @api public 94 | */ 95 | MockExpressResponse.prototype.links = function(links) { 96 | var link = this.get('Link') || ''; 97 | if (link) { 98 | link += ', '; 99 | } 100 | return this.set('Link', link + Object.keys(links).map(function(rel) { 101 | return '<' + links[rel] + '>; rel="' + rel + '"'; 102 | }).join(', ')); 103 | }; 104 | 105 | 106 | /** 107 | * Send a response. 108 | * 109 | * Examples: 110 | * 111 | * res.send(new Buffer('wahoo')); 112 | * res.send({ some: 'json' }); 113 | * res.send('some html
'); 114 | * 115 | * @param {string|number|boolean|object|Buffer} body 116 | * @api public 117 | */ 118 | MockExpressResponse.prototype.send = function send(body) { 119 | var chunk = body; 120 | var encoding; 121 | var len; 122 | var req = this.req; 123 | var type; 124 | 125 | // settings 126 | var app = this.app; 127 | 128 | // allow status / body 129 | if (arguments.length === 2) { 130 | // res.send(body, status) backwards compat 131 | if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') { 132 | deprecate('res.send(body, status): Use res.status(status).send(body) instead'); 133 | this.statusCode = arguments[1]; 134 | } else { 135 | deprecate('res.send(status, body): Use res.status(status).send(body) instead'); 136 | this.statusCode = arguments[0]; 137 | chunk = arguments[1]; 138 | } 139 | } 140 | 141 | // disambiguate res.send(status) and res.send(status, num) 142 | if (typeof chunk === 'number' && arguments.length === 1) { 143 | // res.send(status) will set status message as text string 144 | if (!this.get('Content-Type')) { 145 | this.type('txt'); 146 | } 147 | 148 | deprecate('res.send(status): Use res.sendStatus(status) instead'); 149 | this.statusCode = chunk; 150 | chunk = STATUS_CODES[chunk]; 151 | } 152 | 153 | switch (typeof chunk) { 154 | // string defaulting to html 155 | case 'string': 156 | if (!this.get('Content-Type')) { 157 | this.type('html'); 158 | } 159 | break; 160 | case 'boolean': 161 | case 'number': 162 | case 'object': 163 | if (chunk === null) { 164 | chunk = ''; 165 | } else if (Buffer.isBuffer(chunk)) { 166 | if (!this.get('Content-Type')) { 167 | this.type('bin'); 168 | } 169 | } else { 170 | return this.json(chunk); 171 | } 172 | break; 173 | } 174 | 175 | // write strings in utf-8 176 | if (typeof chunk === 'string') { 177 | encoding = 'utf8'; 178 | type = this.get('Content-Type'); 179 | 180 | // reflect this in content-type 181 | if (typeof type === 'string') { 182 | this.set('Content-Type', setCharset(type, 'utf-8')); 183 | } 184 | } 185 | 186 | // populate Content-Length 187 | if (chunk !== undefined) { 188 | if (!Buffer.isBuffer(chunk)) { 189 | // convert chunk to Buffer; saves later double conversions 190 | chunk = new Buffer(chunk, encoding); 191 | encoding = undefined; 192 | } 193 | 194 | len = chunk.length; 195 | this.set('Content-Length', len); 196 | } 197 | 198 | // populate ETag 199 | var etag; 200 | var generateETag = len !== undefined && app['etag fn']; 201 | if (typeof generateETag === 'function' && !this.get('ETag')) { 202 | if ((etag = generateETag(chunk, encoding))) { 203 | this.set('ETag', etag); 204 | } 205 | } 206 | 207 | // freshness 208 | if (req.fresh) { 209 | this.statusCode = 304; 210 | } 211 | 212 | // strip irrelevant headers 213 | if (204 === this.statusCode || 304 === this.statusCode) { 214 | this.removeHeader('Content-Type'); 215 | this.removeHeader('Content-Length'); 216 | this.removeHeader('Transfer-Encoding'); 217 | chunk = ''; 218 | } 219 | 220 | if (req.method === 'HEAD') { 221 | // skip body for HEAD 222 | this.end(); 223 | } else { 224 | // respond 225 | this.end(chunk, encoding); 226 | } 227 | 228 | return this; 229 | }; 230 | 231 | 232 | /** 233 | * Send JSON response. 234 | * 235 | * Examples: 236 | * 237 | * res.json(null); 238 | * res.json({ user: 'tj' }); 239 | * 240 | * @param {string|number|boolean|object} obj 241 | * @api public 242 | */ 243 | MockExpressResponse.prototype.json = function json(obj) { 244 | var val = obj; 245 | 246 | // allow status / body 247 | if (arguments.length === 2) { 248 | // res.json(body, status) backwards compat 249 | if (typeof arguments[1] === 'number') { 250 | deprecate('res.json(obj, status): Use res.status(status).json(obj) instead'); 251 | this.statusCode = arguments[1]; 252 | } else { 253 | deprecate('res.json(status, obj): Use res.status(status).json(obj) instead'); 254 | this.statusCode = arguments[0]; 255 | val = arguments[1]; 256 | } 257 | } 258 | 259 | // settings 260 | var app = this.app; 261 | var replacer = app['json replacer']; 262 | var spaces = app['json spaces']; 263 | var body = JSON.stringify(val, replacer, spaces); 264 | 265 | // content-type 266 | if (!this.get('Content-Type')) { 267 | this.set('Content-Type', 'application/json'); 268 | } 269 | 270 | return this.send(body); 271 | }; 272 | 273 | 274 | /** 275 | * Send JSON response with JSONP callback support. 276 | * 277 | * Examples: 278 | * 279 | * res.jsonp(null); 280 | * res.jsonp({ user: 'tj' }); 281 | * 282 | * @param {string|number|boolean|object} obj 283 | * @api public 284 | */ 285 | MockExpressResponse.prototype.jsonp = function jsonp(obj) { 286 | var val = obj; 287 | 288 | // allow status / body 289 | if (arguments.length === 2) { 290 | // res.json(body, status) backwards compat 291 | if (typeof arguments[1] === 'number') { 292 | deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead'); 293 | this.statusCode = arguments[1]; 294 | } else { 295 | deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead'); 296 | this.statusCode = arguments[0]; 297 | val = arguments[1]; 298 | } 299 | } 300 | 301 | // settings 302 | var app = this.app; 303 | var replacer = app['json replacer']; 304 | var spaces = app['json spaces']; 305 | var body = JSON.stringify(val, replacer, spaces); 306 | var callback = this.req.query[app['jsonp callback name']]; 307 | 308 | // content-type 309 | if (!this.get('Content-Type')) { 310 | this.set('X-Content-Type-Options', 'nosniff'); 311 | this.set('Content-Type', 'application/json'); 312 | } 313 | 314 | // fixup callback 315 | if (Array.isArray(callback)) { 316 | callback = callback[0]; 317 | } 318 | 319 | // jsonp 320 | if (typeof callback === 'string' && callback.length !== 0) { 321 | this.charset = 'utf-8'; 322 | this.set('X-Content-Type-Options', 'nosniff'); 323 | this.set('Content-Type', 'text/javascript'); 324 | 325 | // restrict callback charset 326 | callback = callback.replace(/[^\[\]\w$.]/g, ''); 327 | 328 | // replace chars not allowed in JavaScript that are in JSON 329 | body = body 330 | .replace(/\u2028/g, '\\u2028') 331 | .replace(/\u2029/g, '\\u2029'); 332 | 333 | // the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse" 334 | // the typeof check is just to reduce client error noise 335 | body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');'; 336 | } 337 | 338 | return this.send(body); 339 | }; 340 | 341 | 342 | /** 343 | * Send given HTTP status code. 344 | * 345 | * Sets the response status to `statusCode` and the body of the 346 | * response to the standard description from node's http.STATUS_CODES 347 | * or the statusCode number if no description. 348 | * 349 | * Examples: 350 | * 351 | * res.sendStatus(200); 352 | * 353 | * @param {number} statusCode 354 | * @api public 355 | */ 356 | MockExpressResponse.prototype.sendStatus = function sendStatus(statusCode) { 357 | var body = STATUS_CODES[statusCode] || String(statusCode); 358 | 359 | this.statusCode = statusCode; 360 | this.type('txt'); 361 | 362 | return this.send(body); 363 | }; 364 | 365 | 366 | // pipe the send file stream 367 | function sendfile(res, file, options, callback) { 368 | var done = false; 369 | var streaming; 370 | 371 | // request aborted 372 | function onaborted() { 373 | if (done) { 374 | return; 375 | } 376 | done = true; 377 | 378 | var err = new Error('Request aborted'); 379 | err.code = 'ECONNABORTED'; 380 | callback(err); 381 | } 382 | 383 | // directory 384 | function ondirectory() { 385 | if (done) { 386 | return; 387 | } 388 | done = true; 389 | 390 | var err = new Error('EISDIR, read'); 391 | err.code = 'EISDIR'; 392 | callback(err); 393 | } 394 | 395 | // errors 396 | function onerror(err) { 397 | if (done) { 398 | return; 399 | } 400 | done = true; 401 | callback(err); 402 | } 403 | 404 | // ended 405 | function onend() { 406 | if (done) { 407 | return; 408 | } 409 | done = true; 410 | callback(); 411 | } 412 | 413 | // file 414 | function onfile() { 415 | streaming = false; 416 | } 417 | 418 | // finished 419 | function onfinish(err) { 420 | if (err && err.code === 'ECONNRESET') { 421 | return onaborted(); 422 | } 423 | if (err) { 424 | return onerror(err); 425 | } 426 | if (done) { 427 | return; 428 | } 429 | 430 | setImmediate(function() { 431 | if (streaming !== false && !done) { 432 | onaborted(); 433 | return; 434 | } 435 | 436 | if (done) { 437 | return; 438 | } 439 | done = true; 440 | callback(); 441 | }); 442 | } 443 | 444 | // streaming 445 | function onstream() { 446 | streaming = true; 447 | } 448 | 449 | file.on('directory', ondirectory); 450 | file.on('end', onend); 451 | file.on('error', onerror); 452 | file.on('file', onfile); 453 | file.on('stream', onstream); 454 | onFinished(res, onfinish); 455 | 456 | if (options.headers) { 457 | // set headers on successful transfer 458 | file.on('headers', function headers(res) { 459 | var obj = options.headers; 460 | var keys = Object.keys(obj); 461 | 462 | for (var i = 0; i < keys.length; i++) { 463 | var k = keys[i]; 464 | res.setHeader(k, obj[k]); 465 | } 466 | }); 467 | } 468 | 469 | // pipe 470 | file.pipe(res); 471 | } 472 | 473 | 474 | /** 475 | * Transfer the file at the given `path`. 476 | * 477 | * Automatically sets the _Content-Type_ response header field. 478 | * The callback `fn(err)` is invoked when the transfer is complete 479 | * or when an error occurs. Be sure to check `res.sentHeader` 480 | * if you wish to attempt responding, as the header and some data 481 | * may have already been transferred. 482 | * 483 | * Options: 484 | * 485 | * - `maxAge` defaulting to 0 (can be string converted by `ms`) 486 | * - `root` root directory for relative filenames 487 | * - `headers` object of headers to serve with file 488 | * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them 489 | * 490 | * Other options are passed along to `send`. 491 | * 492 | * Examples: 493 | * 494 | * The following example illustrates how `res.sendFile()` may 495 | * be used as an alternative for the `static()` middleware for 496 | * dynamic situations. The code backing `res.sendFile()` is actually 497 | * the same code, so HTTP cache support etc is identical. 498 | * 499 | * app.get('/user/:uid/photos/:file', function(req, res){ 500 | * var uid = req.params.uid 501 | * , file = req.params.file; 502 | * 503 | * req.user.mayViewFilesFrom(uid, function(yes){ 504 | * if (yes) { 505 | * res.sendFile('/uploads/' + uid + '/' + file); 506 | * } else { 507 | * res.send(403, 'Sorry! you cant see that.'); 508 | * } 509 | * }); 510 | * }); 511 | * 512 | * @api public 513 | */ 514 | MockExpressResponse.prototype.sendFile = function sendFile(path, options, fn) { 515 | var req = this.req; 516 | var res = this; 517 | var next = req.next; 518 | 519 | if (!path) { 520 | throw new TypeError('path argument is required to res.sendFile'); 521 | } 522 | 523 | // support function as second arg 524 | if (typeof options === 'function') { 525 | fn = options; 526 | options = {}; 527 | } 528 | 529 | options = options || {}; 530 | 531 | if (!options.root && !isAbsolute(path)) { 532 | throw new TypeError('path must be absolute or specify root to res.sendFile'); 533 | } 534 | 535 | // create file stream 536 | var pathname = encodeURI(path); 537 | var file = send(req, pathname, options); 538 | 539 | // transfer 540 | sendfile(res, file, options, function(err) { 541 | if (fn) { 542 | return fn(err); 543 | } 544 | if (err && err.code === 'EISDIR') { 545 | return next(); 546 | } 547 | 548 | // next() all but write errors 549 | if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') { 550 | next(err); 551 | } 552 | }); 553 | }; 554 | 555 | 556 | // /** 557 | // * Transfer the file at the given `path`. 558 | // * 559 | // * Automatically sets the _Content-Type_ response header field. 560 | // * The callback `fn(err)` is invoked when the transfer is complete 561 | // * or when an error occurs. Be sure to check `res.sentHeader` 562 | // * if you wish to attempt responding, as the header and some data 563 | // * may have already been transferred. 564 | // * 565 | // * Options: 566 | // * 567 | // * - `maxAge` defaulting to 0 (can be string converted by `ms`) 568 | // * - `root` root directory for relative filenames 569 | // * - `headers` object of headers to serve with file 570 | // * - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them 571 | // * 572 | // * Other options are passed along to `send`. 573 | // * 574 | // * Examples: 575 | // * 576 | // * The following example illustrates how `res.sendfile()` may 577 | // * be used as an alternative for the `static()` middleware for 578 | // * dynamic situations. The code backing `res.sendfile()` is actually 579 | // * the same code, so HTTP cache support etc is identical. 580 | // * 581 | // * app.get('/user/:uid/photos/:file', function(req, res){ 582 | // * var uid = req.params.uid 583 | // * , file = req.params.file; 584 | // * 585 | // * req.user.mayViewFilesFrom(uid, function(yes){ 586 | // * if (yes) { 587 | // * res.sendfile('/uploads/' + uid + '/' + file); 588 | // * } else { 589 | // * res.send(403, 'Sorry! you cant see that.'); 590 | // * } 591 | // * }); 592 | // * }); 593 | // * 594 | // * @api public 595 | // */ 596 | 597 | // res.sendfile = function(path, options, fn){ 598 | // var req = this.req; 599 | // var res = this; 600 | // var next = req.next; 601 | 602 | // // support function as second arg 603 | // if (typeof options === 'function') { 604 | // fn = options; 605 | // options = {}; 606 | // } 607 | 608 | // options = options || {}; 609 | 610 | // // create file stream 611 | // var file = send(req, path, options); 612 | 613 | // // transfer 614 | // sendfile(res, file, options, function (err) { 615 | // if (fn) return fn(err); 616 | // if (err && err.code === 'EISDIR') return next(); 617 | 618 | // // next() all but write errors 619 | // if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') { 620 | // next(err); 621 | // } 622 | // }); 623 | // }; 624 | 625 | // res.sendfile = deprecate.function(res.sendfile, 626 | // 'res.sendfile: Use res.sendFile instead'); 627 | 628 | // /** 629 | // * Transfer the file at the given `path` as an attachment. 630 | // * 631 | // * Optionally providing an alternate attachment `filename`, 632 | // * and optional callback `fn(err)`. The callback is invoked 633 | // * when the data transfer is complete, or when an error has 634 | // * ocurred. Be sure to check `res.headersSent` if you plan to respond. 635 | // * 636 | // * This method uses `res.sendfile()`. 637 | // * 638 | // * @api public 639 | // */ 640 | 641 | // res.download = function download(path, filename, fn) { 642 | // // support function as second arg 643 | // if (typeof filename === 'function') { 644 | // fn = filename; 645 | // filename = null; 646 | // } 647 | 648 | // filename = filename || path; 649 | 650 | // // set Content-Disposition when file is sent 651 | // var headers = { 652 | // 'Content-Disposition': contentDisposition(filename) 653 | // }; 654 | 655 | // // Resolve the full path for sendFile 656 | // var fullPath = resolve(path); 657 | 658 | // return this.sendFile(fullPath, { headers: headers }, fn); 659 | // }; 660 | 661 | 662 | /** 663 | * Set _Content-Type_ response header with `type` through `mime.lookup()` 664 | * when it does not contain "/", or set the Content-Type to `type` otherwise. 665 | * 666 | * Examples: 667 | * 668 | * res.type('.html'); 669 | * res.type('html'); 670 | * res.type('json'); 671 | * res.type('application/json'); 672 | * res.type('png'); 673 | * 674 | * @param {String} type 675 | * @return {ServerResponse} for chaining 676 | * @api public 677 | */ 678 | MockExpressResponse.prototype.contentType = 679 | MockExpressResponse.prototype.type = function(type) { 680 | /*jshint bitwise:false*/ 681 | return this.set('Content-Type', ~type.indexOf('/') ? type : mime.lookup(type)); 682 | /*jshint bitwise:true*/ 683 | }; 684 | 685 | 686 | /** 687 | * Respond to the Acceptable formats using an `obj` 688 | * of mime-type callbacks. 689 | * 690 | * This method uses `req.accepted`, an array of 691 | * acceptable types ordered by their quality values. 692 | * When "Accept" is not present the _first_ callback 693 | * is invoked, otherwise the first match is used. When 694 | * no match is performed the server responds with 695 | * 406 "Not Acceptable". 696 | * 697 | * Content-Type is set for you, however if you choose 698 | * you may alter this within the callback using `res.type()` 699 | * or `res.set('Content-Type', ...)`. 700 | * 701 | * res.format({ 702 | * 'text/plain': function(){ 703 | * res.send('hey'); 704 | * }, 705 | * 706 | * 'text/html': function(){ 707 | * res.send('hey
'); 708 | * }, 709 | * 710 | * 'appliation/json': function(){ 711 | * res.send({ message: 'hey' }); 712 | * } 713 | * }); 714 | * 715 | * In addition to canonicalized MIME types you may 716 | * also use extnames mapped to these types: 717 | * 718 | * res.format({ 719 | * text: function(){ 720 | * res.send('hey'); 721 | * }, 722 | * 723 | * html: function(){ 724 | * res.send('hey
'); 725 | * }, 726 | * 727 | * json: function(){ 728 | * res.send({ message: 'hey' }); 729 | * } 730 | * }); 731 | * 732 | * By default Express passes an `Error` 733 | * with a `.status` of 406 to `next(err)` 734 | * if a match is not made. If you provide 735 | * a `.default` callback it will be invoked 736 | * instead. 737 | * 738 | * @param {Object} obj 739 | * @return {ServerResponse} for chaining 740 | * @api public 741 | */ 742 | MockExpressResponse.prototype.format = function(obj) { 743 | var req = this.req; 744 | var next = req.next; 745 | 746 | var fn = obj.default; 747 | if (fn) { 748 | delete obj.default; 749 | } 750 | var keys = Object.keys(obj); 751 | 752 | var key = req.accepts(keys); 753 | 754 | this.vary('Accept'); 755 | 756 | if (key) { 757 | this.set('Content-Type', normalizeType(key).value); 758 | obj[key](req, this, next); 759 | } else if (fn) { 760 | fn(); 761 | } else { 762 | var err = new Error('Not Acceptable'); 763 | err.status = 406; 764 | err.types = normalizeTypes(keys).map(function(o) { 765 | return o.value; 766 | }); 767 | next(err); 768 | } 769 | 770 | return this; 771 | }; 772 | 773 | 774 | /** 775 | * Set _Content-Disposition_ header to _attachment_ with optional `filename`. 776 | * 777 | * @param {String} filename 778 | * @return {ServerResponse} 779 | * @api public 780 | */ 781 | MockExpressResponse.prototype.attachment = function attachment(filename) { 782 | if (filename) { 783 | this.type(extname(filename)); 784 | } 785 | 786 | this.set('Content-Disposition', contentDisposition(filename)); 787 | 788 | return this; 789 | }; 790 | 791 | 792 | /** 793 | * Append additional header `field` with value `val`. 794 | * 795 | * Example: 796 | * 797 | * res.append('Link', ['' + STATUS_CODES[status] + '. Redirecting to ' + u + '
'; 1027 | }, 1028 | 1029 | default: function() { 1030 | body = ''; 1031 | } 1032 | }); 1033 | 1034 | // Respond 1035 | this.statusCode = status; 1036 | this.set('Content-Length', Buffer.byteLength(body)); 1037 | 1038 | if (this.req.method === 'HEAD') { 1039 | this.end(); 1040 | } else { 1041 | this.end(body); 1042 | } 1043 | }; 1044 | 1045 | 1046 | /** 1047 | * Add `field` to Vary. If already present in the Vary set, then 1048 | * this call is simply ignored. 1049 | * 1050 | * @param {Array|String} field 1051 | * @return {ServerResponse} for chaining 1052 | * @api public 1053 | */ 1054 | MockExpressResponse.prototype.vary = function(field) { 1055 | // checks for back-compat 1056 | if (!field || (Array.isArray(field) && !field.length)) { 1057 | deprecate('res.vary(): Provide a field name'); 1058 | return this; 1059 | } 1060 | 1061 | vary(this, field); 1062 | 1063 | return this; 1064 | }; 1065 | 1066 | 1067 | /** 1068 | * Render `view` with the given `options` and optional callback `fn`. 1069 | * When a callback function is given a response will _not_ be made 1070 | * automatically, otherwise a response of _200_ and _text/html_ is given. 1071 | * 1072 | * Options: 1073 | * 1074 | * - `cache` boolean hinting to the engine it should cache 1075 | * - `filename` filename of the view being rendered 1076 | * 1077 | * @api public 1078 | */ 1079 | MockExpressResponse.prototype.render = function(view, options, fn) { 1080 | options = options || {}; 1081 | var self = this; 1082 | var req = this.req; 1083 | var app = this.app; 1084 | 1085 | // support callback function as second arg 1086 | if ('function' === typeof options) { 1087 | fn = options, options = {}; 1088 | } 1089 | 1090 | // merge res.locals 1091 | options._locals = self.locals; 1092 | 1093 | // default callback to respond 1094 | fn = fn || function(err, str) { 1095 | if (err) { 1096 | return req.next(err); 1097 | } 1098 | 1099 | self.send(str); 1100 | }; 1101 | 1102 | // render 1103 | app.render(view, options, fn); 1104 | }; 1105 | 1106 | 1107 | /** 1108 | * @description export MockExpressResponse 1109 | * @type {[type]} 1110 | */ 1111 | module.exports = MockExpressResponse; 1112 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-express-response", 3 | "version": "0.3.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 11 | "dev": true 12 | }, 13 | "accepts": { 14 | "version": "1.3.4", 15 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", 16 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 17 | "requires": { 18 | "mime-types": "~2.1.16", 19 | "negotiator": "0.6.1" 20 | } 21 | }, 22 | "ansi-regex": { 23 | "version": "2.1.1", 24 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 25 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", 26 | "dev": true 27 | }, 28 | "ansi-styles": { 29 | "version": "2.2.1", 30 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 31 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 32 | "dev": true 33 | }, 34 | "argparse": { 35 | "version": "1.0.9", 36 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", 37 | "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", 38 | "dev": true, 39 | "requires": { 40 | "sprintf-js": "~1.0.2" 41 | } 42 | }, 43 | "array-find-index": { 44 | "version": "1.0.2", 45 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 46 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", 47 | "dev": true 48 | }, 49 | "assertion-error": { 50 | "version": "1.0.2", 51 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", 52 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", 53 | "dev": true 54 | }, 55 | "async": { 56 | "version": "1.5.2", 57 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 58 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 59 | "dev": true 60 | }, 61 | "balanced-match": { 62 | "version": "1.0.0", 63 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 64 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 65 | "dev": true 66 | }, 67 | "beeper": { 68 | "version": "1.1.1", 69 | "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", 70 | "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", 71 | "dev": true 72 | }, 73 | "body-parser": { 74 | "version": "1.14.2", 75 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", 76 | "integrity": "sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk=", 77 | "dev": true, 78 | "requires": { 79 | "bytes": "2.2.0", 80 | "content-type": "~1.0.1", 81 | "debug": "~2.2.0", 82 | "depd": "~1.1.0", 83 | "http-errors": "~1.3.1", 84 | "iconv-lite": "0.4.13", 85 | "on-finished": "~2.3.0", 86 | "qs": "5.2.0", 87 | "raw-body": "~2.1.5", 88 | "type-is": "~1.6.10" 89 | }, 90 | "dependencies": { 91 | "debug": { 92 | "version": "2.2.0", 93 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 94 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 95 | "dev": true, 96 | "requires": { 97 | "ms": "0.7.1" 98 | } 99 | }, 100 | "http-errors": { 101 | "version": "1.3.1", 102 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", 103 | "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", 104 | "dev": true, 105 | "requires": { 106 | "inherits": "~2.0.1", 107 | "statuses": "1" 108 | } 109 | }, 110 | "iconv-lite": { 111 | "version": "0.4.13", 112 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", 113 | "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", 114 | "dev": true 115 | }, 116 | "ms": { 117 | "version": "0.7.1", 118 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 119 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 120 | "dev": true 121 | }, 122 | "qs": { 123 | "version": "5.2.0", 124 | "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", 125 | "integrity": "sha1-qfMRQq9GjLcrJbMBNrokVoNJFr4=", 126 | "dev": true 127 | } 128 | } 129 | }, 130 | "brace-expansion": { 131 | "version": "1.1.8", 132 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 133 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 134 | "dev": true, 135 | "requires": { 136 | "balanced-match": "^1.0.0", 137 | "concat-map": "0.0.1" 138 | } 139 | }, 140 | "browser-stdout": { 141 | "version": "1.3.0", 142 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 143 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 144 | "dev": true 145 | }, 146 | "builtin-modules": { 147 | "version": "1.1.1", 148 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 149 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 150 | "dev": true 151 | }, 152 | "bytes": { 153 | "version": "2.2.0", 154 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", 155 | "integrity": "sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg=", 156 | "dev": true 157 | }, 158 | "camelcase": { 159 | "version": "2.1.1", 160 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 161 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", 162 | "dev": true 163 | }, 164 | "camelcase-keys": { 165 | "version": "2.1.0", 166 | "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", 167 | "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", 168 | "dev": true, 169 | "requires": { 170 | "camelcase": "^2.0.0", 171 | "map-obj": "^1.0.0" 172 | } 173 | }, 174 | "chai": { 175 | "version": "4.1.2", 176 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 177 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 178 | "dev": true, 179 | "requires": { 180 | "assertion-error": "^1.0.1", 181 | "check-error": "^1.0.1", 182 | "deep-eql": "^3.0.0", 183 | "get-func-name": "^2.0.0", 184 | "pathval": "^1.0.0", 185 | "type-detect": "^4.0.0" 186 | } 187 | }, 188 | "chalk": { 189 | "version": "1.1.3", 190 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 191 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 192 | "dev": true, 193 | "requires": { 194 | "ansi-styles": "^2.2.1", 195 | "escape-string-regexp": "^1.0.2", 196 | "has-ansi": "^2.0.0", 197 | "strip-ansi": "^3.0.0", 198 | "supports-color": "^2.0.0" 199 | } 200 | }, 201 | "check-error": { 202 | "version": "1.0.2", 203 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 204 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 205 | "dev": true 206 | }, 207 | "cli": { 208 | "version": "1.0.1", 209 | "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", 210 | "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", 211 | "dev": true, 212 | "requires": { 213 | "exit": "0.1.2", 214 | "glob": "^7.1.1" 215 | }, 216 | "dependencies": { 217 | "glob": { 218 | "version": "7.1.2", 219 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 220 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 221 | "dev": true, 222 | "requires": { 223 | "fs.realpath": "^1.0.0", 224 | "inflight": "^1.0.4", 225 | "inherits": "2", 226 | "minimatch": "^3.0.4", 227 | "once": "^1.3.0", 228 | "path-is-absolute": "^1.0.0" 229 | } 230 | } 231 | } 232 | }, 233 | "coffee-script": { 234 | "version": "1.10.0", 235 | "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", 236 | "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=", 237 | "dev": true 238 | }, 239 | "colors": { 240 | "version": "1.1.2", 241 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", 242 | "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", 243 | "dev": true 244 | }, 245 | "commander": { 246 | "version": "2.11.0", 247 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 248 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 249 | "dev": true 250 | }, 251 | "concat-map": { 252 | "version": "0.0.1", 253 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 254 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 255 | "dev": true 256 | }, 257 | "console-browserify": { 258 | "version": "1.1.0", 259 | "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", 260 | "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", 261 | "dev": true, 262 | "requires": { 263 | "date-now": "^0.1.4" 264 | } 265 | }, 266 | "content-disposition": { 267 | "version": "0.5.2", 268 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 269 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 270 | }, 271 | "content-type": { 272 | "version": "1.0.4", 273 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 274 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 275 | }, 276 | "cookie": { 277 | "version": "0.3.1", 278 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 279 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 280 | }, 281 | "cookie-signature": { 282 | "version": "1.0.6", 283 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 284 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 285 | }, 286 | "core-util-is": { 287 | "version": "1.0.2", 288 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 289 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 290 | "dev": true 291 | }, 292 | "currently-unhandled": { 293 | "version": "0.4.1", 294 | "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", 295 | "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", 296 | "dev": true, 297 | "requires": { 298 | "array-find-index": "^1.0.1" 299 | } 300 | }, 301 | "date-now": { 302 | "version": "0.1.4", 303 | "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", 304 | "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", 305 | "dev": true 306 | }, 307 | "dateformat": { 308 | "version": "1.0.12", 309 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", 310 | "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", 311 | "dev": true, 312 | "requires": { 313 | "get-stdin": "^4.0.1", 314 | "meow": "^3.3.0" 315 | } 316 | }, 317 | "debug": { 318 | "version": "2.6.9", 319 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 320 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 321 | "requires": { 322 | "ms": "2.0.0" 323 | } 324 | }, 325 | "decamelize": { 326 | "version": "1.2.0", 327 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 328 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 329 | "dev": true 330 | }, 331 | "deep-eql": { 332 | "version": "3.0.1", 333 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 334 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 335 | "dev": true, 336 | "requires": { 337 | "type-detect": "^4.0.0" 338 | } 339 | }, 340 | "depd": { 341 | "version": "1.1.1", 342 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 343 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 344 | }, 345 | "destroy": { 346 | "version": "1.0.4", 347 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 348 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 349 | }, 350 | "diff": { 351 | "version": "3.3.1", 352 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", 353 | "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", 354 | "dev": true 355 | }, 356 | "dom-serializer": { 357 | "version": "0.1.0", 358 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", 359 | "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", 360 | "dev": true, 361 | "requires": { 362 | "domelementtype": "~1.1.1", 363 | "entities": "~1.1.1" 364 | }, 365 | "dependencies": { 366 | "domelementtype": { 367 | "version": "1.1.3", 368 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", 369 | "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", 370 | "dev": true 371 | }, 372 | "entities": { 373 | "version": "1.1.1", 374 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", 375 | "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", 376 | "dev": true 377 | } 378 | } 379 | }, 380 | "domelementtype": { 381 | "version": "1.3.0", 382 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", 383 | "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", 384 | "dev": true 385 | }, 386 | "domhandler": { 387 | "version": "2.3.0", 388 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", 389 | "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", 390 | "dev": true, 391 | "requires": { 392 | "domelementtype": "1" 393 | } 394 | }, 395 | "domutils": { 396 | "version": "1.5.1", 397 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 398 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 399 | "dev": true, 400 | "requires": { 401 | "dom-serializer": "0", 402 | "domelementtype": "1" 403 | } 404 | }, 405 | "ee-first": { 406 | "version": "1.1.1", 407 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 408 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 409 | }, 410 | "ejs": { 411 | "version": "2.5.7", 412 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", 413 | "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=", 414 | "dev": true 415 | }, 416 | "encodeurl": { 417 | "version": "1.0.1", 418 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 419 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" 420 | }, 421 | "entities": { 422 | "version": "1.0.0", 423 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", 424 | "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", 425 | "dev": true 426 | }, 427 | "error-ex": { 428 | "version": "1.3.1", 429 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", 430 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", 431 | "dev": true, 432 | "requires": { 433 | "is-arrayish": "^0.2.1" 434 | } 435 | }, 436 | "escape-html": { 437 | "version": "1.0.3", 438 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 439 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 440 | }, 441 | "escape-string-regexp": { 442 | "version": "1.0.5", 443 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 444 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 445 | "dev": true 446 | }, 447 | "esprima": { 448 | "version": "2.7.3", 449 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", 450 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 451 | "dev": true 452 | }, 453 | "etag": { 454 | "version": "1.8.1", 455 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 456 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 457 | }, 458 | "eventemitter2": { 459 | "version": "0.4.14", 460 | "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", 461 | "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", 462 | "dev": true 463 | }, 464 | "exit": { 465 | "version": "0.1.2", 466 | "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 467 | "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", 468 | "dev": true 469 | }, 470 | "faye-websocket": { 471 | "version": "0.10.0", 472 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", 473 | "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", 474 | "dev": true, 475 | "requires": { 476 | "websocket-driver": ">=0.5.1" 477 | } 478 | }, 479 | "find-up": { 480 | "version": "1.1.2", 481 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 482 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 483 | "dev": true, 484 | "requires": { 485 | "path-exists": "^2.0.0", 486 | "pinkie-promise": "^2.0.0" 487 | } 488 | }, 489 | "findup-sync": { 490 | "version": "0.3.0", 491 | "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", 492 | "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", 493 | "dev": true, 494 | "requires": { 495 | "glob": "~5.0.0" 496 | }, 497 | "dependencies": { 498 | "glob": { 499 | "version": "5.0.15", 500 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 501 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 502 | "dev": true, 503 | "requires": { 504 | "inflight": "^1.0.4", 505 | "inherits": "2", 506 | "minimatch": "2 || 3", 507 | "once": "^1.3.0", 508 | "path-is-absolute": "^1.0.0" 509 | } 510 | } 511 | } 512 | }, 513 | "forwarded": { 514 | "version": "0.1.2", 515 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 516 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 517 | }, 518 | "fresh": { 519 | "version": "0.5.2", 520 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 521 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 522 | }, 523 | "fs.realpath": { 524 | "version": "1.0.0", 525 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 526 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 527 | "dev": true 528 | }, 529 | "gaze": { 530 | "version": "1.1.2", 531 | "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", 532 | "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", 533 | "dev": true, 534 | "requires": { 535 | "globule": "^1.0.0" 536 | } 537 | }, 538 | "get-func-name": { 539 | "version": "2.0.0", 540 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 541 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 542 | "dev": true 543 | }, 544 | "get-stdin": { 545 | "version": "4.0.1", 546 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", 547 | "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", 548 | "dev": true 549 | }, 550 | "getobject": { 551 | "version": "0.1.0", 552 | "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", 553 | "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", 554 | "dev": true 555 | }, 556 | "glob": { 557 | "version": "7.0.6", 558 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", 559 | "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", 560 | "dev": true, 561 | "requires": { 562 | "fs.realpath": "^1.0.0", 563 | "inflight": "^1.0.4", 564 | "inherits": "2", 565 | "minimatch": "^3.0.2", 566 | "once": "^1.3.0", 567 | "path-is-absolute": "^1.0.0" 568 | } 569 | }, 570 | "globule": { 571 | "version": "1.2.0", 572 | "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", 573 | "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", 574 | "dev": true, 575 | "requires": { 576 | "glob": "~7.1.1", 577 | "lodash": "~4.17.4", 578 | "minimatch": "~3.0.2" 579 | }, 580 | "dependencies": { 581 | "glob": { 582 | "version": "7.1.2", 583 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 584 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 585 | "dev": true, 586 | "requires": { 587 | "fs.realpath": "^1.0.0", 588 | "inflight": "^1.0.4", 589 | "inherits": "2", 590 | "minimatch": "^3.0.4", 591 | "once": "^1.3.0", 592 | "path-is-absolute": "^1.0.0" 593 | } 594 | } 595 | } 596 | }, 597 | "graceful-fs": { 598 | "version": "4.1.11", 599 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 600 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 601 | "dev": true 602 | }, 603 | "growl": { 604 | "version": "1.10.3", 605 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", 606 | "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", 607 | "dev": true 608 | }, 609 | "grunt": { 610 | "version": "1.0.1", 611 | "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.1.tgz", 612 | "integrity": "sha1-6HeHZOlEsY8yuw8QuQeEdcnftWs=", 613 | "dev": true, 614 | "requires": { 615 | "coffee-script": "~1.10.0", 616 | "dateformat": "~1.0.12", 617 | "eventemitter2": "~0.4.13", 618 | "exit": "~0.1.1", 619 | "findup-sync": "~0.3.0", 620 | "glob": "~7.0.0", 621 | "grunt-cli": "~1.2.0", 622 | "grunt-known-options": "~1.1.0", 623 | "grunt-legacy-log": "~1.0.0", 624 | "grunt-legacy-util": "~1.0.0", 625 | "iconv-lite": "~0.4.13", 626 | "js-yaml": "~3.5.2", 627 | "minimatch": "~3.0.0", 628 | "nopt": "~3.0.6", 629 | "path-is-absolute": "~1.0.0", 630 | "rimraf": "~2.2.8" 631 | }, 632 | "dependencies": { 633 | "grunt-cli": { 634 | "version": "1.2.0", 635 | "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", 636 | "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", 637 | "dev": true, 638 | "requires": { 639 | "findup-sync": "~0.3.0", 640 | "grunt-known-options": "~1.1.0", 641 | "nopt": "~3.0.6", 642 | "resolve": "~1.1.0" 643 | } 644 | } 645 | } 646 | }, 647 | "grunt-contrib-jshint": { 648 | "version": "1.1.0", 649 | "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", 650 | "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", 651 | "dev": true, 652 | "requires": { 653 | "chalk": "^1.1.1", 654 | "hooker": "^0.2.3", 655 | "jshint": "~2.9.4" 656 | } 657 | }, 658 | "grunt-contrib-watch": { 659 | "version": "1.0.0", 660 | "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.0.0.tgz", 661 | "integrity": "sha1-hKGnodar0m7VaEE0lscxM+mQAY8=", 662 | "dev": true, 663 | "requires": { 664 | "async": "^1.5.0", 665 | "gaze": "^1.0.0", 666 | "lodash": "^3.10.1", 667 | "tiny-lr": "^0.2.1" 668 | }, 669 | "dependencies": { 670 | "lodash": { 671 | "version": "3.10.1", 672 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", 673 | "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", 674 | "dev": true 675 | } 676 | } 677 | }, 678 | "grunt-known-options": { 679 | "version": "1.1.0", 680 | "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz", 681 | "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=", 682 | "dev": true 683 | }, 684 | "grunt-legacy-log": { 685 | "version": "1.0.0", 686 | "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.0.tgz", 687 | "integrity": "sha1-+4bxgJhHvAfcR4Q/ns1srLYt8tU=", 688 | "dev": true, 689 | "requires": { 690 | "colors": "~1.1.2", 691 | "grunt-legacy-log-utils": "~1.0.0", 692 | "hooker": "~0.2.3", 693 | "lodash": "~3.10.1", 694 | "underscore.string": "~3.2.3" 695 | }, 696 | "dependencies": { 697 | "lodash": { 698 | "version": "3.10.1", 699 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", 700 | "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", 701 | "dev": true 702 | } 703 | } 704 | }, 705 | "grunt-legacy-log-utils": { 706 | "version": "1.0.0", 707 | "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz", 708 | "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=", 709 | "dev": true, 710 | "requires": { 711 | "chalk": "~1.1.1", 712 | "lodash": "~4.3.0" 713 | }, 714 | "dependencies": { 715 | "lodash": { 716 | "version": "4.3.0", 717 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz", 718 | "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=", 719 | "dev": true 720 | } 721 | } 722 | }, 723 | "grunt-legacy-util": { 724 | "version": "1.0.0", 725 | "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz", 726 | "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=", 727 | "dev": true, 728 | "requires": { 729 | "async": "~1.5.2", 730 | "exit": "~0.1.1", 731 | "getobject": "~0.1.0", 732 | "hooker": "~0.2.3", 733 | "lodash": "~4.3.0", 734 | "underscore.string": "~3.2.3", 735 | "which": "~1.2.1" 736 | }, 737 | "dependencies": { 738 | "lodash": { 739 | "version": "4.3.0", 740 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz", 741 | "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=", 742 | "dev": true 743 | } 744 | } 745 | }, 746 | "grunt-mocha-test": { 747 | "version": "0.13.3", 748 | "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.13.3.tgz", 749 | "integrity": "sha512-zQGEsi3d+ViPPi7/4jcj78afKKAKiAA5n61pknQYi25Ugik+aNOuRmiOkmb8mN2CeG8YxT+YdT1H1Q7B/eNkoQ==", 750 | "dev": true, 751 | "requires": { 752 | "hooker": "^0.2.3", 753 | "mkdirp": "^0.5.0" 754 | } 755 | }, 756 | "has-ansi": { 757 | "version": "2.0.0", 758 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 759 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 760 | "dev": true, 761 | "requires": { 762 | "ansi-regex": "^2.0.0" 763 | } 764 | }, 765 | "has-flag": { 766 | "version": "2.0.0", 767 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 768 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 769 | "dev": true 770 | }, 771 | "he": { 772 | "version": "1.1.1", 773 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 774 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 775 | "dev": true 776 | }, 777 | "hooker": { 778 | "version": "0.2.3", 779 | "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", 780 | "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", 781 | "dev": true 782 | }, 783 | "hosted-git-info": { 784 | "version": "2.5.0", 785 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", 786 | "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", 787 | "dev": true 788 | }, 789 | "htmlparser2": { 790 | "version": "3.8.3", 791 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", 792 | "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", 793 | "dev": true, 794 | "requires": { 795 | "domelementtype": "1", 796 | "domhandler": "2.3", 797 | "domutils": "1.5", 798 | "entities": "1.0", 799 | "readable-stream": "1.1" 800 | } 801 | }, 802 | "http-errors": { 803 | "version": "1.6.2", 804 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 805 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 806 | "requires": { 807 | "depd": "1.1.1", 808 | "inherits": "2.0.3", 809 | "setprototypeof": "1.0.3", 810 | "statuses": ">= 1.3.1 < 2" 811 | } 812 | }, 813 | "http-parser-js": { 814 | "version": "0.4.9", 815 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", 816 | "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=", 817 | "dev": true 818 | }, 819 | "iconv-lite": { 820 | "version": "0.4.19", 821 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 822 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", 823 | "dev": true 824 | }, 825 | "indent-string": { 826 | "version": "2.1.0", 827 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", 828 | "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", 829 | "dev": true, 830 | "requires": { 831 | "repeating": "^2.0.0" 832 | } 833 | }, 834 | "inflight": { 835 | "version": "1.0.6", 836 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 837 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 838 | "dev": true, 839 | "requires": { 840 | "once": "^1.3.0", 841 | "wrappy": "1" 842 | } 843 | }, 844 | "inherits": { 845 | "version": "2.0.3", 846 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 847 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 848 | }, 849 | "ipaddr.js": { 850 | "version": "1.5.2", 851 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", 852 | "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" 853 | }, 854 | "irregular-plurals": { 855 | "version": "1.4.0", 856 | "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", 857 | "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=", 858 | "dev": true 859 | }, 860 | "is-arrayish": { 861 | "version": "0.2.1", 862 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 863 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 864 | "dev": true 865 | }, 866 | "is-builtin-module": { 867 | "version": "1.0.0", 868 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 869 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 870 | "dev": true, 871 | "requires": { 872 | "builtin-modules": "^1.0.0" 873 | } 874 | }, 875 | "is-finite": { 876 | "version": "1.0.2", 877 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 878 | "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", 879 | "dev": true, 880 | "requires": { 881 | "number-is-nan": "^1.0.0" 882 | } 883 | }, 884 | "is-utf8": { 885 | "version": "0.2.1", 886 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 887 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", 888 | "dev": true 889 | }, 890 | "isarray": { 891 | "version": "0.0.1", 892 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 893 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 894 | "dev": true 895 | }, 896 | "isexe": { 897 | "version": "2.0.0", 898 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 899 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 900 | "dev": true 901 | }, 902 | "js-yaml": { 903 | "version": "3.5.5", 904 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz", 905 | "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=", 906 | "dev": true, 907 | "requires": { 908 | "argparse": "^1.0.2", 909 | "esprima": "^2.6.0" 910 | } 911 | }, 912 | "jshint": { 913 | "version": "2.9.5", 914 | "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz", 915 | "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", 916 | "dev": true, 917 | "requires": { 918 | "cli": "~1.0.0", 919 | "console-browserify": "1.1.x", 920 | "exit": "0.1.x", 921 | "htmlparser2": "3.8.x", 922 | "lodash": "3.7.x", 923 | "minimatch": "~3.0.2", 924 | "shelljs": "0.3.x", 925 | "strip-json-comments": "1.0.x" 926 | }, 927 | "dependencies": { 928 | "lodash": { 929 | "version": "3.7.0", 930 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", 931 | "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=", 932 | "dev": true 933 | } 934 | } 935 | }, 936 | "jshint-stylish": { 937 | "version": "2.2.1", 938 | "resolved": "https://registry.npmjs.org/jshint-stylish/-/jshint-stylish-2.2.1.tgz", 939 | "integrity": "sha1-JCCCosA1rgP9gQROBXDMQgjPbmE=", 940 | "dev": true, 941 | "requires": { 942 | "beeper": "^1.1.0", 943 | "chalk": "^1.0.0", 944 | "log-symbols": "^1.0.0", 945 | "plur": "^2.1.0", 946 | "string-length": "^1.0.0", 947 | "text-table": "^0.2.0" 948 | } 949 | }, 950 | "livereload-js": { 951 | "version": "2.2.2", 952 | "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", 953 | "integrity": "sha1-bIclfmSKtHW8JOoldFftzB+NC8I=", 954 | "dev": true 955 | }, 956 | "load-json-file": { 957 | "version": "1.1.0", 958 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 959 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 960 | "dev": true, 961 | "requires": { 962 | "graceful-fs": "^4.1.2", 963 | "parse-json": "^2.2.0", 964 | "pify": "^2.0.0", 965 | "pinkie-promise": "^2.0.0", 966 | "strip-bom": "^2.0.0" 967 | } 968 | }, 969 | "lodash": { 970 | "version": "4.17.4", 971 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 972 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 973 | }, 974 | "log-symbols": { 975 | "version": "1.0.2", 976 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", 977 | "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", 978 | "dev": true, 979 | "requires": { 980 | "chalk": "^1.0.0" 981 | } 982 | }, 983 | "loud-rejection": { 984 | "version": "1.6.0", 985 | "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", 986 | "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", 987 | "dev": true, 988 | "requires": { 989 | "currently-unhandled": "^0.4.1", 990 | "signal-exit": "^3.0.0" 991 | } 992 | }, 993 | "map-obj": { 994 | "version": "1.0.1", 995 | "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", 996 | "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", 997 | "dev": true 998 | }, 999 | "media-typer": { 1000 | "version": "0.3.0", 1001 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1002 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1003 | }, 1004 | "meow": { 1005 | "version": "3.7.0", 1006 | "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", 1007 | "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", 1008 | "dev": true, 1009 | "requires": { 1010 | "camelcase-keys": "^2.0.0", 1011 | "decamelize": "^1.1.2", 1012 | "loud-rejection": "^1.0.0", 1013 | "map-obj": "^1.0.1", 1014 | "minimist": "^1.1.3", 1015 | "normalize-package-data": "^2.3.4", 1016 | "object-assign": "^4.0.1", 1017 | "read-pkg-up": "^1.0.1", 1018 | "redent": "^1.0.0", 1019 | "trim-newlines": "^1.0.0" 1020 | } 1021 | }, 1022 | "mime": { 1023 | "version": "1.4.1", 1024 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 1025 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 1026 | }, 1027 | "mime-db": { 1028 | "version": "1.30.0", 1029 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", 1030 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 1031 | }, 1032 | "mime-types": { 1033 | "version": "2.1.17", 1034 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", 1035 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 1036 | "requires": { 1037 | "mime-db": "~1.30.0" 1038 | } 1039 | }, 1040 | "minimatch": { 1041 | "version": "3.0.4", 1042 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1043 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1044 | "dev": true, 1045 | "requires": { 1046 | "brace-expansion": "^1.1.7" 1047 | } 1048 | }, 1049 | "minimist": { 1050 | "version": "1.2.0", 1051 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1052 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 1053 | "dev": true 1054 | }, 1055 | "mkdirp": { 1056 | "version": "0.5.1", 1057 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1058 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1059 | "dev": true, 1060 | "requires": { 1061 | "minimist": "0.0.8" 1062 | }, 1063 | "dependencies": { 1064 | "minimist": { 1065 | "version": "0.0.8", 1066 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1067 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 1068 | "dev": true 1069 | } 1070 | } 1071 | }, 1072 | "mocha": { 1073 | "version": "4.0.1", 1074 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", 1075 | "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", 1076 | "dev": true, 1077 | "requires": { 1078 | "browser-stdout": "1.3.0", 1079 | "commander": "2.11.0", 1080 | "debug": "3.1.0", 1081 | "diff": "3.3.1", 1082 | "escape-string-regexp": "1.0.5", 1083 | "glob": "7.1.2", 1084 | "growl": "1.10.3", 1085 | "he": "1.1.1", 1086 | "mkdirp": "0.5.1", 1087 | "supports-color": "4.4.0" 1088 | }, 1089 | "dependencies": { 1090 | "debug": { 1091 | "version": "3.1.0", 1092 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 1093 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 1094 | "dev": true, 1095 | "requires": { 1096 | "ms": "2.0.0" 1097 | } 1098 | }, 1099 | "glob": { 1100 | "version": "7.1.2", 1101 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 1102 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 1103 | "dev": true, 1104 | "requires": { 1105 | "fs.realpath": "^1.0.0", 1106 | "inflight": "^1.0.4", 1107 | "inherits": "2", 1108 | "minimatch": "^3.0.4", 1109 | "once": "^1.3.0", 1110 | "path-is-absolute": "^1.0.0" 1111 | } 1112 | }, 1113 | "supports-color": { 1114 | "version": "4.4.0", 1115 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", 1116 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", 1117 | "dev": true, 1118 | "requires": { 1119 | "has-flag": "^2.0.0" 1120 | } 1121 | } 1122 | } 1123 | }, 1124 | "mock-express-request": { 1125 | "version": "0.2.2", 1126 | "resolved": "https://registry.npmjs.org/mock-express-request/-/mock-express-request-0.2.2.tgz", 1127 | "integrity": "sha512-EymHjY1k1jWIsaVaCsPdFterWO18gcNwQMb99OryhSBtIA33SZJujOLeOe03Rf2DTV997xLPyl2I098WCFm/mA==", 1128 | "requires": { 1129 | "accepts": "^1.3.4", 1130 | "fresh": "^0.5.2", 1131 | "lodash": "^4.17.4", 1132 | "mock-req": "^0.2.0", 1133 | "parseurl": "^1.3.2", 1134 | "range-parser": "^1.2.0", 1135 | "type-is": "^1.6.15" 1136 | } 1137 | }, 1138 | "mock-req": { 1139 | "version": "0.2.0", 1140 | "resolved": "https://registry.npmjs.org/mock-req/-/mock-req-0.2.0.tgz", 1141 | "integrity": "sha1-dJRGgE0sAGFpNC7nvmu6HP/VNMI=" 1142 | }, 1143 | "mock-res": { 1144 | "version": "0.5.0", 1145 | "resolved": "https://registry.npmjs.org/mock-res/-/mock-res-0.5.0.tgz", 1146 | "integrity": "sha1-mDaL6wnfdT9k9m2U5VNql7NqJDA=" 1147 | }, 1148 | "ms": { 1149 | "version": "2.0.0", 1150 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1151 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1152 | }, 1153 | "negotiator": { 1154 | "version": "0.6.1", 1155 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 1156 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 1157 | }, 1158 | "nopt": { 1159 | "version": "3.0.6", 1160 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", 1161 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 1162 | "dev": true, 1163 | "requires": { 1164 | "abbrev": "1" 1165 | } 1166 | }, 1167 | "normalize-package-data": { 1168 | "version": "2.4.0", 1169 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 1170 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 1171 | "dev": true, 1172 | "requires": { 1173 | "hosted-git-info": "^2.1.4", 1174 | "is-builtin-module": "^1.0.0", 1175 | "semver": "2 || 3 || 4 || 5", 1176 | "validate-npm-package-license": "^3.0.1" 1177 | } 1178 | }, 1179 | "number-is-nan": { 1180 | "version": "1.0.1", 1181 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1182 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", 1183 | "dev": true 1184 | }, 1185 | "object-assign": { 1186 | "version": "4.1.1", 1187 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1188 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1189 | "dev": true 1190 | }, 1191 | "on-finished": { 1192 | "version": "2.3.0", 1193 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1194 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1195 | "requires": { 1196 | "ee-first": "1.1.1" 1197 | } 1198 | }, 1199 | "once": { 1200 | "version": "1.4.0", 1201 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1202 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1203 | "dev": true, 1204 | "requires": { 1205 | "wrappy": "1" 1206 | } 1207 | }, 1208 | "parse-json": { 1209 | "version": "2.2.0", 1210 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 1211 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 1212 | "dev": true, 1213 | "requires": { 1214 | "error-ex": "^1.2.0" 1215 | } 1216 | }, 1217 | "parseurl": { 1218 | "version": "1.3.2", 1219 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 1220 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 1221 | }, 1222 | "path-exists": { 1223 | "version": "2.1.0", 1224 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 1225 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 1226 | "dev": true, 1227 | "requires": { 1228 | "pinkie-promise": "^2.0.0" 1229 | } 1230 | }, 1231 | "path-is-absolute": { 1232 | "version": "1.0.1", 1233 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1234 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1235 | "dev": true 1236 | }, 1237 | "path-type": { 1238 | "version": "1.1.0", 1239 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 1240 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 1241 | "dev": true, 1242 | "requires": { 1243 | "graceful-fs": "^4.1.2", 1244 | "pify": "^2.0.0", 1245 | "pinkie-promise": "^2.0.0" 1246 | } 1247 | }, 1248 | "pathval": { 1249 | "version": "1.1.0", 1250 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 1251 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 1252 | "dev": true 1253 | }, 1254 | "pify": { 1255 | "version": "2.3.0", 1256 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1257 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 1258 | "dev": true 1259 | }, 1260 | "pinkie": { 1261 | "version": "2.0.4", 1262 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1263 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 1264 | "dev": true 1265 | }, 1266 | "pinkie-promise": { 1267 | "version": "2.0.1", 1268 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1269 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1270 | "dev": true, 1271 | "requires": { 1272 | "pinkie": "^2.0.0" 1273 | } 1274 | }, 1275 | "plur": { 1276 | "version": "2.1.2", 1277 | "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", 1278 | "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", 1279 | "dev": true, 1280 | "requires": { 1281 | "irregular-plurals": "^1.0.0" 1282 | } 1283 | }, 1284 | "proxy-addr": { 1285 | "version": "2.0.2", 1286 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", 1287 | "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", 1288 | "requires": { 1289 | "forwarded": "~0.1.2", 1290 | "ipaddr.js": "1.5.2" 1291 | } 1292 | }, 1293 | "qs": { 1294 | "version": "6.5.1", 1295 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 1296 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 1297 | }, 1298 | "range-parser": { 1299 | "version": "1.2.0", 1300 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 1301 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 1302 | }, 1303 | "raw-body": { 1304 | "version": "2.1.7", 1305 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", 1306 | "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", 1307 | "dev": true, 1308 | "requires": { 1309 | "bytes": "2.4.0", 1310 | "iconv-lite": "0.4.13", 1311 | "unpipe": "1.0.0" 1312 | }, 1313 | "dependencies": { 1314 | "bytes": { 1315 | "version": "2.4.0", 1316 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", 1317 | "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", 1318 | "dev": true 1319 | }, 1320 | "iconv-lite": { 1321 | "version": "0.4.13", 1322 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", 1323 | "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", 1324 | "dev": true 1325 | } 1326 | } 1327 | }, 1328 | "read-pkg": { 1329 | "version": "1.1.0", 1330 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 1331 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 1332 | "dev": true, 1333 | "requires": { 1334 | "load-json-file": "^1.0.0", 1335 | "normalize-package-data": "^2.3.2", 1336 | "path-type": "^1.0.0" 1337 | } 1338 | }, 1339 | "read-pkg-up": { 1340 | "version": "1.0.1", 1341 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 1342 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 1343 | "dev": true, 1344 | "requires": { 1345 | "find-up": "^1.0.0", 1346 | "read-pkg": "^1.0.0" 1347 | } 1348 | }, 1349 | "readable-stream": { 1350 | "version": "1.1.14", 1351 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1352 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1353 | "dev": true, 1354 | "requires": { 1355 | "core-util-is": "~1.0.0", 1356 | "inherits": "~2.0.1", 1357 | "isarray": "0.0.1", 1358 | "string_decoder": "~0.10.x" 1359 | } 1360 | }, 1361 | "redent": { 1362 | "version": "1.0.0", 1363 | "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", 1364 | "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", 1365 | "dev": true, 1366 | "requires": { 1367 | "indent-string": "^2.1.0", 1368 | "strip-indent": "^1.0.1" 1369 | } 1370 | }, 1371 | "repeating": { 1372 | "version": "2.0.1", 1373 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 1374 | "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", 1375 | "dev": true, 1376 | "requires": { 1377 | "is-finite": "^1.0.0" 1378 | } 1379 | }, 1380 | "resolve": { 1381 | "version": "1.1.7", 1382 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", 1383 | "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", 1384 | "dev": true 1385 | }, 1386 | "rimraf": { 1387 | "version": "2.2.8", 1388 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", 1389 | "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", 1390 | "dev": true 1391 | }, 1392 | "semver": { 1393 | "version": "5.4.1", 1394 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", 1395 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", 1396 | "dev": true 1397 | }, 1398 | "send": { 1399 | "version": "0.16.1", 1400 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", 1401 | "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", 1402 | "requires": { 1403 | "debug": "2.6.9", 1404 | "depd": "~1.1.1", 1405 | "destroy": "~1.0.4", 1406 | "encodeurl": "~1.0.1", 1407 | "escape-html": "~1.0.3", 1408 | "etag": "~1.8.1", 1409 | "fresh": "0.5.2", 1410 | "http-errors": "~1.6.2", 1411 | "mime": "1.4.1", 1412 | "ms": "2.0.0", 1413 | "on-finished": "~2.3.0", 1414 | "range-parser": "~1.2.0", 1415 | "statuses": "~1.3.1" 1416 | } 1417 | }, 1418 | "setprototypeof": { 1419 | "version": "1.0.3", 1420 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 1421 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 1422 | }, 1423 | "shelljs": { 1424 | "version": "0.3.0", 1425 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 1426 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", 1427 | "dev": true 1428 | }, 1429 | "signal-exit": { 1430 | "version": "3.0.2", 1431 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1432 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 1433 | "dev": true 1434 | }, 1435 | "spdx-correct": { 1436 | "version": "1.0.2", 1437 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", 1438 | "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", 1439 | "dev": true, 1440 | "requires": { 1441 | "spdx-license-ids": "^1.0.2" 1442 | } 1443 | }, 1444 | "spdx-expression-parse": { 1445 | "version": "1.0.4", 1446 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", 1447 | "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", 1448 | "dev": true 1449 | }, 1450 | "spdx-license-ids": { 1451 | "version": "1.2.2", 1452 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", 1453 | "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", 1454 | "dev": true 1455 | }, 1456 | "sprintf-js": { 1457 | "version": "1.0.3", 1458 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1459 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1460 | "dev": true 1461 | }, 1462 | "statuses": { 1463 | "version": "1.3.1", 1464 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 1465 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 1466 | }, 1467 | "string-length": { 1468 | "version": "1.0.1", 1469 | "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", 1470 | "integrity": "sha1-VpcPscOFWOnnC3KL894mmsRa36w=", 1471 | "dev": true, 1472 | "requires": { 1473 | "strip-ansi": "^3.0.0" 1474 | } 1475 | }, 1476 | "string_decoder": { 1477 | "version": "0.10.31", 1478 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1479 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1480 | "dev": true 1481 | }, 1482 | "strip-ansi": { 1483 | "version": "3.0.1", 1484 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1485 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1486 | "dev": true, 1487 | "requires": { 1488 | "ansi-regex": "^2.0.0" 1489 | } 1490 | }, 1491 | "strip-bom": { 1492 | "version": "2.0.0", 1493 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 1494 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 1495 | "dev": true, 1496 | "requires": { 1497 | "is-utf8": "^0.2.0" 1498 | } 1499 | }, 1500 | "strip-indent": { 1501 | "version": "1.0.1", 1502 | "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", 1503 | "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", 1504 | "dev": true, 1505 | "requires": { 1506 | "get-stdin": "^4.0.1" 1507 | } 1508 | }, 1509 | "strip-json-comments": { 1510 | "version": "1.0.4", 1511 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 1512 | "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", 1513 | "dev": true 1514 | }, 1515 | "supports-color": { 1516 | "version": "2.0.0", 1517 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1518 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1519 | "dev": true 1520 | }, 1521 | "text-table": { 1522 | "version": "0.2.0", 1523 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1524 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1525 | "dev": true 1526 | }, 1527 | "tiny-lr": { 1528 | "version": "0.2.1", 1529 | "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", 1530 | "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=", 1531 | "dev": true, 1532 | "requires": { 1533 | "body-parser": "~1.14.0", 1534 | "debug": "~2.2.0", 1535 | "faye-websocket": "~0.10.0", 1536 | "livereload-js": "^2.2.0", 1537 | "parseurl": "~1.3.0", 1538 | "qs": "~5.1.0" 1539 | }, 1540 | "dependencies": { 1541 | "debug": { 1542 | "version": "2.2.0", 1543 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 1544 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 1545 | "dev": true, 1546 | "requires": { 1547 | "ms": "0.7.1" 1548 | } 1549 | }, 1550 | "ms": { 1551 | "version": "0.7.1", 1552 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 1553 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 1554 | "dev": true 1555 | }, 1556 | "qs": { 1557 | "version": "5.1.0", 1558 | "resolved": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz", 1559 | "integrity": "sha1-TZMuXH6kEcynajEtOaYGIA/VDNk=", 1560 | "dev": true 1561 | } 1562 | } 1563 | }, 1564 | "trim-newlines": { 1565 | "version": "1.0.0", 1566 | "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", 1567 | "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", 1568 | "dev": true 1569 | }, 1570 | "type-detect": { 1571 | "version": "4.0.5", 1572 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz", 1573 | "integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==", 1574 | "dev": true 1575 | }, 1576 | "type-is": { 1577 | "version": "1.6.15", 1578 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 1579 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 1580 | "requires": { 1581 | "media-typer": "0.3.0", 1582 | "mime-types": "~2.1.15" 1583 | } 1584 | }, 1585 | "underscore.string": { 1586 | "version": "3.2.3", 1587 | "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz", 1588 | "integrity": "sha1-gGmSYzZl1eX8tNsfs6hi62jp5to=", 1589 | "dev": true 1590 | }, 1591 | "unpipe": { 1592 | "version": "1.0.0", 1593 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1594 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1595 | "dev": true 1596 | }, 1597 | "utils-merge": { 1598 | "version": "1.0.1", 1599 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1600 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1601 | }, 1602 | "validate-npm-package-license": { 1603 | "version": "3.0.1", 1604 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", 1605 | "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", 1606 | "dev": true, 1607 | "requires": { 1608 | "spdx-correct": "~1.0.0", 1609 | "spdx-expression-parse": "~1.0.0" 1610 | } 1611 | }, 1612 | "vary": { 1613 | "version": "1.1.2", 1614 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1615 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1616 | }, 1617 | "websocket-driver": { 1618 | "version": "0.7.0", 1619 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", 1620 | "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", 1621 | "dev": true, 1622 | "requires": { 1623 | "http-parser-js": ">=0.4.0", 1624 | "websocket-extensions": ">=0.1.1" 1625 | } 1626 | }, 1627 | "websocket-extensions": { 1628 | "version": "0.1.3", 1629 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", 1630 | "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", 1631 | "dev": true 1632 | }, 1633 | "which": { 1634 | "version": "1.2.14", 1635 | "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", 1636 | "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", 1637 | "dev": true, 1638 | "requires": { 1639 | "isexe": "^2.0.0" 1640 | } 1641 | }, 1642 | "wrappy": { 1643 | "version": "1.0.2", 1644 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1645 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1646 | "dev": true 1647 | } 1648 | } 1649 | } 1650 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-express-response", 3 | "version": "0.3.0", 4 | "description": "Nodejs library to mock express http response", 5 | "keywords": [ 6 | "express", 7 | "connect", 8 | "mock", 9 | "stab", 10 | "unit", 11 | "test", 12 | "spec", 13 | "specification", 14 | "bdd", 15 | "tdd", 16 | "response", 17 | "http", 18 | "htttps" 19 | ], 20 | "main": "index.js", 21 | "scripts": { 22 | "test": "grunt test" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/lykmapipo/mock-express-response.git" 27 | }, 28 | "author": { 29 | "name": "lykmapipo", 30 | "email": "lallyelias87@gmail.com", 31 | "url": "https://github.com/lykmapipo" 32 | }, 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/lykmapipo/mock-express-response/issues" 36 | }, 37 | "homepage": "https://github.com/lykmapipo/mock-express-response", 38 | "contributors": [ 39 | { 40 | "name": "lykmapipo", 41 | "github": "https://github.com/lykmapipo" 42 | } 43 | ], 44 | "devDependencies": { 45 | "chai": "^4.1.2", 46 | "ejs": "^2.5.7", 47 | "grunt": "^1.0.1", 48 | "grunt-contrib-jshint": "^1.1.0", 49 | "grunt-contrib-watch": "^1.0.0", 50 | "grunt-mocha-test": "^0.13.3", 51 | "jshint-stylish": "^2.2.1", 52 | "lodash": "^4.17.4", 53 | "mocha": "^4.0.1" 54 | }, 55 | "dependencies": { 56 | "content-disposition": "^0.5.2", 57 | "content-type": "^1.0.4", 58 | "cookie": "^0.3.1", 59 | "cookie-signature": "^1.0.6", 60 | "depd": "^1.1.1", 61 | "escape-html": "^1.0.3", 62 | "etag": "^1.8.1", 63 | "mock-express-request": "^0.2.2", 64 | "mock-res": "^0.5.0", 65 | "on-finished": "^2.3.0", 66 | "proxy-addr": "^2.0.2", 67 | "qs": "^6.5.1", 68 | "send": "^0.16.1", 69 | "utils-merge": "^1.0.1", 70 | "vary": "^1.1.2" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //dependencies 4 | var path = require('path'); 5 | var ejs = require('ejs'); 6 | var expect = require('chai').expect; 7 | var MockExpressResponse = require(path.join(__dirname, '..', 'index')); 8 | 9 | 10 | describe('MockExpressResponse', function() { 11 | it('should be a function constructor', function(done) { 12 | expect(MockExpressResponse).to.be.a('function'); 13 | done(); 14 | }); 15 | 16 | 17 | it('should be able to instantiate it', function(done) { 18 | var response = new MockExpressResponse(); 19 | 20 | expect(response.statusCode).to.be.equal(200); 21 | expect(response.statusMessage).to.be.equal('OK'); 22 | 23 | done(); 24 | }); 25 | 26 | 27 | it('should be able to set status `code`', function(done) { 28 | var response = new MockExpressResponse(); 29 | response.status(404); 30 | 31 | expect(response.statusCode).to.be.equal(404); 32 | expect(response.statusMessage).to.be.equal('Not Found'); 33 | 34 | done(); 35 | }); 36 | 37 | it('should be able to set Link header field with the given `links`', function(done) { 38 | var response = new MockExpressResponse(); 39 | response.links({ 40 | next: 'http://api.example.com/users?page=2', 41 | last: 'http://api.example.com/users?page=5' 42 | }); 43 | 44 | expect(response._headers.link) 45 | .to.be.equal('some html
'); 66 | 67 | expect(response._getString()).to.be.equal('some html
'); 68 | 69 | done(); 70 | }); 71 | 72 | 73 | it('should be able to send json response', function(done) { 74 | 75 | var response = new MockExpressResponse(); 76 | response.json({ 77 | user: { 78 | isActive: true 79 | } 80 | }); 81 | 82 | expect(response._getJSON()).to.be.eql({ 83 | user: { 84 | isActive: true 85 | } 86 | }); 87 | 88 | done(); 89 | }); 90 | 91 | 92 | it('should be able to send jsonp response', function(done) { 93 | 94 | var response = new MockExpressResponse(); 95 | response.jsonp({ 96 | user: { 97 | isActive: true 98 | } 99 | }); 100 | 101 | expect(response._getJSON()).to.be.eql({ 102 | user: { 103 | isActive: true 104 | } 105 | }); 106 | 107 | done(); 108 | }); 109 | 110 | 111 | it('should be able to send given HTTP status code', function(done) { 112 | var response = new MockExpressResponse(); 113 | response.sendStatus(200); 114 | 115 | expect(response.statusCode).to.be.equal(200); 116 | expect(response.statusMessage).to.be.equal('OK'); 117 | 118 | done(); 119 | }); 120 | 121 | 122 | it('should be able to respond to the Acceptable formats using an `obj` of mime-type callbacks', function(done) { 123 | var response = new MockExpressResponse(); 124 | 125 | response.format({ 126 | 'text/plain': function() { 127 | response.send('hey'); 128 | }, 129 | 130 | 'text/html': function() { 131 | response.send('hey
'); 132 | }, 133 | 134 | 'appliation/json': function() { 135 | response.send({ 136 | message: 'hey' 137 | }); 138 | } 139 | }); 140 | 141 | expect(response._getString()).to.be.equal('hey
'); 142 | 143 | done(); 144 | }); 145 | 146 | 147 | it('should be able to append additional header `field` with value `val`', function(done) { 148 | var response = new MockExpressResponse(); 149 | response.append('Warning', '199 Miscellaneous warning'); 150 | 151 | expect(response.get('Warning')).to.be.equal('199 Miscellaneous warning'); 152 | 153 | done(); 154 | }); 155 | 156 | it('should be able to clear cookie `name`', function(done) { 157 | var response = new MockExpressResponse(); 158 | 159 | response.cookie('rememberme', '1', { 160 | maxAge: 900000, 161 | httpOnly: true 162 | }); 163 | 164 | expect(response.get('Set-Cookie')).to.contain('rememberme=1; Max-Age=900;'); 165 | 166 | response.clearCookie(); 167 | 168 | expect(response.get('Set-Cookie').toString()) 169 | .to.be.contain('Expires=Thu, 01 Jan 1970 00:00:00 GMT'); 170 | 171 | done(); 172 | 173 | }); 174 | 175 | 176 | it('should be able to Set cookie `name` to `val`, with the given `options`', function(done) { 177 | var response = new MockExpressResponse(); 178 | 179 | response.cookie('rememberme', '1', { 180 | maxAge: 900000, 181 | httpOnly: true 182 | }); 183 | 184 | expect(response.get('Set-Cookie')).to.contain('rememberme=1; Max-Age=900;'); 185 | 186 | done(); 187 | 188 | }); 189 | 190 | it('should be able to Set the location header to `url`', function(done) { 191 | var response = new MockExpressResponse(); 192 | response.location('http://example.com'); 193 | 194 | expect(response.get('Location')).to.be.equal('http://example.com'); 195 | 196 | done(); 197 | }); 198 | 199 | 200 | it('should be able to redirect to the given `url` with optional response `status` defaulting to 302.', function(done) { 201 | var response = new MockExpressResponse(); 202 | response.redirect('/home'); 203 | 204 | expect(response.statusCode).to.be.equal(302); 205 | expect(response.get('Location')).to.be.equal('/home'); 206 | 207 | done(); 208 | }); 209 | 210 | 211 | it('should be able to set _Content-Disposition_ header to _attachment_ with optional `filename`', function(done) { 212 | var response = new MockExpressResponse(); 213 | response.attachment('home.pdf'); 214 | 215 | expect(response.get('content-type')).to.be.equal('application/pdf'); 216 | expect(response.get('content-disposition')) 217 | .to.be.equal('attachment; filename="home.pdf"'); 218 | 219 | done(); 220 | }); 221 | 222 | 223 | it('should be able to render `view` with the given `options` and optional callback `fn`', function(done) { 224 | var response = new MockExpressResponse(); 225 | response.render('Hi
'); 226 | 227 | expect(response.get('content-type')).to.be.equal('text/html; charset=utf-8'); 228 | expect(response._getString()).to.be.equal('Hi
'); 229 | 230 | //make use of template engine 231 | var _response = new MockExpressResponse({ 232 | render: ejs.renderFile 233 | }); 234 | 235 | _response.render(path.join(__dirname, 'template.ejs'), { 236 | name: 'Mock' 237 | }); 238 | 239 | expect(_response._getString()).to.be.equal('Hello Mock
'); 240 | 241 | done(); 242 | }); 243 | 244 | 245 | it('should be able to transfer the file at the given `path`', function(done) { 246 | var response = new MockExpressResponse(); 247 | response 248 | .sendFile(path.join(__dirname, 'template.ejs'), function(error, result) { 249 | console.log(error); 250 | console.log(result); 251 | // console.log(response); 252 | 253 | done(); 254 | }); 255 | }); 256 | 257 | 258 | it('should be able to add `field` to Vary', function(done) { 259 | var response = new MockExpressResponse(); 260 | response.vary('w'); 261 | 262 | expect(response.get('vary')).to.be.equal('w'); 263 | 264 | done(); 265 | }); 266 | 267 | 268 | it('should be able to set and get value for header `field`', function(done) { 269 | var response = new MockExpressResponse(); 270 | 271 | response.set('Type', 'x'); 272 | 273 | expect(response.get('Type')).to.be.equal('x'); 274 | 275 | done(); 276 | }); 277 | 278 | it('should be able to set locals property', function(done) { 279 | var response = new MockExpressResponse(); 280 | response.locals.email = 'john.doe@aol.com'; 281 | 282 | expect(response.locals.email).to.be.equal('john.doe@aol.com'); 283 | 284 | done(); 285 | }); 286 | 287 | 288 | }); 289 | -------------------------------------------------------------------------------- /test/template.ejs: -------------------------------------------------------------------------------- 1 |Hello <%= name%>
-------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 5 | * MIT Licensed 6 | */ 7 | 8 | /** 9 | * Module dependencies. 10 | * @api private 11 | */ 12 | 13 | var contentDisposition = require('content-disposition'); 14 | var contentType = require('content-type'); 15 | var deprecate = require('depd')('express'); 16 | var mime = require('send').mime; 17 | var basename = require('path').basename; 18 | var etag = require('etag'); 19 | var proxyaddr = require('proxy-addr'); 20 | var qs = require('qs'); 21 | var querystring = require('querystring'); 22 | 23 | /** 24 | * Return strong ETag for `body`. 25 | * 26 | * @param {String|Buffer} body 27 | * @param {String} [encoding] 28 | * @return {String} 29 | * @api private 30 | */ 31 | 32 | exports.etag = function (body, encoding) { 33 | var buf = !Buffer.isBuffer(body) 34 | ? new Buffer(body, encoding) 35 | : body; 36 | 37 | return etag(buf, {weak: false}); 38 | }; 39 | 40 | /** 41 | * Return weak ETag for `body`. 42 | * 43 | * @param {String|Buffer} body 44 | * @param {String} [encoding] 45 | * @return {String} 46 | * @api private 47 | */ 48 | 49 | exports.wetag = function wetag(body, encoding){ 50 | var buf = !Buffer.isBuffer(body) 51 | ? new Buffer(body, encoding) 52 | : body; 53 | 54 | return etag(buf, {weak: true}); 55 | }; 56 | 57 | /** 58 | * Check if `path` looks absolute. 59 | * 60 | * @param {String} path 61 | * @return {Boolean} 62 | * @api private 63 | */ 64 | 65 | exports.isAbsolute = function(path){ 66 | if ('/' == path[0]) return true; 67 | if (':' == path[1] && '\\' == path[2]) return true; 68 | if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path 69 | }; 70 | 71 | /** 72 | * Flatten the given `arr`. 73 | * 74 | * @param {Array} arr 75 | * @return {Array} 76 | * @api private 77 | */ 78 | 79 | exports.flatten = function(arr, ret){ 80 | ret = ret || []; 81 | var len = arr.length; 82 | for (var i = 0; i < len; ++i) { 83 | if (Array.isArray(arr[i])) { 84 | exports.flatten(arr[i], ret); 85 | } else { 86 | ret.push(arr[i]); 87 | } 88 | } 89 | return ret; 90 | }; 91 | 92 | /** 93 | * Normalize the given `type`, for example "html" becomes "text/html". 94 | * 95 | * @param {String} type 96 | * @return {Object} 97 | * @api private 98 | */ 99 | 100 | exports.normalizeType = function(type){ 101 | return ~type.indexOf('/') 102 | ? acceptParams(type) 103 | : { value: mime.lookup(type), params: {} }; 104 | }; 105 | 106 | /** 107 | * Normalize `types`, for example "html" becomes "text/html". 108 | * 109 | * @param {Array} types 110 | * @return {Array} 111 | * @api private 112 | */ 113 | 114 | exports.normalizeTypes = function(types){ 115 | var ret = []; 116 | 117 | for (var i = 0; i < types.length; ++i) { 118 | ret.push(exports.normalizeType(types[i])); 119 | } 120 | 121 | return ret; 122 | }; 123 | 124 | /** 125 | * Generate Content-Disposition header appropriate for the filename. 126 | * non-ascii filenames are urlencoded and a filename* parameter is added 127 | * 128 | * @param {String} filename 129 | * @return {String} 130 | * @api private 131 | */ 132 | 133 | exports.contentDisposition = deprecate.function(contentDisposition, 134 | 'utils.contentDisposition: use content-disposition npm module instead'); 135 | 136 | /** 137 | * Parse accept params `str` returning an 138 | * object with `.value`, `.quality` and `.params`. 139 | * also includes `.originalIndex` for stable sorting 140 | * 141 | * @param {String} str 142 | * @return {Object} 143 | * @api private 144 | */ 145 | 146 | function acceptParams(str, index) { 147 | var parts = str.split(/ *; */); 148 | var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index }; 149 | 150 | for (var i = 1; i < parts.length; ++i) { 151 | var pms = parts[i].split(/ *= */); 152 | if ('q' == pms[0]) { 153 | ret.quality = parseFloat(pms[1]); 154 | } else { 155 | ret.params[pms[0]] = pms[1]; 156 | } 157 | } 158 | 159 | return ret; 160 | } 161 | 162 | /** 163 | * Compile "etag" value to function. 164 | * 165 | * @param {Boolean|String|Function} val 166 | * @return {Function} 167 | * @api private 168 | */ 169 | 170 | exports.compileETag = function(val) { 171 | var fn; 172 | 173 | if (typeof val === 'function') { 174 | return val; 175 | } 176 | 177 | switch (val) { 178 | case true: 179 | fn = exports.wetag; 180 | break; 181 | case false: 182 | break; 183 | case 'strong': 184 | fn = exports.etag; 185 | break; 186 | case 'weak': 187 | fn = exports.wetag; 188 | break; 189 | default: 190 | throw new TypeError('unknown value for etag function: ' + val); 191 | } 192 | 193 | return fn; 194 | } 195 | 196 | /** 197 | * Compile "query parser" value to function. 198 | * 199 | * @param {String|Function} val 200 | * @return {Function} 201 | * @api private 202 | */ 203 | 204 | exports.compileQueryParser = function compileQueryParser(val) { 205 | var fn; 206 | 207 | if (typeof val === 'function') { 208 | return val; 209 | } 210 | 211 | switch (val) { 212 | case true: 213 | fn = querystring.parse; 214 | break; 215 | case false: 216 | fn = newObject; 217 | break; 218 | case 'extended': 219 | fn = qs.parse; 220 | break; 221 | case 'simple': 222 | fn = querystring.parse; 223 | break; 224 | default: 225 | throw new TypeError('unknown value for query parser function: ' + val); 226 | } 227 | 228 | return fn; 229 | } 230 | 231 | /** 232 | * Compile "proxy trust" value to function. 233 | * 234 | * @param {Boolean|String|Number|Array|Function} val 235 | * @return {Function} 236 | * @api private 237 | */ 238 | 239 | exports.compileTrust = function(val) { 240 | if (typeof val === 'function') return val; 241 | 242 | if (val === true) { 243 | // Support plain true/false 244 | return function(){ return true }; 245 | } 246 | 247 | if (typeof val === 'number') { 248 | // Support trusting hop count 249 | return function(a, i){ return i < val }; 250 | } 251 | 252 | if (typeof val === 'string') { 253 | // Support comma-separated values 254 | val = val.split(/ *, */); 255 | } 256 | 257 | return proxyaddr.compile(val || []); 258 | } 259 | 260 | /** 261 | * Set the charset in a given Content-Type string. 262 | * 263 | * @param {String} type 264 | * @param {String} charset 265 | * @return {String} 266 | * @api private 267 | */ 268 | 269 | exports.setCharset = function setCharset(type, charset) { 270 | if (!type || !charset) { 271 | return type; 272 | } 273 | 274 | // parse type 275 | var parsed = contentType.parse(type); 276 | 277 | // set charset 278 | parsed.parameters.charset = charset; 279 | 280 | // format type 281 | return contentType.format(parsed); 282 | }; 283 | 284 | /** 285 | * Return new empty object. 286 | * 287 | * @return {Object} 288 | * @api private 289 | */ 290 | 291 | function newObject() { 292 | return {}; 293 | } 294 | --------------------------------------------------------------------------------