tinyinput 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/lint.yml +22 -0
- package/README.md +60 -7
- package/biome.json +34 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -59
- package/dist/index.mjs +2 -34
- package/package.json +28 -24
- package/test/index.test.ts +143 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Code quality
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
quality:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout
|
|
14
|
+
uses: actions/checkout@v5
|
|
15
|
+
with:
|
|
16
|
+
persist-credentials: false
|
|
17
|
+
- name: Setup Biome
|
|
18
|
+
uses: biomejs/setup-biome@v2
|
|
19
|
+
with:
|
|
20
|
+
version: latest
|
|
21
|
+
- name: Run Biome
|
|
22
|
+
run: biome ci .
|
package/README.md
CHANGED
|
@@ -27,20 +27,73 @@ npm install tinyinput
|
|
|
27
27
|
**Usage**
|
|
28
28
|
-----
|
|
29
29
|
|
|
30
|
-
To use TinyInput,
|
|
30
|
+
To use TinyInput, import the library and call the `input()` function. By default, it returns a string and ensures the input is not empty.
|
|
31
|
+
|
|
31
32
|
```ts
|
|
32
33
|
import { input } from 'tinyinput';
|
|
33
34
|
|
|
34
|
-
async function main(){
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
35
|
+
async function main() {
|
|
36
|
+
// Basic usage (returns string)
|
|
37
|
+
const name = await input('What is your name? ');
|
|
38
|
+
console.log(`Hello, ${name}!`);
|
|
39
|
+
|
|
40
|
+
// Integer input (retries until valid)
|
|
41
|
+
const age = await input('How old are you? ', 'int');
|
|
42
|
+
console.log(`Next year you will be ${age + 1}`);
|
|
43
|
+
|
|
44
|
+
// Float input (retries until valid)
|
|
45
|
+
const price = await input('Enter price: ', 'float');
|
|
46
|
+
console.log(`Total with tax: ${(price * 1.15).toFixed(2)}`);
|
|
47
|
+
|
|
48
|
+
// Password input (hides typing)
|
|
49
|
+
const password = await input('Enter password: ', 'password');
|
|
50
|
+
console.log('Securely received password');
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
// Email validation
|
|
53
|
+
const email = await input("Enter email: ", "email");
|
|
54
|
+
console.log(`Updating record for ${email}`);
|
|
40
55
|
|
|
41
|
-
//
|
|
56
|
+
// Confirmation (returns boolean)
|
|
57
|
+
const save = await confirm("Save changes?");
|
|
58
|
+
if (save) {
|
|
59
|
+
console.log("Saved!");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Selection (returns the string choice)
|
|
63
|
+
const color = await select("Pick a color", ["Red", "Green", "Blue"]);
|
|
64
|
+
console.log(`You chose ${color}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
main();
|
|
42
68
|
```
|
|
43
69
|
|
|
70
|
+
**API Reference**
|
|
71
|
+
-----------
|
|
72
|
+
|
|
73
|
+
### `input(question, opt)`
|
|
74
|
+
|
|
75
|
+
The main function for reading input. Returns a `Promise<string | number>`.
|
|
76
|
+
|
|
77
|
+
* **question** (string): The prompt to show.
|
|
78
|
+
* **opt** (string, optional):
|
|
79
|
+
* `"string"` (default): Returns trimmed, non-empty string.
|
|
80
|
+
* `"int"`: Parses and returns a valid integer.
|
|
81
|
+
* `"float"`: Parses and returns a valid number.
|
|
82
|
+
* `"password"`: Hides input while typing.
|
|
83
|
+
* `"email"`: Validates email format.
|
|
84
|
+
|
|
85
|
+
### `confirm(question, defaultValue?)`
|
|
86
|
+
|
|
87
|
+
Simplified helper for yes/no questions. Returns a `Promise<boolean>`.
|
|
88
|
+
|
|
89
|
+
* **defaultValue** (boolean, optional): Defaults to `true`. Used if the user just presses Enter.
|
|
90
|
+
|
|
91
|
+
### `select(question, choices)`
|
|
92
|
+
|
|
93
|
+
Displays a numbered list of choices. Returns a `Promise<string>`.
|
|
94
|
+
|
|
95
|
+
* **choices** (string[]): A non-empty array of options to choose from.
|
|
96
|
+
|
|
44
97
|
**Technologies**
|
|
45
98
|
-------------
|
|
46
99
|
|
package/biome.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"includes": ["**", "!!**/dist"]
|
|
10
|
+
},
|
|
11
|
+
"formatter": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"indentStyle": "tab"
|
|
14
|
+
},
|
|
15
|
+
"linter": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"rules": {
|
|
18
|
+
"recommended": true
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"javascript": {
|
|
22
|
+
"formatter": {
|
|
23
|
+
"quoteStyle": "double"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"assist": {
|
|
27
|
+
"enabled": true,
|
|
28
|
+
"actions": {
|
|
29
|
+
"source": {
|
|
30
|
+
"organizeImports": "on"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
type Opt = "int" | "float" | "string";
|
|
1
|
+
type Opt = "int" | "float" | "string" | "password" | "email";
|
|
2
2
|
|
|
3
|
-
declare function input(question: string, opt
|
|
3
|
+
declare function input(question: string, opt?: Opt): Promise<string | number>;
|
|
4
|
+
declare function confirm(question: string, defaultValue?: boolean): Promise<boolean>;
|
|
5
|
+
declare function select(question: string, choices: string[]): Promise<string>;
|
|
4
6
|
|
|
5
|
-
export { input };
|
|
7
|
+
export { confirm, input, select };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
type Opt = "int" | "float" | "string";
|
|
1
|
+
type Opt = "int" | "float" | "string" | "password" | "email";
|
|
2
2
|
|
|
3
|
-
declare function input(question: string, opt
|
|
3
|
+
declare function input(question: string, opt?: Opt): Promise<string | number>;
|
|
4
|
+
declare function confirm(question: string, defaultValue?: boolean): Promise<boolean>;
|
|
5
|
+
declare function select(question: string, choices: string[]): Promise<string>;
|
|
4
6
|
|
|
5
|
-
export { input };
|
|
7
|
+
export { confirm, input, select };
|
package/dist/index.js
CHANGED
|
@@ -1,59 +1,2 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
input: () => input
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(index_exports);
|
|
26
|
-
var import_promises = require("readline/promises");
|
|
27
|
-
async function input(question, opt) {
|
|
28
|
-
const r1 = (0, import_promises.createInterface)({
|
|
29
|
-
input: process.stdin,
|
|
30
|
-
output: process.stdout
|
|
31
|
-
});
|
|
32
|
-
let value;
|
|
33
|
-
let parse;
|
|
34
|
-
while (true) {
|
|
35
|
-
value = await r1.question(question);
|
|
36
|
-
r1.close();
|
|
37
|
-
value = value.trim();
|
|
38
|
-
if (opt === "int") {
|
|
39
|
-
parse = parseInt(value, 10);
|
|
40
|
-
if (!Number.isNaN(parse)) break;
|
|
41
|
-
console.log("Please enter a valid integer");
|
|
42
|
-
} else if (opt === "float") {
|
|
43
|
-
parse = parseFloat(value);
|
|
44
|
-
if (!Number.isNaN(value)) break;
|
|
45
|
-
console.log("Please enter a valid number");
|
|
46
|
-
} else {
|
|
47
|
-
if (value.length > 0) {
|
|
48
|
-
parse = value;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
console.log("Please enter a non-empty string.");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return parse;
|
|
55
|
-
}
|
|
56
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
57
|
-
0 && (module.exports = {
|
|
58
|
-
input
|
|
59
|
-
});
|
|
1
|
+
"use strict";var a=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var g=(r,e)=>{for(var s in e)a(r,s,{get:e[s],enumerable:!0})},d=(r,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of p(e))!m.call(r,t)&&t!==s&&a(r,t,{get:()=>e[t],enumerable:!(n=f(e,t))||n.enumerable});return r};var w=r=>d(a({},"__esModule",{value:!0}),r);var P={};g(P,{confirm:()=>b,input:()=>y,select:()=>N});module.exports=w(P);var l=require("readline/promises"),u=require("stream");async function y(r,e="string"){let s=!1,n=new u.Writable({write:(o,i,c)=>{s||process.stdout.write(o,i),c()}}),t=(0,l.createInterface)({input:process.stdin,output:n,terminal:!0});try{for(;;){e==="password"&&(process.stdout.write(r),s=!0);let o=await t.question(e==="password"?"":r);if(e==="password"&&(s=!1,process.stdout.write(`
|
|
2
|
+
`)),o=o.trim(),e==="int"){let i=parseInt(o,10);if(!Number.isNaN(i))return i;console.log("Please enter a valid integer")}else if(e==="float"){let i=parseFloat(o);if(!Number.isNaN(i))return i;console.log("Please enter a valid number")}else if(e==="email"){if(/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(o))return o;console.log("Please enter a valid email address")}else{if(o.length>0)return o;console.log(e==="password"?"Password cannot be empty":"Please enter a non-empty string.")}}}finally{t.close()}}async function b(r,e=!0){let s=(0,l.createInterface)({input:process.stdin,output:process.stdout});try{let n=e?" [Y/n] ":" [y/N] ";for(;;){let t=(await s.question(r+n)).toLowerCase().trim();if(t==="")return e;if(t==="y"||t==="yes"||t==="true")return!0;if(t==="n"||t==="no"||t==="false")return!1;console.log("Please enter 'y' or 'n'")}}finally{s.close()}}async function N(r,e){if(e.length===0)throw new Error("Choices array cannot be empty");let s=(0,l.createInterface)({input:process.stdin,output:process.stdout});try{console.log(r);for(let n=0;n<e.length;n++)console.log(`${n+1}) ${e[n]}`);for(;;){let n=(await s.question("Select an option (number): ")).trim(),t=parseInt(n,10)-1;if(!Number.isNaN(t)&&t>=0&&t<e.length)return e[t];console.log(`Please enter a number between 1 and ${e.length}`)}}finally{s.close()}}0&&(module.exports={confirm,input,select});
|
package/dist/index.mjs
CHANGED
|
@@ -1,34 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
async function input(question, opt) {
|
|
4
|
-
const r1 = createInterface({
|
|
5
|
-
input: process.stdin,
|
|
6
|
-
output: process.stdout
|
|
7
|
-
});
|
|
8
|
-
let value;
|
|
9
|
-
let parse;
|
|
10
|
-
while (true) {
|
|
11
|
-
value = await r1.question(question);
|
|
12
|
-
r1.close();
|
|
13
|
-
value = value.trim();
|
|
14
|
-
if (opt === "int") {
|
|
15
|
-
parse = parseInt(value, 10);
|
|
16
|
-
if (!Number.isNaN(parse)) break;
|
|
17
|
-
console.log("Please enter a valid integer");
|
|
18
|
-
} else if (opt === "float") {
|
|
19
|
-
parse = parseFloat(value);
|
|
20
|
-
if (!Number.isNaN(value)) break;
|
|
21
|
-
console.log("Please enter a valid number");
|
|
22
|
-
} else {
|
|
23
|
-
if (value.length > 0) {
|
|
24
|
-
parse = value;
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
27
|
-
console.log("Please enter a non-empty string.");
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return parse;
|
|
31
|
-
}
|
|
32
|
-
export {
|
|
33
|
-
input
|
|
34
|
-
};
|
|
1
|
+
import{createInterface as l}from"readline/promises";import{Writable as u}from"stream";async function g(i,e="string"){let n=!1,r=new u({write:(s,o,a)=>{n||process.stdout.write(s,o),a()}}),t=l({input:process.stdin,output:r,terminal:!0});try{for(;;){e==="password"&&(process.stdout.write(i),n=!0);let s=await t.question(e==="password"?"":i);if(e==="password"&&(n=!1,process.stdout.write(`
|
|
2
|
+
`)),s=s.trim(),e==="int"){let o=parseInt(s,10);if(!Number.isNaN(o))return o;console.log("Please enter a valid integer")}else if(e==="float"){let o=parseFloat(s);if(!Number.isNaN(o))return o;console.log("Please enter a valid number")}else if(e==="email"){if(/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s))return s;console.log("Please enter a valid email address")}else{if(s.length>0)return s;console.log(e==="password"?"Password cannot be empty":"Please enter a non-empty string.")}}}finally{t.close()}}async function d(i,e=!0){let n=l({input:process.stdin,output:process.stdout});try{let r=e?" [Y/n] ":" [y/N] ";for(;;){let t=(await n.question(i+r)).toLowerCase().trim();if(t==="")return e;if(t==="y"||t==="yes"||t==="true")return!0;if(t==="n"||t==="no"||t==="false")return!1;console.log("Please enter 'y' or 'n'")}}finally{n.close()}}async function w(i,e){if(e.length===0)throw new Error("Choices array cannot be empty");let n=l({input:process.stdin,output:process.stdout});try{console.log(i);for(let r=0;r<e.length;r++)console.log(`${r+1}) ${e[r]}`);for(;;){let r=(await n.question("Select an option (number): ")).trim(),t=parseInt(r,10)-1;if(!Number.isNaN(t)&&t>=0&&t<e.length)return e[t];console.log(`Please enter a number between 1 and ${e.length}`)}}finally{n.close()}}export{d as confirm,g as input,w as select};
|
package/package.json
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
2
|
+
"name": "tinyinput",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A simple library that takes user input the same way it is done in python.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"lint-format": "biome check --write --unsafe"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"url": "https://github.com/walonCode/tinyinput/"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"input",
|
|
18
|
+
"stdin",
|
|
19
|
+
"stdout"
|
|
20
|
+
],
|
|
21
|
+
"author": "Mohamed Lamin Walon-Jalloh",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@biomejs/biome": "2.3.13",
|
|
25
|
+
"@types/node": "^24.3.0",
|
|
26
|
+
"tsup": "^8.5.0",
|
|
27
|
+
"typescript": "^5.9.2",
|
|
28
|
+
"vitest": "^4.0.18"
|
|
29
|
+
}
|
|
26
30
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as readline from "node:readline/promises";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { confirm, input, select } from "../src/index";
|
|
4
|
+
|
|
5
|
+
// Mock readline/promises
|
|
6
|
+
vi.mock("readline/promises", () => {
|
|
7
|
+
return {
|
|
8
|
+
createInterface: vi.fn(),
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("tinyinput", () => {
|
|
13
|
+
let mockRl: any;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
mockRl = {
|
|
18
|
+
question: vi.fn(),
|
|
19
|
+
close: vi.fn(),
|
|
20
|
+
};
|
|
21
|
+
(readline.createInterface as any).mockReturnValue(mockRl);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("input", () => {
|
|
25
|
+
it("should return a string when opt is 'string'", async () => {
|
|
26
|
+
mockRl.question.mockResolvedValueOnce("hello");
|
|
27
|
+
const result = await input("Question?", "string");
|
|
28
|
+
expect(result).toBe("hello");
|
|
29
|
+
expect(mockRl.question).toHaveBeenCalledWith("Question?");
|
|
30
|
+
expect(mockRl.close).toHaveBeenCalled();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should return an integer when opt is 'int'", async () => {
|
|
34
|
+
mockRl.question.mockResolvedValueOnce("42");
|
|
35
|
+
const result = await input("Age?", "int");
|
|
36
|
+
expect(result).toBe(42);
|
|
37
|
+
expect(mockRl.close).toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should return a float when opt is 'float'", async () => {
|
|
41
|
+
mockRl.question.mockResolvedValueOnce("3.14");
|
|
42
|
+
const result = await input("Pi?", "float");
|
|
43
|
+
expect(result).toBe(3.14);
|
|
44
|
+
expect(mockRl.close).toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should retry if invalid integer is provided", async () => {
|
|
48
|
+
mockRl.question
|
|
49
|
+
.mockResolvedValueOnce("abc") // Invalid
|
|
50
|
+
.mockResolvedValueOnce("10"); // Valid
|
|
51
|
+
|
|
52
|
+
const result = await input("Number?", "int");
|
|
53
|
+
expect(result).toBe(10);
|
|
54
|
+
expect(mockRl.question).toHaveBeenCalledTimes(2);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should retry if empty string is provided when opt is 'string'", async () => {
|
|
58
|
+
mockRl.question
|
|
59
|
+
.mockResolvedValueOnce("") // Invalid
|
|
60
|
+
.mockResolvedValueOnce(" ") // Invalid (trimmed)
|
|
61
|
+
.mockResolvedValueOnce("Valid"); // Valid
|
|
62
|
+
|
|
63
|
+
const result = await input("Name?", "string");
|
|
64
|
+
expect(result).toBe("Valid");
|
|
65
|
+
expect(mockRl.question).toHaveBeenCalledTimes(3);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should default to 'string' if opt is not provided", async () => {
|
|
69
|
+
mockRl.question.mockResolvedValueOnce("default value");
|
|
70
|
+
const result = await input("Prompt?");
|
|
71
|
+
expect(result).toBe("default value");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should return a trimmed string for email if valid", async () => {
|
|
75
|
+
mockRl.question.mockResolvedValueOnce(" test@example.com ");
|
|
76
|
+
const result = await input("Email?", "email");
|
|
77
|
+
expect(result).toBe("test@example.com");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should retry if invalid email is provided", async () => {
|
|
81
|
+
mockRl.question
|
|
82
|
+
.mockResolvedValueOnce("invalid-email")
|
|
83
|
+
.mockResolvedValueOnce("valid@email.com");
|
|
84
|
+
|
|
85
|
+
const result = await input("Email?", "email");
|
|
86
|
+
expect(result).toBe("valid@email.com");
|
|
87
|
+
expect(mockRl.question).toHaveBeenCalledTimes(2);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should return password if non-empty", async () => {
|
|
91
|
+
mockRl.question.mockResolvedValueOnce("secret123");
|
|
92
|
+
const result = await input("Password:", "password");
|
|
93
|
+
expect(result).toBe("secret123");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should retry if empty password is provided", async () => {
|
|
97
|
+
mockRl.question
|
|
98
|
+
.mockResolvedValueOnce("")
|
|
99
|
+
.mockResolvedValueOnce("password123");
|
|
100
|
+
|
|
101
|
+
const result = await input("Password:", "password");
|
|
102
|
+
expect(result).toBe("password123");
|
|
103
|
+
expect(mockRl.question).toHaveBeenCalledTimes(2);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("confirm", () => {
|
|
108
|
+
it("should return true for 'y'", async () => {
|
|
109
|
+
mockRl.question.mockResolvedValueOnce("y");
|
|
110
|
+
const result = await confirm("Sure?");
|
|
111
|
+
expect(result).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should return false for 'n'", async () => {
|
|
115
|
+
mockRl.question.mockResolvedValueOnce("n");
|
|
116
|
+
const result = await confirm("Sure?");
|
|
117
|
+
expect(result).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should return default value for empty input", async () => {
|
|
121
|
+
mockRl.question.mockResolvedValueOnce("");
|
|
122
|
+
const result = await confirm("Sure?", true);
|
|
123
|
+
expect(result).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("select", () => {
|
|
128
|
+
it("should return the chosen option", async () => {
|
|
129
|
+
mockRl.question.mockResolvedValueOnce("2");
|
|
130
|
+
const result = await select("Choose:", ["A", "B", "C"]);
|
|
131
|
+
expect(result).toBe("B");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should retry if index is out of bounds", async () => {
|
|
135
|
+
mockRl.question
|
|
136
|
+
.mockResolvedValueOnce("5") // Invalid
|
|
137
|
+
.mockResolvedValueOnce("1"); // Valid
|
|
138
|
+
const result = await select("Choose:", ["A", "B", "C"]);
|
|
139
|
+
expect(result).toBe("A");
|
|
140
|
+
expect(mockRl.question).toHaveBeenCalledTimes(2);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|