@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
260 lines • 9.59 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
const test_utils_1 = require("@sprucelabs/test-utils");
const test_utility_1 = __importDefault(require("../tests/utilities/test.utility"));
const duration_utility_1 = __importDefault(require("../utilities/duration.utility"));
const TerminalInterface_1 = __importDefault(require("./TerminalInterface"));
class SpyInterface {
invocations = [];
cursorPosition = { x: 0, y: 0 };
promptResolver;
confirmResolver;
waitForEnterResolver;
promptDefaultValue;
term;
startTime;
promptTimeout;
error;
constructor() {
this.startTime = new Date().getTime();
this.term = this.shouldRenderTestLogs()
? new TerminalInterface_1.default(process.cwd(), true, (...strs) => {
this.optionallyRenderLine(strs.join(' '));
})
: undefined;
}
setTitle(_title) { }
shouldRenderTestLogs() {
return process.env.SHOULD_RENDER_TEST_LOGS === 'true';
}
renderWarning(message, effects) {
this.trackInvocation('renderWarning', { message, effects });
this.term?.renderWarning(message);
}
renderHint(message, effects) {
this.trackInvocation('renderHint', { message, effects });
this.optionallyRenderLine(`Hint: ${message}`);
this.term?.renderHint(message);
}
trackInvocation(command, options) {
// testUtil.log(command, JSON.stringify(options), '\n')
this.invocations.push({ command, options });
}
isWaitingForInput() {
return !!(this.promptResolver ||
this.confirmResolver ||
this.waitForEnterResolver);
}
reset() {
this.promptResolver = undefined;
this.confirmResolver = undefined;
this.waitForEnterResolver = undefined;
clearTimeout(this.promptTimeout);
}
setError(err) {
this.error = err;
}
getLastInvocation() {
return this.invocations[this.invocations.length - 1];
}
async sendInput(input) {
this.trackInvocation('sendInput', input);
this.optionallyRenderLine(`Sending input: "${input.path
? input.path
: input.length > 0
? input
: 'ENTER'}"`);
if (this.waitForEnterResolver) {
const resolver = this.waitForEnterResolver;
this.waitForEnterResolver = undefined;
resolver();
}
else if (this.promptResolver) {
const resolver = this.promptResolver;
this.promptResolver = undefined;
resolver(input !== '\n' && input !== '' ? input : this.promptDefaultValue);
}
else if (this.confirmResolver) {
const resolver = this.confirmResolver;
this.confirmResolver = undefined;
resolver(input === '\n' ||
input.length === 0 ||
(typeof input === 'string' && input.toLowerCase() === 'y'));
}
else {
throw new Error('Sent input before prompted for input');
}
return new Promise((resolve) => {
setTimeout(resolve, 50);
});
}
renderSection(options) {
this.trackInvocation('renderSection', options);
this.term?.renderSection(options);
}
renderObject(obj) {
this.trackInvocation('renderObject', obj);
this.term?.renderObject(obj);
}
renderError(err) {
this.trackInvocation('renderError', err);
this.term?.renderError(err);
}
renderCodeSample(code) {
this.trackInvocation('renderCodeSample', code);
this.term?.renderCodeSample(code);
}
renderActionSummary(results) {
this.trackInvocation('renderCommandSummary', results);
this.term?.renderActionSummary(results);
}
renderHero(message, effects) {
this.trackInvocation('renderHero', { message, effects });
this.term?.renderHero(message, effects);
}
renderHeadline(message, effects, dividerEffects) {
this.trackInvocation('renderHeadline', {
message,
effects,
dividerEffects,
});
this.term?.renderHeadline(message, effects, dividerEffects);
}
renderDivider(effects) {
this.trackInvocation('renderDivider', effects);
this.term?.renderDivider(effects);
}
renderLine(message, effects) {
this.trackInvocation('renderLine', { message, effects });
message && message.length > 0 && this.optionallyRenderLine(message);
}
optionallyRenderLine(message) {
if (this.shouldRenderTestLogs()) {
const duration = new Date().getTime() - this.startTime;
const friendly = duration_utility_1.default.msToFriendly(duration);
spruce_skill_utils_1.testLog.info(`${
//@ts-ignore
global.activeTest?.test
? //@ts-ignore
global.activeTest?.test + ' :: '
: ''}${friendly} :: ${message}`);
}
}
async renderImage(path, options) {
this.trackInvocation('renderImage', { path, options });
}
renderLines(messages, effects) {
this.trackInvocation('renderLines', { messages, effects });
this.term?.renderLines(messages, effects);
}
async prompt(definition) {
this.trackInvocation('prompt', definition);
if (this.promptResolver) {
throw new Error('Tried to double prompt. Try this.term?.sendInput() before calling prompt next.');
}
let msg = `${spruce_skill_utils_1.namesUtil.toPascal(definition.type)} Prompt: ${definition.label}`;
if (definition.type === 'select') {
msg += '\n\nChoices:\n';
definition.options.choices.forEach((choice) => {
msg += `\n${choice.value}: ${choice.label}`;
});
msg += '\n';
}
this.optionallyRenderLine(msg);
return new Promise((resolve, reject) => {
this.promptResolver = (...args) => {
clearTimeout(this.promptTimeout);
//@ts-ignore
resolve(...args);
};
this.promptTimeout = setTimeout(() => {
this.reset();
try {
test_utils_1.assert.fail(`Timed out waiting for input with label: '${definition.label}'\n\nConsider passing what you need to Action().execute({ something })`);
}
catch (err) {
reject(err);
}
}, 10000);
this.promptDefaultValue = definition.defaultValue;
});
}
startLoading(message) {
this.trackInvocation('startLoading', message);
this.optionallyRenderLine(message ? `${message}` : 'Start loading...');
}
stopLoading() {
this.trackInvocation('stopLoading');
this.optionallyRenderLine('Stop loading...');
}
async waitForEnter(message) {
this.trackInvocation('waitForEnter', message);
this.optionallyRenderLine(`${message ? ` ${message}\n\n` : ``}Waiting for enter...`);
return new Promise((resolve) => {
this.waitForEnterResolver = resolve;
});
}
async waitForInput() {
const ttl = 1000 * 60 * 2;
const checkInterval = 100;
let loops = ttl / checkInterval;
let lastWriteCount = this.invocations.length;
while (!this.isWaitingForInput()) {
if (loops-- === 0) {
test_utils_1.assert.fail(`Waiting for input timed out.`);
}
const hasWritten = lastWriteCount != this.invocations.length;
if (hasWritten) {
loops = ttl / checkInterval;
lastWriteCount = this.invocations.length;
if (this.shouldRenderTestLogs()) {
test_utility_1.default.log('waitForInput timeout reset because of new output.');
}
}
if (this.error) {
throw this.error;
}
await new Promise((resolve) => setTimeout(resolve, checkInterval));
}
}
confirm(question) {
this.trackInvocation('confirm', question);
this.optionallyRenderLine(`${question} :: Y/N...`);
return new Promise((resolve) => {
this.confirmResolver = resolve;
});
}
clear() {
this.trackInvocation('clear');
}
renderProgressBar(options) {
this.trackInvocation('renderProgressBar', options);
this.optionallyRenderLine(`Showing progress${options.title ? ` ${options.title}` : ``}`);
}
updateProgressBar(options) {
this.trackInvocation('updateProgressBar', options);
}
removeProgressBar() {
this.trackInvocation('removeProgressBar');
this.optionallyRenderLine(`Hiding progress`);
}
async getCursorPosition() {
this.trackInvocation('getCursorPosition');
return this.cursorPosition;
}
setCursorPosition(pos) {
this.cursorPosition = pos;
}
moveCursorTo(x, y) {
this.trackInvocation('moveCursorTo', { x, y });
}
clearBelowCursor() {
this.trackInvocation('clearBelowCursor');
}
}
exports.default = SpyInterface;
//# sourceMappingURL=SpyInterface.js.map