├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── UNLICENSE ├── bin └── grunt-curl ├── docs └── getting-started.js ├── grunt.js ├── package.json ├── tasks └── curl.js └── test ├── Gruntfile.js ├── curl-dir_test.js ├── curl_test.js ├── expected ├── braceExpansion │ ├── LAB.min.js │ └── cookiejar.js ├── cookiejar.js ├── file.js ├── file.zip ├── get.txt ├── gzip.txt ├── multi │ ├── LAB.min.js │ └── cookiejar.js ├── multiPost │ └── post.txt ├── post.txt └── router │ └── ajax │ └── libs │ ├── cookiejar │ └── 0.5 │ │ └── cookiejar.js │ └── labjs │ └── 2.0.3 │ └── LAB.min.js ├── grunt.js └── utils ├── fs.js ├── grunt.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /test/actual/ 3 | docs/location/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "7" 5 | - "6" 6 | - "4" 7 | matrix: 8 | allow_failures: 9 | - node_js: "7" 10 | 11 | before_install: 12 | # Upgrade npm to avoid semver issues 13 | - curl --location http://rawgit.com/twolfson/fix-travis-ci/master/lib/install.sh | bash -s 14 | 15 | notifications: 16 | email: false 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # grunt-curl changelog 2 | 2.5.1 - Upgraded to shell-quote@1.6.1 to fix GitHub vulnerability warning 3 | 4 | 2.5.0 - Upgraded to `lodash@4` to fix GitHub vulnerability warning 5 | 6 | 2.4.2 - Upgraded to `express@3.21.2` to fix GitHub vulnerability warning 7 | 8 | 2.4.1 - Replaced Gittip with support me page 9 | 10 | 2.4.0 - Upgraded to `request@2.83.0` to fix security flaw via @maxcutler in #40 11 | 12 | 2.3.1 - Fixed Travis CI configuration 13 | 14 | 2.3.0 - Added foundry for releases 15 | 16 | 2.2.1 - Added authentication example via @lynchmaniac in #39 17 | 18 | 2.2.0 - Upgraded to `request@2.54.0` to fix security flaw via @duereg in #31 19 | 20 | 2.1.1 - Fixed Travis CI configuration 21 | 22 | 2.1.0 - Added support for decoding `gzipped` content via `gzip: true` request option 23 | 24 | 2.0.3 - Added example for using a `proxy` 25 | 26 | 2.0.2 - Updated README 27 | 28 | 2.0.1 - Fixed regression with exiting early and bad event listener 29 | 30 | 2.0.0 - Moved to streams for creating files, deprecated `helper`. Fixes #21 31 | 32 | 1.5.1 - Added test for local `GET` in order to resolve #18 faster 33 | 34 | 1.5.0 - Moved from `doubleshot` to `mocha` to make tests more canonical for contributors 35 | 36 | 1.4.0 - Moved from `grunt.util` dependencies to `async` and `lodash` dependencies via @shinnn #13 37 | 38 | 1.3.1 - Added Travis CI 39 | 40 | 1.3.0 - Upgraded to latest `grunt-retro` to support description-less multitasks 41 | 42 | 1.2.1 - Removing accidental `console.log` 43 | 44 | 1.2.0 - Added support for all `request` options 45 | 46 | Before 1.2.0 - See `git log` 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-curl [![Build status](https://travis-ci.org/twolfson/grunt-curl.png?branch=master)](https://travis-ci.org/twolfson/grunt-curl) 2 | 3 | Download files from the internet via [grunt][]. 4 | 5 | This was created for dependency management via [`grunt-curl`][] and [`grunt-zip`][] as a low-tech alternative to `bower` and similar solutions. 6 | 7 | http://twolfson.com/2014-01-19-low-tech-dependency-management-via-grunt-tasks 8 | 9 | [grunt]: http://gruntjs.com/ 10 | [`grunt-curl`]: https://github.com/twolfson/grunt-curl 11 | [`grunt-zip`]: https://github.com/twolfson/grunt-zip 12 | 13 | ## Getting Started 14 | `grunt-curl` can be installed via npm: `npm install grunt-curl` 15 | 16 | Then, add and configure it in your grunt file: 17 | 18 | ```js 19 | module.exports = function (grunt) { 20 | // Configure `curl` with URLs 21 | // If you would like to download multiple files 22 | // to the same directory, there is `curl-dir` 23 | grunt.initConfig({ 24 | curl: { 25 | 'location/to/download/github.html': 'http://github.com/', 26 | } 27 | }); 28 | 29 | // Load in `grunt-curl` 30 | grunt.loadNpmTasks('grunt-curl'); 31 | }; 32 | ``` 33 | 34 | Now, we can run our task: 35 | 36 | ```bash 37 | $ grunt curl 38 | Running "curl:location/to/download/github.html" (curl) task 39 | File "location/to/download/github.html" created. 40 | 41 | Done, without errors. 42 | ``` 43 | 44 | ## Documentation 45 | `grunt-curl` creates 2 `grunt` tasks for you to use/configure, `curl` and `curl-dir`. `curl` is designed for downloading single files at a time. `curl-dir` is designed for downloading multiple files to a common directory. 46 | 47 | Both tasks support accepting [`request`] parameters as a `src` file. [Here is an example creating a `POST` request][post-example]. 48 | 49 | [`request`]: https://github.com/mikeal/request 50 | [post-example]: #using-request-options 51 | 52 | ### `curl` 53 | We support 2 different formats for configuring `curl`. 54 | 55 | #### Short format 56 | The short format relies on [`grunt's` support of `{dest: src}`][grunt-short-format] 57 | 58 | [grunt-short-format]: http://gruntjs.com/configuring-tasks#older-formats 59 | 60 | ```js 61 | curl: { 62 | 'location/to/download/file.js': 'http://files.com/path/to/file.js' 63 | } 64 | ``` 65 | 66 | This format is suggested only if you don't need to run `curl` tasks separately 67 | 68 | ```bash 69 | grunt curl 70 | ``` 71 | 72 | If you want to run this task standalone, it must be executed via: 73 | 74 | ```bash 75 | grunt curl:dest 76 | # grunt curl:location/to/download/file.js 77 | ``` 78 | 79 | #### Long format 80 | ```js 81 | curl: { 82 | 'task-name': { 83 | src: 'http://files.com/path/to/file.js', 84 | dest: 'location/to/download/file.js' 85 | } 86 | } 87 | ``` 88 | 89 | This can be run standalone via 90 | 91 | ```bash 92 | grunt curl:task-name 93 | ``` 94 | 95 | #### Using request options 96 | This is an example of the long format leveraging [`request`][] parameters for making a `POST` request. 97 | 98 | ```js 99 | curl: { 100 | 'task-name': { 101 | src: { 102 | url: 'http://files.com/path/to/file.js', 103 | method: 'POST', 104 | body: 'abc' 105 | }, 106 | dest: 'location/to/download/file.js' 107 | } 108 | } 109 | ``` 110 | 111 | ### `curl-dir` 112 | `curl-dir` supports 2 configuration formats. 113 | 114 | #### Short format 115 | As with `curl`, we leverage `grunt's {dest: src}` format for our short format. 116 | 117 | ```js 118 | 'curl-dir': { 119 | // These will be saved as: 120 | // 'location/to/save/files/file1.js' and 121 | // 'location/to/save/files/file2.js' 122 | 'location/to/save/files': [ 123 | 'http://files.com/path/to/file1.js', 124 | 'http://generic.com/scripts/file2.js' 125 | ] 126 | } 127 | ``` 128 | 129 | As with before, this can be executed via `grunt curl-dir` but will execute other tasks at the same level. To run this task standalone, it must be run via: 130 | 131 | ```bash 132 | grunt curl-dir:location/to/save/files 133 | ``` 134 | 135 | #### Long format 136 | ```js 137 | 'curl-dir': { 138 | 'task-name': { 139 | src: [ 140 | 'http://files.com/path/to/file1.js', 141 | 'http://files.com/path/to/file2.js' 142 | ], 143 | dest: 'location/to/save/files' 144 | } 145 | } 146 | ``` 147 | 148 | This task can be executed from the command line via 149 | 150 | ```bash 151 | grunt curl-dir:task-name 152 | ``` 153 | 154 | #### Brace expansion 155 | `curl-dir` supports brace expansion for `src` in both formats. 156 | 157 | ```js 158 | 'curl-dir': { 159 | 'brace-expansion': { 160 | src: ['http://files.com/path/to/{file1,file2}.js'], 161 | // Expands to: [ 162 | // 'http://files.com/path/to/file1.js', 163 | // 'http://files.com/path/to/file2.js' 164 | // ] 165 | dest: 'location/to/save/files' 166 | } 167 | } 168 | ``` 169 | 170 | #### Filepath mapping 171 | URLs can be mapped to custom filepaths via the `router` option in the long format. 172 | 173 | ```js 174 | 'curl-dir': { 175 | 'custom-filepaths': { 176 | src: [ 177 | 'http://files.com/path/to/file1.js', 178 | 'http://generic.com/scripts/file2.js' 179 | ], 180 | router: function (url) { 181 | // Save `file1.js` to 'location/to/save/files/hello/world/file1.js' 182 | // and `file2.js` to 'location/to/save/files/goodbye/moon/file2.js' 183 | var filepath = url.replace('http://files.com/path/to', 'hello/world'); 184 | return url.replace('http://generic.com/scripts', 'goodbye/moon'); 185 | }, 186 | dest: 'location/to/save/files' 187 | } 188 | } 189 | ``` 190 | 191 | #### Using request options 192 | As demonstrated in `curl`, we can use [`request`][] options to leverage special HTTP actions (e.g. make a `POST` request). 193 | 194 | ```js 195 | 'curl-dir': { 196 | custom: { 197 | src: [{ 198 | url: 'http://files.com/path/to/file.js', 199 | method: 'POST', 200 | body: 'abc' 201 | }], 202 | dest: 'location/to/save/files' 203 | } 204 | } 205 | ``` 206 | 207 | ## Examples 208 | ### Using a proxy 209 | Using [`request`][] options we can add a proxy to our requests 210 | 211 | ```js 212 | curl: { 213 | custom: { 214 | src: { 215 | url: 'http://google.com/', 216 | proxy: 'http://127.0.0.1:9001/' 217 | }, 218 | dest: 'google.html' 219 | } 220 | } 221 | ``` 222 | 223 | ### Using authentication 224 | Using [`request`][] options we can add authentication to our requests 225 | 226 | ```js 227 | curl: { 228 | custom: { 229 | src: { 230 | url: 'http://secureserver.com/members', 231 | auth: { 232 | user: 'my-username', 233 | pass: 'my-password' 234 | } 235 | }, 236 | dest: 'secure.html' 237 | } 238 | } 239 | ``` 240 | 241 | ## Contributing 242 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint your code using [grunt][grunt] and test via `npm test`. 243 | 244 | ## Donating 245 | Support this project and [others by twolfson][twolfson-projects] via [donations][twolfson-support-me]. 246 | 247 | 248 | 249 | [twolfson-projects]: http://twolfson.com/projects 250 | [twolfson-support-me]: http://twolfson.com/support-me 251 | 252 | ## Unlicense 253 | As of Jun 14 2014, Todd Wolfson has released this repository and its contents to the public domain. 254 | 255 | It has been released under the [UNLICENSE][]. 256 | 257 | [UNLICENSE]: UNLICENSE 258 | 259 | Prior to Jun 14 2014, this repository and its contents were licensed under the [MIT license][]. 260 | 261 | [MIT license]: https://github.com/twolfson/grunt-curl/blob/1.5.1/LICENSE-MIT 262 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /bin/grunt-curl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('grunt').npmTasks('grunt-curl').cli(); 3 | -------------------------------------------------------------------------------- /docs/getting-started.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | // Configure `curl` with URLs 3 | // If you would like to download multiple files 4 | // to the same directory, there is `curl-dir` 5 | grunt.initConfig({ 6 | curl: { 7 | 'location/to/download/github.html': 'http://github.com/', 8 | } 9 | }); 10 | 11 | // Load in `grunt-curl` 12 | grunt.loadTasks('../tasks'); 13 | }; 14 | -------------------------------------------------------------------------------- /grunt.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | lint: { 6 | files: ['grunt.js', 'tasks/**/*.js', 'test/*.{js,json}'] 7 | }, 8 | watch: { 9 | files: '', 10 | tasks: 'default' 11 | }, 12 | jshint: { 13 | options: { 14 | curly: true, 15 | eqeqeq: true, 16 | immed: true, 17 | // latedef: true, 18 | newcap: true, 19 | noarg: true, 20 | sub: true, 21 | undef: true, 22 | boss: true, 23 | eqnull: true, 24 | node: true, 25 | es5: true, 26 | 27 | strict: false 28 | }, 29 | globals: {} 30 | } 31 | }); 32 | 33 | // Default task. 34 | grunt.registerTask('default', 'lint'); 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-curl", 3 | "description": "Download files from the internet via grunt.", 4 | "version": "2.5.1", 5 | "homepage": "https://github.com/twolfson/grunt-curl", 6 | "author": { 7 | "name": "Todd Wolfson", 8 | "email": "todd@twolfson.com", 9 | "url": "http://twolfson.com/" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/twolfson/grunt-curl.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/twolfson/grunt-curl/issues" 17 | }, 18 | "licenses": [ 19 | { 20 | "type": "MIT", 21 | "url": "https://github.com/twolfson/grunt-curl/blob/master/LICENSE-MIT" 22 | } 23 | ], 24 | "main": "grunt.js", 25 | "bin": "bin/grunt-curl", 26 | "engines": { 27 | "node": ">= 4.0.0" 28 | }, 29 | "scripts": { 30 | "test": "rm -r test/actual 2> /dev/null; mocha" 31 | }, 32 | "dependencies": { 33 | "async": "~0.2.10", 34 | "grunt-retro": "~0.7.0", 35 | "lodash": "~4.17.11", 36 | "request": "~2.83.0" 37 | }, 38 | "_devDependencies": { 39 | "grunt": "~0.3.17", 40 | "doubleshot": "~2.13.1", 41 | "chai": "~1.5.0", 42 | "grunt-contrib-clean": "~0.3.2", 43 | "express": "~3.4.4" 44 | }, 45 | "devDependencies": { 46 | "chai": "~1.9.0", 47 | "express": "~3.21.2", 48 | "foundry": "~4.3.3", 49 | "foundry-release-git": "~2.0.2", 50 | "foundry-release-npm": "~2.0.2", 51 | "grunt": "~0.4.0", 52 | "grunt-cli": "~0.1.13", 53 | "grunt-contrib-clean": "~0.5.0", 54 | "mocha": "~1.18.2", 55 | "shell-quote": "~1.6.1" 56 | }, 57 | "keywords": [ 58 | "gruntplugin", 59 | "grunt", 60 | "curl", 61 | "download", 62 | "request", 63 | "file", 64 | "url", 65 | "uri" 66 | ], 67 | "foundry": { 68 | "releaseCommands": [ 69 | "foundry-release-git", 70 | "foundry-release-npm" 71 | ] 72 | } 73 | } -------------------------------------------------------------------------------- /tasks/curl.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var gruntRetro = require('grunt-retro'); 7 | var request = require('request'); 8 | 9 | // Define our tasks 10 | module.exports = function (grunt) { 11 | // Load in grunt-retro 12 | grunt = gruntRetro(grunt); 13 | 14 | // Define a task for single files 15 | grunt.registerMultiTask('curl', 'Download files from the internet via grunt.', function () { 16 | // Collect the filepaths we need 17 | var src = this.file.src; 18 | var dest = this.file.dest; 19 | var done = this.async(); 20 | 21 | // If we have received an array, condense it 22 | // DEV: If there are multiple files, we cannot concatenate them since that requires buffering 23 | // DEV: which creates issues with large files. As a result, do not allow multiple files. 24 | var srcFile = src; 25 | var srcFiles = srcFile; 26 | if (Array.isArray(srcFiles)) { 27 | // If there were no files found, be informative 28 | if (srcFiles.length === 0) { 29 | grunt.fail.warn('No source files were specified. Stopping `grunt-curl` early.'); 30 | return done(); 31 | // Otherwise, if there were too many files, complain and leave 32 | } else if (srcFiles.length > 1) { 33 | grunt.fail.warn('Too many source files received. Expected: 1, Actual: ' + srcFiles.length + '. Stopping `grunt-curl` early.'); 34 | return done(); 35 | } 36 | 37 | // Collapse first element 38 | srcFile = srcFiles[0]; 39 | } 40 | 41 | // Asynchronously fetch the file 42 | grunt.helper('curl', { 43 | src: srcFile, 44 | dest: dest 45 | }, function handleCurlComplete (err) { 46 | // If there is an error, fail 47 | if (err) { 48 | grunt.fail.warn(err); 49 | return done(); 50 | } 51 | 52 | // Otherwise, print a success message. 53 | grunt.log.writeln('File "' + dest + '" created.'); 54 | 55 | // Callback 56 | done(); 57 | }); 58 | }); 59 | 60 | // Define a task for multiple files 61 | grunt.registerMultiTask('curl-dir', 'Download collections of files from the internet via grunt.', function () { 62 | // Collect the filepaths we need 63 | var src = this.file.src; 64 | var dest = this.file.dest; 65 | var done = this.async(); 66 | 67 | // Upcast the srcFiles to an array 68 | var srcFiles = src; 69 | if (!Array.isArray(srcFiles)) { 70 | srcFiles = [src]; 71 | } 72 | 73 | // Iterate over the array and expand the braces 74 | var minimatch = grunt.file.glob.minimatch, 75 | braceExpand = minimatch.braceExpand; 76 | srcFiles = srcFiles.reduce(function expandSrcFiles (retArr, srcFile) { 77 | var srcFileArr = typeof srcFile === 'string' ? braceExpand(srcFile) : [srcFile]; 78 | retArr = retArr.concat(srcFileArr); 79 | return retArr; 80 | }, []); 81 | 82 | // Determine the destinations 83 | var router = this.data.router || function defaultRouter (filepath) { 84 | if (typeof filepath !== 'string') { 85 | filepath = filepath.url || filepath.uri; 86 | } 87 | return path.basename(filepath); 88 | }; 89 | var fileInfos = srcFiles.map(function getDest (srcFile, i) { 90 | // Route the file, append it to dest, and return 91 | var filepath = router(srcFile), 92 | retStr = path.join(dest, filepath); 93 | return { 94 | src: srcFile, 95 | dest: retStr 96 | }; 97 | }); 98 | 99 | // Asynchronously fetch the files in parallel 100 | async.map(fileInfos, grunt.helper.bind(grunt, 'curl'), function handleCurlResult (err) { 101 | // If there is an error, fail 102 | if (err) { 103 | grunt.fail.warn(err); 104 | return done(); 105 | } 106 | 107 | // Otherwise, print a success message. 108 | var destArr = _.map(fileInfos, 'dest'); 109 | grunt.log.writeln('Files "' + destArr.join('", "') + '" created.'); 110 | 111 | // Callback 112 | done(); 113 | }); 114 | }); 115 | 116 | // Register our curl helper 117 | grunt.registerHelper('curl', function (info, cb) { 118 | // Default to a binary request 119 | var options = info.src; 120 | var dest = info.dest; 121 | 122 | // Request the url 123 | var req = request(options); 124 | 125 | // On error, callback 126 | req.on('error', cb); 127 | 128 | // On response, callback for writing out the stream 129 | req.on('response', function handleResponse (res) { 130 | // Assert the statusCode was good 131 | var statusCode = res.statusCode; 132 | if (statusCode < 200 || statusCode >= 300) { 133 | return cb(new Error('Fetching ' + JSON.stringify(options) + ' failed with HTTP status code ' + statusCode)); 134 | } 135 | 136 | // Otherwise, write out the content 137 | var destdir = path.dirname(dest); 138 | grunt.file.mkdir(destdir); 139 | var writeStream = fs.createWriteStream(dest); 140 | // Use `req` as the source of data https://github.com/request/request/blob/v2.51.1/request.js#L1255-L1267 141 | // DEV: This is to pipe out gunzipped data 142 | var dataStream = req; 143 | dataStream.pipe(writeStream); 144 | 145 | // When the stream errors or completes, exit 146 | writeStream.on('error', cb); 147 | writeStream.on('close', cb); 148 | }); 149 | }); 150 | }; 151 | -------------------------------------------------------------------------------- /test/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | // Load in legacy config 3 | require('./grunt')(grunt); 4 | 5 | // Run project tasks 6 | grunt.registerTask('default', ['clean', 'curl', 'curl-dir']); 7 | }; 8 | -------------------------------------------------------------------------------- /test/curl-dir_test.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var expect = require('chai').expect; 3 | var fsUtils = require('./utils/fs'); 4 | var gruntUtils = require('./utils/grunt'); 5 | var serverUtils = require('./utils/server'); 6 | 7 | // curl-dir tests 8 | describe('grunt curl-dir', function () { 9 | describe('downloading multiple files', function () { 10 | gruntUtils.runTask('curl-dir:multi'); 11 | 12 | describe('the first file', function () { 13 | fsUtils.readExpectedFile('multi/LAB.min.js', 'utf8'); 14 | fsUtils.readActualFile('multi/LAB.min.js', 'utf8'); 15 | 16 | it('is successfully downloaded', function () { 17 | expect(this.err).to.equal(null); 18 | expect(this.actualContent).to.equal(this.expectedContent); 19 | }); 20 | }); 21 | 22 | describe('the second file', function () { 23 | fsUtils.readExpectedFile('multi/cookiejar.js', 'utf8'); 24 | fsUtils.readActualFile('multi/cookiejar.js', 'utf8'); 25 | 26 | it('is successfully downloaded', function () { 27 | expect(this.err).to.equal(null); 28 | expect(this.actualContent).to.equal(this.expectedContent); 29 | }); 30 | }); 31 | }); 32 | 33 | describe('downloading brace expanded files', function () { 34 | gruntUtils.runTask('curl-dir:braceExpansion'); 35 | 36 | describe('the first file', function () { 37 | fsUtils.readExpectedFile('braceExpansion/LAB.min.js', 'utf8'); 38 | fsUtils.readActualFile('braceExpansion/LAB.min.js', 'utf8'); 39 | 40 | it('is successfully downloaded', function () { 41 | expect(this.err).to.equal(null); 42 | expect(this.actualContent).to.equal(this.expectedContent); 43 | }); 44 | }); 45 | 46 | describe('the second file', function () { 47 | fsUtils.readExpectedFile('braceExpansion/cookiejar.js', 'utf8'); 48 | fsUtils.readActualFile('braceExpansion/cookiejar.js', 'utf8'); 49 | 50 | it('is successfully downloaded', function () { 51 | expect(this.err).to.equal(null); 52 | expect(this.actualContent).to.equal(this.expectedContent); 53 | }); 54 | }); 55 | }); 56 | 57 | describe('using a custom router', function () { 58 | gruntUtils.runTask('curl-dir:router'); 59 | 60 | describe('the first file', function () { 61 | fsUtils.readExpectedFile('router/ajax/libs/labjs/2.0.3/LAB.min.js', 'utf8'); 62 | fsUtils.readActualFile('router/ajax/libs/labjs/2.0.3/LAB.min.js', 'utf8'); 63 | 64 | it('is successfully downloaded', function () { 65 | expect(this.err).to.equal(null); 66 | expect(this.actualContent).to.equal(this.expectedContent); 67 | }); 68 | }); 69 | 70 | describe('the second file', function () { 71 | fsUtils.readExpectedFile('router/ajax/libs/cookiejar/0.5/cookiejar.js', 'utf8'); 72 | fsUtils.readActualFile('router/ajax/libs/cookiejar/0.5/cookiejar.js', 'utf8'); 73 | 74 | it('is successfully downloaded', function () { 75 | expect(this.err).to.equal(null); 76 | expect(this.actualContent).to.equal(this.expectedContent); 77 | }); 78 | }); 79 | }); 80 | 81 | describe('using POST', function () { 82 | serverUtils.runPostServer(); 83 | gruntUtils.runTask('curl-dir:post'); 84 | fsUtils.readExpectedFile('multiPost/post.txt', 'utf8'); 85 | fsUtils.readActualFile('multiPost/post.txt', 'utf8'); 86 | 87 | it('is successful', function () { 88 | expect(this.err).to.equal(null); 89 | expect(this.actualContent).to.equal(this.expectedContent); 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/curl_test.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var expect = require('chai').expect; 3 | var fsUtils = require('./utils/fs'); 4 | var gruntUtils = require('./utils/grunt'); 5 | var serverUtils = require('./utils/server'); 6 | 7 | // curl tests 8 | describe('grunt curl', function () { 9 | describe('downloading a GET file', function () { 10 | serverUtils.runGetServer(); 11 | gruntUtils.runTask('curl:get'); 12 | fsUtils.readExpectedFile('get.txt', 'utf8'); 13 | fsUtils.readActualFile('get.txt', 'utf8'); 14 | 15 | it('is successful', function () { 16 | expect(this.err).to.equal(null); 17 | expect(this.actualContent).to.equal(this.expectedContent); 18 | }); 19 | }); 20 | 21 | describe('downloading a POST file', function () { 22 | serverUtils.runPostServer(); 23 | gruntUtils.runTask('curl:post'); 24 | fsUtils.readExpectedFile('post.txt', 'utf8'); 25 | fsUtils.readActualFile('post.txt', 'utf8'); 26 | 27 | it('is successful', function () { 28 | expect(this.err).to.equal(null); 29 | expect(this.actualContent).to.equal(this.expectedContent); 30 | }); 31 | }); 32 | 33 | describe('downloading a utf8 file (js)', function () { 34 | gruntUtils.runTask('curl:js'); 35 | fsUtils.readExpectedFile('file.js', 'utf8'); 36 | fsUtils.readActualFile('file.js', 'utf8'); 37 | 38 | it('is successful', function () { 39 | expect(this.err).to.equal(null); 40 | expect(this.actualContent).to.equal(this.expectedContent); 41 | }); 42 | }); 43 | 44 | describe('downloading a binary file (zip)', function () { 45 | gruntUtils.runTask('curl:zip'); 46 | fsUtils.readExpectedFile('file.zip', 'binary'); 47 | fsUtils.readActualFile('file.zip', 'binary'); 48 | 49 | it('is successful', function () { 50 | expect(this.err).to.equal(null); 51 | expect(this.actualContent).to.equal(this.expectedContent); 52 | }); 53 | }); 54 | 55 | describe('downloading a gzipped file', function () { 56 | serverUtils.runGzipServer(); 57 | gruntUtils.runTask('curl:gzip'); 58 | fsUtils.readExpectedFile('gzip.txt', 'utf8'); 59 | fsUtils.readActualFile('gzip.txt', 'utf8'); 60 | 61 | it('decodes the content', function () { 62 | expect(this.err).to.equal(null); 63 | expect(this.actualContent).to.equal(this.expectedContent); 64 | }); 65 | }); 66 | 67 | describe('downloading a file from an invalid domain', function () { 68 | gruntUtils.runTask('curl:nonExistingDomain'); 69 | fsUtils.exists('actual/nonexistent-domain'); 70 | 71 | it('throws an error', function () { 72 | expect(this.err).to.not.equal(null); 73 | }); 74 | it('does not create the file', function () { 75 | expect(this.fileExiss).to.not.equal(false); 76 | 77 | }); 78 | }); 79 | 80 | describe('downloading a nonexistant file', function () { 81 | gruntUtils.runTask('curl:nonExistingFile'); 82 | fsUtils.exists('actual/nonexistent-file'); 83 | 84 | it('throws an error', function () { 85 | expect(this.err).to.not.equal(null); 86 | }); 87 | it('does not create the file', function () { 88 | expect(this.fileExiss).to.not.equal(false); 89 | 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/expected/braceExpansion/LAB.min.js: -------------------------------------------------------------------------------- 1 | /*! LAB.js (LABjs :: Loading And Blocking JavaScript) 2 | v2.0.3 (c) Kyle Simpson 3 | MIT License 4 | */ 5 | (function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); -------------------------------------------------------------------------------- /test/expected/braceExpansion/cookiejar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Javascript code to store data as JSON strings in cookies. 3 | * It uses prototype.js 1.5.1 (http://www.prototypejs.org) 4 | * 5 | * Author : Lalit Patel 6 | * Website: http://www.lalit.org/lab/jsoncookies 7 | * License: Apache Software License 2 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * Version: 0.5 10 | * Updated: Jan 26, 2009 11 | * 12 | * Chnage Log: 13 | * v 0.5 14 | * - Changed License from CC to Apache 2 15 | * v 0.4 16 | * - Removed a extra comma in options (was breaking in IE and Opera). (Thanks Jason) 17 | * - Removed the parameter name from the initialize function 18 | * - Changed the way expires date was being calculated. (Thanks David) 19 | * v 0.3 20 | * - Removed dependancy on json.js (http://www.json.org/json.js) 21 | * - empty() function only deletes the cookies set by CookieJar 22 | */ 23 | 24 | var CookieJar = Class.create(); 25 | 26 | CookieJar.prototype = { 27 | 28 | /** 29 | * Append before all cookie names to differntiate them. 30 | */ 31 | appendString: "__CJ_", 32 | 33 | /** 34 | * Initializes the cookie jar with the options. 35 | */ 36 | initialize: function(options) { 37 | this.options = { 38 | expires: 3600, // seconds (1 hr) 39 | path: '', // cookie path 40 | domain: '', // cookie domain 41 | secure: '' // secure ? 42 | }; 43 | Object.extend(this.options, options || {}); 44 | 45 | if (this.options.expires != '') { 46 | var date = new Date(); 47 | date = new Date(date.getTime() + (this.options.expires * 1000)); 48 | this.options.expires = '; expires=' + date.toGMTString(); 49 | } 50 | if (this.options.path != '') { 51 | this.options.path = '; path=' + escape(this.options.path); 52 | } 53 | if (this.options.domain != '') { 54 | this.options.domain = '; domain=' + escape(this.options.domain); 55 | } 56 | if (this.options.secure == 'secure') { 57 | this.options.secure = '; secure'; 58 | } else { 59 | this.options.secure = ''; 60 | } 61 | }, 62 | 63 | /** 64 | * Adds a name values pair. 65 | */ 66 | put: function(name, value) { 67 | name = this.appendString + name; 68 | cookie = this.options; 69 | var type = typeof value; 70 | switch(type) { 71 | case 'undefined': 72 | case 'function' : 73 | case 'unknown' : return false; 74 | case 'boolean' : 75 | case 'string' : 76 | case 'number' : value = String(value.toString()); 77 | } 78 | var cookie_str = name + "=" + escape(Object.toJSON(value)); 79 | try { 80 | document.cookie = cookie_str + cookie.expires + cookie.path + cookie.domain + cookie.secure; 81 | } catch (e) { 82 | return false; 83 | } 84 | return true; 85 | }, 86 | 87 | /** 88 | * Removes a particular cookie (name value pair) form the Cookie Jar. 89 | */ 90 | remove: function(name) { 91 | name = this.appendString + name; 92 | cookie = this.options; 93 | try { 94 | var date = new Date(); 95 | date.setTime(date.getTime() - (3600 * 1000)); 96 | var expires = '; expires=' + date.toGMTString(); 97 | document.cookie = name + "=" + expires + cookie.path + cookie.domain + cookie.secure; 98 | } catch (e) { 99 | return false; 100 | } 101 | return true; 102 | }, 103 | 104 | /** 105 | * Return a particular cookie by name; 106 | */ 107 | get: function(name) { 108 | name = this.appendString + name; 109 | var cookies = document.cookie.match(name + '=(.*?)(;|$)'); 110 | if (cookies) { 111 | return (unescape(cookies[1])).evalJSON(); 112 | } else { 113 | return null; 114 | } 115 | }, 116 | 117 | /** 118 | * Empties the Cookie Jar. Deletes all the cookies. 119 | */ 120 | empty: function() { 121 | keys = this.getKeys(); 122 | size = keys.size(); 123 | for(i=0; i0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); -------------------------------------------------------------------------------- /test/expected/file.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twolfson/grunt-curl/5679ae1c8170d981ddf501b93a51ea3ba5066bac/test/expected/file.zip -------------------------------------------------------------------------------- /test/expected/get.txt: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } -------------------------------------------------------------------------------- /test/expected/gzip.txt: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } -------------------------------------------------------------------------------- /test/expected/multi/LAB.min.js: -------------------------------------------------------------------------------- 1 | /*! LAB.js (LABjs :: Loading And Blocking JavaScript) 2 | v2.0.3 (c) Kyle Simpson 3 | MIT License 4 | */ 5 | (function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); -------------------------------------------------------------------------------- /test/expected/multi/cookiejar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Javascript code to store data as JSON strings in cookies. 3 | * It uses prototype.js 1.5.1 (http://www.prototypejs.org) 4 | * 5 | * Author : Lalit Patel 6 | * Website: http://www.lalit.org/lab/jsoncookies 7 | * License: Apache Software License 2 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * Version: 0.5 10 | * Updated: Jan 26, 2009 11 | * 12 | * Chnage Log: 13 | * v 0.5 14 | * - Changed License from CC to Apache 2 15 | * v 0.4 16 | * - Removed a extra comma in options (was breaking in IE and Opera). (Thanks Jason) 17 | * - Removed the parameter name from the initialize function 18 | * - Changed the way expires date was being calculated. (Thanks David) 19 | * v 0.3 20 | * - Removed dependancy on json.js (http://www.json.org/json.js) 21 | * - empty() function only deletes the cookies set by CookieJar 22 | */ 23 | 24 | var CookieJar = Class.create(); 25 | 26 | CookieJar.prototype = { 27 | 28 | /** 29 | * Append before all cookie names to differntiate them. 30 | */ 31 | appendString: "__CJ_", 32 | 33 | /** 34 | * Initializes the cookie jar with the options. 35 | */ 36 | initialize: function(options) { 37 | this.options = { 38 | expires: 3600, // seconds (1 hr) 39 | path: '', // cookie path 40 | domain: '', // cookie domain 41 | secure: '' // secure ? 42 | }; 43 | Object.extend(this.options, options || {}); 44 | 45 | if (this.options.expires != '') { 46 | var date = new Date(); 47 | date = new Date(date.getTime() + (this.options.expires * 1000)); 48 | this.options.expires = '; expires=' + date.toGMTString(); 49 | } 50 | if (this.options.path != '') { 51 | this.options.path = '; path=' + escape(this.options.path); 52 | } 53 | if (this.options.domain != '') { 54 | this.options.domain = '; domain=' + escape(this.options.domain); 55 | } 56 | if (this.options.secure == 'secure') { 57 | this.options.secure = '; secure'; 58 | } else { 59 | this.options.secure = ''; 60 | } 61 | }, 62 | 63 | /** 64 | * Adds a name values pair. 65 | */ 66 | put: function(name, value) { 67 | name = this.appendString + name; 68 | cookie = this.options; 69 | var type = typeof value; 70 | switch(type) { 71 | case 'undefined': 72 | case 'function' : 73 | case 'unknown' : return false; 74 | case 'boolean' : 75 | case 'string' : 76 | case 'number' : value = String(value.toString()); 77 | } 78 | var cookie_str = name + "=" + escape(Object.toJSON(value)); 79 | try { 80 | document.cookie = cookie_str + cookie.expires + cookie.path + cookie.domain + cookie.secure; 81 | } catch (e) { 82 | return false; 83 | } 84 | return true; 85 | }, 86 | 87 | /** 88 | * Removes a particular cookie (name value pair) form the Cookie Jar. 89 | */ 90 | remove: function(name) { 91 | name = this.appendString + name; 92 | cookie = this.options; 93 | try { 94 | var date = new Date(); 95 | date.setTime(date.getTime() - (3600 * 1000)); 96 | var expires = '; expires=' + date.toGMTString(); 97 | document.cookie = name + "=" + expires + cookie.path + cookie.domain + cookie.secure; 98 | } catch (e) { 99 | return false; 100 | } 101 | return true; 102 | }, 103 | 104 | /** 105 | * Return a particular cookie by name; 106 | */ 107 | get: function(name) { 108 | name = this.appendString + name; 109 | var cookies = document.cookie.match(name + '=(.*?)(;|$)'); 110 | if (cookies) { 111 | return (unescape(cookies[1])).evalJSON(); 112 | } else { 113 | return null; 114 | } 115 | }, 116 | 117 | /** 118 | * Empties the Cookie Jar. Deletes all the cookies. 119 | */ 120 | empty: function() { 121 | keys = this.getKeys(); 122 | size = keys.size(); 123 | for(i=0; i0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); -------------------------------------------------------------------------------- /test/grunt.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | curl: { 6 | js: { 7 | src: 'http://cdnjs.cloudflare.com/ajax/libs/labjs/2.0.3/LAB.min.js', 8 | dest: 'actual/file.js' 9 | }, 10 | zip: { 11 | src: 'https://github.com/twitter/bootstrap/blob/91b92f9dd09c1794d02c6157daba5405d8f09e39/assets/bootstrap.zip?raw=true', 12 | dest: 'actual/file.zip' 13 | }, 14 | get: { 15 | src: { 16 | url: 'http://localhost:4000/get.txt', 17 | method: 'GET', 18 | qs: { 19 | hello: 'world' 20 | } 21 | }, 22 | dest: 'actual/get.txt' 23 | }, 24 | post: { 25 | src: { 26 | url: 'http://localhost:4000/post.txt', 27 | method: 'POST', 28 | form: { 29 | hello: 'world' 30 | } 31 | }, 32 | dest: 'actual/post.txt' 33 | }, 34 | gzip: { 35 | src: { 36 | url: 'http://localhost:4000/gzip.txt', 37 | gzip: true, 38 | qs: { 39 | hello: 'world' 40 | } 41 | }, 42 | dest: 'actual/gzip.txt' 43 | }, 44 | nonExistingDomain: { 45 | src: 'http://nonexistent--foo--domain', 46 | dest: 'actual/nonexistent-domain' 47 | }, 48 | nonExistingFile: { 49 | src: 'https://github.com/nonexistent--foo--file', 50 | dest: 'actual/nonexistent-file' 51 | } 52 | }, 53 | 'curl-dir': { 54 | multi: { 55 | src: [ 56 | 'http://cdnjs.cloudflare.com/ajax/libs/labjs/2.0.3/LAB.min.js', 57 | 'http://cdnjs.cloudflare.com/ajax/libs/cookiejar/0.5/cookiejar.js' 58 | ], 59 | dest: 'actual/multi' 60 | }, 61 | braceExpansion: { 62 | src: [ 63 | 'http://cdnjs.cloudflare.com/ajax/libs/{labjs/2.0.3/LAB.min,cookiejar/0.5/cookiejar}.js' 64 | ], 65 | dest: 'actual/braceExpansion' 66 | }, 67 | router: { 68 | src: [ 69 | 'http://cdnjs.cloudflare.com/ajax/libs/labjs/2.0.3/LAB.min.js', 70 | 'http://cdnjs.cloudflare.com/ajax/libs/cookiejar/0.5/cookiejar.js' 71 | ], 72 | router: function curlDirRouter (url) { 73 | return url.replace('http://cdnjs.cloudflare.com/', ''); 74 | }, 75 | dest: 'actual/router' 76 | }, 77 | post: { 78 | src: [{ 79 | url: 'http://localhost:4000/post.txt', 80 | method: 'POST', 81 | form: { 82 | hello: 'world' 83 | } 84 | }], 85 | dest: 'actual/multiPost' 86 | } 87 | } 88 | }); 89 | 90 | // Load local tasks. 91 | grunt.loadTasks('../tasks'); 92 | 93 | // Load grunt contrib clean (chdir for 0.4) 94 | process.chdir('..'); 95 | grunt.loadNpmTasks('grunt-contrib-clean'); 96 | process.chdir(__dirname); 97 | 98 | // Run project tasks 99 | grunt.registerTask('default', 'clean curl curl-dir'); 100 | }; 101 | -------------------------------------------------------------------------------- /test/utils/fs.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | // Helper to load file into mocha's `this` context 4 | exports._readFile = function (key, path, encoding) { 5 | before(function loadFile (done) { 6 | var that = this; 7 | fs.readFile(path, encoding, function (err, content) { 8 | that[key] = content; 9 | done(err); 10 | }); 11 | }); 12 | after(function cleanupFile () { 13 | delete this[key]; 14 | }); 15 | }; 16 | 17 | exports.readExpectedFile = function (path, encoding) { 18 | exports._readFile('expectedContent', 'expected/' + path, encoding); 19 | }; 20 | 21 | exports.readActualFile = function (path, encoding) { 22 | exports._readFile('actualContent', 'actual/' + path, encoding); 23 | }; 24 | 25 | // Helper to determine file existence 26 | exports.exists = function (path) { 27 | before(function loadFile (done) { 28 | var that = this; 29 | fs.exists(path, function (exists) { 30 | that.fileExists = exists; 31 | done(); 32 | }); 33 | }); 34 | after(function cleanupFile () { 35 | delete this.fileExists; 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /test/utils/grunt.js: -------------------------------------------------------------------------------- 1 | // Load in dependencies 2 | var exec = require('child_process').exec; 3 | var quote = require('shell-quote').quote; 4 | 5 | exports.runTask = function (task) { 6 | before(function runTask (done) { 7 | // Relocate to test directory 8 | process.chdir(__dirname + '/..'); 9 | 10 | // Execute the cmd and task combination 11 | var that = this; 12 | this.timeout(5000); 13 | exec(quote(['grunt', task]), function (err, stdout, stderr) { 14 | // Save results for later 15 | that.err = err; 16 | that.stdout = stdout; 17 | that.stderr = stderr; 18 | 19 | // Callback 20 | done(); 21 | }); 22 | }); 23 | 24 | after(function cleanupTask () { 25 | delete this.err; 26 | delete this.stdout; 27 | delete this.stderr; 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /test/utils/server.js: -------------------------------------------------------------------------------- 1 | // Load in depednecies 2 | var zlib = require('zlib'); 3 | var express = require('express'); 4 | 5 | // Create a server for GET data 6 | exports.runGetServer = function () { 7 | var _server; 8 | before(function startServer () { 9 | var server = express(); 10 | server.get('/get.txt', function (req, res) { 11 | res.send(req.query); 12 | }); 13 | _server = server.listen(4000); 14 | }); 15 | after(function stopServer (done) { 16 | _server.close(done); 17 | }); 18 | }; 19 | 20 | // Create a server for POST data 21 | exports.runPostServer = function () { 22 | var _server; 23 | before(function startServer () { 24 | var server = express(); 25 | server.post('/post.txt', express.urlencoded(), function (req, res) { 26 | res.send(req.body); 27 | }); 28 | _server = server.listen(4000); 29 | }); 30 | after(function stopServer (done) { 31 | _server.close(done); 32 | }); 33 | }; 34 | 35 | exports.runGzipServer = function () { 36 | var _server; 37 | before(function startServer () { 38 | var server = express(); 39 | server.get('/gzip.txt', function (req, res) { 40 | // Guarantee gzip response *always* 41 | res.setHeader('Content-Encoding', 'gzip'); 42 | 43 | // Take the query, stringify it, gzip it, and send it back 44 | var queryJson = JSON.stringify(req.query, null, 2); 45 | zlib.gzip(queryJson, function handleGzippedContent (err, gzipData) { 46 | // If there was an error, throw it 47 | if (err) { 48 | throw err; 49 | } 50 | 51 | // Otherwise, send back our gzipped content 52 | res.send(gzipData); 53 | }); 54 | 55 | }); 56 | _server = server.listen(4000); 57 | }); 58 | after(function stopServer (done) { 59 | _server.close(done); 60 | }); 61 | }; 62 | --------------------------------------------------------------------------------