codeceptjs-testit-reporter
Version:
Plugin for sending CodeceptJS test run reports to TestIT
331 lines (330 loc) • 16.8 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var testit_api_client_1 = require("testit-api-client");
var StepsProcessor_1 = __importDefault(require("./StepsProcessor"));
var constants_1 = require("./constants");
var utils_1 = require("./utils");
var defaultConfig = {
namespace: constants_1.CJS_NAMESPACE,
};
var Adapter = /** @class */ (function () {
function Adapter(config) {
var _this = this;
this.initTestRun = function () { return __awaiter(_this, void 0, void 0, function () {
var testRunData, testRun, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
testRunData = this._prepareTestRunData();
return [4 /*yield*/, this.client.createTestRun(testRunData)];
case 1:
testRun = _a.sent();
this.testRunId = testRun.id;
return [4 /*yield*/, this.client.startTestRun(this.testRunId)];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
(0, utils_1.log)("Failed to create and start test run: ".concat((0, utils_1.parseError)(e_1)));
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); };
this._prepareTestRunData = function () {
var testRunDate = new Date();
return {
projectId: _this.config.projectId,
name: "".concat(testRunDate.toISOString(), ": ").concat(_this.config.namespace, " Run"),
};
};
this.completeTestRun = function () { return __awaiter(_this, void 0, void 0, function () {
var existingTests, existingTestsIds_1, filteredResults_1, testRunLink, e_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.testRunId) {
(0, utils_1.log)("Failed to complete test run: testRunId cannot be null");
return [2 /*return*/];
}
_a.label = 1;
case 1:
_a.trys.push([1, 5, , 6]);
return [4 /*yield*/, this.client.getAutotest({ projectId: this.config.projectId })];
case 2:
existingTests = _a.sent();
existingTestsIds_1 = new Set();
existingTests.forEach(function (t) { return existingTestsIds_1.add(t.externalId); });
filteredResults_1 = [];
this.testRunResults.forEach(function (r) {
// Autotest creates in TestIT only when test passed.
// If test failed at first run, autotest will not exist,
// so we cannot load results for it.
if (existingTestsIds_1.has(r.autotestExternalId))
filteredResults_1.push(r);
else
(0, utils_1.log)("Not found \"".concat(r.autotestExternalId, "\" in existing tests ids, test run result ignored"));
});
return [4 /*yield*/, this.client.completeTestRun(this.testRunId)];
case 3:
_a.sent();
return [4 /*yield*/, this.client.loadTestRunResults(this.testRunId, filteredResults_1)];
case 4:
_a.sent();
testRunLink = this._prepareTestRunLink();
if (testRunLink)
(0, utils_1.log)("Test run link: ".concat(testRunLink));
return [3 /*break*/, 6];
case 5:
e_2 = _a.sent();
(0, utils_1.log)("Failed to complete test run: ".concat((0, utils_1.parseError)(e_2)));
return [3 /*break*/, 6];
case 6: return [2 /*return*/];
}
});
}); };
this.forceCompleteTestRun = function () {
if (!_this.testRunId)
return;
(0, utils_1.log)('Trying to force complete test run..');
_this.client.completeTestRun(_this.testRunId)
.catch(function (e) { return (0, utils_1.log)("Failed to force complete test run: ".concat((0, utils_1.parseError)(e))); })
.finally(process.exit);
_this.testRunId = null;
};
this.initAutoTest = function (test) { return __awaiter(_this, void 0, void 0, function () {
var autoTest, autoTests, e_3, autoTestData, e_4;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
autoTest = null;
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.client.getAutotest({
projectId: this.config.projectId,
externalId: (0, utils_1.getTestId)(test),
})];
case 2:
autoTests = _a.sent();
autoTest = autoTests[0];
return [3 /*break*/, 4];
case 3:
e_3 = _a.sent();
(0, utils_1.log)("Failed to fetch autotest: ".concat((0, utils_1.parseError)(e_3)));
return [3 /*break*/, 4];
case 4:
autoTestData = this._prepareAutoTestData(test);
_a.label = 5;
case 5:
_a.trys.push([5, 10, , 11]);
if (!!autoTest) return [3 /*break*/, 7];
return [4 /*yield*/, this.client.createAutotest(autoTestData)];
case 6:
_a.sent();
return [3 /*break*/, 9];
case 7: return [4 /*yield*/, this.client.updateAutotest(autoTestData)];
case 8:
_a.sent();
_a.label = 9;
case 9: return [3 /*break*/, 11];
case 10:
e_4 = _a.sent();
(0, utils_1.log)("Failed to create/update autotest: ".concat((0, utils_1.parseError)(e_4)));
return [3 /*break*/, 11];
case 11: return [2 /*return*/];
}
});
}); };
this._prepareAutoTestData = function (test) {
var _a;
return {
projectId: _this.config.projectId,
externalId: (0, utils_1.getTestId)(test),
namespace: _this.config.namespace,
classname: (_a = test.parent) === null || _a === void 0 ? void 0 : _a.title,
name: test.title,
title: test.title,
steps: _this.stepsProcessor.getSteps(),
labels: test.tags.map(function (t) { return ({ name: t }); }),
};
};
this.resetTestState = function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.stepsProcessor.reset();
return [2 /*return*/];
});
}); };
this.initTest = function (test) {
_this.testTimeMap[(0, utils_1.getTestId)(test)] = new Date();
};
this.completeTest = function (test) { return __awaiter(_this, void 0, void 0, function () {
var attachment, testRunResult;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (!((_a = test.artifacts) === null || _a === void 0 ? void 0 : _a.screenshot)) return [3 /*break*/, 2];
return [4 /*yield*/, this._uploadAttachment(test.artifacts.screenshot)];
case 1:
attachment = _b.sent();
if (attachment === null || attachment === void 0 ? void 0 : attachment.id)
this.testAttachments[(0, utils_1.getTestId)(test)] = attachment.id;
_b.label = 2;
case 2:
testRunResult = this._prepareTestRunResult(test);
if (testRunResult)
this.testRunResults.push(testRunResult);
return [2 /*return*/];
}
});
}); };
this._uploadAttachment = function (filepath) { return __awaiter(_this, void 0, void 0, function () {
var attachment, e_5;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, this.client.loadAttachment(filepath)];
case 1:
attachment = _a.sent();
return [3 /*break*/, 3];
case 2:
e_5 = _a.sent();
(0, utils_1.log)("Failed to upload attachment: ".concat((0, utils_1.parseError)(e_5)));
return [2 /*return*/, null];
case 3: return [2 /*return*/, attachment];
}
});
}); };
this._prepareTestRunResult = function (test) {
var status = (0, utils_1.cjs2testItStatus)(test.state);
if (!status) {
(0, utils_1.log)("Cannot map CodeceptJS status \"".concat(test.state, "\" to TestIT status, test run result ignored"));
return null;
}
var testRunResult;
try {
testRunResult = {
configurationId: _this.config.configurationId,
autotestExternalId: (0, utils_1.getTestId)(test),
outcome: status,
duration: _this._getTestDuration(test),
startedOn: _this._getTestStartTime(test),
traces: (0, utils_1.parseTestError)(test),
stepResults: _this.stepsProcessor.getStepResults(),
};
if (_this.testAttachments[(0, utils_1.getTestId)(test)])
testRunResult.attachments = [{ id: _this.testAttachments[(0, utils_1.getTestId)(test)] }];
}
catch (e) {
(0, utils_1.log)("Failed to prepare test run result: ".concat(e === null || e === void 0 ? void 0 : e.toString()));
return null;
}
return testRunResult;
};
this._getTestDuration = function (test) {
return _this.testTimeMap[(0, utils_1.getTestId)(test)]
? Date.now() - _this.testTimeMap[(0, utils_1.getTestId)(test)].getTime()
: 0;
};
this._getTestStartTime = function (test) {
return _this.testTimeMap[(0, utils_1.getTestId)(test)]
? _this.testTimeMap[(0, utils_1.getTestId)(test)].toISOString()
: undefined;
};
this.initStep = function (step) {
_this.stepsProcessor.patchStepId(step);
};
this.completeStep = function (step) {
_this.stepsProcessor.processStep(step);
};
this.handleStepComment = function (step) {
_this.stepsProcessor.processComment(step);
};
this.testTimeMap = {};
this.testRunId = null;
this.testRunResults = [];
this.testAttachments = {};
this.stepsProcessor = new StepsProcessor_1.default();
this.config = __assign(__assign({}, defaultConfig), config);
var testItClientConfig = {
url: this.config.url,
configurationId: this.config.configurationId,
privateToken: this.config.privateToken,
projectId: this.config.projectId,
};
var emptyRequiredProps = Object.keys(testItClientConfig)
.filter(function (p) { return !testItClientConfig[p]; })
.map(function (p) { return p; });
if (emptyRequiredProps.length)
throw new Error("Invalid TestIT config, following properties are required: ".concat(emptyRequiredProps.join(", ")));
this.client = new testit_api_client_1.Client(testItClientConfig);
// axios default content-type for empty POST is "application/x-www-form-urlencoded",
// TestIT server rejects such requests with 415 status code starting from 4+ versions,
// so we can fix it changing defaults in TestIT client instance
// @ts-ignore
this.client.axios.defaults.headers.post['Content-Type'] = 'application/json';
}
Adapter.prototype._prepareTestRunLink = function () {
if (!this.config.url ||
!this.config.globalProjectId ||
!this.testRunId)
return null;
return "".concat(this.config.url, "/projects/").concat(this.config.globalProjectId, "/test-runs/").concat(this.testRunId);
};
return Adapter;
}());
exports.default = Adapter;