@sasjs/cli
Version:
Command line interface for SASjs
448 lines (447 loc) • 30.2 kB
JavaScript
;
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;