├── .editorconfig ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE ├── README.md ├── package.json ├── tasks ├── dev_update.js └── lib │ └── dev_update.js └── test ├── dev_update_test.js ├── expected ├── custom_options └── default_options └── fixtures ├── 123 └── testing /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | 8 | indent_style = space 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.{js,json,html,jade,md}] 15 | indent_size = 4 16 | 17 | [*.js] 18 | jslint_happy = true 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules/ 3 | npm-debug.log 4 | tmp/ 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr" : 50, 3 | "bitwise" : false, 4 | "camelcase" : true, 5 | "curly" : true, 6 | "eqeqeq" : true, 7 | "forin" : true, 8 | "immed" : true, 9 | "indent" : 4, 10 | "latedef" : true, 11 | "newcap" : true, 12 | "noarg" : false, 13 | "noempty" : true, 14 | "nonew" : true, 15 | "regexp" : true, 16 | "quotmark" : "single", 17 | "undef" : true, 18 | "unused" : true, 19 | "strict" : false, 20 | "trailing" : true, 21 | "maxparams" : 7, 22 | "maxdepth" : 5, 23 | "maxstatements" : 25, 24 | "maxcomplexity" : 10, 25 | "maxlen" : 140, 26 | "funcscope" : false, 27 | "globalstrict" : true, 28 | "multistr" : true, 29 | "browser" : true, 30 | "devel" : true, 31 | "jquery" : true, 32 | "node" : true, 33 | "nonstandard" : true, 34 | "onevar" : false, 35 | "white" : false, 36 | "globals" : 37 | { 38 | "$" : true, 39 | "jQuery" : true, 40 | "angular" : true, 41 | "_" : true 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-dev-update 3 | * 4 | * Copyright (c) 2014 Gilad Peleg 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | module.exports = function (grunt) { 9 | 10 | // Project configuration. 11 | grunt.initConfig({ 12 | jshint: { 13 | all: [ 14 | 'Gruntfile.js', 15 | 'tasks/*.js', 16 | '<%= nodeunit.tests %>' 17 | ], 18 | options: { 19 | jshintrc: '.jshintrc', 20 | reporter: require('jshint-stylish') 21 | } 22 | }, 23 | 24 | // Before generating any new files, remove any previously-created files. 25 | clean: { 26 | tests: ['tmp'] 27 | }, 28 | 29 | // Unit tests. 30 | nodeunit: { 31 | tests: ['test/*_test.js'] 32 | }, 33 | 34 | devUpdate: { 35 | main: { 36 | options: { 37 | //just report 38 | updateType: 'prompt', 39 | //stick to semver matching in packages 40 | semver: false, 41 | //don't report ok packages by default 42 | reportUpdated: false, 43 | //what packages to check 44 | packages: { 45 | //only devDependencies by default 46 | devDependencies: true, 47 | dependencies: true 48 | }, 49 | //by deafult - use matchdep default findup to locate package.json 50 | packageJson: null 51 | } 52 | } 53 | } 54 | }); 55 | 56 | // load all npm grunt tasks 57 | require('load-grunt-tasks')(grunt); 58 | 59 | // Actually load this plugin's task(s). 60 | grunt.loadTasks('tasks'); 61 | 62 | // Whenever the "test" task is run, first clean the "tmp" dir, then run this 63 | // plugin's task(s), then test the result. 64 | grunt.registerTask('test', ['clean', 'devUpdate']); 65 | 66 | // By default, lint and run all tests. 67 | grunt.registerTask('default', ['jshint', 'test']); 68 | 69 | }; 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Gilad Peleg (https://www.giladpeleg.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [grunt](http://gruntjs.com/)-dev-update 2 | > Update your devDependencies and dependencies automatically with a grunt task 3 | 4 | [![NPM Version](http://img.shields.io/npm/v/grunt-dev-update.svg?style=flat)](https://npmjs.org/package/grunt-dev-update) 5 | [![NPM Downloads](http://img.shields.io/npm/dm/grunt-dev-update.svg?style=flat)](https://npmjs.org/package/grunt-dev-update) 6 | [![Built with Grunt](http://img.shields.io/badge/BUILT_WITH-GRUNT-orange.svg?style=flat)](http://gruntjs.com/) 7 | 8 | ## Getting Started 9 | This plugin requires Grunt. 10 | 11 | If you haven't used [Grunt](http://gruntjs.com/) before, 12 | be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, 13 | as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. 14 | Once you're familiar with that process, you may install this plugin with this command: 15 | 16 | ```bash 17 | npm install --save-dev grunt-dev-update 18 | ``` 19 | 20 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 21 | 22 | ```js 23 | grunt.loadNpmTasks('grunt-dev-update'); 24 | ``` 25 | 26 | The best way to load tasks is probably using [load-grunt-tasks](https://github.com/sindresorhus/load-grunt-tasks) 27 | 28 | ```bash 29 | npm install --save-dev load-grunt-tasks 30 | ``` 31 | 32 | And then add to your gruntfile.js: 33 | ```js 34 | require('load-grunt-tasks')(grunt); 35 | ``` 36 | 37 | ## The "devUpdate" task 38 | 39 | #### This plugin allows you to both update your dependencies and devDependencies with an automated task. 40 | 41 | 1. See outdated packages 42 | 2. Choose whether to just get notified, update them with a prompt, or automatically update them. 43 | 3. Determine whether to stay with semver rules when updating, or to update to latest version. 44 | 4. Update either or both your devDependencies and dependencies 45 | 46 | *Q: Why not use `npm update` or `npm install`?* 47 | 48 | **A: First, npm update doesn't work on dev dependencies. Second, npm update stays inside your semver matching in your package.json, 49 | thirdly - npm isn't automated like your grunt tasks.** 50 | 51 | ### Overview 52 | In your project's Gruntfile, add a task config named `devUpdate` to the data object passed into `grunt.initConfig()`. 53 | 54 | ```js 55 | grunt.initConfig({ 56 | devUpdate: { 57 | main: { 58 | options: { 59 | //task options go here 60 | } 61 | } 62 | } 63 | }) 64 | ``` 65 | 66 | ### Options 67 | 68 | #### options.reportUpdated 69 | Type: `Boolean` 70 | Default value: `false` 71 | 72 | Whether to report an already updated package 73 | 74 | #### options.updateType 75 | Type: `String` 76 | Default value: `report` 77 | 78 | How devUpdate should handle the outdated packages. Valid options: 79 | 80 | * `report` - Just report that the package is outdated. 81 | * `prompt` - Prompt user to confirm update of every package 82 | * `force` - Automatically force the update for the outdated packages. 83 | * `fail` - Fail task if an outdated package was found. 84 | 85 | #### options.packages 86 | Type: `Object` 87 | Default value: `{devDependencies: true}` 88 | 89 | What kind of packages should be checked. Valid options: 90 | 91 | * `dependencies` - Specify true to check **production dependencies**. 92 | 93 | > Outdated **dependencies** are installed using the `--save` option. 94 | 95 | * `devDependencies` - Specify true to check **development dependencies**. This is **true** by default. 96 | 97 | > Outdated **devDependencies** are installed using the `--save-dev` option. 98 | 99 | #### options.semver 100 | Type: `Boolean` 101 | Default value: `true` 102 | 103 | `true` - Packages will be updated with `npm update` and will be installed up to your allowed version in 104 | your `package.json`. Your allowed version is determined using [semver](http://semver.org). 105 | 106 | `false` - Packages will be updated to the latest version there is, regardless of your `package.json` specifications. 107 | 108 | **Warning** - this could break packages and only use this option if you're sure of what you're doing. 109 | 110 | #### options.packageJson 111 | Type: `null|Object|String` 112 | Default value: `null` 113 | 114 | This option allow you to manually configure the path of your **package.json**. Valid options: 115 | 116 | * `null` - This will use `matchdep` own logic for finding your package.json (using `findup` to find 117 | nearest package.json). This is the recommended and default option. 118 | * `String` - specify a relative path from your **process.cwd()** to find your package.json. 119 | * `Object` - pass in an object representing your package.json 120 | 121 | For better understanding the `String` and `Object` option, please see [matchdep config](https://github.com/tkellen/node-matchdep#config). 122 | 123 | #### options.reportOnlyPkgs 124 | Type: `Array` 125 | Default value: `[]` 126 | 127 | Specify packages that will be checked for newer version but only reported if outdated. 128 | 129 | This is useful if you are aware of packages that will be outdated, but don't want to update them. 130 | 131 | ### Usage Examples 132 | 133 | #### Default Options 134 | Example usage with all options specified with defaults: 135 | 136 | ```js 137 | grunt.initConfig({ 138 | devUpdate: { 139 | main: { 140 | options: { 141 | updateType: 'report', //just report outdated packages 142 | reportUpdated: false, //don't report up-to-date packages 143 | semver: true, //stay within semver when updating 144 | packages: { 145 | devDependencies: true, //only check for devDependencies 146 | dependencies: false 147 | }, 148 | packageJson: null, //use matchdep default findup to locate package.json 149 | reportOnlyPkgs: [] //use updateType action on all packages 150 | } 151 | } 152 | } 153 | }) 154 | ``` 155 | 156 | ## Contributing 157 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). 158 | 159 | ## License 160 | 161 | MIT © [Gilad Peleg](https://www.giladpeleg.com) 162 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-dev-update", 3 | "version": "2.3.0", 4 | "description": "Automate the updating of your package.json packages with a grunt task", 5 | "repository": "pgilad/grunt-dev-update", 6 | "license": "MIT", 7 | "author": "Gilad Peleg (http://giladpeleg.com)", 8 | "files": [ 9 | "tasks" 10 | ], 11 | "engines": { 12 | "node": ">=6.0.0" 13 | }, 14 | "scripts": { 15 | "test": "grunt test" 16 | }, 17 | "keywords": [ 18 | "gruntplugin", 19 | "devDependencies", 20 | "dependencies", 21 | "install", 22 | "save-dev", 23 | "save", 24 | "automation", 25 | "updates" 26 | ], 27 | "dependencies": { 28 | "async-each-series": "^1.1.0", 29 | "findup-sync": "^0.4.0", 30 | "inquirer": "^0.12.0", 31 | "lodash": "^4.8.2", 32 | "npm-package-arg": "^4.1.0", 33 | "semver": "^5.1.0" 34 | }, 35 | "devDependencies": { 36 | "grunt": "^1.0.1", 37 | "grunt-contrib-clean": "^1.0.0", 38 | "grunt-contrib-jshint": "^1.0.0", 39 | "grunt-contrib-nodeunit": "^1.0.0", 40 | "jshint-stylish": "^2.1.0", 41 | "load-grunt-tasks": "^3.4.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tasks/dev_update.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-dev-update 3 | * 4 | * Copyright (c) 2014 Gilad Peleg 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | var includes = require('lodash/includes'); 9 | var path = require('path'); 10 | 11 | module.exports = function (grunt) { 12 | 13 | var devUpdate = require('./lib/dev_update')(grunt); 14 | var possibleUpdateTypes = ['report', 'force', 'prompt', 'fail']; 15 | 16 | grunt.registerMultiTask('devUpdate', 'See your outdated devDependencies and update them', function () { 17 | 18 | //set default options 19 | var options = this.options({ 20 | updateType: 'report', //just report outdated packages 21 | reportUpdated: false, //don't report up-to-date packages 22 | semver: true, //stay within semver when updating 23 | packages: { 24 | devDependencies: true, //only check for devDependencies 25 | dependencies: false 26 | }, 27 | packageJson: null, //use matchdep default findup to locate package.json 28 | reportOnlyPkgs: [] //use updateType action on all packages 29 | }); 30 | 31 | grunt.verbose.writelns('Processing target: ' + this.target); 32 | 33 | //validate updateType option 34 | var updateType = options.updateType; 35 | if (!includes(possibleUpdateTypes, updateType)) { 36 | grunt.warn('updateType ' + String(updateType).cyan + ' not supported.'); 37 | //if force 38 | options.updateType = 'report'; 39 | } 40 | 41 | if (updateType === 'force') { 42 | //warn user for using force option 43 | grunt.log.writelns('Running with update type of ' + 'force'.red); 44 | } 45 | 46 | if (!Array.isArray(options.reportOnlyPkgs)) { 47 | grunt.warn('ignoredPackages must be an array.', 3); 48 | //if force 49 | options.reportOnlyPkgs = []; 50 | } 51 | 52 | var _pkgjson = options.packageJson; 53 | //use packageJson option as string, but file doesn't exist. 54 | if (typeof _pkgjson === 'string') { 55 | //path supplied is relative to process.cwd() 56 | _pkgjson = path.resolve(process.cwd(), _pkgjson); 57 | 58 | if (!grunt.file.exists(_pkgjson)) { 59 | grunt.warn('Cannot locate package.json in supplied path ' + _pkgjson); 60 | //if force 61 | options.packageJson = null; 62 | } else { 63 | //update option to reflect change 64 | options.packageJson = _pkgjson; 65 | } 66 | } 67 | //run task 68 | devUpdate.runTask(options, this.async()); 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /tasks/lib/dev_update.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-dev-update 3 | * 4 | * Copyright (c) 2016 Gilad Peleg 5 | * Licensed under the MIT license. 6 | */ 7 | 8 | var asyncEach = require('async-each-series'); 9 | var filter = require('lodash/filter'); 10 | var find = require('lodash/find'); 11 | var findup = require('findup-sync'); 12 | var forEach = require('lodash/forEach'); 13 | var includes = require('lodash/includes'); 14 | var keys = require('lodash/keys'); 15 | var map = require('lodash/map'); 16 | var npa = require('npm-package-arg'); 17 | var semver = require('semver'); 18 | 19 | //default spawn options 20 | var spawnOptions = { 21 | cmd: 'npm', 22 | grunt: false, 23 | opts: {} 24 | }; 25 | 26 | var shouldOnlyReport = function (reportOnlyPkgs, pkgName) { 27 | return reportOnlyPkgs.length && includes(reportOnlyPkgs, pkgName); 28 | }; 29 | 30 | /** 31 | * Get the spawn arguments for the action 32 | * @param {String} phase 33 | * @param {String} dependency 34 | * @param {String} saveType should be either --save or --save-dev 35 | */ 36 | var getSpawnArguments = function (phase, dependency, saveType) { 37 | switch (phase) { 38 | case 'outdated': 39 | return ['outdated', '--json', '--depth=0']; 40 | case 'update': 41 | return ['update', dependency, saveType]; 42 | case 'install': 43 | //this will force the version to install to override locks in package.json 44 | return ['install', dependency + '@latest', saveType]; 45 | //no action detected 46 | default: 47 | return []; 48 | } 49 | }; 50 | 51 | var devDeps = { 52 | type: 'devDependencies', 53 | installType: '--save-dev' 54 | }; 55 | 56 | var prodDeps = { 57 | type: 'dependencies', 58 | installType: '--save' 59 | }; 60 | 61 | module.exports = function (grunt) { 62 | var exports = { 63 | options: {}, 64 | }; 65 | 66 | var getPkgJsonPath = function () { 67 | //how is package.json located 68 | if (exports.options.packageJson) { 69 | grunt.verbose.writelns('Using custom option for package.json: ' + exports.options.packageJson); 70 | return exports.options.packageJson; 71 | } else { 72 | return findup('package.json', { 73 | cwd: process.cwd() 74 | }); 75 | } 76 | }; 77 | 78 | var getPackageJson = function (from) { 79 | var pkg; 80 | try { 81 | //load package json 82 | pkg = require(from); 83 | } catch (e) { 84 | //couldn't get packages... critical error 85 | grunt.verbose.writelns('Error ', e); 86 | grunt.fail.fatal('Could not read from package.json', from); 87 | } 88 | return pkg; 89 | }; 90 | 91 | /** 92 | * Get the dev dependencies packages to update from package.json 93 | */ 94 | var getPackageNames = function (packages) { 95 | var pkg = getPackageJson(getPkgJsonPath()); 96 | var mappedPkgs = []; 97 | forEach(packages, function (dep) { 98 | //get packages by type from package.json 99 | dep.deps = pkg[dep.type]; 100 | grunt.log.writeln('Found ' + keys(dep.deps).length + ' ' + dep.type.blue + ' to check for latest version'); 101 | forEach(dep.deps, function (item, key) { 102 | var parsed = npa(key + '@' + item); 103 | grunt.verbose.writelns('Parsed package:', key, parsed); 104 | if (!includes(['version', 'tag', 'range'], parsed.type)) { 105 | grunt.verbose.writelns(key.red + ' - doesn\'t seem local to npm. Skipping...'); 106 | return null; 107 | } 108 | mappedPkgs.push({ 109 | name: key, 110 | installType: dep.installType, 111 | type: dep.type 112 | }); 113 | }); 114 | }); 115 | return mappedPkgs; 116 | }; 117 | 118 | var getOutdatedPkgs = function (packages, done) { 119 | var pkgNames = map(packages, 'name'); 120 | spawnOptions.args = getSpawnArguments('outdated').concat(pkgNames); 121 | spawnOptions.opts = {}; 122 | grunt.util.spawn(spawnOptions, function (error, result) { 123 | if (error && !result) { 124 | grunt.verbose.writelns(error); 125 | grunt.fatal('Task failed due to ' + error); 126 | return done(error); 127 | } 128 | if (!result || !result.stdout) { 129 | return done(); 130 | } 131 | var jsonResults; 132 | try { 133 | jsonResults = JSON.parse(result.stdout); 134 | } catch (e) { 135 | grunt.fatal('Task failed with JSON.parse due to ' + e); 136 | } 137 | return done(null, jsonResults); 138 | }); 139 | }; 140 | 141 | var processByUpdateType = function (pkg, specs, done) { 142 | /** Update phase **/ 143 | grunt.log.subhead('Package name\t:', pkg.name); 144 | grunt.log.writelns('Package type\t:', pkg.type); 145 | grunt.log.writelns('Current version\t:', specs.current && specs.current.green || ''); 146 | grunt.log.writelns('Wanted version\t:', specs.wanted); 147 | grunt.log.writelns('Latest version\t:', specs.latest.red); 148 | 149 | var updateType = exports.options.updateType; 150 | var onlyReportPkg = shouldOnlyReport(exports.options.reportOnlyPkgs, pkg.name); 151 | if (exports.options.semver && specs.current === specs.wanted) { 152 | grunt.log.ok('Package is up to date'); 153 | return done(); 154 | } 155 | if ((updateType === 'fail') && (!onlyReportPkg)) { 156 | grunt.warn('Found an outdated package: ' + String(pkg.name).underline + '.', 3); 157 | } 158 | if (updateType === 'report') { 159 | return done(); 160 | } 161 | if (onlyReportPkg) { 162 | return done(); 163 | } 164 | var spawnArgs = getSpawnArguments( 165 | exports.options.semver ? 'update' : 'install', 166 | pkg.name, 167 | pkg.installType 168 | ); 169 | 170 | //force package update 171 | if (updateType === 'force') { 172 | //update without asking user 173 | return updatePackage(spawnArgs, done); 174 | } 175 | //assume updateType === 'prompt' 176 | var msg = 'update using [npm ' + spawnArgs.join(' ') + ']'; 177 | return require('inquirer').prompt({ 178 | name: 'confirm', 179 | message: msg, 180 | default: false, 181 | type: 'confirm' 182 | }, function (result) { 183 | if (!result.confirm) { 184 | return done(); 185 | } 186 | //user accepted update 187 | updatePackage(spawnArgs, done); 188 | }); 189 | }; 190 | 191 | var updatePackage = function (spawnArgs, done) { 192 | //assign args 193 | spawnOptions.args = spawnArgs; 194 | spawnOptions.opts = { 195 | stdio: 'inherit' 196 | }; 197 | grunt.util.spawn(spawnOptions, function (error) { 198 | if (error) { 199 | grunt.verbose.writelns(error); 200 | grunt.log.writelns('Error while running ' + spawnArgs); 201 | } 202 | return done(); 203 | }); 204 | }; 205 | 206 | exports.runTask = function (options, done) { 207 | exports.options = options; 208 | 209 | //get only the kind of packages user wants 210 | var packageTypes = filter([devDeps, prodDeps], function (pkgType) { 211 | return options.packages[pkgType.type]; 212 | }); 213 | 214 | if (!packageTypes || !packageTypes.length) { 215 | return done(); 216 | } 217 | 218 | //get the package names 219 | var packages = getPackageNames(packageTypes); 220 | //no packages to check 221 | if (!packages || !packages.length) { 222 | return done(); 223 | } 224 | 225 | getOutdatedPkgs(packages, function (err, result) { 226 | if (!result) { 227 | grunt.log.oklns('All packages are up to date'); 228 | return done(); 229 | } 230 | asyncEach(keys(result), function (pkgName, cb) { 231 | var pkg = find(packages, { 232 | name: pkgName 233 | }); 234 | var specs = result[pkgName]; 235 | if (!semver.valid(specs.wanted) || !semver.valid(specs.latest)) { 236 | grunt.verbose.writelns('Package ' + pkgName.blue + ' isn\'t from NPM, currently not handling those. Skipping...'); 237 | return cb(); 238 | } 239 | processByUpdateType(pkg, specs, cb); 240 | }, done); 241 | }); 242 | }; 243 | 244 | return exports; 245 | }; 246 | -------------------------------------------------------------------------------- /test/dev_update_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var grunt = require('grunt'); 4 | 5 | /* 6 | ======== A Handy Little Nodeunit Reference ======== 7 | https://github.com/caolan/nodeunit 8 | 9 | Test methods: 10 | test.expect(numAssertions) 11 | test.done() 12 | Test assertions: 13 | test.ok(value, [message]) 14 | test.equal(actual, expected, [message]) 15 | test.notEqual(actual, expected, [message]) 16 | test.deepEqual(actual, expected, [message]) 17 | test.notDeepEqual(actual, expected, [message]) 18 | test.strictEqual(actual, expected, [message]) 19 | test.notStrictEqual(actual, expected, [message]) 20 | test.throws(block, [error], [message]) 21 | test.doesNotThrow(block, [error], [message]) 22 | test.ifError(value) 23 | */ 24 | 25 | exports.devUpdate = { 26 | setUp: function (done) { 27 | // setup here if necessary 28 | done(); 29 | }, 30 | defaultOptions: function (test) { 31 | test.expect(1); 32 | 33 | var actual = grunt.file.read('tmp/default_options'); 34 | var expected = grunt.file.read('test/expected/default_options'); 35 | test.equal(actual, expected, 'should describe what the default behavior is.'); 36 | 37 | test.done(); 38 | }, 39 | customOptions: function (test) { 40 | test.expect(1); 41 | 42 | var actual = grunt.file.read('tmp/custom_options'); 43 | var expected = grunt.file.read('test/expected/custom_options'); 44 | test.equal(actual, expected, 'should describe what the custom option(s) behavior is.'); 45 | 46 | test.done(); 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /test/expected/custom_options: -------------------------------------------------------------------------------- 1 | Testing: 1 2 3 !!! -------------------------------------------------------------------------------- /test/expected/default_options: -------------------------------------------------------------------------------- 1 | Testing, 1 2 3. -------------------------------------------------------------------------------- /test/fixtures/123: -------------------------------------------------------------------------------- 1 | 1 2 3 -------------------------------------------------------------------------------- /test/fixtures/testing: -------------------------------------------------------------------------------- 1 | Testing --------------------------------------------------------------------------------