UNPKG

@sasjs/cli

Version:

Command line interface for SASjs

448 lines (447 loc) 30.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTestUrl = exports.runTest = void 0; var os_1 = __importDefault(require("os")); var saveOutput_1 = require("./internal/saveOutput"); var types_1 = require("../../types"); var file_1 = require("../../utils/file"); var config_1 = require("../../utils/config"); var displayResult_1 = require("../../utils/displayResult"); var utils_1 = require("@sasjs/utils"); var path_1 = __importDefault(require("path")); var chalk_1 = __importDefault(require("chalk")); var utils_2 = require("../../utils/utils"); var createSASjsInstance_1 = require("../../utils/createSASjsInstance"); // interface function runTest(target, testRegExps, outDirectory, flowSourcePath, force) { var _a, _b, _c; if (testRegExps === void 0) { testRegExps = []; } return __awaiter(this, void 0, void 0, function () { var testFlowData, testFlow, flow, sasjs, authConfig, username, password, _d, sas9CredentialsError, e_1, result, isCodeExamplePrinted, printCodeExample, resultFileName, jsonPath, xmlPath, csvPath, coverageReportPath, csvData, resultTable, testSuites, testsCount, passedTestsCount, testsWithResultsCount, resultProvidedTests, passedTests, testsWithResultsPercentage, passingTestsPercentage, errorMessage, failedTestsCount, testsWithoutResultCount; var _this = this; return __generator(this, function (_e) { switch (_e.label) { case 0: if (outDirectory) outDirectory = path_1.default.join(process.currentDir, outDirectory); else outDirectory = process.sasjsConstants.buildDestinationResultsFolder; if (!target) { return [2 /*return*/, Promise.reject('Target not found! Please try again with another target.')]; } flowSourcePath = flowSourcePath ? path_1.default.join(process.currentDir, flowSourcePath) : path_1.default.join(process.sasjsConstants.buildDestinationFolder, 'testFlow.json'); return [4 /*yield*/, (0, utils_1.readFile)(flowSourcePath).catch(function (_) { return Promise.reject("Test flow file was not found at ".concat(flowSourcePath)); })]; case 1: testFlowData = _e.sent(); if (!testFlowData) return [2 /*return*/]; testFlow = { tests: [] }; try { testFlow = JSON.parse(testFlowData); } catch (error) { return [2 /*return*/, Promise.reject("Provided test flow file is not valid.")]; } flow = []; if (testFlow.testSetUp) flow.push(testFlow.testSetUp); if (testFlow.tests) { flow = __spreadArray(__spreadArray([], __read(flow), false), __read(testFlow.tests.filter(function (test) { var e_2, _a; if (!testRegExps.length) return true; var match = false; try { for (var testRegExps_1 = __values(testRegExps), testRegExps_1_1 = testRegExps_1.next(); !testRegExps_1_1.done; testRegExps_1_1 = testRegExps_1.next()) { var pattern = testRegExps_1_1.value; if (new RegExp(pattern).test(test)) match = true; } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (testRegExps_1_1 && !testRegExps_1_1.done && (_a = testRegExps_1.return)) _a.call(testRegExps_1); } finally { if (e_2) throw e_2.error; } } return match; })), false); } if (testFlow.testTearDown) flow.push(testFlow.testTearDown); sasjs = (0, createSASjsInstance_1.createSASjsInstance)({ serverUrl: target.serverUrl, httpsAgentOptions: target.httpsAgentOptions, appLoc: target.appLoc, serverType: target.serverType, debug: true, contextName: target.contextName, useComputeApi: false }); _d = target.serverType; switch (_d) { case utils_1.ServerType.SasViya: return [3 /*break*/, 2]; case utils_1.ServerType.Sas9: return [3 /*break*/, 4]; case utils_1.ServerType.Sasjs: return [3 /*break*/, 5]; } return [3 /*break*/, 11]; case 2: return [4 /*yield*/, (0, config_1.getAuthConfig)(target)]; case 3: authConfig = _e.sent(); return [3 /*break*/, 11]; case 4: if (target.authConfigSas9) { username = target.authConfigSas9.userName; password = target.authConfigSas9.password; } else { username = process.env.SAS_USERNAME; password = process.env.SAS_PASSWORD; } if (!username || !password) { sas9CredentialsError = process.sasjsConstants.sas9CredentialsError; throw new Error(sas9CredentialsError); } password = (0, utils_1.decodeFromBase64)(password); return [3 /*break*/, 11]; case 5: _e.trys.push([5, 9, , 10]); return [4 /*yield*/, (0, utils_2.isSasJsServerInServerMode)(target)]; case 6: if (!_e.sent()) return [3 /*break*/, 8]; return [4 /*yield*/, (0, config_1.getAuthConfig)(target)]; case 7: authConfig = _e.sent(); _e.label = 8; case 8: return [3 /*break*/, 10]; case 9: e_1 = _e.sent(); return [3 /*break*/, 10]; case 10: // FIXME: handle error properly return [3 /*break*/, 11]; case 11: result = { sasjs_test_meta: [] }; isCodeExamplePrinted = false; printCodeExample = function () { var _a; if (!isCodeExamplePrinted) { (_a = process.logger) === null || _a === void 0 ? void 0 : _a.info("Code example to provide output:\n data work.test_results;\n test_description=\"some description\";\n test_result=\"PASS\";\n output;\n run;\n %webout(OPEN)\n %webout(OBJ, test_results)\n %webout(CLOSE)\n"); isCodeExamplePrinted = true; } }; return [4 /*yield*/, (0, utils_1.asyncForEach)(flow, function (test) { return __awaiter(_this, void 0, void 0, function () { var pathSepRegExp, sasJobLocation, testTarget, testId, testUrl, printTestUrl; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: pathSepRegExp = new RegExp(path_1.default.sep.replace(/\\/g, '\\\\'), 'g'); sasJobLocation = [ target.appLoc, test.replace(pathSepRegExp, '/').replace(file_1.sasFileRegExp, '') ].join('/'); testTarget = test .split('/') .pop() .replace(/(.test)?(.\d+)?(.sas)?$/i, ''); testId = (0, utils_1.uuidv4)(); testUrl = (0, exports.getTestUrl)(target, sasJobLocation); if (target.contextName) { testUrl = "".concat(testUrl, "&_contextName=").concat(encodeURIComponent(target.contextName)); } printTestUrl = function () { return process.logger.info("Test URL: ".concat(testUrl)); }; printTestUrl(); return [4 /*yield*/, sasjs .request(sasJobLocation, {}, { username: username, password: password }, function () { (0, displayResult_1.displayError)(null, 'Login callback called. Request failed.'); }, authConfig, ['log']) .then(function (res) { return __awaiter(_this, void 0, void 0, function () { var lineBreak, test_results, existingTestTarget, existingTestTarget; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: lineBreak = true; test_results = (_a = res.result) === null || _a === void 0 ? void 0 : _a.test_results; if (!test_results) { (0, displayResult_1.displayError)({}, "Job did not return a response, to debug click ".concat(testUrl)); printCodeExample(); lineBreak = false; existingTestTarget = result.sasjs_test_meta.find(function (testResult) { return testResult.test_target === testTarget; }); if (existingTestTarget) { existingTestTarget.results.push({ test_loc: test, sasjs_test_id: testId, result: types_1.TestResultStatus.notProvided, test_url: testUrl }); } else { result.sasjs_test_meta.push({ test_target: testTarget, results: [ { test_loc: test, sasjs_test_id: testId, result: types_1.TestResultStatus.notProvided, test_url: testUrl } ] }); } } if (!res.log) return [3 /*break*/, 2]; return [4 /*yield*/, (0, saveOutput_1.saveLog)(outDirectory, test, res.log, lineBreak)]; case 1: _b.sent(); _b.label = 2; case 2: if (test_results) { existingTestTarget = result.sasjs_test_meta.find(function (testResult) { return testResult.test_target === testTarget; }); if (existingTestTarget) { existingTestTarget.results.push({ test_loc: test, sasjs_test_id: testId, result: test_results, test_url: testUrl }); } else { result.sasjs_test_meta.push({ test_target: testTarget, results: [ { test_loc: test, sasjs_test_id: testId, result: test_results, test_url: testUrl } ] }); } } else if (target.serverType === utils_1.ServerType.SasViya || target.serverType === utils_1.ServerType.Sas9) { (0, displayResult_1.displayError)({}, "'test_results' not found in server response, to debug click ".concat(testUrl)); printCodeExample(); } return [2 /*return*/]; } }); }); }) .catch(function (err) { return __awaiter(_this, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: printTestUrl(); if ((err === null || err === void 0 ? void 0 : err.errorCode) === 404) { (0, utils_2.displaySasjsRunnerError)(username); } else { (0, displayResult_1.displayError)({}, "An error occurred while executing the request. Job location: ".concat(sasJobLocation)); } if (!((_b = (_a = err === null || err === void 0 ? void 0 : err.error) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.result)) return [3 /*break*/, 2]; return [4 /*yield*/, (0, saveOutput_1.saveLog)(outDirectory, test, err.error.details.result)]; case 1: _c.sent(); _c.label = 2; case 2: return [2 /*return*/]; } }); }); })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); })]; case 12: _e.sent(); resultFileName = 'testResults'; jsonPath = path_1.default.join(outDirectory, "".concat(resultFileName, ".json")); xmlPath = path_1.default.join(outDirectory, "".concat(resultFileName, ".xml")); csvPath = path_1.default.join(outDirectory, "".concat(resultFileName, ".csv")); coverageReportPath = path_1.default.join(outDirectory, 'coverage.lcov'); result.local_date_time = new Date().toISOString(); result.local_user_id = os_1.default.userInfo().username; result.local_machine_name = os_1.default.hostname(); result.target_name = target.name; result.target_server_type = target.serverType; result.target_server_url = target.serverUrl; result.csv_result_path = csvPath; result.xml_result_path = xmlPath; result.coverage_report_path = coverageReportPath; return [4 /*yield*/, (0, saveOutput_1.saveResultXml)(xmlPath, result)]; case 13: _e.sent(); return [4 /*yield*/, (0, saveOutput_1.saveResultCsv)(csvPath, result)]; case 14: csvData = _e.sent(); return [4 /*yield*/, (0, saveOutput_1.saveCoverageLcov)(coverageReportPath, flow)]; case 15: _e.sent(); resultTable = {}; if (Array.isArray(csvData)) { testSuites = csvData.reduce(function (acc, item) { if (!acc.filter(function (i) { return i.sasjs_test_id === item.sasjs_test_id; }).length) acc.push(item); return acc; }, []); testSuites.forEach(function (test) { var passedTests = csvData.filter(function (item) { return item.sasjs_test_id === test.sasjs_test_id && item.test_suite_result === types_1.TestResultStatus.pass; }); var failedTests = csvData.filter(function (item) { return item.sasjs_test_id === test.sasjs_test_id && item.test_suite_result === types_1.TestResultStatus.fail; }); resultTable[test.sasjs_test_id] = { test_target: test.test_target, test_suite_result: passedTests.length && !failedTests.length ? types_1.TestResultStatus.pass : types_1.TestResultStatus.fail }; }); } (_a = process.logger) === null || _a === void 0 ? void 0 : _a.table(Object.keys(resultTable).map(function (key) { return [ key, resultTable[key].test_target, resultTable[key].test_suite_result === types_1.TestResultStatus.pass ? chalk_1.default.greenBright(types_1.TestResultStatus.pass) : resultTable[key].test_suite_result === types_1.TestResultStatus.fail ? chalk_1.default.redBright(types_1.TestResultStatus.fail) : resultTable[key].test_suite_result ]; }), { head: ['SASjs Test ID', 'Test Target', 'Test Suite Result'] }); testsCount = flow.length; passedTestsCount = Object.values(resultTable).filter(function (res) { return res.test_suite_result === types_1.TestResultStatus.pass; }).length; testsWithResultsCount = Object.values(resultTable).filter(function (res) { return res.test_suite_result !== types_1.TestResultStatus.notProvided; }).length; resultProvidedTests = testsWithResultsCount + '/' + testsCount; passedTests = passedTestsCount + '/' + testsWithResultsCount; testsWithResultsPercentage = Math.round((testsWithResultsCount / testsCount) * 100 || 0) + '%'; passingTestsPercentage = Math.round((passedTestsCount / testsWithResultsCount) * 100 || 0) + '%'; result.tests_with_results = "".concat(resultProvidedTests, " (").concat(testsWithResultsPercentage, ")"); result.tests_that_pass = "".concat(passedTests, " (").concat(passingTestsPercentage, ")"); (_b = process.logger) === null || _b === void 0 ? void 0 : _b.info("Tests provided results: ".concat(resultProvidedTests, " (").concat(chalk_1.default.greenBright(testsWithResultsPercentage), ")")); (_c = process.logger) === null || _c === void 0 ? void 0 : _c.info("\n Tests that pass: ".concat(passedTests, " (").concat(chalk_1.default.greenBright(passingTestsPercentage), ")\n ")); errorMessage = ''; if (!force) { failedTestsCount = testsWithResultsCount - passedTestsCount; testsWithoutResultCount = testsCount - testsWithResultsCount; result.failed_to_complete = failedTestsCount; result.completed_with_failures = testsWithoutResultCount; if (testsWithoutResultCount > 0) errorMessage = "".concat(testsWithoutResultCount, " ").concat(testsWithoutResultCount === 1 ? 'test' : 'tests', " failed to complete!\n"); if (failedTestsCount > 0) errorMessage += "".concat(failedTestsCount, " ").concat(failedTestsCount === 1 ? 'test' : 'tests', " completed with failures!"); } return [4 /*yield*/, (0, saveOutput_1.saveResultJson)(jsonPath, result)]; case 16: _e.sent(); (0, displayResult_1.displaySuccess)("Tests execution finished. The results are stored at:\n ".concat(jsonPath, "\n ").concat(csvPath, "\n ").concat(xmlPath)); (0, displayResult_1.displaySuccess)("Tests coverage report:\n ".concat(coverageReportPath)); if (errorMessage) return [2 /*return*/, Promise.reject(errorMessage)]; return [2 /*return*/]; } }); }); } exports.runTest = runTest; var getTestUrl = function (target, jobLocation) { return "".concat(target.serverUrl, "/").concat(target.serverType === utils_1.ServerType.SasViya ? 'SASJobExecution' : target.serverType === utils_1.ServerType.Sas9 ? 'SASStoredProcess' : 'SASjsApi/stp/execute', "/?_program=").concat(jobLocation, "&_debug=2477"); }; exports.getTestUrl = getTestUrl;