UNPKG

@sprucelabs/spruce-cli

Version:

Command line interface for building Spruce skills.

362 lines • 13.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.optionsSchema = void 0; const path_1 = __importDefault(require("path")); const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils"); const open_1 = __importDefault(require("open")); const testOptions_schema_1 = __importDefault(require("./../../../.spruce/schemas/spruceCli/v2020_07_22/testOptions.schema")); const SpruceError_1 = __importDefault(require("../../../errors/SpruceError")); const AbstractAction_1 = __importDefault(require("../../AbstractAction")); const TestReporter_1 = __importDefault(require("../TestReporter")); const TestRunner_1 = __importDefault(require("../TestRunner")); exports.optionsSchema = testOptions_schema_1.default; class TestAction extends AbstractAction_1.default { optionsSchema = exports.optionsSchema; invocationMessage = 'Starting tests... 🛡'; testReporter; testRunner; runnerStatus = 'hold'; pattern; inspect; holdPromiseResolve; lastTestResults = { totalTestFiles: 0 }; originalInspect; watcher; watchMode = 'off'; isRpTraining = false; fileChangeTimeout; hasWatchEverBeenEnabled = false; watchDelaySec = 2; constructor(options) { super(options); } async execute(options) { if (!options.watchMode) { const settings = this.Service('settings'); options.watchMode = settings.get('test.watchMode') ?? 'off'; } const normalizedOptions = this.validateAndNormalizeOptions(options); const { pattern, shouldReportWhileRunning, inspect, shouldHoldAtStart, watchMode, shouldReturnImmediately, } = normalizedOptions; this.originalInspect = inspect ?? 5200; this.inspect = inspect; this.pattern = pattern; this.hasWatchEverBeenEnabled = watchMode !== 'off'; this.watchMode = watchMode; if (shouldReportWhileRunning) { this.testReporter = new TestReporter_1.default({ cwd: this.cwd, watchMode: this.watchMode, status: shouldHoldAtStart ? 'stopped' : 'ready', isDebugging: !!inspect, filterPattern: pattern ?? undefined, isRpTraining: this.isRpTraining, handleRestart: this.handleRestart.bind(this), handleStartStop: this.handleStartStop.bind(this), handleQuit: this.handleQuit.bind(this), handleRerunTestFile: this.handleRerunTestFile.bind(this), handleOpenTestFile: this.handleOpenTestFile.bind(this), handleFilterPatternChange: this.handleFilterPatternChange.bind(this), handleToggleDebug: this.handleToggleDebug.bind(this), handletoggleStandardWatch: this.handletoggleStandardWatch.bind(this), handleToggleSmartWatch: this.handleToggleSmartWatch?.bind(this), handleToggleRpTraining: this.handleToggleRpTraining?.bind(this), }); await this.testReporter.start(); } this.watcher = this.getFeature('watch'); void this.watcher.startWatching({ delay: 0 }); await this.emitter.on('watcher.did-detect-change', this.handleFileChange.bind(this)); this.runnerStatus = shouldHoldAtStart ? 'hold' : 'run'; const promise = this.startTestRunner(normalizedOptions); if (shouldReturnImmediately) { return { meta: { promise, test: this, }, }; } void this.emitter.emit('test.reporter-did-boot', { reporter: this, }); const testResults = await promise; await this.watcher?.stopWatching(); await this.testReporter?.destroy(); const actionResponse = { meta: { testResults }, summaryLines: [ `Test files: ${testResults.totalTestFiles}`, `Tests: ${testResults.totalTests ?? '0'}`, `Passed: ${testResults.totalPassed ?? '0'}`, `Failed: ${testResults.totalFailed ?? '0'}`, `Skipped: ${testResults.totalSkipped ?? '0'}`, `Todo: ${testResults.totalTodo ?? '0'}`, ], }; if (testResults.totalFailed ?? 0 > 0) { actionResponse.errors = this.generateErrorsFromTestResults(testResults); } return actionResponse; } handleFileChange(payload) { if (this.watchMode === 'off' || !(this.runnerStatus === 'run' || this.runnerStatus == 'hold')) { return; } const { changes } = payload; let shouldRestart = false; const filesWeCareAbout = []; for (const change of changes) { const { path, name } = change.values; if (this.doWeCareAboutThisFileChanging(path)) { this.testReporter?.setStatusLabel(`Built file: ${name}`); shouldRestart = true; filesWeCareAbout.push(path); break; } } if (shouldRestart) { if (this.fileChangeTimeout) { clearTimeout(this.fileChangeTimeout); } this.testReporter?.startCountdownTimer(this.watchDelaySec); this.fileChangeTimeout = setTimeout(() => { if (this.watchMode === 'smart') { const smartFilter = this.generateFilterFromChangedFiles(filesWeCareAbout); if (smartFilter.length > 0) { this.handleFilterPatternChange(smartFilter); } else { this.restart(); } } else { this.restart(); } }, this.watchDelaySec * 1000); } } generateFilterFromChangedFiles(filesWeCareAbout) { const filter = filesWeCareAbout .filter((file) => file.search('test.js') > -1) .map((file) => this.fileToFilterPattern(file)) .join(' '); return filter; } doWeCareAboutThisFileChanging(path) { const ext = path_1.default.extname(path); if (path.search('testDirsAndFiles') > -1 || path.search('.change_cache') > -1) { return false; } if (ext === '.js') { return true; } return false; } handleToggleDebug() { if (this.inspect) { this.inspect = undefined; } else { this.inspect = this.originalInspect; } this.testReporter?.setIsDebugging(!!this.inspect); this.restart(); } handletoggleStandardWatch() { if (this.watchMode === 'standard') { this.testReporter?.setWatchMode('off'); } else { this.setWatchMode('standard'); } } handleToggleSmartWatch() { if (this.watchMode === 'smart') { this.setWatchMode('off'); } else { this.setWatchMode('smart'); } } async handleToggleRpTraining() { // this.isRpTraining = !this.isRpTraining // this.testReporter?.setIsRpTraining(this.isRpTraining) // if (this.isRpTraining) { await this.testReporter?.askForTrainingToken(); // } } setWatchMode(mode) { this.watchMode = mode; this.testReporter?.setWatchMode(mode); this.hasWatchEverBeenEnabled = true; } restart() { this.runnerStatus = 'restart'; this.kill(); } handleQuit() { this.runnerStatus = 'quit'; this.kill(); } handleRerunTestFile(file) { const name = this.fileToFilterPattern(file); this.testReporter?.setFilterPattern(name); this.handleFilterPatternChange(name); } fileToFilterPattern(file) { const filename = path_1.default .basename(file, '.ts') .replace('.tsx', '') .replace('.js', ''); const dirname = path_1.default.dirname(file).split(path_1.default.sep).pop() ?? ''; const name = path_1.default.join(dirname, filename); return name; } handleFilterPatternChange(filterPattern) { this.pattern = filterPattern; this.testReporter?.setFilterPattern(filterPattern); this.restart(); } handleStartStop() { if (this.runnerStatus === 'hold') { this.runnerStatus = 'run'; this.holdPromiseResolve?.(); this.holdPromiseResolve = undefined; } else if (this.runnerStatus === 'run') { this.runnerStatus = 'hold'; this.kill(); } } handleRestart() { this.restart(); } kill() { this.testRunner?.kill(); this.holdPromiseResolve?.(); this.holdPromiseResolve = undefined; } async handleOpenTestFile(fileName) { await this.openTestFile(fileName); } async startTestRunner(options) { if (this.runnerStatus === 'hold') { await this.waitForStart(); } if (this.runnerStatus === 'quit') { return this.lastTestResults; } this.testReporter?.setStatus('ready'); this.testReporter?.stopCountdownTimer(); this.testRunner = new TestRunner_1.default({ cwd: this.cwd, commandService: this.Service('command'), }); let firstUpdate = true; if (this.testReporter) { await this.testRunner.on('did-update', (payload) => { if (firstUpdate) { firstUpdate = false; this.testReporter?.setStatus('running'); this.testReporter?.reset(); } if (this.watchMode === 'smart' && payload.results.totalFailed > 0) { const failed = payload.results.testFiles.find((file) => file.status === 'failed'); if (failed) { const pattern = this.fileToFilterPattern(failed.path); if (this.pattern !== pattern) { this.handleFilterPatternChange(pattern); } return; } } this.testReporter?.updateResults(payload.results); this.testReporter?.render(); }); await this.testRunner.on('did-error', (payload) => { this.testReporter?.appendError(payload.message); this.testReporter?.render(); }); } this.runnerStatus = 'run'; let testResults = await this.testRunner.run({ pattern: this.pattern, debugPort: this.inspect, }); if ( //@ts-ignore this.runnerStatus !== 'restart' && (!options.shouldReportWhileRunning || !this.hasWatchEverBeenEnabled || this.runnerStatus === 'quit')) { return testResults; } if (this.runnerStatus === 'run' && this.watchMode === 'smart' && this.testRunner?.hasFailedTests() === false && !this.testRunner?.hasSkippedTests() && (this.pattern ?? []).length > 0) { this.testReporter?.setStatusLabel('Restarting...'); this.runnerStatus = 'restart'; this.testReporter?.startCountdownTimer(3); return await new Promise((resolve) => { setTimeout(() => { this.pattern = ''; this.testReporter?.setFilterPattern(''); resolve(this.startTestRunner(options)); }, 3000); }); } if (this.runnerStatus === 'run') { this.runnerStatus = 'hold'; } this.testReporter?.setStatus('stopped'); this.lastTestResults = testResults; return this.startTestRunner(options); } async waitForStart() { await new Promise((resolve) => { this.runnerStatus = 'hold'; this.holdPromiseResolve = resolve; }); } async openTestFile(fileName) { const path = spruce_skill_utils_1.diskUtil.resolvePath(this.cwd, 'src', '__tests__', fileName); await (0, open_1.default)(path); } generateErrorsFromTestResults(testResults) { const errors = []; testResults.testFiles?.forEach((file) => { file.tests?.forEach((test) => { test.errorMessages?.forEach((message) => { const err = this.mapErrorResultToSpruceError(test, file, message); errors.push(err); }); }); }); if (errors.length > 0) { return errors; } return undefined; } mapErrorResultToSpruceError(test, file, message) { return new SpruceError_1.default({ code: 'TEST_FAILED', testName: test.name, fileName: file.path, errorMessage: message, }); } getWatchMode() { return this.watchMode; } } exports.default = TestAction; //# sourceMappingURL=TestAction.js.map