Skip to content

Commit

Permalink
feat: add timeout option to prompt (Close #37) (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
loopingz committed Oct 7, 2020
1 parent 822a6ab commit 3b84dd5
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 8 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Available options:
| replace | Replace each character with the specified string when `silent` is true | string | '' |
| input | Input stream to read from | [Stream](https://nodejs.org/api/process.html#process_process_stdin) | process.stdin |
| output | Output stream to write to | [Stream](https://nodejs.org/api/process.html#process_process_stdout) | process.stdout |
| timeout | Timeout in ms | number | 0 |
| useDefaultOnTimeout | Return default value if timed out | boolean | false |

The same **options** are available to **all functions** but with different default values.

Expand Down Expand Up @@ -103,6 +105,19 @@ The same **options** are available to **all functions** but with different defau
})();
```

- Ask for a name with timeout:

```js
const promptly = require('promptly');

(async () => {
const name = await promptly.prompt('Name: ', { timeout: 3000 });
console.log(name);
})();
```

It throws an `Error("timed out")` if timeout is reached and no default value is provided

#### Validators

The validators have two purposes: to check and transform input.
Expand Down
2 changes: 2 additions & 0 deletions lib/getOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ function getOptions(options) {
retry: true,
trim: true,
default: undefined,
useDefaultOnTimeout: false,

// `read` package options
silent: false,
replace: '',
input: process.stdin,
output: process.stdout,
timeout: 0,

...options,
};
Expand Down
25 changes: 18 additions & 7 deletions lib/prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@ const { promisify } = require('util');
const read = promisify(require('read'));

async function prompt(message, options) {
let value;

// Read input
let value = await read({
prompt: message,
silent: options.silent,
replace: options.replace,
input: options.input,
output: options.output,
});
// Manage timeout
try {
value = await read({
prompt: message,
silent: options.silent,
replace: options.replace,
input: options.input,
output: options.output,
timeout: options.timeout,
});
} catch (err) {
if (err.message !== 'timed out' || options.default === undefined || !options.useDefaultOnTimeout) {
throw Object.assign(new Error(err.message), { code: 'TIMEDOUT' });
}
value = options.default;
}

// Trim?
if (options.trim) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
],
"scripts": {
"lint": "eslint .",
"test": "jest --env node --coverage",
"test": "jest --env node --coverage --runInBand",
"prerelease": "npm t && npm run lint",
"release": "standard-version",
"postrelease": "git push --follow-tags origin HEAD && npm publish"
Expand Down
18 changes: 18 additions & 0 deletions test/prompt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,21 @@ it('should write input using options.replace to stdout if silent is enabled', as
expect(process.stdout.write).toHaveBeenCalledWith('a');
expect(process.stdout.write).not.toHaveBeenCalledWith('z');
});

it('should timeout if user is not fast enough', async () => {
await expect(promptly.prompt('prompt: ', { timeout: 10 })).rejects.toEqual(new Error('timed out'));
});

it('should take default value if timed out with timeoutToDefault', async () => {
expect(await promptly.prompt('prompt: ', { timeout: 10, default: 'plop', useDefaultOnTimeout: true })).toEqual('plop');
expect(await promptly.prompt('prompt: ', { timeout: 10, default: '', useDefaultOnTimeout: true })).toEqual('');
});

it('should take default value if timed out', async () => {
await expect(promptly.prompt('prompt: ', { timeout: 10, default: 'plop' })).rejects.toEqual(new Error('timed out'));
});

it('should take input value if not timed out', async () => {
sendLine('plop2', 10);
expect(await promptly.prompt('prompt: ', { timeout: 20, default: 'plop' })).toEqual('plop2');
});

0 comments on commit 3b84dd5

Please sign in to comment.