Comparing version 3.2.5 to 4.0.0
@@ -5,5 +5,3 @@ import './polyfills'; | ||
run(config: Loadmill.Configuration, paramsOrCallback?: Loadmill.ParamsOrCallback, callback?: Loadmill.Callback): Promise<string>; | ||
runFolder(folderPath: string, paramsOrCallback?: Loadmill.ParamsOrCallback, callback?: Loadmill.Callback): Promise<Array<Loadmill.TestResult>>; | ||
wait(testDefOrId: string | Loadmill.TestDef, callback?: Loadmill.Callback): Promise<Loadmill.TestResult>; | ||
runTestSuite(suite: Loadmill.TestSuiteDef, paramsOrCallback?: Loadmill.ParamsOrCallback, callback?: Loadmill.Callback): Promise<Loadmill.TestDef>; | ||
runTestPlan(testPlan: Loadmill.TestPlanDef, params: Loadmill.Params): Promise<Loadmill.TestDef | undefined>; | ||
@@ -10,0 +8,0 @@ junitReport(testResult: Loadmill.TestResult, path?: string | undefined): Promise<void>; |
125
lib/index.js
@@ -8,44 +8,10 @@ "use strict"; | ||
var reporter_1 = require("./reporter"); | ||
var TEST_PLAN_POLL_INTERVAL_IN_MS = 10 * 1000; // 10 seconds | ||
function Loadmill(options) { | ||
var _a = options, token = _a.token, _b = _a._testingServerHost, _testingServerHost = _b === void 0 ? utils_1.TESTING_HOST : _b; | ||
var testingServer = "https://" + _testingServerHost; | ||
var testSuitesAPI = testingServer + "/api/test-suites"; | ||
var testPlansAPI = testingServer + "/api/test-plans"; | ||
function _runFolderSync(listOfFiles, execFunc) { | ||
var funcArgs = []; | ||
for (var _i = 2; _i < arguments.length; _i++) { | ||
funcArgs[_i - 2] = arguments[_i]; | ||
} | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var results, _a, listOfFiles_1, file, res, testResult; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
results = []; | ||
_a = 0, listOfFiles_1 = listOfFiles; | ||
_b.label = 1; | ||
case 1: | ||
if (!(_a < listOfFiles_1.length)) return [3 /*break*/, 5]; | ||
file = listOfFiles_1[_a]; | ||
return [4 /*yield*/, execFunc.apply(void 0, tslib_1.__spreadArray([file], funcArgs))]; | ||
case 2: | ||
res = _b.sent(); | ||
return [4 /*yield*/, _wait(res)]; | ||
case 3: | ||
testResult = _b.sent(); | ||
results.push(testResult); | ||
if (!testResult.passed) | ||
return [3 /*break*/, 5]; | ||
_b.label = 4; | ||
case 4: | ||
_a++; | ||
return [3 /*break*/, 1]; | ||
case 5: return [2 /*return*/, results]; | ||
} | ||
}); | ||
}); | ||
} | ||
function _wait(testDefOrId, callback) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var resolve, reject, testDef, apiUrl, webUrl, intervalId; | ||
var resolve, reject, testDef, apiUrl, webUrl, retries, intervalId; | ||
var _this = this; | ||
@@ -59,2 +25,3 @@ return tslib_1.__generator(this, function (_a) { | ||
webUrl = getTestWebUrl(testDef, testingServer); | ||
retries = 1; | ||
intervalId = setInterval(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { | ||
@@ -79,9 +46,4 @@ var body, bodyWithFlows, testResult, err_1; | ||
case 3: | ||
testResult = tslib_1.__assign(tslib_1.__assign({}, testDef), { url: webUrl, description: body && body.description, passed: isTestPassed(body, testDef.type), startTime: body.startTime, endTime: body.endTime, status: body.status }); | ||
if (testDef.type === Loadmill.TYPES.SUITE) { | ||
testResult.flowRuns = reductFlowRunsData(body.testSuiteFlowRuns); | ||
} | ||
else if (testDef.type === Loadmill.TYPES.TEST_PLAN) { | ||
testResult.testSuitesRuns = reductTestSuitesRuns(body.testSuitesRuns, testingServer); | ||
} | ||
testResult = toTestResult(testDef, webUrl, body); | ||
redactData(testResult, body, testingServer); | ||
if (callback) { | ||
@@ -97,3 +59,9 @@ callback(null, testResult); | ||
err_1 = _a.sent(); | ||
clearInterval(intervalId); | ||
if (retries < 3) { | ||
retries++; | ||
return [2 /*return*/]; | ||
} | ||
else { | ||
clearInterval(intervalId); | ||
} | ||
if (callback) { | ||
@@ -109,3 +77,3 @@ callback(err_1, null); | ||
}); | ||
}); }, 10 * 1000); | ||
}); }, TEST_PLAN_POLL_INTERVAL_IN_MS); | ||
return [2 /*return*/, callback ? null : new Promise(function (_resolve, _reject) { | ||
@@ -118,35 +86,2 @@ resolve = _resolve; | ||
} | ||
function _runTestSuite(suite, paramsOrCallback, callback) { | ||
var _a; | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var params, overrideParameters, suiteId, additionalDescription, labels, failGracefully, pool; | ||
var _this = this; | ||
return tslib_1.__generator(this, function (_b) { | ||
params = paramsOrCallback && typeof paramsOrCallback !== 'function' ? paramsOrCallback : {}; | ||
overrideParameters = toParams(params, (_a = suite.options) === null || _a === void 0 ? void 0 : _a.parametersFile); | ||
suiteId = suite.id; | ||
additionalDescription = suite.options && suite.options.additionalDescription; | ||
labels = suite.options && suite.options.labels && utils_1.filterLabels(suite.options.labels); | ||
failGracefully = suite.options && suite.options.failGracefully; | ||
pool = suite.options && suite.options.pool; | ||
return [2 /*return*/, wrap(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { | ||
var _a, testSuiteRunId, err; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, superagent.post(testSuitesAPI + "/" + suiteId + "/run" + (failGracefully ? '?failGracefully=true' : '')) | ||
.send({ overrideParameters: overrideParameters, additionalDescription: additionalDescription, labels: labels, pool: pool }) | ||
.auth(token, '')]; | ||
case 1: | ||
_a = (_b.sent()).body, testSuiteRunId = _a.testSuiteRunId, err = _a.err; | ||
if (err || !testSuiteRunId) { | ||
console.error(err ? JSON.stringify(err) : "The server encountered an error while handling the request"); | ||
return [2 /*return*/]; | ||
} | ||
return [2 /*return*/, { id: testSuiteRunId, type: Loadmill.TYPES.SUITE }]; | ||
} | ||
}); | ||
}); }, callback || paramsOrCallback)]; | ||
}); | ||
}); | ||
} | ||
function _runTestPlan(testPlan, params) { | ||
@@ -219,24 +154,5 @@ var _a; | ||
}, | ||
runFolder: function (folderPath, paramsOrCallback, callback) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var listOfFiles; | ||
return tslib_1.__generator(this, function (_a) { | ||
listOfFiles = utils_1.getJSONFilesInFolderRecursively(folderPath); | ||
if (listOfFiles.length === 0) { | ||
console.log("No Loadmill test files were found at " + folderPath + " - exiting..."); | ||
} | ||
return [2 /*return*/, _runFolderSync(listOfFiles, this.run, paramsOrCallback, callback)]; | ||
}); | ||
}); | ||
}, | ||
wait: function (testDefOrId, callback) { | ||
return _wait(testDefOrId, callback); | ||
}, | ||
runTestSuite: function (suite, paramsOrCallback, callback) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
return tslib_1.__generator(this, function (_a) { | ||
return [2 /*return*/, _runTestSuite(suite, paramsOrCallback, callback)]; | ||
}); | ||
}); | ||
}, | ||
runTestPlan: function (testPlan, params) { | ||
@@ -274,2 +190,8 @@ return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
}; | ||
function toTestResult(testDef, webUrl, body) { | ||
return tslib_1.__assign(tslib_1.__assign({}, testDef), { url: webUrl, description: body && body.description, passed: isTestPassed(body, testDef.type), startTime: body.startTime, endTime: body.endTime, status: body.status }); | ||
} | ||
function redactData(testResult, body, testingServer) { | ||
testResult.testSuitesRuns = reductTestSuitesRuns(body.testSuitesRuns, testingServer); | ||
} | ||
function isTestInFinalState(body, runType) { | ||
@@ -310,11 +232,2 @@ if (runType === Loadmill.TYPES.TEST_PLAN) { | ||
} | ||
function reductFlowRunsData(flowRuns) { | ||
if (flowRuns) { | ||
return flowRuns.map(function (f) { return ({ | ||
id: f.id, | ||
description: f.description, | ||
status: f.status | ||
}); }); | ||
} | ||
} | ||
function reductTestSuitesRuns(suitesRuns, testingServer) { | ||
@@ -321,0 +234,0 @@ if (suitesRuns) { |
@@ -9,4 +9,4 @@ "use strict"; | ||
program | ||
.usage("<testSuiteId | load-config-file-or-folder> -t <token> [options] [parameter=value...]") | ||
.description("Run a test suite (default option), test plan or a load test on loadmill.com.\n " + | ||
.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 " + | ||
@@ -16,4 +16,3 @@ "Learn more at https://www.npmjs.com/package/loadmill#cli") | ||
.option("-l, --load-test", "Launch a load test.") | ||
.option("--test-plan", "Launch a test plan.") | ||
.option("-s, --test-suite", "Launch a test suite (default option). If set then a test suite id must be provided instead of config file.") | ||
.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") | ||
@@ -44,3 +43,3 @@ .option("--additional-description <description>", "Add an additional description at the end of the current suite's description - available only for test suites.") | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var wait, bail, quiet, token, verbose, colors, report, junitReport, junitReportPath, mochawesomeReport, mochawesomeReportPath, parallel, loadTest, testPlan, additionalDescription, labels, labelsExpression, pool, branch, retryFailedFlows, parametersFile, _a, input, rawParams, logger, parameters, testSuite, loadmill, testFailed, testStopped, res, suiteLabels, running, e_1, extInfo, planLabels, running, e_2, extInfo, fileOrFolder, listOfFiles, _i, listOfFiles_1, file, res_1, id; | ||
var wait, bail, quiet, token, verbose, colors, report, junitReport, junitReportPath, mochawesomeReport, mochawesomeReportPath, parallel, loadTest, testPlan, additionalDescription, labels, labelsExpression, pool, branch, retryFailedFlows, parametersFile, _a, input, rawParams, logger, parameters, loadmill, testFailed, testStopped, res, planLabels, running, e_1, extInfo, configFile, res_1, id; | ||
return tslib_1.__generator(this, function (_b) { | ||
@@ -55,3 +54,2 @@ switch (_b.label) { | ||
parameters = toParams(rawParams, parametersFile); | ||
testSuite = !loadTest && !testPlan; | ||
if (verbose) { | ||
@@ -76,3 +74,2 @@ // verbose trumps quiet: | ||
testPlan: testPlan, | ||
testSuite: testSuite, | ||
additionalDescription: additionalDescription, | ||
@@ -102,6 +99,6 @@ labels: labels, | ||
}; | ||
if (!testSuite) return [3 /*break*/, 13]; | ||
suiteLabels = utils_1.convertStrToArr(labels); | ||
if (!utils_1.isUUID(input)) { //if test suite flag is on then the input should be uuid | ||
validationFailed("Test suite run flag is on but no valid test suite id was provided."); | ||
if (!(testPlan || !loadTest)) return [3 /*break*/, 13]; | ||
planLabels = utils_1.convertStrToArr(labels); | ||
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."); | ||
} | ||
@@ -111,7 +108,13 @@ _b.label = 1; | ||
_b.trys.push([1, 11, , 12]); | ||
logger.verbose("Executing suite with id " + input); | ||
return [4 /*yield*/, loadmill.runTestSuite({ | ||
logger.verbose("Executing test plan with id " + input); | ||
return [4 /*yield*/, loadmill.runTestPlan({ | ||
id: input, | ||
options: { | ||
additionalDescription: additionalDescription, labels: suiteLabels, pool: pool | ||
additionalDescription: additionalDescription, | ||
labels: planLabels, | ||
labelsExpression: labelsExpression, | ||
pool: pool, | ||
parallel: parallel, | ||
branch: branch, | ||
maxFlakyFlowRetries: retryFailedFlows | ||
} | ||
@@ -123,10 +126,14 @@ }, parameters)]; | ||
if (!wait) return [3 /*break*/, 8]; | ||
logger.verbose("Waiting for test suite run with id", running.id); | ||
logger.verbose("Waiting for test plan run with id", running.id); | ||
return [4 /*yield*/, loadmill.wait(running)]; | ||
case 3: | ||
res = _b.sent(); | ||
if (report && res.flowRuns) { | ||
utils_1.printFlowRunsReport(res.description, res.flowRuns, logger, colors); | ||
if (!quiet) { | ||
logger.log(res ? utils_1.getObjectAsString(res, colors) : running.id); | ||
} | ||
if (!(res && junitReport)) return [3 /*break*/, 5]; | ||
if (report && res.testSuitesRuns) { | ||
utils_1.printTestSuitesRunsReport(res.description, 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)]; | ||
@@ -137,3 +144,3 @@ case 4: | ||
case 5: | ||
if (!(res && mochawesomeReport)) return [3 /*break*/, 7]; | ||
if (!mochawesomeReport) return [3 /*break*/, 7]; | ||
return [4 /*yield*/, reporter_1.mochawesomeReport(res, token, mochawesomeReportPath)]; | ||
@@ -144,13 +151,12 @@ case 6: | ||
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 suite " + (res.id || input) + " has failed"); | ||
testFailed("Test plan with id " + (res.id || input) + " has failed"); | ||
} | ||
_b.label = 8; | ||
case 8: | ||
if (!quiet) { | ||
logger.log(res ? utils_1.getObjectAsString(res, colors) : running.id); | ||
} | ||
return [3 /*break*/, 10]; | ||
case 8: return [3 /*break*/, 10]; | ||
case 9: | ||
testFailed("Couldn't run test suite with id " + input); | ||
testFailed("Couldn't run test plan with id " + input); | ||
_b.label = 10; | ||
@@ -164,101 +170,21 @@ case 10: return [3 /*break*/, 12]; | ||
extInfo = e_1.response && e_1.response.res && e_1.response.res.text; | ||
testFailed("Couldn't run test suite with id " + input + ". " + (extInfo ? extInfo : '')); | ||
testFailed("Couldn't run test plan with id " + input + " " + (extInfo ? extInfo : '')); | ||
return [3 /*break*/, 12]; | ||
case 12: return [3 /*break*/, 32]; | ||
case 12: return [3 /*break*/, 17]; | ||
case 13: | ||
if (!testPlan) return [3 /*break*/, 26]; | ||
planLabels = utils_1.convertStrToArr(labels); | ||
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."); | ||
configFile = input; | ||
if (!configFile) { | ||
validationFailed("No configuration file were provided."); | ||
} | ||
_b.label = 14; | ||
logger.verbose("Launching " + configFile + " as load test"); | ||
return [4 /*yield*/, loadmill.run(configFile, parameters)]; | ||
case 14: | ||
_b.trys.push([14, 24, , 25]); | ||
logger.verbose("Executing test plan with id " + input); | ||
return [4 /*yield*/, loadmill.runTestPlan({ | ||
id: input, | ||
options: { | ||
additionalDescription: additionalDescription, | ||
labels: planLabels, | ||
labelsExpression: labelsExpression, | ||
pool: pool, | ||
parallel: parallel, | ||
branch: branch, | ||
maxFlakyFlowRetries: retryFailedFlows | ||
} | ||
}, parameters)]; | ||
case 15: | ||
running = _b.sent(); | ||
if (!(running && running.id)) return [3 /*break*/, 22]; | ||
if (!wait) return [3 /*break*/, 21]; | ||
logger.verbose("Waiting for test plan run with id", running.id); | ||
return [4 /*yield*/, loadmill.wait(running)]; | ||
case 16: | ||
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 (!res) return [3 /*break*/, 20]; | ||
if (!junitReport) return [3 /*break*/, 18]; | ||
return [4 /*yield*/, reporter_1.junitReport(res, token, junitReportPath)]; | ||
case 17: | ||
_b.sent(); | ||
_b.label = 18; | ||
case 18: | ||
if (!mochawesomeReport) return [3 /*break*/, 20]; | ||
return [4 /*yield*/, reporter_1.mochawesomeReport(res, token, mochawesomeReportPath)]; | ||
case 19: | ||
_b.sent(); | ||
_b.label = 20; | ||
case 20: | ||
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 = 21; | ||
case 21: return [3 /*break*/, 23]; | ||
case 22: | ||
testFailed("Couldn't run test plan with id " + input); | ||
_b.label = 23; | ||
case 23: return [3 /*break*/, 25]; | ||
case 24: | ||
e_2 = _b.sent(); | ||
if (verbose) { | ||
logger.error(e_2); | ||
} | ||
extInfo = e_2.response && e_2.response.res && e_2.response.res.text; | ||
testFailed("Couldn't run test plan with id " + input + " " + (extInfo ? extInfo : '')); | ||
return [3 /*break*/, 25]; | ||
case 25: return [3 /*break*/, 32]; | ||
case 26: | ||
fileOrFolder = input; | ||
if (!fileOrFolder) { | ||
validationFailed("No configuration file or folder were provided."); | ||
} | ||
listOfFiles = utils_1.getJSONFilesInFolderRecursively(fileOrFolder); | ||
if (listOfFiles.length === 0) { | ||
logger.log("No Loadmill test files were found at " + fileOrFolder + " - exiting..."); | ||
} | ||
_i = 0, listOfFiles_1 = listOfFiles; | ||
_b.label = 27; | ||
case 27: | ||
if (!(_i < listOfFiles_1.length)) return [3 /*break*/, 32]; | ||
file = listOfFiles_1[_i]; | ||
res_1 = void 0; | ||
logger.verbose("Launching " + file + " as load test"); | ||
return [4 /*yield*/, loadmill.run(file, parameters)]; | ||
case 28: | ||
id = _b.sent(); | ||
if (!(wait && loadTest)) return [3 /*break*/, 30]; | ||
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 29: | ||
case 15: | ||
res_1 = _b.sent(); | ||
_b.label = 30; | ||
case 30: | ||
_b.label = 16; | ||
case 16: | ||
if (!quiet) { | ||
@@ -268,3 +194,3 @@ logger.log(JSON.stringify(res_1, null, 4) || id); | ||
if (res_1 && res_1.passed != null && !res_1.passed) { | ||
logger.error("\u274C Test " + file + " failed."); | ||
logger.error("\u274C Test " + configFile + " failed."); | ||
if (bail) { | ||
@@ -274,7 +200,4 @@ process.exit(1); | ||
} | ||
_b.label = 31; | ||
case 31: | ||
_i++; | ||
return [3 /*break*/, 27]; | ||
case 32: return [2 /*return*/]; | ||
_b.label = 17; | ||
case 17: return [2 /*return*/]; | ||
} | ||
@@ -281,0 +204,0 @@ }); |
"use strict"; | ||
exports.__esModule = true; | ||
exports.TESTING_HOST = exports.getLogger = exports.Logger = exports.sleep = exports.readRawParams = exports.toLoadmillParams = exports.isUUID = exports.isString = exports.isEmptyObj = exports.getJSONFilesInFolderRecursively = exports.filterLabels = exports.convertArrToLabelQueryParams = exports.convertStrToArr = exports.printTestSuitesRunsReport = exports.printFlowRunsReport = exports.getObjectAsString = void 0; | ||
exports.TESTING_HOST = exports.getLogger = exports.Logger = exports.sleep = exports.readRawParams = exports.toLoadmillParams = exports.isUUID = exports.isString = exports.isEmptyObj = exports.filterLabels = exports.convertArrToLabelQueryParams = exports.convertStrToArr = exports.printTestSuitesRunsReport = exports.printFlowRunsReport = exports.getObjectAsString = void 0; | ||
var tslib_1 = require("tslib"); | ||
var fs = require("fs"); | ||
var path = require("path"); | ||
var isEmpty = require("lodash/isEmpty"); | ||
@@ -58,18 +57,2 @@ var isAString = require("lodash/isString"); | ||
exports.filterLabels = filterLabels; | ||
var getJSONFilesInFolderRecursively = function (fileOrFolder, filelist) { | ||
if (filelist === void 0) { filelist = []; } | ||
var isFile = fs.statSync(fileOrFolder).isFile(); | ||
if (isFile && endsWith(fileOrFolder, '.json')) { | ||
filelist.push(fileOrFolder); | ||
} | ||
else if (!isFile) { | ||
fs.readdirSync(fileOrFolder) | ||
.map(function (file) { | ||
return exports.getJSONFilesInFolderRecursively(path.join(fileOrFolder, file), filelist); | ||
}); | ||
} | ||
return filelist; | ||
}; | ||
exports.getJSONFilesInFolderRecursively = getJSONFilesInFolderRecursively; | ||
var endsWith = function (str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; }; | ||
var isEmptyObj = function (obj) { return isEmpty(obj); }; | ||
@@ -76,0 +59,0 @@ exports.isEmptyObj = isEmptyObj; |
{ | ||
"stats": { | ||
"suites": 2, | ||
"tests": 2, | ||
"suites": 1, | ||
"tests": 1, | ||
"passes": 1, | ||
"failures": 1, | ||
"start": "2021-08-01T13:22:15.261Z", | ||
"end": "2021-08-01T13:22:36.176Z", | ||
"failures": 0, | ||
"start": "2023-05-17T09:28:29.751Z", | ||
"end": "2023-05-17T09:28:50.430Z", | ||
"pending": 0, | ||
"testsRegistered": 2, | ||
"testsRegistered": 1, | ||
"pendingPercent": 0, | ||
"passPercent": 50, | ||
"passPercent": 100, | ||
"other": 0, | ||
@@ -17,3 +17,3 @@ "hasOther": false, | ||
"hasSkipped": false, | ||
"duration": 13641 | ||
"duration": 16984 | ||
}, | ||
@@ -25,9 +25,9 @@ "results": [ | ||
{ | ||
"title": "yigal node junit - Yigal plan", | ||
"title": "long suite - long plan", | ||
"tests": [ | ||
{ | ||
"title": "Flow 1 good", | ||
"fullTitle": "Flow 1 good", | ||
"title": "New Flow", | ||
"fullTitle": "New Flow", | ||
"timedOut": false, | ||
"duration": 2164, | ||
"duration": 5446, | ||
"state": "passed", | ||
@@ -39,55 +39,41 @@ "pass": true, | ||
"pending": false, | ||
"code": "https://app.loadmill.com/app/api-tests/test-suite-runs/08e9591f-a21b-4144-ad2b-e9d14a27ef16/flows/b9f24e01-8225-45cd-a655-df47c988c311", | ||
"code": "https://1910-207-232-22-138.ngrok-free.app/app/api-tests/test-suite-runs/b5adc8c7-0f80-4f25-b008-8b913d8cc004/flows/de1d8280-4d6f-4b74-8e32-0e8d3e818562", | ||
"err": {}, | ||
"uuid": "b9f24e01-8225-45cd-a655-df47c988c311" | ||
"uuid": "de1d8280-4d6f-4b74-8e32-0e8d3e818562" | ||
}, | ||
{ | ||
"title": "Flow 1 bad", | ||
"fullTitle": "Flow 1 bad", | ||
"title": "New Flow (copy)", | ||
"fullTitle": "New Flow (copy)", | ||
"timedOut": false, | ||
"duration": 1792, | ||
"state": "failed", | ||
"pass": false, | ||
"fail": true, | ||
"duration": 5279, | ||
"state": "passed", | ||
"pass": true, | ||
"fail": false, | ||
"isHook": false, | ||
"skipped": false, | ||
"pending": false, | ||
"code": "https://app.loadmill.com/app/api-tests/test-suite-runs/08e9591f-a21b-4144-ad2b-e9d14a27ef16/flows/aeb60043-e175-4917-a764-83601428b528", | ||
"err": { | ||
"showDiff": true, | ||
"actual": "", | ||
"negate": false, | ||
"_message": "", | ||
"generatedMessage": false, | ||
"diff": "Request #2 - GET http://httpbin.org/status/401 => HTTP status 401 - Unauthorized \n " | ||
}, | ||
"uuid": "aeb60043-e175-4917-a764-83601428b528" | ||
"code": "https://1910-207-232-22-138.ngrok-free.app/app/api-tests/test-suite-runs/b5adc8c7-0f80-4f25-b008-8b913d8cc004/flows/444da308-a5f5-467f-92b3-94c6589dcdb7", | ||
"err": {}, | ||
"uuid": "444da308-a5f5-467f-92b3-94c6589dcdb7" | ||
}, | ||
{ | ||
"title": "Flow 1 good but assertions", | ||
"fullTitle": "Flow 1 good but assertions", | ||
"title": "New Flow (copy) (copy)", | ||
"fullTitle": "New Flow (copy) (copy)", | ||
"timedOut": false, | ||
"duration": 2400, | ||
"state": "failed", | ||
"pass": false, | ||
"fail": true, | ||
"duration": 5284, | ||
"state": "passed", | ||
"pass": true, | ||
"fail": false, | ||
"isHook": false, | ||
"skipped": false, | ||
"pending": false, | ||
"code": "https://app.loadmill.com/app/api-tests/test-suite-runs/08e9591f-a21b-4144-ad2b-e9d14a27ef16/flows/785ae469-9cff-41e2-ac95-fb6dbd8dcb4d", | ||
"err": { | ||
"showDiff": true, | ||
"actual": "", | ||
"negate": false, | ||
"_message": "", | ||
"generatedMessage": false, | ||
"diff": "Request #2 - Flow desc Rivi - GET http://httpbin.org/status/200 => \n \n \n+ \"Expected: __status Equals 300 \n- \"Actual: 200 \n \n \n+ \"Expected: __launchedBy Equals Rivi \n- \"Actual: Yigal Dviri \n \n \n+ \"Expected: __status Matches 534534 \n- \"Actual: 200 \n \n \n+ \"Expected: __status Doesn't contain 200 \n- \"Actual: 200 \n \n \n+ \"Expected: __responseEndTime Doesn't exist \n- \"Actual: 1627824141763 \n \n \n+ \"Expected: __status Less than 100 \n- \"Actual: 200 \n \n \n+ \"Expected: rivi Exists \n- \"Actual: null \n \n \n+ \"Expected: __status Greater than 300 \n- \"Actual: 200 " | ||
}, | ||
"uuid": "785ae469-9cff-41e2-ac95-fb6dbd8dcb4d" | ||
"code": "https://1910-207-232-22-138.ngrok-free.app/app/api-tests/test-suite-runs/b5adc8c7-0f80-4f25-b008-8b913d8cc004/flows/f9fae9f7-3af8-4088-a6bb-575ded2c3a48", | ||
"err": {}, | ||
"uuid": "f9fae9f7-3af8-4088-a6bb-575ded2c3a48" | ||
}, | ||
{ | ||
"title": "timout", | ||
"fullTitle": "timout", | ||
"title": "this one should fail", | ||
"fullTitle": "this one should fail", | ||
"timedOut": false, | ||
"duration": 4784, | ||
"duration": 654, | ||
"state": "failed", | ||
@@ -99,3 +85,3 @@ "pass": false, | ||
"pending": false, | ||
"code": "https://app.loadmill.com/app/api-tests/test-suite-runs/08e9591f-a21b-4144-ad2b-e9d14a27ef16/flows/2670ae3e-ebf6-4b2a-b959-62f5edc9acb6", | ||
"code": "https://1910-207-232-22-138.ngrok-free.app/app/api-tests/test-suite-runs/b5adc8c7-0f80-4f25-b008-8b913d8cc004/flows/dea2de7a-572b-44bd-b6f0-d1b935e2d0cb", | ||
"err": { | ||
@@ -107,40 +93,17 @@ "showDiff": true, | ||
"generatedMessage": false, | ||
"diff": "Request #2 - GET http://httpbin.org/delay/5 => Timeout of 2000ms exceeded \n " | ||
"diff": "Request #1 - GET https://bin.blockchain-socks.xyz/status/412 => HTTP status 412 - Precondition Failed " | ||
}, | ||
"uuid": "2670ae3e-ebf6-4b2a-b959-62f5edc9acb6" | ||
}, | ||
{ | ||
"title": "Loops", | ||
"fullTitle": "Loops", | ||
"timedOut": false, | ||
"duration": 1401, | ||
"state": "failed", | ||
"pass": false, | ||
"fail": true, | ||
"isHook": false, | ||
"skipped": false, | ||
"pending": false, | ||
"code": "https://app.loadmill.com/app/api-tests/test-suite-runs/08e9591f-a21b-4144-ad2b-e9d14a27ef16/flows/4ce96ac5-8dfd-48d3-9455-48b32c975381", | ||
"err": { | ||
"showDiff": true, | ||
"actual": "", | ||
"negate": false, | ||
"_message": "", | ||
"generatedMessage": false, | ||
"diff": "Request #1 - Flow desc Rivi - GET http://httpbin.org/status/200 => Request failed to meet loop condition for 2 iterations \n " | ||
}, | ||
"uuid": "4ce96ac5-8dfd-48d3-9455-48b32c975381" | ||
"uuid": "dea2de7a-572b-44bd-b6f0-d1b935e2d0cb" | ||
} | ||
], | ||
"duration": 12906, | ||
"duration": 16984, | ||
"suites": [], | ||
"uuid": "08e9591f-a21b-4144-ad2b-e9d14a27ef16", | ||
"uuid": "b5adc8c7-0f80-4f25-b008-8b913d8cc004", | ||
"passes": [ | ||
"b9f24e01-8225-45cd-a655-df47c988c311" | ||
"de1d8280-4d6f-4b74-8e32-0e8d3e818562", | ||
"444da308-a5f5-467f-92b3-94c6589dcdb7", | ||
"f9fae9f7-3af8-4088-a6bb-575ded2c3a48" | ||
], | ||
"failures": [ | ||
"aeb60043-e175-4917-a764-83601428b528", | ||
"785ae469-9cff-41e2-ac95-fb6dbd8dcb4d", | ||
"2670ae3e-ebf6-4b2a-b959-62f5edc9acb6", | ||
"4ce96ac5-8dfd-48d3-9455-48b32c975381" | ||
"dea2de7a-572b-44bd-b6f0-d1b935e2d0cb" | ||
], | ||
@@ -155,37 +118,2 @@ "root": false, | ||
"pending": [] | ||
}, | ||
{ | ||
"title": "yigal test prod ipfy - Yigal plan", | ||
"tests": [ | ||
{ | ||
"title": "ipify yay", | ||
"fullTitle": "ipify yay", | ||
"timedOut": false, | ||
"duration": 462, | ||
"state": "passed", | ||
"pass": true, | ||
"fail": false, | ||
"isHook": false, | ||
"skipped": false, | ||
"pending": false, | ||
"code": "https://app.loadmill.com/app/api-tests/test-suite-runs/fc868bae-b167-4495-bcbe-ac58811f6755/flows/d26f109a-3a34-4199-9930-5b7c933e7285", | ||
"err": {}, | ||
"uuid": "d26f109a-3a34-4199-9930-5b7c933e7285" | ||
} | ||
], | ||
"duration": 735, | ||
"suites": [], | ||
"uuid": "fc868bae-b167-4495-bcbe-ac58811f6755", | ||
"passes": [ | ||
"d26f109a-3a34-4199-9930-5b7c933e7285" | ||
], | ||
"failures": [], | ||
"root": false, | ||
"_timeout": 0, | ||
"file": "", | ||
"fullFile": "", | ||
"beforeHooks": [], | ||
"afterHooks": [], | ||
"skipped": [], | ||
"pending": [] | ||
} | ||
@@ -197,3 +125,3 @@ ], | ||
"_timeout": 0, | ||
"uuid": "08e9591f-a21b-4144-ad2b-e9d14a27ef16", | ||
"uuid": "b5adc8c7-0f80-4f25-b008-8b913d8cc004", | ||
"beforeHooks": [], | ||
@@ -206,3 +134,3 @@ "afterHooks": [], | ||
"skipped": [], | ||
"duration": 13641, | ||
"duration": 16984, | ||
"rootEmpty": true | ||
@@ -209,0 +137,0 @@ } |
{ | ||
"name": "loadmill", | ||
"version": "3.2.5", | ||
"version": "4.0.0", | ||
"description": "A node.js module for running load tests and functional tests on loadmill.com", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
105
README.md
@@ -33,76 +33,2 @@ # Loadmill | ||
### Test Suites | ||
You may launch an existing test suite by supplying the suite id - this is usually useful for testing your API for regressions after every new deployment. | ||
Test suites are launched and not awaiting the results. | ||
```js | ||
const loadmill = require('loadmill')({token: process.env.LOADMILL_API_TOKEN}); | ||
/** | ||
* @returns { id: 'uuid', type: 'test-suite' } | ||
*/ | ||
const result = await loadmill.runTestSuite({id: "test-suite-uuid"}); | ||
``` | ||
You can also extend the suite object with `options` object - containing: | ||
* additionalDescription - added at the end of the test suite description. | ||
* labels - will execute only flows attached to these labales. | ||
Also, you may add a second argument if you wish to override suite parameters | ||
```js | ||
const result = await loadmill.runTestSuite( | ||
{ | ||
id: "test-suite-uuid", | ||
options: { //optional | ||
additionalDescription: "description to add", // will be added to the end of the test suite description. | ||
labels: ["label1", "label2"], //run flows that are assigned to specific label/s | ||
pool: "some-pool-name" // Execute tests from a dedicated agent's pool (when using private agent) | ||
} | ||
}, | ||
{ | ||
"parameterKey": "overrided value" | ||
} | ||
); | ||
``` | ||
You can run the test suite and create a junit-like report in the end: | ||
```js | ||
/** | ||
* @returns {id: string, type: 'load' | 'test-suite', passed: boolean, url: string} | ||
*/ | ||
loadmill.runTestSuite({id: "test-suite-uuid"}) | ||
.then(loadmill.wait) | ||
.then(loadmill.junitReport); | ||
// promise with async/await | ||
const id = await loadmill.runTestSuite({id: "test-suite-uuid"}); | ||
const result = await loadmill.wait(id); | ||
loadmill.junitReport(result); // may add a second arg of path to save the report to. | ||
``` | ||
You can run the test suite and create a mochawesome report in the end: | ||
```js | ||
/** | ||
* @returns {id: string, type: 'load' | 'test-suite', passed: boolean, url: string} | ||
*/ | ||
loadmill.runTestSuite({id: "test-suite-uuid"}) | ||
.then(loadmill.wait) | ||
.then(loadmill.mochawesomeReport); | ||
// promise with async/await | ||
const id = await loadmill.runTestSuite( | ||
{ | ||
id: "test-suite-uuid", // required | ||
options: { //optional | ||
additionalDescription: "description to add", // added at the end of the test suite description. | ||
labels: ["label1", "label2"], // run flows that are assigned to specific label/s | ||
pool: "some-pool-name" // Execute tests from a dedicated agent's pool (when using private agent) | ||
} | ||
}, | ||
{ "parameterKey": "overrided value" } //optional | ||
); | ||
const result = await loadmill.wait(id); | ||
loadmill.mochawesomeReport(result); // may add a second arg of path to save the report to. | ||
``` | ||
### Test Plans | ||
@@ -153,3 +79,3 @@ | ||
/** | ||
* @returns {id: string, type: 'load' | 'test-suite', passed: boolean, url: string} | ||
* @returns {id: string, type: 'load' | 'test-plan', passed: boolean, url: string} | ||
*/ | ||
@@ -179,17 +105,17 @@ loadmill.run("./load-tests/long_test.json") | ||
``` | ||
loadmill <load-config-file-or-folder | test-suite-id> -t <token> [options] [parameter=value...] | ||
loadmill <test-plan-id || load-test-config-file> -t <token> [options] [parameter=value...] | ||
``` | ||
### Test suites | ||
### Test Plan | ||
You may launch a test suite by setting the `-s` or `--test-suite` option: | ||
You may launch a test plan by setting the --test-plan option: | ||
``` | ||
loadmill test-suite-id --test-suite -t DW2rTlkNmE6A3ax5LVTSDxv2Jfw4virjQpmbOaLG | ||
loadmill <test-plan-id> --test-plan -w -v -t <token> --report --colors --labels "label1,label2" | ||
``` | ||
The test suite will be launched and its unique identifier will be printed to the standard output. You may alternatively | ||
set the `-w` or `--wait` option in order to wait for the test-suite to finish, in which case only the result JSON will be | ||
set the `-w` or `--wait` option in order to wait for the test-plan to finish, in which case only the result JSON will be | ||
printed out at the end | ||
You can add an additional description at the end of the current suite's description with the `--additional-description <description>` option. | ||
You can add an additional description at the end of the current plan's description with the `--additional-description <description>` option. | ||
@@ -199,13 +125,5 @@ You can tell loadmill to run flows that are assigned to a specific label with the `--labels <labels>` option. Multiple labels can be provided by seperated them with "," (e.g. 'label1,label2'). | ||
``` | ||
loadmill <test-suite-id> --test-suite -t <token> --labels "label1,label2" | ||
loadmill <test-plan-id> --test-plan -t <token> --labels "label1,label2" --additional-description "build 1986" | ||
``` | ||
### Test Plan | ||
You may launch a test plan by setting the --test-plan option: | ||
``` | ||
loadmill <test-plan-id> --test-plan -w -v -t <token> --report --colors --labels "label1,label2" | ||
``` | ||
### Load Tests | ||
@@ -245,6 +163,5 @@ | ||
- `-l, --load-test` Launch a load test. | ||
- `--test-plan` Launch a test plan. | ||
- `-s, --test-suite` Launch a test suite. If set then a test suite id must be provided instead of config file. | ||
- `--test-plan` Launch a test plan (default). | ||
- `-p, --parallel` Set the concurrency of a running test suites in a test plan. Max concurrency is 10. | ||
- `--additional-description <description>` Add an additional description at the end of the current suite's / test-plan's description. | ||
- `--additional-description <description>` Add an additional description at the end of the current test-plan's description. | ||
- `--labels <labels>`, Run flows that are assigned to a specific label. Multiple labels can be provided by seperated them with "," (e.g. 'label1,label2'). | ||
@@ -251,0 +168,0 @@ - `--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') |
136
src/index.ts
@@ -5,10 +5,11 @@ import './polyfills' | ||
import { | ||
getJSONFilesInFolderRecursively, | ||
filterLabels, | ||
TESTING_HOST, | ||
toLoadmillParams, | ||
readRawParams | ||
readRawParams, | ||
} from './utils'; | ||
import { junitReport as createJunitReport, mochawesomeReport as createMochawesomeReport } from './reporter'; | ||
const TEST_PLAN_POLL_INTERVAL_IN_MS = 10 * 1000 // 10 seconds | ||
export = Loadmill; | ||
@@ -24,22 +25,4 @@ | ||
const testingServer = "https://" + _testingServerHost; | ||
const testSuitesAPI = `${testingServer}/api/test-suites`; | ||
const testPlansAPI = `${testingServer}/api/test-plans`; | ||
async function _runFolderSync( | ||
listOfFiles: string[], | ||
execFunc: (...args) => Promise<any>, | ||
...funcArgs) { | ||
const results: Loadmill.TestResult[] = []; | ||
for (let file of listOfFiles) { | ||
let res = await execFunc(file, ...funcArgs); | ||
const testResult = await _wait(res); | ||
results.push(testResult); | ||
if (!testResult.passed) break; | ||
} | ||
return results; | ||
} | ||
async function _wait(testDefOrId: string | Loadmill.TestDef, callback?: Loadmill.Callback): Promise<Loadmill.TestResult> { | ||
@@ -56,2 +39,3 @@ let resolve, reject; | ||
let retries = 1; | ||
const intervalId = setInterval(async () => { | ||
@@ -70,18 +54,5 @@ try { | ||
const testResult: Loadmill.TestResult = { | ||
...testDef, | ||
url: webUrl, | ||
description: body && body.description, | ||
passed: isTestPassed(body, testDef.type), | ||
startTime: body.startTime, | ||
endTime: body.endTime, | ||
status: body.status | ||
}; | ||
const testResult: Loadmill.TestResult = toTestResult(testDef, webUrl, body); | ||
if (testDef.type === Loadmill.TYPES.SUITE) { | ||
testResult.flowRuns = reductFlowRunsData(body.testSuiteFlowRuns); | ||
} | ||
else if (testDef.type === Loadmill.TYPES.TEST_PLAN) { | ||
testResult.testSuitesRuns = reductTestSuitesRuns(body.testSuitesRuns, testingServer) | ||
} | ||
redactData(testResult, body, testingServer); | ||
@@ -97,4 +68,10 @@ if (callback) { | ||
catch (err) { | ||
clearInterval(intervalId); | ||
if (retries < 3) { | ||
retries++; | ||
return; | ||
} else { | ||
clearInterval(intervalId); | ||
} | ||
if (callback) { | ||
@@ -107,4 +84,3 @@ callback(err, null); | ||
} | ||
}, | ||
10 * 1000); | ||
}, TEST_PLAN_POLL_INTERVAL_IN_MS); | ||
@@ -117,38 +93,2 @@ return callback ? null! as Promise<any> : new Promise((_resolve, _reject) => { | ||
async function _runTestSuite( | ||
suite: Loadmill.TestSuiteDef, | ||
paramsOrCallback: Loadmill.ParamsOrCallback, | ||
callback?: Loadmill.Callback) { | ||
const params = paramsOrCallback && typeof paramsOrCallback !== 'function' ? paramsOrCallback : {}; | ||
const overrideParameters = toParams(params, suite.options?.parametersFile); | ||
const suiteId = suite.id; | ||
const additionalDescription = suite.options && suite.options.additionalDescription; | ||
const labels = suite.options && suite.options.labels && filterLabels(suite.options.labels); | ||
const failGracefully = suite.options && suite.options.failGracefully; | ||
const pool = suite.options && suite.options.pool; | ||
return wrap( | ||
async () => { | ||
const { | ||
body: { | ||
testSuiteRunId, | ||
err | ||
} | ||
} = await superagent.post(`${testSuitesAPI}/${suiteId}/run${failGracefully ? '?failGracefully=true' : ''}`) | ||
.send({ overrideParameters, additionalDescription, labels, pool }) | ||
.auth(token, ''); | ||
if (err || !testSuiteRunId) { | ||
console.error(err ? JSON.stringify(err) : "The server encountered an error while handling the request"); | ||
return; | ||
} | ||
return { id: testSuiteRunId, type: Loadmill.TYPES.SUITE }; | ||
}, | ||
callback || paramsOrCallback | ||
); | ||
} | ||
async function _runTestPlan( | ||
@@ -214,15 +154,2 @@ testPlan: Loadmill.TestPlanDef, | ||
async runFolder( | ||
folderPath: string, | ||
paramsOrCallback?: Loadmill.ParamsOrCallback, | ||
callback?: Loadmill.Callback): Promise<Array<Loadmill.TestResult>> { | ||
const listOfFiles = getJSONFilesInFolderRecursively(folderPath); | ||
if (listOfFiles.length === 0) { | ||
console.log(`No Loadmill test files were found at ${folderPath} - exiting...`); | ||
} | ||
return _runFolderSync(listOfFiles, this.run, paramsOrCallback, callback); | ||
}, | ||
wait(testDefOrId: string | Loadmill.TestDef, callback?: Loadmill.Callback): Promise<Loadmill.TestResult> { | ||
@@ -232,10 +159,2 @@ return _wait(testDefOrId, callback); | ||
async runTestSuite( | ||
suite: Loadmill.TestSuiteDef, | ||
paramsOrCallback?: Loadmill.ParamsOrCallback, | ||
callback?: Loadmill.Callback): Promise<Loadmill.TestDef> { | ||
return _runTestSuite(suite, paramsOrCallback, callback); | ||
}, | ||
async runTestPlan( | ||
@@ -269,2 +188,19 @@ testPlan: Loadmill.TestPlanDef, | ||
} | ||
function toTestResult(testDef: Loadmill.TestDef, webUrl: string, body: any): Loadmill.TestResult { | ||
return { | ||
...testDef, | ||
url: webUrl, | ||
description: body && body.description, | ||
passed: isTestPassed(body, testDef.type), | ||
startTime: body.startTime, | ||
endTime: body.endTime, | ||
status: body.status | ||
}; | ||
} | ||
function redactData(testResult: Loadmill.TestResult, body: any, testingServer: string) { | ||
testResult.testSuitesRuns = reductTestSuitesRuns(body.testSuitesRuns, testingServer); | ||
} | ||
function isTestInFinalState(body, runType) { | ||
@@ -307,12 +243,2 @@ if (runType === Loadmill.TYPES.TEST_PLAN) { | ||
function reductFlowRunsData(flowRuns) { | ||
if (flowRuns) { | ||
return flowRuns.map(f => ({ | ||
id: f.id, | ||
description: f.description, | ||
status: f.status | ||
})); | ||
} | ||
} | ||
function reductTestSuitesRuns(suitesRuns, testingServer) { | ||
@@ -319,0 +245,0 @@ if (suitesRuns) { |
import * as Loadmill from './index'; | ||
import * as program from 'commander'; | ||
import { | ||
getJSONFilesInFolderRecursively, | ||
getLogger, | ||
@@ -9,3 +8,2 @@ isUUID, | ||
convertStrToArr, | ||
printFlowRunsReport, | ||
printTestSuitesRunsReport, | ||
@@ -18,5 +16,5 @@ toLoadmillParams, | ||
program | ||
.usage("<testSuiteId | load-config-file-or-folder> -t <token> [options] [parameter=value...]") | ||
.usage("<testPlanId | load-config-file> -t <token> [options] [parameter=value...]") | ||
.description( | ||
"Run a test suite (default option), test plan or a load test on loadmill.com.\n " + | ||
"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 " + | ||
@@ -27,4 +25,3 @@ "Learn more at https://www.npmjs.com/package/loadmill#cli" | ||
.option("-l, --load-test", "Launch a load test.") | ||
.option("--test-plan", "Launch a test plan.") | ||
.option("-s, --test-suite", "Launch a test suite (default option). If set then a test suite id must be provided instead of config file.") | ||
.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") | ||
@@ -91,3 +88,2 @@ .option("--additional-description <description>", "Add an additional description at the end of the current suite's description - available only for test suites.") | ||
const testSuite = !loadTest && !testPlan; | ||
if (verbose) { | ||
@@ -113,3 +109,2 @@ // verbose trumps quiet: | ||
testPlan, | ||
testSuite, | ||
additionalDescription, | ||
@@ -146,62 +141,3 @@ labels, | ||
let res: Loadmill.TestResult | undefined; | ||
if (testSuite) { | ||
const suiteLabels = convertStrToArr(labels) | ||
if (!isUUID(input)) { //if test suite flag is on then the input should be uuid | ||
validationFailed("Test suite run flag is on but no valid test suite id was provided."); | ||
} | ||
try { | ||
logger.verbose(`Executing suite with id ${input}`); | ||
let running = await loadmill.runTestSuite( | ||
{ | ||
id: input, | ||
options: { | ||
additionalDescription, labels: suiteLabels, pool | ||
} | ||
}, | ||
parameters); | ||
if (running && running.id) { | ||
if (wait) { | ||
logger.verbose("Waiting for test suite run with id", running.id); | ||
res = await loadmill.wait(running); | ||
if (report && res.flowRuns) { | ||
printFlowRunsReport(res.description, res.flowRuns, logger, colors); | ||
} | ||
if (res && junitReport) { | ||
await createJunitReport(res, token, junitReportPath); | ||
} | ||
if (res && mochawesomeReport) { | ||
await createMochawesomeReport(res, token, mochawesomeReportPath); | ||
} | ||
if (res && res.passed != null && !res.passed) { | ||
testFailed(`Test suite ${res.id || input} has failed`); | ||
} | ||
} | ||
if (!quiet) { | ||
logger.log(res ? getObjectAsString(res, colors) : running.id); | ||
} | ||
} else { | ||
testFailed(`Couldn't run test suite with id ${input}`); | ||
} | ||
} catch (e) { | ||
if (verbose) { | ||
logger.error(e); | ||
} | ||
const extInfo = e.response && e.response.res && e.response.res.text; | ||
testFailed(`Couldn't run test suite with id ${input}. ${extInfo ? extInfo : ''}`); | ||
} | ||
} | ||
else if (testPlan) { | ||
if (testPlan || !loadTest) { | ||
const planLabels = convertStrToArr(labels) | ||
@@ -275,36 +211,28 @@ | ||
else { // if test suite flag is off then the input should be fileOrFolder | ||
else { // if test plan flag is off then the input should be a conf file | ||
const fileOrFolder = input; | ||
if (!fileOrFolder) { | ||
validationFailed("No configuration file or folder were provided."); | ||
const configFile = input; | ||
if (!configFile) { | ||
validationFailed("No configuration file were provided."); | ||
} | ||
let res; | ||
const listOfFiles = getJSONFilesInFolderRecursively(fileOrFolder); | ||
if (listOfFiles.length === 0) { | ||
logger.log(`No Loadmill test files were found at ${fileOrFolder} - exiting...`); | ||
logger.verbose(`Launching ${configFile} as load test`); | ||
const id = await loadmill.run(configFile, parameters); | ||
if (wait && loadTest) { | ||
logger.verbose("Waiting for test:", res ? res.id : id); | ||
res = await loadmill.wait(res || id); | ||
} | ||
for (let file of listOfFiles) { | ||
let res; | ||
if (!quiet) { | ||
logger.log(JSON.stringify(res, null, 4) || id); | ||
} | ||
logger.verbose(`Launching ${file} as load test`); | ||
const id = await loadmill.run(file, parameters); | ||
if (res && res.passed != null && !res.passed) { | ||
logger.error(`❌ Test ${configFile} failed.`); | ||
if (wait && loadTest) { | ||
logger.verbose("Waiting for test:", res ? res.id : id); | ||
res = await loadmill.wait(res || id); | ||
if (bail) { | ||
process.exit(1); | ||
} | ||
if (!quiet) { | ||
logger.log(JSON.stringify(res, null, 4) || id); | ||
} | ||
if (res && res.passed != null && !res.passed) { | ||
logger.error(`❌ Test ${file} failed.`); | ||
if (bail) { | ||
process.exit(1); | ||
} | ||
} | ||
} | ||
@@ -311,0 +239,0 @@ } |
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import isEmpty = require('lodash/isEmpty'); | ||
@@ -58,20 +57,2 @@ import isAString = require('lodash/isString'); | ||
export const getJSONFilesInFolderRecursively = (fileOrFolder: string, filelist: string[] = []): string[] => { | ||
let isFile = fs.statSync(fileOrFolder).isFile(); | ||
if (isFile && endsWith(fileOrFolder, '.json')) { | ||
filelist.push(fileOrFolder); | ||
} else if (!isFile) { | ||
fs.readdirSync(fileOrFolder) | ||
.map(file => | ||
getJSONFilesInFolderRecursively(path.join(fileOrFolder, file), filelist)); | ||
} | ||
return filelist; | ||
}; | ||
const endsWith = (str, suffix) => str.indexOf(suffix, str.length - suffix.length) !== -1; | ||
export const isEmptyObj = (obj) => isEmpty(obj); | ||
@@ -78,0 +59,0 @@ export const isString = (obj) => isAString(obj); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
120070
2437
177