Permalink
Browse files

Implement .password(), closes #1.

  • Loading branch information...
1 parent a2c803e commit 3a0979889bbe68735b1a705a4bbfaf0203895620 @satazor satazor committed Jan 27, 2013
Showing with 109 additions and 29 deletions.
  1. +6 −4 README.md
  2. +40 −19 index.js
  3. +5 −2 package.json
  4. +58 −4 test/test.js
View
@@ -5,7 +5,7 @@ Simple command line prompting utility.
## Installation ##
-npm install promptly
+`$ npm install promptly`
## API ##
@@ -29,6 +29,8 @@ Default options:
'validator': null,
// Automatically retry if a validator fails
'retry': false,
+ // Do not print what the user types
+ 'silent': false,
// Input and output streams to read and write to
'input': process.stdin,
'output': process.stdout
@@ -103,7 +105,7 @@ promptly.prompt('Name: ', { validator: validator , retry: true}, function (err,
Ask the user to confirm something.
Calls `fn` with `error` and `value` (true or false).
-The available options are the same, except that `retry` defauls to `true`.
+The available options are the same, except that `retry` defaults to `true`.
Truthy values are: `y`, `yes` and `1`.
Falsy values are `n`, `no`, and `0`.
Comparison is made in case insensitive way.
@@ -122,7 +124,7 @@ promptly.confirm('Are you sure? ', function (err, value) {
Ask the user to choose between multiple `choices` (array of choices).
Calls `fn` with `error` and `value` (true or false).
-The available options are the same, except that `retry` defauls to `true`.
+The available options are the same, except that `retry` defaults to `true`.
Example usage:
@@ -138,7 +140,7 @@ promptly.choose('Do you want an apple or an orange? ', ['apple', 'orange'], func
Prompts for a password, printing the `message` and waiting for the input.
When available, calls `fn` with `error` and `value`.
-The available options are the same, except that `trim` defauls to `false`.
+The available options are the same, except that `trim` and `silent` defaults to `false`.
Example usage:
View
@@ -1,6 +1,6 @@
'use strict';
-var readline = require('readline');
+var read = require('read');
var promptly = module.exports;
promptly.prompt = function (message, opts, fn) {
@@ -16,17 +16,22 @@ promptly.prompt = function (message, opts, fn) {
opts.trim = true;
}
- // Instantiate node's readline
- var rl = opts.rl;
- if (!rl) {
- rl = opts.rl = readline.createInterface({
- input: opts.input || process.stdin,
- output: opts.output || process.stdout
- });
- }
+ // Setup read's options
+ var readOpts = {
+ prompt: message,
+ stdin: opts.input || process.stdin,
+ stdout: opts.output || process.stdout,
+ silent: opts.silent
+ };
// Use readline question
- rl.question(message, function (response) {
+ read(readOpts, function (err, response) {
+ // Ignore the error attribute
+ // It is set on SIGINT or if timeout reached (we are not using timeout)
+ if (err) {
+ return;
+ }
+
// Trim?
if (opts.trim) {
response = response.trim();
@@ -57,8 +62,6 @@ promptly.prompt = function (message, opts, fn) {
return promptly.prompt(message, opts, fn);
}
- rl.close();
- delete opts.rl;
e.retry = promptly.prompt.bind(promptly, message, opts, fn);
return fn(e);
@@ -67,13 +70,29 @@ promptly.prompt = function (message, opts, fn) {
}
// Everything ok
- rl.close();
fn(null, response);
});
};
-promptly.password = function () {
- // TODO:
+promptly.password = function (message, opts, fn) {
+ // Arguments parsing
+ if (typeof opts === 'function') {
+ fn = opts;
+ opts = {};
+ } else {
+ opts = opts || {};
+ }
+
+ // Set default options
+ if (opts.silent === undefined) {
+ opts.silent = true;
+ }
+ if (opts.trim === undefined) {
+ opts.trim = false;
+ }
+
+ // Use prompt()
+ promptly.prompt(message, opts, fn);
};
promptly.confirm = function (message, opts, fn) {
@@ -113,7 +132,7 @@ promptly.confirm = function (message, opts, fn) {
};
opts.validator.push(validator);
- // Use choose with true, false
+ // Use choose() with true, false
promptly.choose(message, [true, false], opts, fn);
};
@@ -126,14 +145,16 @@ promptly.choose = function (message, choices, opts, fn) {
opts = opts || {};
}
- if (opts.retry === undefined) {
- opts.retry = true;
- }
opts.validator = opts.validator || [];
if (!Array.isArray(opts.validator)) {
opts.validator = [opts.validator];
}
+ // Set the default options
+ if (opts.retry === undefined) {
+ opts.retry = true;
+ }
+
// Push the choice validator
var validator = function (value) {
if (choices.indexOf(value) === -1) {
View
@@ -16,7 +16,7 @@
"url": "git://github.com/IndigoUnited/node-promptly"
},
"bugs": {
- "url" : "http://github.com/IndigoUnited/node-promptly/issues"
+ "url": "http://github.com/IndigoUnited/node-promptly/issues"
},
"keywords": [
"prompt",
@@ -27,5 +27,8 @@
"line"
],
"author": "IndigoUnited <hello@indigounited.com> (http://indigounited.com)",
- "license": "MIT"
+ "license": "MIT",
+ "dependencies": {
+ "read": "~1.0.4"
+ }
}
View
@@ -25,7 +25,7 @@ describe('prompt()', function () {
process.stdin.emit('data', 'yeaa\n');
});
- it('should keep asking if no value is passed and not default was defined', function (next) {
+ it('should keep asking if no value is passed and no default was defined', function (next) {
stdout = '';
promptly.prompt('something: ', function (err, value) {
@@ -43,9 +43,9 @@ describe('prompt()', function () {
it('should assume default value if nothing is passed', function (next) {
stdout = '';
- promptly.prompt('something: ', { 'default': 'yeaa' }, function (err, value) {
+ promptly.prompt('something: ', { 'default': '' }, function (err, value) {
expect(err).to.be(null);
- expect(value).to.be('yeaa');
+ expect(value).to.be('');
expect(stdout).to.contain('something: ');
next();
});
@@ -129,7 +129,7 @@ describe('prompt()', function () {
},
times = 0;
- promptly.prompt('something: ', { trim: true, validator: validator }, function (err, value) {
+ promptly.prompt('something: ', { validator: validator }, function (err, value) {
times++;
if (times === 1) {
@@ -138,11 +138,35 @@ describe('prompt()', function () {
return process.stdin.emit('data', 'yeaa\n');
}
+ expect(value).to.equal('yeaa');
+ expect(stdout).to.contain('something: ');
+ expect(stdout.indexOf('something')).to.not.be(stdout.lastIndexOf('something'));
+ next();
+ });
+
+ process.stdin.emit('data', 'wtf\n');
+ });
+
+ it('should automatically retry if a validator fails and retry is enabled', function (next) {
+ stdout = '';
+
+ var validator = function (value) {
+ if (value !== 'yeaa') {
+ throw new Error('bla');
+ }
+
+ return value;
+ };
+
+ promptly.prompt('something: ', { validator: validator, retry: true }, function (err, value) {
+ expect(stdout).to.contain('something: ');
+ expect(stdout.indexOf('something')).to.not.be(stdout.lastIndexOf('something'));
expect(value).to.equal('yeaa');
next();
});
process.stdin.emit('data', 'wtf\n');
+ process.stdin.emit('data', 'yeaa\n');
});
});
@@ -243,4 +267,34 @@ describe('confirm()', function () {
process.stdin.emit('data', 'bleh\n');
});
+});
+
+describe('password()', function () {
+ it('should prompt the user silently', function (next) {
+ stdout = '';
+
+ promptly.password('something: ', function (err, value) {
+ expect(value).to.be('yeaa');
+ expect(stdout).to.contain('something: ');
+ expect(stdout).to.not.contain('yeaa');
+
+ next();
+ });
+
+ process.stdin.emit('data', 'yeaa\n');
+ });
+
+ it('should not trim by default', function (next) {
+ stdout = '';
+
+ promptly.password('something: ', function (err, value) {
+ expect(value).to.be(' yeaa ');
+ expect(stdout).to.contain('something: ');
+ expect(stdout).to.not.contain(' yeaa ');
+
+ next();
+ });
+
+ process.stdin.emit('data', ' yeaa \n');
+ });
});

0 comments on commit 3a09798

Please sign in to comment.