loadmill
Version:
A node.js module for running load tests and functional tests on loadmill.com
337 lines (336 loc) • 15.8 kB
JavaScript
;
var tslib_1 = require("tslib");
require("./polyfills");
var fs = require("fs");
var http_request_1 = require("./http-request");
var utils_1 = require("./utils");
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, _testingServerHost = _a._testingServerHost;
var testingServer = _testingServerHost ? "https://" + _testingServerHost : utils_1.TESTING_ORIGIN;
var testPlansAPI = testingServer + "/api/test-plans";
function _wait(testDefOrId, callback) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var resolve, reject, testDef, apiUrl, webUrl, retries, intervalId;
var _this = this;
return tslib_1.__generator(this, function (_a) {
testDef = typeof testDefOrId === 'string' ? {
id: testDefOrId,
type: Loadmill.TYPES.LOAD
} : testDefOrId;
apiUrl = getTestAPIUrl(testDef, testingServer);
webUrl = getTestWebUrl(testDef, testingServer);
retries = 1;
intervalId = setInterval(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var body, bodyWithFlows, testResult, err_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 5, , 6]);
return [4 /*yield*/, http_request_1.sendHttpRequest({ url: apiUrl, token: token })];
case 1:
body = (_a.sent()).body;
if (!isTestInFinalState(body, testDef.type)) return [3 /*break*/, 4];
clearInterval(intervalId);
if (!(testDef.type === Loadmill.TYPES.TEST_PLAN)) return [3 /*break*/, 3];
return [4 /*yield*/, http_request_1.sendHttpRequest({
url: apiUrl,
query: {
fetchAllFlows: true,
groupFlowAttempts: true
},
token: token
})];
case 2:
bodyWithFlows = (_a.sent()).body;
body = bodyWithFlows;
_a.label = 3;
case 3:
testResult = toTestResult(testDef, webUrl, body);
redactData(testResult, body);
if (callback) {
callback(null, testResult);
}
else {
resolve(testResult);
}
_a.label = 4;
case 4: return [3 /*break*/, 6];
case 5:
err_1 = _a.sent();
if (retries < 3) {
retries++;
return [2 /*return*/];
}
else {
clearInterval(intervalId);
}
if (callback) {
callback(err_1, null);
}
else {
reject(err_1);
}
return [3 /*break*/, 6];
case 6: return [2 /*return*/];
}
});
}); }, TEST_PLAN_POLL_INTERVAL_IN_MS);
return [2 /*return*/, callback ? null : new Promise(function (_resolve, _reject) {
resolve = _resolve;
reject = _reject;
})];
});
});
}
function _runTestPlan(testPlan, params) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function () {
var testPlanId, overrideParameters, labels, labelsExpression, additionalDescription, pool, tags, parallel, branch, inlineParameterOverride, maxFlakyFlowRetries, apiCatalogService, turboParallel, _b, testPlanRunId, err;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
testPlanId = testPlan.id;
overrideParameters = toParams(params, (_a = testPlan.options) === null || _a === void 0 ? void 0 : _a.parametersFile);
labels = testPlan.options && testPlan.options.labels && utils_1.filterLabels(testPlan.options.labels);
labelsExpression = testPlan.options && testPlan.options.labelsExpression;
additionalDescription = testPlan.options && testPlan.options.additionalDescription;
pool = testPlan.options && testPlan.options.pool;
tags = testPlan.options && testPlan.options.tags && utils_1.filterTags(testPlan.options.tags);
parallel = testPlan.options && testPlan.options.parallel;
branch = testPlan.options && testPlan.options.branch;
inlineParameterOverride = !!(testPlan.options && testPlan.options.inlineParameterOverride);
maxFlakyFlowRetries = testPlan.options && testPlan.options.maxFlakyFlowRetries;
apiCatalogService = testPlan.options && testPlan.options.apiCatalogService;
turboParallel = !!(testPlan.options && testPlan.options.turboParallel);
return [4 /*yield*/, http_request_1.sendHttpRequest({
method: http_request_1.HttpMethods.POST,
url: testPlansAPI + "/" + testPlanId + "/run",
body: {
overrideParameters: overrideParameters,
additionalDescription: additionalDescription,
labels: labels,
pool: pool,
parallel: parallel,
tags: tags,
branch: branch,
maxFlakyFlowRetries: maxFlakyFlowRetries,
labelsExpression: labelsExpression,
inlineParameterOverride: inlineParameterOverride,
apiCatalogService: apiCatalogService,
turboParallel: turboParallel
},
token: token
})];
case 1:
_b = (_c.sent()).body, testPlanRunId = _b.testPlanRunId, err = _b.err;
if (err || !testPlanRunId) {
console.error(err ? JSON.stringify(err) : "The server encountered an error while handling the request");
return [2 /*return*/];
}
return [2 /*return*/, { id: testPlanRunId, type: Loadmill.TYPES.TEST_PLAN }];
}
});
});
}
function _junitReport(testResult, path) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, reporter_1.junitReport(testResult, token, path)];
});
});
}
function _mochawesomeReport(testResult, path) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, reporter_1.mochawesomeReport(testResult, token, path)];
});
});
}
return {
run: function (config, paramsOrCallback, callback) {
var _this = this;
return wrap(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var testId;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
config = toConfig(config, paramsOrCallback);
return [4 /*yield*/, http_request_1.sendHttpRequest({
method: http_request_1.HttpMethods.POST,
url: testingServer + "/api/tests",
body: config,
token: token
})];
case 1:
testId = (_a.sent()).body.testId;
return [4 /*yield*/, http_request_1.sendHttpRequest({
method: http_request_1.HttpMethods.PUT,
url: testingServer + "/api/tests/" + testId + "/load",
token: token
})];
case 2:
_a.sent();
return [2 /*return*/, testId];
}
});
}); }, callback || paramsOrCallback);
},
wait: function (testDefOrId, callback) {
return _wait(testDefOrId, callback);
},
runTestPlan: function (testPlan, params) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, _runTestPlan(testPlan, params)];
});
});
},
junitReport: function (testResult, path) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, _junitReport(testResult, path)];
});
});
},
mochawesomeReport: function (testResult, path) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
return [2 /*return*/, _mochawesomeReport(testResult, path)];
});
});
}
};
}
var isTestPassed = function (body, type) {
switch (type) {
case Loadmill.TYPES.SUITE:
case Loadmill.TYPES.TEST_PLAN:
return body.status === "PASSED";
default: //load
return body.result === 'done';
}
};
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) {
testResult.testSuitesRuns = reductTestSuitesRuns(body.testSuitesRuns);
}
function isTestInFinalState(body, runType) {
if (runType === Loadmill.TYPES.TEST_PLAN) {
if (body.testSuitesRuns.some(function (suite) { return suite.status === "RUNNING"; })) {
return false;
}
}
var trialResult = body.trialResult, result = body.result, status = body.status;
return ((result || trialResult === false) || // load tests
(status && status !== "RUNNING") // test suites or test plan
);
}
function getTestAPIUrl(_a, server) {
var id = _a.id, type = _a.type;
var prefix = server + "/api";
switch (type) {
case Loadmill.TYPES.SUITE:
return prefix + "/test-suites-runs/" + id;
case Loadmill.TYPES.TEST_PLAN:
return prefix + "/test-plans-runs/" + id;
default: //load
return prefix + "/tests/" + id;
}
}
function getTestWebUrl(_a, server) {
var id = _a.id, type = _a.type;
var prefix = server + "/app";
switch (type) {
case Loadmill.TYPES.SUITE:
return prefix + "/api-tests/test-suite-runs/" + id;
case Loadmill.TYPES.TEST_PLAN:
return prefix + "/api-tests/test-plan-runs/" + id;
default: //load
return prefix + "/test/" + id;
}
}
function reductTestSuitesRuns(suitesRuns) {
if (suitesRuns) {
return suitesRuns.map(function (s) {
var suiteRun = {
id: s.id,
type: Loadmill.TYPES.SUITE,
description: s.description,
status: s.status,
url: s.url,
passed: s.status === "PASSED",
startTime: s.startTime,
endTime: s.endTime
};
s.error && (suiteRun.error = s.error.message);
if (Array.isArray(s.testSuiteFlowRuns)) {
suiteRun.flowRuns = s.testSuiteFlowRuns.map(function (fr) {
var _a;
var flowRun = {
id: fr.id,
status: fr.status,
description: fr.description,
flowStatus: fr.testSuiteFlowStatus,
duration: (fr.endTime - fr.startTime) || 0
};
if ((_a = fr.runs) === null || _a === void 0 ? void 0 : _a.length) {
flowRun.retries = fr.runs.length - 1;
flowRun.duration = fr.runs[fr.runs.length - 1].endTime - fr.runs[0].startTime;
}
return flowRun;
});
}
return suiteRun;
});
}
}
function wrap(asyncFunction, paramsOrCallback) {
var promise = asyncFunction();
if (typeof paramsOrCallback === 'function') {
promise.then(function (res) { return paramsOrCallback(null, res); })["catch"](function (err) { return paramsOrCallback(err, null); });
}
else {
return promise;
}
}
function toConfig(config, paramsOrCallback) {
if (typeof config === 'string') {
var text = fs.readFileSync(config).toString();
config = JSON.parse(text);
}
if (typeof paramsOrCallback === 'object' && paramsOrCallback != null) {
var parameters = config.parameters;
if (!parameters) {
config.parameters = paramsOrCallback;
}
else if (typeof parameters.push === 'function') {
parameters.push(paramsOrCallback);
}
else {
config.parameters = [parameters, paramsOrCallback];
}
}
return config;
}
function toParams(params, filePath) {
if (params === void 0) { params = {}; }
if (filePath) {
var fileParams = utils_1.toLoadmillParams(utils_1.readRawParams(filePath));
return tslib_1.__assign(tslib_1.__assign({}, fileParams), params);
}
return params;
}
(function (Loadmill) {
var TYPES;
(function (TYPES) {
TYPES["LOAD"] = "load";
TYPES["SUITE"] = "test-suite";
TYPES["TEST_PLAN"] = "test-plan";
})(TYPES = Loadmill.TYPES || (Loadmill.TYPES = {}));
;
})(Loadmill || (Loadmill = {}));
module.exports = Loadmill;