Repository: yargs/yargs
Pull request #457 (2016-03-30 05:00:57)

Improve $0 in Windows
I made some changes to the way the `$0` variable is created, so that the Windows version matches the Linux one:
  - It ignores `argv[0]` if it ends in "node.exe" or "iojs.exe"
  - It allows to use the rebased version of `argv[1]` if it uses Windows style paths

Now, the tricky part is that I had to make platform dependent tests, because the behavior of `node.path.relative` changes depending on platform, and there's not a straightforward way to mock it. Let me know what you think.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14b52e6..b1fb493 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,12 @@
-## Change Log
+### v4.4.0 (2016/04/03 21:10 +07:00)
 
-### v4.3.3 (2016/4/20 22:00 +07:00)
-
-- [446](https://github.com/yargs/yargs/pull/446) update yargs-parser, y18n, nyc, cliui (@bcoe)
+- [#454](https://github.com/yargs/yargs/pull/454) fix demand() when second argument is an array (@elas7)
+- [#452](https://github.com/yargs/yargs/pull/452) fix code example for `.help()` docs (@maxrimue)
+- [#450](https://github.com/yargs/yargs/pull/450) fix for bash completion trailing space edge-case (@elas7)
+- [#448](https://github.com/yargs/yargs/pull/448) allow a method to be passed to `showHelp`, rather than a log-level (@osher)
+- [#446](https://github.com/yargs/yargs/pull/446) update yargs-parser, y18n, nyc, cliui, pkg-conf (@bcoe)
+- [#436](https://github.com/yargs/yargs/pull/436) the rebase method is only used by tests, do not export it in two places (@elas7)
+- [#428](https://github.com/yargs/yargs/pull/428) initial support for subcommands (@nexdrew)
 
 ### v4.3.2 (2016/3/20 15:07 +07:00)
 
@@ -10,7 +14,6 @@
 - [#443](https://github.com/yargs/yargs/pull/443) adds Italian translation \o/ (@madrisan)
 - [#441](https://github.com/yargs/yargs/pull/441) remove duplicate keys from array options configuration (@elas7)
 - [#437](https://github.com/yargs/yargs/pull/437) standardize tests for .command() (@lrlna)
-- [#436](https://github.com/yargs/yargs/pull/436) the rebase method is only used by tests, do not export it in two places (@elas7)
 
 ### v4.3.0 (2016/3/12 14:19 +07:00)
 
diff --git a/README.md b/README.md
index 7b929fe..cf8fdce 100644
--- a/README.md
+++ b/README.md
@@ -454,9 +454,11 @@ var argv = require('yargs')
 ```
 
 .command(cmd, desc, [builder], [handler])
--------------------
+-----------------------------------------
 .command(cmd, desc, [module])
--------------------
+-----------------------------
+.command(module)
+----------------
 
 Document the commands exposed by your application.
 
@@ -528,16 +530,33 @@ yargs.command('get <source> [proxy]', 'make a get HTTP request')
   .argv
 ```
 
+#### Variadic Positional Arguments
+
+The last positional argument can optionally accept an array of
+values, by using the `..` operator:
+
+```js
+yargs.command('download <url> [files..]', 'download several files')
+  .help()
+  .argv
+```
+
 ### Providing a Command Module
 
 For complicated commands you can pull the logic into a module. A module
 simply needs to export:
 
-* `exports.builder`: which describes the options that a command accepts.
+* `exports.command`: string that executes this command when given on the command line, may contain positional args
+* `exports.describe`: string used as the description for the command in help text, use `false` for a hidden command
+* `exports.builder`: object declaring the options the command accepts, or a function accepting and returning a yargs instance
 * `exports.handler`: a function which will be passed the parsed argv.
 
 ```js
 // my-module.js
+exports.command = 'get <source> [proxy]'
+
+exports.describe = 'make a get HTTP request'
+
 exports.builder = {
   banana: {
     default: 'cool'
@@ -554,14 +573,22 @@ exports.handler = function (argv) {
 
 You then register the module like so:
 
+```js
+yargs.command(require('my-module'))
+  .help()
+  .argv
+```
+
+Or if the module does not export `command` and `describe` (or if you just want to override them):
+
 ```js
 yargs.command('get <source> [proxy]', 'make a get HTTP request', require('my-module'))
   .help()
   .argv
 ```
 
-.completion(cmd, [description], [fn]);
--------------
+.completion([cmd], [description], [fn]);
+----------------------------------------
 
 Enable bash-completion shortcuts for commands and options.
 
@@ -576,6 +603,9 @@ that generates bash completion scripts.
 shiver me timbers is pretty awesome, you can provide your own completion
 method.
 
+If invoked without parameters, `.completion()` will make `completion` the command to output 
+the completion script.
+
 ```js
 var argv = require('yargs')
   .completion('completion', function(current, argv) {
@@ -619,13 +649,15 @@ var argv = require('yargs')
   .argv;
 ```
 
-<a name="config"></a>.config(key, [description], [parseFn])
+<a name="config"></a>.config([key], [description], [parseFn])
 ------------
 
 Tells the parser that if the option specified by `key` is passed in, it
 should be interpreted as a path to a JSON config file. The file is loaded
 and parsed, and its properties are set as arguments.
 
+If invoked without parameters, `.config()` will make `--config` the option to pass the JSON config file.
+
 An optional `description` can be provided to customize the config (`key`) option
 in the usage string.
 
@@ -813,7 +845,19 @@ yargs have been validated.
 
 Method to execute when a failure occurs, rather than printing the failure message.
 
-`fn` is called with the failure message that would have been printed.
+`fn` is called with the failure message that would have been printed and the
+`Error` instance originally thrown, if any.
+
+```js
+var argv = require('yargs')
+  .fail(function (msg, err) {
+    if (err) throw err // preserve stack
+    console.error('You broke it!')
+    console.error(msg)
+    process.exit(1)
+  })
+  .argv
+```
 
 <a name="global"></a>.global(globals)
 ------------
@@ -880,9 +924,10 @@ help output.
 Example:
 
 ```js
-var yargs = require("yargs")['--help']
-  .usage("$0 -operand1 number -operand2 number -operation [add|subtract]");
+var yargs = require("yargs")(['--help'])
+  .usage("$0 -operand1 number -operand2 number -operation [add|subtract]")
   .help()
+  .argv
 ```
 
 Later on, `argv` can be retrieved with `yargs.argv`.
@@ -953,10 +998,12 @@ Locales currently supported:
 * **es:** Spanish.
 * **fr:** French.
 * **id:** Indonesian.
+* **it:** Italian.
 * **ja:** Japanese.
 * **ko:** Korean.
 * **nb:** Norwegian Bokmål.
 * **pirate:** American Pirate.
+* **pl:** Polish.
 * **pt:** Portuguese.
 * **pt_BR:** Brazilian Portuguese.
 * **tr:** Turkish.
diff --git a/completion.sh.hbs b/completion.sh.hbs
index 8392ad5..14445b8 100644
--- a/completion.sh.hbs
+++ b/completion.sh.hbs
@@ -10,10 +10,10 @@ _yargs_completions()
     local cur_word args type_list
 
     cur_word="${COMP_WORDS[COMP_CWORD]}"
-    args=$(printf "%s " "${COMP_WORDS[@]}")
+    args=("${COMP_WORDS[@]}")
 
     # ask yargs to generate completions.
-    type_list=`{{app_path}} --get-yargs-completions $args`
+    type_list=$({{app_path}} --get-yargs-completions "${args[@]}")
 
     COMPREPLY=( $(compgen -W "${type_list}" -- ${cur_word}) )
 
diff --git a/lib/command.js b/lib/command.js
index b046a66..7665edc 100644
--- a/lib/command.js
+++ b/lib/command.js
@@ -45,15 +45,26 @@ module.exports = function (yargs, usage, validation) {
 
   function parseCommand (cmd) {
     var splitCommand = cmd.split(/\s/)
-    var bregex = /[\][<>]/g
+    var bregex = /\.*[\][<>]/g
     var parsedCommand = {
       cmd: (splitCommand.shift()).replace(bregex, ''),
       demanded: [],
       optional: []
     }
-    splitCommand.forEach(function (cmd) {
-      if (/^\[/.test(cmd)) parsedCommand.optional.push(cmd.replace(bregex, ''))
-      else parsedCommand.demanded.push(cmd.replace(bregex, ''))
+    splitCommand.forEach(function (cmd, i) {
+      var variadic = false
+      if (/\.+[\]>]/.test(cmd) && i === splitCommand.length - 1) variadic = true
+      if (/^\[/.test(cmd)) {
+        parsedCommand.optional.push({
+          cmd: cmd.replace(bregex, ''),
+          variadic: variadic
+        })
+      } else {
+        parsedCommand.demanded.push({
+          cmd: cmd.replace(bregex, ''),
+          variadic: variadic
+        })
+      }
     })
     return parsedCommand
   }
@@ -70,6 +81,9 @@ module.exports = function (yargs, usage, validation) {
     var argv = parsed.argv
     var commandHandler = handlers[command]
     var innerArgv = argv
+    var currentContext = yargs.getContext()
+    var parentCommands = currentContext.commands.slice()
+    currentContext.commands.push(command)
     if (commandHandler.builder && typeof commandHandler.builder === 'function') {
       // a function can be provided, which interacts which builds
       // up a yargs chain and returns it.
@@ -79,48 +93,53 @@ module.exports = function (yargs, usage, validation) {
       // original command string as usage() for consistent behavior with
       // options object below
       if (yargs.parsed === false && typeof yargs.getUsageInstance().getUsage() === 'undefined') {
-        yargs.usage('$0 ' + commandHandler.original)
+        yargs.usage('$0 ' + (parentCommands.length ? parentCommands.join(' ') + ' ' : '') + commandHandler.original)
       }
       innerArgv = innerArgv ? innerArgv.argv : argv
     } else if (commandHandler.builder && typeof commandHandler.builder === 'object') {
       // as a short hand, an object can instead be provided, specifying
       // the options that a command takes.
       innerArgv = yargs.reset(parsed.aliases)
-      innerArgv.usage('$0 ' + commandHandler.original)
+      innerArgv.usage('$0 ' + (parentCommands.length ? parentCommands.join(' ') + ' ' : '') + commandHandler.original)
       Object.keys(commandHandler.builder).forEach(function (key) {
         innerArgv.option(key, commandHandler.builder[key])
       })
       innerArgv = innerArgv.argv
     }
 
-    populatePositional(commandHandler, innerArgv)
+    populatePositional(commandHandler, innerArgv, currentContext)
 
     if (commandHandler.handler) {
       commandHandler.handler(innerArgv)
     }
+    currentContext.commands.pop()
     return innerArgv
   }
 
-  function populatePositional (commandHandler, argv) {
-    var cmd = argv._.shift() // nuke the first argument (the current command)
+  function populatePositional (commandHandler, argv, context) {
+    argv._ = argv._.slice(context.commands.length) // nuke the current commands
     var demanded = commandHandler.demanded.slice(0)
     var optional = commandHandler.optional.slice(0)
 
     validation.positionalCount(demanded.length, argv._.length)
 
     while (demanded.length) {
-      if (!argv._.length) break
       var demand = demanded.shift()
-      argv[demand] = argv._.shift()
+      if (demand.variadic) argv[demand.cmd] = []
+      if (!argv._.length) break
+      if (demand.variadic) argv[demand.cmd] = argv._.splice(0)
+      else argv[demand.cmd] = argv._.shift()
     }
 
     while (optional.length) {
-      if (!argv._.length) break
       var maybe = optional.shift()
-      argv[maybe] = argv._.shift()
+      if (maybe.variadic) argv[maybe.cmd] = []
+      if (!argv._.length) break
+      if (maybe.variadic) argv[maybe.cmd] = argv._.splice(0)
+      else argv[maybe.cmd] = argv._.shift()
     }
 
-    argv._.unshift(cmd)
+    argv._ = context.commands.concat(argv._)
   }
 
   self.reset = function () {
diff --git a/lib/usage.js b/lib/usage.js
index 584bb1b..bee2611 100644
--- a/lib/usage.js
+++ b/lib/usage.js
@@ -320,8 +320,9 @@ module.exports = function (yargs, y18n) {
   }
 
   self.showHelp = function (level) {
-    level = level || 'error'
-    console[level](self.help())
+    if (!level) level = 'error'
+    var emit = typeof level === 'function' ? level : console[ level ]
+    emit(self.help())
   }
 
   self.functionDescription = function (fn) {
diff --git a/lib/validation.js b/lib/validation.js
index 0f86fe1..388f1c4 100644
--- a/lib/validation.js
+++ b/lib/validation.js
@@ -109,6 +109,7 @@ module.exports = function (yargs, usage, y18n) {
     var demanded = yargs.getDemanded()
     var commandKeys = yargs.getCommandInstance().getCommands()
     var unknown = []
+    var currentContext = yargs.getContext()
 
     Object.keys(aliases).forEach(function (key) {
       aliases[key].forEach(function (alias) {
@@ -126,7 +127,7 @@ module.exports = function (yargs, usage, y18n) {
     })
 
     if (commandKeys.length > 0) {
-      argv._.forEach(function (key) {
+      argv._.slice(currentContext.commands.length).forEach(function (key) {
         if (commandKeys.indexOf(key) === -1) {
           unknown.push(key)
         }
diff --git a/locales/en.json b/locales/en.json
index ce19199..0fab5cc 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -5,6 +5,7 @@
   "boolean": "boolean",
   "count": "count",
   "string": "string",
+  "number": "number",
   "array": "array",
   "required": "required",
   "default:": "default:",
diff --git a/locales/it.json b/locales/it.json
index 85a5cd2..bd9573a 100644
--- a/locales/it.json
+++ b/locales/it.json
@@ -5,6 +5,7 @@
   "boolean": "booleano",
   "count": "contatore",
   "string": "stringa",
+  "number": "numero",
   "array": "vettore",
   "required": "richiesto",
   "default:": "predefinito:",
diff --git a/package.json b/package.json
index 76ee28f..e92946a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "yargs",
-  "version": "4.3.2",
+  "version": "4.4.0",
   "description": "Light-weight option parsing with an argv hash. No optstrings attached.",
   "main": "./index.js",
   "files": [
@@ -13,22 +13,23 @@
   ],
   "dependencies": {
     "camelcase": "^2.0.1",
-    "cliui": "^3.1.1",
+    "cliui": "^3.1.2",
     "decamelize": "^1.1.1",
     "lodash.assign": "^4.0.3",
     "os-locale": "^1.4.0",
-    "pkg-conf": "^1.1.1",
+    "pkg-conf": "^1.1.2",
     "read-pkg-up": "^1.0.1",
     "require-main-filename": "^1.0.1",
     "string-width": "^1.0.1",
     "window-size": "^0.2.0",
     "y18n": "^3.2.1",
-    "yargs-parser": "^2.1.2"
+    "yargs-parser": "^2.2.0"
   },
   "devDependencies": {
     "chai": "^3.4.1",
-    "chalk": "^1.1.1",
-    "coveralls": "^2.11.4",
+    "chalk": "^1.1.3",
+    "conventional-recommended-workflow": "^1.0.0",
+    "coveralls": "^2.11.9",
     "cpr": "^1.0.0",
     "es6-promise": "^3.0.2",
     "hashish": "0.0.4",
@@ -42,7 +43,8 @@
   "scripts": {
     "pretest": "standard",
     "test": "nyc --cache mocha --require ./test/before.js --timeout=4000 --check-leaks",
-    "coverage": "nyc report --reporter=text-lcov | coveralls"
+    "coverage": "nyc report --reporter=text-lcov | coveralls",
+    "release": "conventional-recommended-workflow"
   },
   "repository": {
     "type": "git",
diff --git a/yargs.js b/yargs.js
index 9a10c72..03ff310 100644
--- a/yargs.js
+++ b/yargs.js
@@ -35,9 +35,9 @@ function Yargs (processArgs, cwd, parentRequire) {
     .map(function (x, i) {
       // ignore the node bin, specify this in your
       // bin file with #!/usr/bin/env node
-      if (i === 0 && /\b(node|iojs)$/.test(x)) return
+      if (i === 0 && /\b(node|iojs)(\.exe)?$/.test(x)) return
       var b = rebase(cwd, x)
-      return x.match(/^\//) && b.length < x.length ? b : x
+      return x.match(/^(\/|([a-zA-Z]:)?\\)/) && b.length < x.length ? b : x
     })
     .join(' ').trim()
 
@@ -47,11 +47,19 @@ function Yargs (processArgs, cwd, parentRequire) {
     )
   }
 
+  // use context object to keep track of resets, subcommand execution, etc
+  // submodules should modify and check the state of context as necessary
+  var context = { resets: -1, commands: [] }
+  self.getContext = function () {
+    return context
+  }
+
   // puts yargs back into an initial state. any keys
   // that have been set to "global" will not be reset
   // by this action.
   var options
   self.resetOptions = self.reset = function (aliases) {
+    context.resets++
     aliases = aliases || {}
     options = options || {}
     // put yargs back into an initial state, this
@@ -171,6 +179,8 @@ function Yargs (processArgs, cwd, parentRequire) {
       parseFn = msg
       msg = null
     }
+
+    key = key || 'config'
     self.describe(key, msg || usage.deferY18nLookup('Path to JSON config file'))
     ;(Array.isArray(key) ? key : [key]).forEach(function (k) {
       options.config[k] = parseFn || true
@@ -234,6 +244,7 @@ function Yargs (processArgs, cwd, parentRequire) {
       max.forEach(function (key) {
         self.demand(key, msg)
       })
+      max = Infinity
     } else if (typeof max !== 'number') {
       msg = max
       max = Infinity


Test patch:
diff --git a/test/command.js b/test/command.js
index 4e895d5..5f5b696 100644
--- a/test/command.js
+++ b/test/command.js
@@ -17,8 +17,14 @@ describe('Command', function () {
         })
       var command = y.getCommandInstance()
       var handlers = command.getCommandHandlers()
-      handlers.foo.demanded.should.include('bar')
-      handlers.foo.optional.should.include('awesome')
+      handlers.foo.demanded.should.include({
+        cmd: 'bar',
+        variadic: false
+      })
+      handlers.foo.optional.should.include({
+        cmd: 'awesome',
+        variadic: false
+      })
     })
 
     it('populates inner argv with positional arguments', function (done) {
@@ -41,6 +47,72 @@ describe('Command', function () {
       argv.bar.should.equal('hello')
       argv.awesome.should.equal('world')
     })
+
+    it('populates subcommand\'s inner argv with positional arguments', function () {
+      yargs('foo bar hello world')
+        .command('foo', 'my awesome command', function (yargs) {
+          return yargs.command(
+            'bar <greeting> [recipient]',
+            'subcommands are cool',
+            function () {},
+            function (argv) {
+              argv._.should.deep.equal(['foo', 'bar'])
+              argv.greeting.should.equal('hello')
+              argv.recipient.should.equal('world')
+            }
+          )
+        })
+        .argv
+    })
+  })
+
+  describe('variadic', function () {
+    it('allows required arguments to be variadic', function () {
+      var argv = yargs('foo /root file1 file2 file3')
+        .command('foo <root> <files..>')
+        .argv
+
+      argv.root.should.equal('/root')
+      argv.files.should.deep.equal(['file1', 'file2', 'file3'])
+    })
+
+    it('allows optional arguments to be variadic', function () {
+      var argv = yargs('foo /root file1 file2 file3')
+        .command('foo <root> [files..]')
+        .argv
+
+      argv.root.should.equal('/root')
+      argv.files.should.deep.equal(['file1', 'file2', 'file3'])
+    })
+
+    it('fails if required arguments are missing', function (done) {
+      yargs('foo /root')
+        .command('foo <root> <files..>')
+        .fail(function (err) {
+          err.should.match(/Not enough non-option arguments/)
+          return done()
+        })
+        .argv
+    })
+
+    it('does not fail if zero optional arguments are provided', function () {
+      var argv = yargs('foo /root')
+        .command('foo <root> [files...]')
+        .argv
+
+      argv.root.should.equal('/root')
+      argv.files.should.deep.equal([])
+    })
+
+    it('only allows the last argument to be variadic', function () {
+      var argv = yargs('foo /root file1 file2')
+        .command('foo <root..> <file>')
+        .argv
+
+      argv.root.should.equal('/root')
+      argv.file.should.equal('file1')
+      argv._.should.include('file2')
+    })
   })
 
   describe('missing positional arguments', function () {
diff --git a/test/usage.js b/test/usage.js
index 66eba2b..05bbcd9 100644
--- a/test/usage.js
+++ b/test/usage.js
@@ -301,6 +301,18 @@ describe('usage tests', function () {
           'pork chop sandwiches'
         ])
       })
+
+      it("shouldn't interpret the second argument as a max when it is an array", function () {
+        var r = checkUsage(function () {
+          return yargs(['koala', 'wombat', '--1'])
+              .usage('Usage: foo')
+              .demand(1, ['1'])
+              .wrap(null)
+              .argv
+        })
+
+        r.errors.length.should.equal(0)
+      })
     })
   })
 
@@ -1718,6 +1730,44 @@ describe('usage tests', function () {
         ''
       ])
     })
+
+    it('displays given command chain with positional args in default usage for subcommand with builder object', function () {
+      var r = checkUsage(function () {
+        return yargs('one two --help')
+          .command('one <sub>', 'level one, requires subcommand', function (yargs) {
+            return yargs.command('two [next]', 'level two', {}, function (argv) {})
+          }, function (argv) {})
+          .help().wrap(null)
+          .argv
+      })
+
+      r.logs[0].split('\n').should.deep.equal([
+        './usage one two [next]',
+        '',
+        'Options:',
+        '  --help  Show help  [boolean]',
+        ''
+      ])
+    })
+
+    it('displays given command chain with positional args in default usage for subcommand with builder function', function () {
+      var r = checkUsage(function () {
+        return yargs('one two --help')
+          .command('one <sub>', 'level one, requires subcommand', function (yargs) {
+            return yargs.command('two [next]', 'level two', function (yargs) { return yargs }, function (argv) {})
+          }, function (argv) {})
+          .help().wrap(null)
+          .argv
+      })
+
+      r.logs[0].split('\n').should.deep.equal([
+        './usage one two [next]',
+        '',
+        'Options:',
+        '  --help  Show help  [boolean]',
+        ''
+      ])
+    })
   })
 
   describe('epilogue', function () {
@@ -2019,6 +2069,45 @@ describe('usage tests', function () {
         ''
       ])
     })
+
+    it('should call the correct console.log method', function () {
+      var r = checkUsage(function () {
+        var y = yargs(['--foo'])
+          .options('foo', {
+            alias: 'f',
+            describe: 'foo option'
+          })
+          .wrap(null)
+
+        y.showHelp('log')
+      })
+
+      r.errors.length.should.eql(0)
+      r.logs.join('\n').split(/\n+/).should.deep.equal([
+        'Options:',
+        '  --foo, -f  foo option',
+        ''
+      ])
+    })
+
+    it('should provide a help string to handler when provided', function (done) {
+      var y = yargs(['--foo'])
+        .options('foo', {
+          alias: 'f',
+          describe: 'foo option'
+        })
+        .wrap(null)
+
+      y.showHelp(testHandler)
+      function testHandler (msg) {
+        msg.split(/\n+/).should.deep.equal([
+          'Options:',
+          '  --foo, -f  foo option',
+          ''
+        ])
+        return done()
+      }
+    })
   })
 
   describe('$0', function () {
@@ -2076,6 +2165,46 @@ describe('usage tests', function () {
         yargs([]).$0.should.equal('/code/iojs/script.js')
       })
     })
+
+    it('is detected correctly when argv contains "node.exe"', function () {
+      mockProcessArgv(['node.exe', 'script.js'], function () {
+        yargs([]).$0.should.equal('script.js')
+      })
+    })
+
+    it('is detected correctly when argv contains "iojs.exe"', function () {
+      mockProcessArgv(['iojs.exe', 'script.js'], function () {
+        yargs([]).$0.should.equal('script.js')
+      })
+    })
+
+    if (process.platform !== 'win32') {
+      it('is resolved to the relative path if it is shorter', function () {
+        mockProcessArgv(['node', '/code/node/script.js'], function () {
+          yargs([], '/code/python/').$0.should.equal('../node/script.js')
+        })
+      })
+
+      it('is not resolved to the relative path if it is larger', function () {
+        mockProcessArgv(['node', '/script.js'], function () {
+          yargs([], '/very/long/current/directory/').$0.should.equal('/script.js')
+        })
+      })
+    }
+
+    if (process.platform === 'win32') {
+      it('is resolved to the relative path if it is shorter, using Windows paths', function () {
+        mockProcessArgv(['node.exe', 'C:\\code\\node\\script.js'], function () {
+          yargs([], 'C:\\code\\python\\').$0.should.equal('..\\node\\script.js')
+        })
+      })
+
+      it('is not resolved to the relative path if it is larger, using Windows paths', function () {
+        mockProcessArgv(['node', 'C:\\script.js'], function () {
+          yargs([], 'C:\\very\\long\\current\\directory\\').$0.should.equal('C:\\script.js')
+        })
+      })
+    }
   })
 
   describe('choices', function () {
diff --git a/test/validation.js b/test/validation.js
index a54d0e5..ac0b48a 100644
--- a/test/validation.js
+++ b/test/validation.js
@@ -240,6 +240,24 @@ describe('validation tests', function () {
       ])
     })
 
+    it('should be displayed in the help message with its default name', function () {
+      var checkUsage = require('./helpers/utils').checkOutput
+      var r = checkUsage(function () {
+        return yargs(['--help'])
+            .config()
+            .help('help')
+            .wrap(null)
+            .argv
+      })
+      r.should.have.property('logs').with.length(1)
+      r.logs.join('\n').split(/\n+/).should.deep.equal([
+        'Options:',
+        '  --config  Path to JSON config file',
+        '  --help    Show help  [boolean]',
+        ''
+      ])
+    })
+
     it('should allow help message to be overridden', function () {
       var checkUsage = require('./helpers/utils').checkOutput
       var r = checkUsage(function () {
@@ -315,4 +333,22 @@ describe('validation tests', function () {
         .parse([])
     })
   })
+
+  describe('strict mode', function () {
+    it('does not fail when command with subcommands called', function () {
+      yargs('one')
+        .command('one', 'level one', function (yargs) {
+          return yargs
+            .command('twoA', 'level two A')
+            .command('twoB', 'level two B')
+            .strict()
+            .fail(function (msg) {
+              expect.fail()
+            })
+        }, function (argv) {
+          argv._[0].should.equal('one')
+        })
+        .argv
+    })
+  })
 })
diff --git a/test/yargs.js b/test/yargs.js
index 93c8b0f..f8494d9 100644
--- a/test/yargs.js
+++ b/test/yargs.js
@@ -887,3 +887,43 @@ describe('yargs dsl tests', function () {
     })
   })
 })
+
+describe('yargs context', function () {
+  beforeEach(function () {
+    delete require.cache[require.resolve('../')]
+    yargs = require('../')
+  })
+
+  it('should begin with initial state', function () {
+    var context = yargs.getContext()
+    context.resets.should.equal(0)
+    context.commands.should.deep.equal([])
+  })
+
+  it('should track number of resets', function () {
+    var context = yargs.getContext()
+    yargs.reset()
+    context.resets.should.equal(1)
+    yargs.reset()
+    yargs.reset()
+    context.resets.should.equal(3)
+  })
+
+  it('should track commands being executed', function () {
+    var context
+    yargs('one two')
+      .command('one', 'level one', function (yargs) {
+        context = yargs.getContext()
+        context.commands.should.deep.equal(['one'])
+        return yargs.command('two', 'level two', function (yargs) {
+          context.commands.should.deep.equal(['one', 'two'])
+        }, function (argv) {
+          context.commands.should.deep.equal(['one', 'two'])
+        })
+      }, function (argv) {
+        context.commands.should.deep.equal(['one'])
+      })
+      .argv
+    context.commands.should.deep.equal([])
+  })
+})