DefinitelyTyped/types/terminal-kit/terminal-kit-tests.ts
Louis-Dominique Dubeau 2c58b77f66
[terminal-kit]: fix the typings for Callback and inputField (#42804)
* Fix the typing of Callback.

The previous typing for `Callback` makes no sense. If someone used a callback that had both attributes (which is the most common case), then `err` would be typed as
`undefined` inside the callback. However, whether or not `err` is undefined is not determined by the number of
parameters set on the callback. The previous typing would have been correct if there was a guaranteed that when the
callback has two parameters, the first one is necessarily `undefined`. But there is no such guarantee. If an error
happened, then the first parameter is **not** undefined.

* Fix the return values of inputField.

The Promise was declared to resolve to `boolean` which is absolutely incorrect. `inputField`'s promise resolves to a
string. If cancelation was allowed the user canceled (hit escape), then the final value may be undefined this is true both for the promise and the callback.

* Make the promise mandatory on the return value of inputField.

The promise on the object returned by inputField is always present.

* Fix the return value of inputField to always return a value.

inputField *always* returns an object, *irrespective* of whether a callback is passed. The object is more complex than
currently defined but I only use the `promise` field on it and have no inclination to figure the whole object's
structure. Someone else will have to figure it out.

* Drop the explicit typings on the callback parameters.

They override, and consequently they obscure, the typings that are provided by the definition files.

* Test that abort field is present on return value of inputField.

terminal-kit *always* provides it, callback or no callback.

* The new typings make input be possibly undefined.
2020-03-13 18:48:05 -07:00

499 lines
11 KiB
TypeScript

// Require the lib, get a working terminal
import t, {
terminal as term,
autoComplete as ac,
getDetectedTerminal,
ScreenBufferHD,
ScreenBuffer,
Terminal
} from "terminal-kit";
import "node";
import * as fs from "fs";
import assert from "assert";
new t.Rect({width: 4, height: 4});
// The term() function simply output a string to stdout, using current style
// output "Hello world!" in default terminal's colors
t.terminal("Hello world!\n");
// This output 'red' in red
term.red("red");
// This output 'bold' in bold
term.bold("bold");
// output 'mixed' using bold, underlined & red, exposing the style-mixing syntax
term.bold.underline.red("mixed");
// printf() style formatting everywhere:
// this will output 'My name is Jack, I'm 32.' in green
term.green("My name is %s, I'm %d.\n", "Jack", 32);
// Since v0.16.x, style markup are supported as a shorthand.
// Those two lines produce the same result.
term("My name is ")
.red("Jack")(" and I'm ")
.green("32\n");
term("My name is ^rJack^ and I'm ^g32\n");
// Width and height of the terminal
term("The terminal size is %dx%d", term.width, term.height);
// Move the cursor at the upper-left corner
term.moveTo(1, 1);
// We can always pass additional arguments that will be displayed...
term.moveTo(1, 1, "Upper-left corner");
// ... and formated
term.moveTo(1, 1, "My name is %s, I'm %d.\n", "Jack", 32);
// ... or even combined with other styles
term.moveTo.cyan(1, 1, "My name is %s, I'm %d.\n", "Jack", 32);
// Get some user input
term.magenta("Enter your name: ");
assert(term.inputField((error, input) => {
term.green("\nYour name is '%s'\n", input);
}).abort);
function terminate() {
term.grabInput(false);
setTimeout(() => {}, 100);
}
term.bold.cyan("Type anything on the keyboard...\n");
term.green("Hit CTRL-C to quit.\n\n");
term.grabInput({ mouse: "button" });
term.on("key", (name: string, matches: any[], data: any) => {
console.log("'key' event:", name);
if (name === "CTRL_C") {
terminate();
}
});
term.on("terminal", (name: string, data: any) => {
console.log("'terminal' event:", name, data);
});
term.on("mouse", (name: string, data: any) => {
console.log("'mouse' event:", name, data);
});
// Word-wrap this along the full terminal width
term.wrap.yellow(
`'Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish'`
);
// Word-wrap this inside a column starting at x=10 with a width of 25 terminal cells
term.wrapColumn({ x: 10, width: 25 });
term.wrap.green(
`'Permission is hereby granted, free of charge, to any person obtaining a copy of this software an
d associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish'`
);
// This reset the offset
term("\n");
// term.wrapColumn() could be used as well, but the next text would overwrite the last line
// Text continuation: the second text start at the end of line of the first text
term.wrap.blue("^GP^re^Yr^um^Mi^bs^bs^ci^ro^mn^ is ");
term.wrap.red("hereby granted");
function question() {
term("Do you like javascript? [Y|n]\n");
// Exit on y and ENTER key
// Ask again on n
term.yesOrNo(
{ yes: ["y", "ENTER"], no: ["n"] },
(error: any, result: any) => {
if (result) {
term.green("'Yes' detected! Good bye!\n");
} else {
term.red("'No' detected, are you sure?\n");
question();
}
}
);
}
question();
const history = ["John", "Jack", "Joey", "Billy", "Bob"];
const autoComplete = [
"Barack Obama",
"George W. Bush",
"Bill Clinton",
"George Bush",
"Ronald W. Reagan",
"Jimmy Carter",
"Gerald Ford",
"Richard Nixon",
"Lyndon Johnson",
"John F. Kennedy",
"Dwight Eisenhower",
"Harry Truman",
"Franklin Roosevelt"
];
term("Please enter your name: ");
term.inputField(
{ history, autoComplete, autoCompleteMenu: true },
(error: any, input: string | undefined) => {
term.green("\nYour name is '%s'\n", input);
}
);
const history1 = ["John", "Jack", "Joey", "Billy", "Bob"];
const autoComplete1 = [
"Barack Obama",
"George W. Bush",
"Bill Clinton",
"George Bush",
"Ronald W. Reagan",
"Jimmy Carter",
"Gerald Ford",
"Richard Nixon",
"Lyndon Johnson",
"John F. Kennedy",
"Dwight Eisenhower",
"Harry Truman",
"Franklin Roosevelt"
];
term("Please enter your name: ");
() => {
const input = term.inputField({
history: history1,
autoComplete: autoComplete1,
autoCompleteMenu: true
}).promise;
term.green("\nYour name is '%s'\n", input);
};
const autoCompleter = function autoCompleter(
inputString: string,
callback: (err: any, input: string) => void
) {
fs.readdir(__dirname, (error, files) => {
callback(undefined, ac(files, inputString, true));
});
};
term("Choose a file: ");
term.inputField(
{ autoComplete: autoCompleter, autoCompleteMenu: true },
(error: any, input: any) => {
if (error) {
term.red.bold(`'\nAn error occurs: ' + ${error} + '\n'`);
} else {
term.green("\nYour file is '%s'\n", input);
}
}
);
const autoComplete2 = [
"dnf install",
"dnf install nodejs",
"dnf search",
"sudo",
"sudo dnf install",
"sudo dnf install nodejs",
"sudo dnf search"
];
term.inputField(
{
autoComplete: autoComplete2,
autoCompleteHint: true,
autoCompleteMenu: true,
tokenHook: (
token: any,
isEndOfInput: any,
previousTokens: any,
term: any,
config: any
) => {
const previousText = previousTokens.join(" ");
switch (token) {
case "sudo":
config.style = term.red;
return previousTokens.length ? null : term.bold.red;
case "dnf":
return previousText === "" || previousText === "sudo"
? term.brightMagenta
: null;
case "install":
config.style = term.brightBlue;
config.hintStyle = term.brightBlack.italic;
return previousText === "dnf" || previousText === "sudo dnf"
? term.brightYellow
: null;
case "search":
config.style = term.brightBlue;
return previousText === "dnf" || previousText === "sudo dnf"
? term.brightCyan
: null;
default:
return;
}
}
},
(error: any, input: any) => {
term.green("\nYour command is: '%s'\n", input);
}
);
term("Choose a file: ");
term.fileInput({ baseDir: "../" }, (error: any, input: any) => {
if (error) {
term.red.bold(`'\nAn error occurs: ' + ${error} + '\n'`);
} else {
term.green("\nYour file is '%s'\n", input);
}
});
const items1 = [
"File",
"Edit",
"View",
"History",
"Bookmarks",
"Tools",
"Help"
];
const options = {
y: 1, // the menu will be on the top of the terminal
style: term.inverse,
selectedStyle: term.dim.blue.bgGreen
};
term.clear();
term.singleLineMenu(items1, options, (error: any, response: any) => {
term("\n").eraseLineAfter.green(
"#%s selected: %s (%s,%s)\n",
response.selectedIndex,
response.selectedText,
response.x,
response.y
);
});
term.cyan("The hall is spacious. Someone lighted few chandeliers.\n");
term.cyan("There are doorways south and west.\n");
const items2 = ["a. Go south", "b. Go west", "c. Go back to the street"];
term.singleColumnMenu(items2, (error: any, response: any) => {
term("\n").eraseLineAfter.green(
"#%s selected: %s (%s,%s)\n",
response.selectedIndex,
response.selectedText,
response.x,
response.y
);
});
term.cyan("Choose a file:\n");
const items = fs.readdirSync(process.cwd());
term.gridMenu(items, (error: any, response: any) => {
term("\n").eraseLineAfter.green(
"#%s selected: %s (%s,%s)\n",
response.selectedIndex,
response.selectedText,
response.x,
response.y
);
});
let progressBar: Terminal.ProgressBarController;
let progress = 0;
function doProgress() {
// Add random progress
progress += Math.random() / 10;
progressBar.update(progress);
if (progress >= 1) {
// Cleanup and exit
setTimeout(() => {
term("\n");
}, 200);
} else {
setTimeout(doProgress, 100 + Math.random() * 400);
}
}
progressBar = term.progressBar({
width: 80,
title: "Serious stuff in progress:",
eta: true,
percent: true
});
doProgress();
const thingsToDo = [
"update my lib",
"data analyzing",
"serious business",
"decrunching data",
"do my laundry",
"optimizing"
];
let countDown = thingsToDo.length;
function start() {
const task = thingsToDo.shift();
if (!task) {
return;
}
progressBar.startItem(task);
// Finish the task in...
setTimeout(done.bind(null, task), 500 + Math.random() * 1200);
// Start another parallel task in...
setTimeout(start, 400 + Math.random() * 400);
}
function done(task: string) {
progressBar.itemDone(task);
countDown--;
// Cleanup and exit
if (!countDown) {
setTimeout(() => {
term("\n");
}, 200);
}
}
progressBar = term.progressBar({
width: 80,
title: "Daily tasks:",
eta: true,
percent: true,
items: thingsToDo.length
});
start();
term.slowTyping(
"What a wonderful world!\n",
{ flashStyle: term.brightWhite },
() => {}
);
// low level
term("My name is ")
.red("Jack")(" and I'm ")
.green("32\n");
term("My name is ^rJack^ and I'm ^g32\n");
term.noFormat.red("hello");
term.noFormat("hello");
// color methods with a second argument
term.color(1, "test");
term.darkColor(1, "test");
term.brightColor(1, "test");
term.color256(1, "test");
term.colorRgb(255, 0, 0, "test");
term.colorRgbHex("#ff0000", "test");
term.colorGrayscale(192, "test");
// bgColor methods with a second argument
term.bgColor(1, "test");
term.bgDarkColor(1, "test");
term.bgBrightColor(1, "test");
term.bgColor256(1, "test");
term.bgColorRgb(255, 0, 0, "test");
term.bgColorRgbHex("#ff0000", "test");
term.bgColorGrayscale(192, "test");
// new color & bgColor with color name
term.color("red");
term.color("red", "test");
term.bgColor("red");
term.bgColor("red", "test");
getDetectedTerminal((error: any, term: any) => {
term.cyan("Terminal name: %s\n", term.appName);
term.cyan("Terminal app: %s\n", term.app);
term.cyan("Terminal generic: %s\n", term.generic);
term.cyan("Config file: %s\n", term.termconfigFile);
});
const screen = new ScreenBufferHD({ dst: term, noFill: true });
screen.fill({
attr: {
// Both foreground and background must have the same color
r: 40,
g: 20,
b: 0,
bgR: 40,
bgG: 20,
bgB: 0
}
});
const path_to_image = "/home/imoti/Downloads/photo_2019-01-24_13-15-50.jpg";
ScreenBufferHD.loadImage(
path_to_image,
{ shrink: { width: term.width, height: term.height * 2 } },
(error: any, image: any) => {
if (error) {
throw error;
} // Doh!
image.draw({ dst: screen, blending: true });
screen.draw();
}
);
const screen1 = new ScreenBuffer({ dst: term, noFill: true });
screen1.fill({
attr: {
// Both foreground and background must have the same color
color: 0,
bgColor: 0
}
});
ScreenBuffer.loadImage(
path_to_image,
{ terminal: term, shrink: { width: term.width, height: term.height * 2 } },
(error: any, image: any) => {
if (error) {
throw error;
} // Doh!
image.draw({ dst: screen, blending: true });
screen.draw();
}
);