loadmill
Version:
A node.js module for running load tests and functional tests on loadmill.com
233 lines (232 loc) • 14.1 kB
JavaScript
"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var Loadmill = require("./index");
var program = require("commander");
var utils_1 = require("./utils");
var reporter_1 = require("./reporter");
program
.usage("<testPlanId | load-config-file> -t <token> [options] [parameter=value...]")
.description("Run a test plan (default option) or a load test on loadmill.com.\n " +
"You may set parameter values by passing space-separated 'name=value' pairs, e.g. 'host=www.myapp.com port=80' or supply a file using --parameters-file.\n\n " +
"Learn more at https://www.npmjs.com/package/loadmill#cli")
.option("-t, --token <token>", "Loadmill API Token. You must provide a token in order to run tests.")
.option("-l, --load-test", "Launch a load test.")
.option("--test-plan", "Launch a test plan (default option).")
.option("-p, --parallel <parallel>", "Set the concurrency of a running test suites in a test plan")
.option("--additional-description <description>", "Add an additional description at the end of the current suite's description - available only for test suites.")
.option("--labels <labels>", "Run flows that are assigned to a specific label (when running a test suite).. Multiple labels can be provided by seperated them with ',' (e.g. 'label1,label2').")
.option("--labels-expression <labelsExpression>", "Run a test plan's suites with flows that match the labels expression. An expression may contain the characters ( ) & | ! (e.g. '(label1 | label2) & !label3')")
.option("--pool <pool>", "Execute tests from a dedicated agent's pool (when using private agent)")
.option("--tags <tags>", "Tag a test plan run with a comma separated list of tags (e.g. 'tag1,tag2')")
.option("-w, --wait", "Wait for the test to finish.")
.option("-n, --no-bail", "Return exit code 0 even if test fails.")
.option("-q, --quiet", "Do not print out anything (except errors).")
.option("-v, --verbose", "Print out extra information for debugging.")
.option("-r, --report", "Print out Test Suite Flow Runs report when the plan has ended.")
.option("--errors-report", "Print out Test Suite Flow Runs errors report when the plan has ended.")
.option("-j, --junit-report", "Create Test Suite (junit style) report when the suite has ended.")
.option("--junit-report-path <junitReportPath>", "Save junit styled report to a path (defaults to current location).")
.option("-m, --mochawesome-report", "Create Test Suite (mochawesome style) report when the suite has ended.")
.option("--mochawesome-report-path <mochawesomeReportPath>", "Save JSON mochawesome styled report to a path (defaults to current location).")
.option("--colors", "Print test results in color")
.option("-b, --branch <branch>", "Run the test plan's suites from a GitHub branch. The latest version of the selected Git branch will be used as the test configuration for the chosen Test Plan")
.option("--retry-failed-flows <numberOfRetries>", "Configure the test plan to re-run failed flows in case your tested system is unstable. Tests that pass after a retry will be considered successful.")
.option("--parameters-file <parametersFile>", "Supply a file with parameters to override. File format should be 'name=value' divided by new line.")
.option("--inlineParameterOverride", "Override parameters strategy: by default, overrided parameters are appended to the end of the parameters list. Using this flag will replace the parameters inline.")
.option("--apiCatalogService <apiCatalogService>", "Use the provided service when mapping the APIs in the catalog. Service will be created if not exist")
.option("--turbo-parallel", "Run the test plan in turbo mode")
.parse(process.argv);
start()["catch"](function (err) {
console.error(err);
process.exit(2);
});
function start() {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var wait, bail, quiet, token, verbose, colors, report, errorsReport, junitReport, junitReportPath, mochawesomeReport, mochawesomeReportPath, parallel, loadTest, testPlan, additionalDescription, labels, labelsExpression, pool, tags, branch, retryFailedFlows, parametersFile, inlineParameterOverride, apiCatalogService, turboParallel, _a, input, rawParams, logger, parameters, loadmill, testFailed, testStopped, res, planLabels, planTags, running, e_1, extInfo, configFile, res_1, id;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
wait = program.wait, bail = program.bail, quiet = program.quiet, token = program.token, verbose = program.verbose, colors = program.colors, report = program.report, errorsReport = program.errorsReport, junitReport = program.junitReport, junitReportPath = program.junitReportPath, mochawesomeReport = program.mochawesomeReport, mochawesomeReportPath = program.mochawesomeReportPath, parallel = program.parallel, loadTest = program.loadTest, testPlan = program.testPlan, additionalDescription = program.additionalDescription, labels = program.labels, labelsExpression = program.labelsExpression, pool = program.pool, tags = program.tags, branch = program.branch, retryFailedFlows = program.retryFailedFlows, parametersFile = program.parametersFile, inlineParameterOverride = program.inlineParameterOverride, apiCatalogService = program.apiCatalogService, turboParallel = program.turboParallel, _a = program.args, input = _a[0], rawParams = _a.slice(1);
logger = utils_1.getLogger({ verbose: verbose, colors: colors });
if (!token) {
validationFailed("No API token provided.");
}
parameters = toParams(rawParams, parametersFile);
if (verbose) {
// verbose trumps quiet:
quiet = false;
logger.log("Inputs:", {
wait: wait,
bail: bail,
quiet: quiet,
token: token,
verbose: verbose,
colors: colors,
report: report,
errorsReport: errorsReport,
junitReport: junitReport,
junitReportPath: junitReportPath,
mochawesomeReport: mochawesomeReport,
mochawesomeReportPath: mochawesomeReportPath,
parallel: parallel,
input: input,
loadTest: loadTest,
testPlan: testPlan,
additionalDescription: additionalDescription,
labels: labels,
labelsExpression: labelsExpression,
pool: pool,
tags: tags,
branch: branch,
retryFailedFlows: retryFailedFlows,
inlineParameterOverride: inlineParameterOverride,
apiCatalogService: apiCatalogService,
turboParallel: turboParallel,
parameters: parameters
});
}
loadmill = Loadmill({ token: token });
testFailed = function (msg) {
logger.log("");
logger.error("\u274C " + msg + ".");
if (bail) {
process.exit(1);
}
};
testStopped = function (msg) {
logger.log("");
logger.error("\u270B " + msg + ".");
if (bail) {
process.exit(1);
}
};
if (!(testPlan || !loadTest)) return [3 /*break*/, 13];
if (!utils_1.isUUID(input)) { //if test plan flag is on then the input should be uuid
validationFailed("Test plan run flag is on but no valid test plan id was provided.");
}
planLabels = utils_1.convertStrToArr(labels);
planTags = utils_1.convertStrToArr(tags);
_b.label = 1;
case 1:
_b.trys.push([1, 11, , 12]);
logger.verbose("Executing test plan with id " + input);
return [4 /*yield*/, loadmill.runTestPlan({
id: input,
options: {
additionalDescription: additionalDescription,
labels: planLabels,
labelsExpression: labelsExpression,
pool: pool,
tags: planTags,
parallel: parallel,
branch: branch,
maxFlakyFlowRetries: retryFailedFlows,
inlineParameterOverride: inlineParameterOverride,
apiCatalogService: apiCatalogService,
turboParallel: turboParallel
}
}, parameters)];
case 2:
running = _b.sent();
if (!(running && running.id)) return [3 /*break*/, 9];
if (!wait) return [3 /*break*/, 8];
logger.verbose("Waiting for test plan run with id", running.id);
return [4 /*yield*/, loadmill.wait(running)];
case 3:
res = _b.sent();
if (!quiet) {
logger.log(res ? utils_1.getObjectAsString(res, colors) : running.id);
}
if (report && res.testSuitesRuns) {
utils_1.printTestSuitesRunsReport(res.description, res.testSuitesRuns, logger, colors);
}
if (errorsReport && res.testSuitesRuns) {
utils_1.printOnlyFailedFlowRunsReport(res.testSuitesRuns, logger, colors);
}
if (!res) return [3 /*break*/, 7];
if (!junitReport) return [3 /*break*/, 5];
return [4 /*yield*/, reporter_1.junitReport(res, token, junitReportPath)];
case 4:
_b.sent();
_b.label = 5;
case 5:
if (!mochawesomeReport) return [3 /*break*/, 7];
return [4 /*yield*/, reporter_1.mochawesomeReport(res, token, mochawesomeReportPath)];
case 6:
_b.sent();
_b.label = 7;
case 7:
if (res && res.status === 'STOPPED') {
testStopped("Test plan with id " + (res.id || input) + " has stopped");
}
if (res && res.passed != null && !res.passed) {
testFailed("Test plan with id " + (res.id || input) + " has failed");
}
_b.label = 8;
case 8: return [3 /*break*/, 10];
case 9:
testFailed("Couldn't run test plan with id " + input);
_b.label = 10;
case 10: return [3 /*break*/, 12];
case 11:
e_1 = _b.sent();
if (verbose) {
logger.error(e_1);
}
extInfo = e_1.response && e_1.response.res && e_1.response.res.text;
testFailed("Couldn't run test plan with id " + input + " " + (extInfo ? extInfo : ''));
return [3 /*break*/, 12];
case 12: return [3 /*break*/, 17];
case 13:
configFile = input;
if (!configFile) {
validationFailed("No configuration file were provided.");
}
logger.verbose("Launching " + configFile + " as load test");
return [4 /*yield*/, loadmill.run(configFile, parameters)];
case 14:
id = _b.sent();
if (!(wait && loadTest)) return [3 /*break*/, 16];
logger.verbose("Waiting for test:", res_1 ? res_1.id : id);
return [4 /*yield*/, loadmill.wait(res_1 || id)];
case 15:
res_1 = _b.sent();
_b.label = 16;
case 16:
if (!quiet) {
logger.log(JSON.stringify(res_1, null, 4) || id);
}
if (res_1 && res_1.passed != null && !res_1.passed) {
logger.error("\u274C Test " + configFile + " failed.");
if (bail) {
process.exit(1);
}
}
_b.label = 17;
case 17: return [2 /*return*/];
}
});
});
}
function validationFailed() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
console.log('');
console.error.apply(console, args);
console.log('');
program.outputHelp();
process.exit(3);
}
function toParams(rawParams, filePath) {
try {
var paramsArray = filePath ? tslib_1.__spreadArray(tslib_1.__spreadArray([], utils_1.readRawParams(filePath)), rawParams) : rawParams;
return utils_1.toLoadmillParams(paramsArray);
}
catch (err) {
validationFailed(err.message);
return {};
}
}