@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
403 lines • 16.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const globby_1 = __importDefault(require("@sprucelabs/globby"));
const mercury_client_1 = require("@sprucelabs/mercury-client");
const schema_1 = require("@sprucelabs/schema");
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
const spruce_templates_1 = require("@sprucelabs/spruce-templates");
const test_utils_1 = __importStar(require("@sprucelabs/test-utils"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const uuid = __importStar(require("uuid"));
const EventFaker_1 = __importDefault(require("../__tests__/support/EventFaker"));
const ActionExecuter_1 = __importDefault(require("../features/ActionExecuter"));
const ActionFactory_1 = __importDefault(require("../features/ActionFactory"));
const EventStore_1 = __importDefault(require("../features/event/stores/EventStore"));
const FeatureInstallerFactory_1 = __importDefault(require("../features/FeatureInstallerFactory"));
const OnboardingStore_1 = __importDefault(require("../features/onboard/stores/OnboardingStore"));
const SkillStore_1 = __importDefault(require("../features/skill/stores/SkillStore"));
const GlobalEmitter_1 = __importDefault(require("../GlobalEmitter"));
const SpyInterface_1 = __importDefault(require("../interfaces/SpyInterface"));
const CommandService_1 = __importDefault(require("../services/CommandService"));
const ImportService_1 = __importDefault(require("../services/ImportService"));
const LintService_1 = __importDefault(require("../services/LintService"));
const ServiceFactory_1 = __importDefault(require("../services/ServiceFactory"));
const StoreFactory_1 = __importDefault(require("../stores/StoreFactory"));
const WriterFactory_1 = __importDefault(require("../writers/WriterFactory"));
const CommandFaker_1 = __importDefault(require("./CommandFaker"));
const FeatureFixture_1 = __importDefault(require("./fixtures/FeatureFixture"));
const MercuryFixture_1 = __importDefault(require("./fixtures/MercuryFixture"));
const OrganizationFixture_1 = __importDefault(require("./fixtures/OrganizationFixture"));
const PersonFixture_1 = __importDefault(require("./fixtures/PersonFixture"));
const SkillFixture_1 = __importDefault(require("./fixtures/SkillFixture"));
const ViewFixture_1 = __importDefault(require("./fixtures/ViewFixture"));
const test_utility_1 = __importDefault(require("./utilities/test.utility"));
class AbstractCliTest extends test_utils_1.default {
static cliRoot = path_1.default.join(__dirname, '..');
static homeDir;
static commandFaker;
static _ui;
static _emitter;
static mercuryFixture;
static personFixture;
static organizationFixture;
static skillFixture;
static _featureInstaller;
static viewFixture;
static originalEnv;
static _writers;
static eventFaker;
static async beforeAll() {
await super.beforeAll();
await this.cleanTestDirsAndFiles();
LintService_1.default.disableLinting();
ServiceFactory_1.default.reset();
WriterFactory_1.default.reset();
ImportService_1.default.setCacheDir(spruce_skill_utils_1.diskUtil.createRandomTempDir());
ImportService_1.default.enableCaching();
this.eventFaker = new EventFaker_1.default();
process.env.ENABLE_INSTALL_ENTERTAINMENT = 'false';
this.originalEnv = { ...process.env };
}
static async beforeEach() {
await super.beforeEach();
delete this._writers;
ActionFactory_1.default.clearActionOverrides();
//@ts-ignore
process.env = { ...this.originalEnv };
test_utility_1.default.startLogTimer();
schema_1.SchemaRegistry.getInstance().forgetAllSchemas();
this.cwd = this.freshTmpDir();
this.homeDir = this.freshTmpDir();
spruce_skill_utils_1.AuthService.homeDir = this.homeDir;
this._emitter = undefined;
this._featureInstaller = undefined;
OnboardingStore_1.default.overrideCwd(spruce_skill_utils_1.diskUtil.createRandomTempDir());
this.ui.reset();
this.ui.invocations = [];
this.ui.setCursorPosition({ x: 0, y: 0 });
this.clearFixtures();
ImportService_1.default.clearCache();
SkillStore_1.default.clearCurrentSkill();
EventStore_1.default.clearCache();
CommandService_1.default.clearFakedResponses();
mercury_client_1.MercuryClientFactory.reset();
mercury_client_1.MercuryClientFactory.setIsTestMode(false);
this.commandFaker = new CommandFaker_1.default();
}
static async afterEach() {
await super.afterEach();
await this.organizationFixture?.clearAllOrgs();
await this.mercuryFixture?.disconnectAll();
this.clearFixtures();
if (this._ui?.isWaitingForInput()) {
throw new Error(`Terminal interface is waiting for input. Make sure you are invoking this.term.sendInput() as many times as needed.`);
}
if (spruce_skill_utils_1.diskUtil.doesDirExist(this.cwd) && test_utility_1.default.shouldClearCache()) {
spruce_skill_utils_1.diskUtil.deleteDir(this.cwd);
}
if (spruce_skill_utils_1.diskUtil.doesDirExist(this.homeDir) &&
test_utility_1.default.shouldClearCache()) {
spruce_skill_utils_1.diskUtil.deleteDir(this.homeDir);
}
if (test_utility_1.default.shouldCleanupAfterEach()) {
FeatureFixture_1.default.deleteOldSkillDirs();
}
}
static async afterAll() {
await super.afterAll();
if (test_utility_1.default.shouldCleanupAfterAll()) {
FeatureFixture_1.default.deleteOldSkillDirs();
}
}
static async cleanTestDirsAndFiles() {
const pattern = this.resolveTestPath('**/*.d.ts');
const matches = await (0, globby_1.default)(pattern);
for (const match of matches) {
spruce_skill_utils_1.diskUtil.deleteFile(match);
}
}
static clearFixtures() {
this._emitter = undefined;
this.mercuryFixture = undefined;
this.organizationFixture = undefined;
this.personFixture = undefined;
this.skillFixture = undefined;
this.viewFixture = undefined;
}
static freshTmpDir() {
const tmpDirectory = test_utility_1.default.resolveTestDir(uuid.v4());
fs_extra_1.default.ensureDirSync(tmpDirectory);
return tmpDirectory;
}
static get ui() {
if (!this._ui) {
this._ui = new SpyInterface_1.default();
}
return this._ui;
}
static get emitter() {
if (!this._emitter) {
this._emitter = GlobalEmitter_1.default.Emitter();
}
return this._emitter;
}
static set emitter(emitter) {
this._emitter = emitter;
}
static resolveTestPath(...pathAfterTestDirsAndFiles) {
return path_1.default.join(this.cliRoot, '__tests__', 'testDirsAndFiles', ...pathAfterTestDirsAndFiles);
}
static async Cli(options) {
return this.FeatureFixture(options).Cli({
cwd: this.cwd,
homeDir: this.homeDir,
...(options ?? {}),
});
}
static async linkLocalPackages() {
const fixture = this.FeatureFixture();
await fixture.linkLocalPackages();
}
static Service(type, cwd) {
const sf = this.ServiceFactory();
return sf.Service(cwd ?? this.cwd, type);
}
static ServiceFactory() {
return new ServiceFactory_1.default();
}
static FeatureFixture(options) {
return new FeatureFixture_1.default({
cwd: this.cwd,
serviceFactory: this.ServiceFactory(),
ui: this.ui,
apiClientFactory: this.getMercuryFixture().getApiClientFactory(),
featureInstaller: this.featureInstaller,
emitter: this.emitter,
...options,
});
}
static getMercuryFixture() {
if (!this.mercuryFixture) {
this.mercuryFixture = new MercuryFixture_1.default(this.cwd, this.ServiceFactory());
}
return this.mercuryFixture;
}
static get people() {
if (!this.personFixture) {
this.personFixture = new PersonFixture_1.default(this.getMercuryFixture().getApiClientFactory());
}
return this.personFixture;
}
static getViewFixture() {
if (!this.viewFixture) {
const viewWriter = this.writers.Writer('view', {
fileDescriptions: [],
});
this.viewFixture = new ViewFixture_1.default(this.cwd, viewWriter, this.Action('view', 'sync'));
}
return this.viewFixture;
}
static get writers() {
if (!this._writers) {
this._writers = this.WriterFactory();
}
return this._writers;
}
static WriterFactory() {
return new WriterFactory_1.default({
templates: spruce_templates_1.templates,
ui: this.ui,
settings: this.Service('settings'),
});
}
static async skipInstallSkillPrompts(execute) {
const promise = execute?.();
await this.waitForInput();
await this.ui.sendInput('n');
await this.waitForInput();
await this.ui.sendInput('');
const results = await promise;
return results;
}
static getOrganizationFixture() {
if (!this.organizationFixture) {
this.organizationFixture = new OrganizationFixture_1.default(this.people, this.StoreFactory());
}
return this.organizationFixture;
}
static getSkillFixture() {
if (!this.skillFixture) {
this.skillFixture = new SkillFixture_1.default(this.people, this.StoreFactory(), this.getMercuryFixture().getApiClientFactory());
}
return this.skillFixture;
}
static resolveHashSprucePath(...filePath) {
return spruce_skill_utils_1.diskUtil.resolveHashSprucePath(this.cwd, ...filePath);
}
static get featureInstaller() {
if (!this._featureInstaller) {
const installer = this.FeatureInstaller();
this._featureInstaller = installer;
}
return this._featureInstaller;
}
static set featureInstaller(installer) {
this._featureInstaller = installer;
}
static FeatureInstaller(options) {
const serviceFactory = this.ServiceFactory();
const storeFactory = this.StoreFactory(options);
const emitter = options?.emitter ?? this.emitter;
const apiClientFactory = this.getMercuryFixture().getApiClientFactory();
const actionExecuter = this.ActionExecuter();
const installer = FeatureInstallerFactory_1.default.WithAllFeatures({
cwd: this.cwd,
serviceFactory,
storeFactory,
ui: this.ui,
emitter,
apiClientFactory,
actionExecuter,
...options,
});
return installer;
}
static StoreFactory(options) {
const serviceFactory = this.ServiceFactory();
return new StoreFactory_1.default({
cwd: this.cwd,
serviceFactory,
homeDir: this.homeDir,
apiClientFactory: this.getMercuryFixture().getApiClientFactory(),
emitter: this.emitter,
...options,
});
}
static Store(code, options) {
return this.StoreFactory().Store(code, {
cwd: this.cwd,
...options,
});
}
static async waitForInput() {
return this.ui.waitForInput();
}
static async assertIsFeatureInstalled(code) {
const isInstalled = await this.featureInstaller.isInstalled(code);
test_utils_1.assert.isTrue(isInstalled);
}
static async assertValidActionResponseFiles(results) {
for (const file of results.files ?? []) {
await this.assertFilePassesTypeChecks(file.path);
}
// await Promise.all(
// (results.files ?? []).map((file) => {
// const checker = this.Service('typeChecker')
// return checker.check(file.path)
// })
// )
}
static async assertFilePassesTypeChecks(file) {
const checker = this.Service('typeChecker');
await checker.check(file);
}
static async connectToApi(options) {
return this.getMercuryFixture().connectToApi(options);
}
static async openInVsCode(options) {
await this.Action('vscode', 'setup').execute({ all: true });
await this.Service('command').execute(`code ${options?.file ?? options?.dir ?? this.cwd}`);
await this.wait(options?.timeout ?? 99999999);
}
static async openInFinder(options) {
await this.Service('command').execute(`open ${options?.file ?? options?.dir ?? this.cwd}`);
await this.wait(options?.timeout ?? 99999999);
}
static log(...args) {
test_utility_1.default.log(...args);
}
static Action(featureCode, actionCode, options) {
const executer = this.ActionExecuter({
shouldThrowOnListenerError: true,
...options,
}).Action(featureCode, actionCode);
return executer;
}
static ActionExecuter(options) {
const serviceFactory = this.ServiceFactory();
const actionFactory = new ActionFactory_1.default({
writerFactory: this.writers,
ui: this.ui,
emitter: this.emitter,
apiClientFactory: this.getMercuryFixture().getApiClientFactory(),
cwd: this.cwd,
serviceFactory,
storeFactory: this.StoreFactory(),
templates: spruce_templates_1.templates,
optionOverrides: {
'sync.schemas': {
shouldInstallMissingDependencies: true,
},
...options?.optionOverrides,
},
});
const executer = new ActionExecuter_1.default({
ui: this.ui,
emitter: this.emitter,
actionFactory,
featureInstallerFactory: () => {
return this.featureInstaller;
},
shouldAutoHandleDependencies: false,
...options,
});
return executer;
}
static selectOptionBasedOnLabel(label) {
const last = this.ui.getLastInvocation();
test_utils_1.assert.doesInclude(last.options.options.choices, {
label,
});
const match = last.options.options.choices.find((o) => o.label === label);
void this.ui.sendInput(`${match.value}`);
}
}
exports.default = AbstractCliTest;
//# sourceMappingURL=AbstractCliTest.js.map