@mmisty/cypress-allure-adapter
Version:
cypress allure adapter to generate allure results during tests execution (Allure TestOps compatible)
971 lines (970 loc) • 41.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AllureReporter = void 0;
const allure_js_commons_1 = require("allure-js-commons");
const uuid_by_string_1 = __importDefault(require("uuid-by-string"));
const allure_js_parser_1 = require("allure-js-parser");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const path_1 = __importStar(require("path"));
const fast_glob_1 = __importDefault(require("fast-glob"));
const debug_1 = __importDefault(require("debug"));
const allure_global_hook_1 = require("./allure-global-hook");
const allure_types_1 = require("./allure-types");
const common_1 = require("../common");
const crypto_1 = require("crypto");
const fs_tools_1 = require("./fs-tools");
const helper_1 = require("./helper");
const beforeEachHookName = '"before each" hook';
const beforeAllHookName = '"before all" hook';
const afterEachHookName = '"after each" hook';
const isBeforeEachHook = (ttl) => (ttl === null || ttl === void 0 ? void 0 : ttl.indexOf(beforeEachHookName)) !== -1;
const isAfterEachHook = (ttl) => (ttl === null || ttl === void 0 ? void 0 : ttl.indexOf(afterEachHookName)) !== -1;
const isBeforeAllHook = (ttl) => (ttl === null || ttl === void 0 ? void 0 : ttl.indexOf(beforeAllHookName)) !== -1;
const log = (0, debug_1.default)('cypress-allure:reporter');
const allTasks = [];
const createNewContentForContainer = (nameAttAhc, existingContents, ext, specname) => {
const getContentJson = () => {
try {
return JSON.parse(existingContents.toString());
}
catch (e) {
(0, common_1.logWithPackage)('error', `Could not parse the contents of attachment ${nameAttAhc}`);
return {};
}
};
const containerJSON = getContentJson();
const after = {
name: 'video',
attachments: [
{
name: `${specname}${ext}`,
type: 'video/mp4',
source: nameAttAhc,
},
],
parameters: [],
start: Date.now(),
stop: Date.now(),
status: allure_js_commons_1.Status.PASSED,
statusDetails: {},
stage: allure_types_1.Stage.FINISHED,
steps: [],
};
if (!containerJSON.afters) {
containerJSON.afters = [];
}
containerJSON.afters.push(after);
return containerJSON;
};
/**
* Will copy test results and all attachments to watch folder
* for results to appear in TestOps
* @param input
* @param allureResultsWatch
*/
const copyFileToWatch = (input, allureResultsWatch) => {
const { test: allureResultFile, attachments } = input;
const allureResults = path_1.default.dirname(allureResultFile);
if (allureResults === allureResultsWatch) {
log(`copyFileToWatch allureResultsWatch the same as allureResults ${allureResults}, will not copy`);
return;
}
(0, fs_tools_1.mkdirSyncWithTry)(allureResultsWatch);
log(`allureResults: ${allureResults}`);
log(`allureResultsWatch: ${allureResultsWatch}`);
log(`attachments: ${JSON.stringify(attachments)}`);
(0, fs_tools_1.copyAttachments)(allTasks, attachments, allureResultsWatch, allureResultFile);
(0, fs_tools_1.copyTest)(allTasks, allureResultFile, allureResultsWatch);
};
/**
* Get all attachments for test to move them to watch folder
* @param item test item
*/
const getAllAttachments = (item) => {
const attachmentsResult = [];
const inner = (steps, accumulatedRes) => {
if (steps.length === 0) {
return accumulatedRes;
}
const [first, ...rest] = steps;
const newRes = [...accumulatedRes, ...first.attachments];
return inner(rest, newRes);
};
const stepAttachments = inner(item.steps, attachmentsResult);
return [...stepAttachments, ...item.attachments];
};
// all tests for session
const allTests = [];
class AllureReporter {
constructor(opts) {
var _a, _b;
this.groups = [];
this.tests = [];
this.steps = [];
this.labels = [];
this.globalHooks = new allure_global_hook_1.GlobalHooks(this);
// this is variable for global hooks only
this.hooks = [];
this.allHooks = [];
this.descriptionHtml = [];
this.screenshotsTest = [];
this.showDuplicateWarn = opts.showDuplicateWarn;
this.allureResults = opts.allureResults;
this.allureResultsWatch = opts.techAllureResults;
this.allureAddVideoOnPass = opts.allureAddVideoOnPass;
this.videos = opts.videos;
this.screenshots = opts.screenshots;
this.allureSkipSteps =
(_b = (_a = opts.allureSkipSteps) === null || _a === void 0 ? void 0 : _a.split(',').map(x => new RegExp(`^${x.replace(/\./g, '.').replace(/\*/g, '.*')}$`))) !== null && _b !== void 0 ? _b : [];
log('Created reporter');
log(opts);
this.allureRuntime = new allure_js_commons_1.AllureRuntime({ resultsDir: this.allureResults });
}
get currentTestAll() {
if (this.currentTest && allTests[allTests.length - 1]) {
return allTests[allTests.length - 1];
}
return undefined;
}
get currentGroup() {
if (this.groups.length === 0) {
return undefined;
}
return this.groups[this.groups.length - 1];
}
get currentTest() {
if (this.tests.length === 0) {
log('No current test!');
return undefined;
}
log('current test');
return this.tests[this.tests.length - 1];
}
get currentHook() {
if (this.hooks.length === 0) {
return undefined;
}
log('current hook');
return this.hooks[this.hooks.length - 1].hook;
}
get currentStep() {
if (this.steps.length === 0) {
return undefined;
}
log('current step');
return this.steps[this.steps.length - 1];
}
get currentExecutable() {
return this.currentStep || this.currentHook || this.currentTest;
}
addGlobalHooks(_nestedLevel) {
log('>>> add Global Hooks');
if (!this.globalHooks.hasHooks()) {
log('not root hooks');
return;
}
log('add root hooks');
this.globalHooks.process();
}
suiteStarted(arg) {
var _a;
const { title } = arg;
log(`start group: ${title}`);
const group = ((_a = this.currentGroup) !== null && _a !== void 0 ? _a : this.allureRuntime).startGroup(title);
this.groups.push(group);
log(`SUITES: ${JSON.stringify(this.groups.map(t => t.name))}`);
this.addGlobalHooks(this.groups.length);
this.addHooks(this.groups.length);
}
addHooks(nested) {
const hooks = this.allHooks.filter(t => t.nested <= nested - 1);
if (!!this.currentGroup && hooks.length > 0) {
hooks.forEach(hk => {
const currentHook = isBeforeAllHook(hk.hook.name)
? this.currentGroup.addBefore()
: this.currentGroup.addAfter();
currentHook.name = hk.name;
currentHook.wrappedItem.status = hk.hook.status;
currentHook.wrappedItem.stop = hk.hook.wrappedItem.stop;
currentHook.wrappedItem.start = hk.hook.wrappedItem.start;
currentHook.wrappedItem.attachments = hk.hook.wrappedItem.attachments;
currentHook.wrappedItem.statusDetails = hk.hook.wrappedItem.statusDetails;
currentHook.wrappedItem.parameters = hk.hook.wrappedItem.parameters;
});
}
}
specStarted(args) {
log('SPEC started');
log(JSON.stringify(args));
this.currentSpec = args.spec;
if (!(0, fs_1.existsSync)(this.allureResults)) {
(0, fs_1.mkdirSync)(this.allureResults, { recursive: true });
}
}
hookStarted(arg) {
var _a, _b;
const { title, hookId, date } = arg !== null && arg !== void 0 ? arg : {};
if (!this.currentGroup) {
log(`no current group - start added hook to storage: ${JSON.stringify(arg)}`);
this.globalHooks.start(title, hookId);
return;
}
if (!title) {
return;
}
// when before each or after each we create just step inside current test
if (this.currentTest && (isBeforeEachHook(title) || isAfterEachHook(title))) {
log(`${title} will not be added to suite:${hookId} ${title}`);
// need to end all steps before logging hook - it should be logged as parent
this.endAllSteps({ status: allure_types_1.UNKNOWN });
this.startStep({ name: title });
return;
}
if (this.allureSkipSteps.every(t => !t.test(title))) {
const currentHook = isBeforeAllHook(title) ? this.currentGroup.addBefore() : this.currentGroup.addAfter();
currentHook.name = title;
currentHook.wrappedItem.start = date !== null && date !== void 0 ? date : Date.now();
this.hooks.push({ id: hookId, hook: currentHook, nested: this.groups.length, name: title });
this.allHooks.push({
id: hookId,
hook: currentHook,
suite: (_a = this.currentGroup) === null || _a === void 0 ? void 0 : _a.uuid,
nested: this.groups.length,
name: title,
});
}
else {
// create but not add to suite for steps to be added there
const currentHook = new allure_js_commons_1.ExecutableItemWrapper({
name: title,
uuid: '',
historyId: '',
links: [],
attachments: [],
parameters: [],
labels: [],
steps: [],
statusDetails: { message: '', trace: '' },
stage: allure_types_1.Stage.FINISHED,
});
currentHook.wrappedItem.start = date !== null && date !== void 0 ? date : Date.now();
this.hooks.push({ id: hookId, hook: currentHook, nested: this.groups.length, name: title });
this.allHooks.push({
id: hookId,
hook: currentHook,
suite: (_b = this.currentGroup) === null || _b === void 0 ? void 0 : _b.uuid,
nested: this.groups.length,
name: title,
});
}
}
setExecutableStatus(executable, res, dtls) {
if (!executable) {
return;
}
if (res === allure_js_commons_1.Status.PASSED) {
executable.status = allure_js_commons_1.Status.PASSED;
executable.stage = allure_types_1.Stage.FINISHED;
}
if (res === allure_js_commons_1.Status.BROKEN) {
executable.status = allure_js_commons_1.Status.BROKEN;
executable.stage = allure_types_1.Stage.FINISHED;
}
if (res === allure_js_commons_1.Status.FAILED) {
executable.status = allure_js_commons_1.Status.FAILED;
executable.stage = allure_types_1.Stage.FINISHED;
executable.detailsMessage = dtls === null || dtls === void 0 ? void 0 : dtls.message;
executable.detailsTrace = dtls === null || dtls === void 0 ? void 0 : dtls.trace;
}
if (res === allure_js_commons_1.Status.SKIPPED) {
executable.status = allure_js_commons_1.Status.SKIPPED;
executable.stage = allure_types_1.Stage.PENDING;
executable.detailsMessage = (dtls === null || dtls === void 0 ? void 0 : dtls.message) || 'Suite disabled';
}
if (res !== allure_js_commons_1.Status.FAILED && res !== allure_js_commons_1.Status.BROKEN && res !== allure_js_commons_1.Status.PASSED && res !== allure_js_commons_1.Status.SKIPPED) {
executable.status = allure_types_1.UNKNOWN;
executable.stage = allure_types_1.Stage.PENDING;
executable.detailsMessage = (dtls === null || dtls === void 0 ? void 0 : dtls.message) || `Result: ${res !== null && res !== void 0 ? res : '<no>'}`;
}
if (dtls) {
executable.statusDetails = dtls;
}
}
setExecutableItemStatus(executableItem, res, dtls) {
if (!executableItem) {
return;
}
executableItem.status = res;
if (res === allure_js_commons_1.Status.FAILED) {
if (dtls) {
executableItem.statusDetails.message = dtls === null || dtls === void 0 ? void 0 : dtls.message;
executableItem.statusDetails.trace = dtls === null || dtls === void 0 ? void 0 : dtls.trace;
}
}
if (res === allure_js_commons_1.Status.SKIPPED) {
executableItem.statusDetails.message = dtls === null || dtls === void 0 ? void 0 : dtls.message;
}
// unknown
if (res !== allure_js_commons_1.Status.FAILED && res !== allure_js_commons_1.Status.BROKEN && res !== allure_js_commons_1.Status.PASSED && res !== allure_js_commons_1.Status.SKIPPED) {
executableItem.statusDetails.message = dtls === null || dtls === void 0 ? void 0 : dtls.message;
}
}
hookEnded(arg) {
var _a;
const { title, date, result, details } = arg !== null && arg !== void 0 ? arg : {};
if (!this.currentGroup) {
log('no current group - will end hook in storage');
this.globalHooks.end(result, details);
return;
}
if (isBeforeEachHook(title) || isAfterEachHook(title)) {
this.endStep({ status: ((_a = this.currentStep) === null || _a === void 0 ? void 0 : _a.isAnyStepFailed) ? allure_js_commons_1.Status.FAILED : allure_js_commons_1.Status.PASSED });
this.endAllSteps({ status: allure_types_1.UNKNOWN });
return;
}
if (this.currentHook) {
this.filterSteps(this.currentHook.wrappedItem, this.allureSkipSteps);
this.currentHook.wrappedItem.stop = date !== null && date !== void 0 ? date : Date.now();
this.setExecutableStatus(this.currentHook, result, details);
(0, helper_1.mergeStepsWithSingleChild)(this.currentHook.wrappedItem.steps);
this.hooks.pop();
return;
}
}
endHooks(status = allure_js_commons_1.Status.PASSED) {
this.hooks.forEach(h => {
this.hookEnded({ title: h.hook.name, result: status });
});
}
// after spec attach
attachScreenshots(arg) {
// attach auto screenshots for fails
const { screenshots } = arg;
log('attachScreenshots:');
if (!screenshots) {
return;
}
log('screenshotsTest:');
log(JSON.stringify(this.screenshotsTest));
log('screenshots arg:');
log(JSON.stringify(screenshots));
const arr = [...screenshots, ...this.screenshotsTest.filter(x => !x.attached)];
const uniqueScreenshotsArr = arr.reduce((acc, current) => {
const key = `${current.path}`;
if (!acc.map.has(key)) {
acc.map.set(key, true);
current.specName = (0, path_1.basename)((0, path_1.dirname)(current.path));
acc.list.push(current);
}
else {
const existing = acc.list.find(t => t.path === current.path);
const merged = Object.assign(Object.assign({}, existing), current);
acc.list = acc.list.map(item => (item.path === current.path ? merged : item));
}
return acc;
}, { map: new Map(), list: [] }).list;
uniqueScreenshotsArr.forEach(afterSpecRes => {
var _a;
log(`attachScreenshots: ${afterSpecRes.path}`);
const getUuiToAdd = () => {
return allTests.filter(t => {
var _a;
return t.status !== allure_js_commons_1.Status.PASSED &&
t.retryIndex === afterSpecRes.testAttemptIndex &&
(0, path_1.basename)((_a = t.specRelative) !== null && _a !== void 0 ? _a : '') === afterSpecRes.specName &&
(afterSpecRes.testId ? t.mochaId === afterSpecRes.testId : true);
});
};
const uuids = getUuiToAdd().map(t => t.uuid);
if (uuids.length === 0) {
log('no attach auto screens, only for non-success tests tests');
return;
}
if (afterSpecRes.testAttemptIndex && afterSpecRes.testId && !uuids[(_a = afterSpecRes.testAttemptIndex) !== null && _a !== void 0 ? _a : 0]) {
log(`no attach, current attempt ${afterSpecRes.testAttemptIndex}`);
// test passed or no
return;
}
uuids.forEach(uuid => {
var _a;
const testFile = `${this.allureResults}/${uuid}-result.json`;
try {
const contents = (0, fs_1.readFileSync)(testFile);
const ext = path_1.default.extname(afterSpecRes.path);
const name = path_1.default.basename(afterSpecRes.path);
const testCon = JSON.parse(contents.toString());
const uuidNew = (0, crypto_1.randomUUID)();
const nameAttach = `${uuidNew}-attachment${ext}`; // todo not copy same image
const newPath = path_1.default.join(this.allureResults, nameAttach);
if (!(0, fs_1.existsSync)(newPath)) {
(0, fs_1.copyFileSync)(afterSpecRes.path, path_1.default.join(this.allureResults, nameAttach));
}
if (!testCon.attachments) {
testCon.attachments = [];
}
testCon.attachments.push({
name: name,
type: 'image/png',
source: nameAttach, // todo
});
(0, fs_1.writeFileSync)(testFile, JSON.stringify(testCon));
}
catch (e) {
(0, common_1.logWithPackage)('error', `Could not attach screenshot ${(_a = afterSpecRes.screenshotId) !== null && _a !== void 0 ? _a : afterSpecRes.path}`);
}
});
});
}
keyWhenNoTest(testId) {
return testId !== null && testId !== void 0 ? testId : 'NoTestId';
}
screenshotAttachment(arg) {
const { testId, path, testAttemptIndex, specName, testFailure } = arg;
this.screenshotsTest.push({ testId, path, testAttemptIndex, specName, testFailure });
}
screenshotOne(arg) {
const { name, forStep } = arg;
if (!name) {
log('No name specified for screenshot, will not attach');
return;
}
const pattern = `${this.screenshots}/**/${name}*.png`;
const files = fast_glob_1.default.sync(pattern);
if (files.length === 0) {
log(`NO SCREENSHOTS: ${pattern}`);
return;
}
files.forEach(file => {
const executable = this.currentExecutable;
const attachTo = forStep && this.currentStep ? this.currentStep : executable;
// to have it in allure-results directory
const newUuid = (0, crypto_1.randomUUID)();
const fileNew = `${newUuid}-attachment.png`;
if (!(0, fs_1.existsSync)(this.allureResults)) {
(0, fs_1.mkdirSync)(this.allureResults, { recursive: true });
}
if (!(0, fs_1.existsSync)(file)) {
(0, common_1.logWithPackage)('log', `file ${file} doesnt exist`);
return;
}
(0, fs_1.copyFileSync)(file, `${this.allureResults}/${fileNew}`);
attachTo === null || attachTo === void 0 ? void 0 : attachTo.addAttachment((0, path_1.basename)(file), { contentType: 'image/png', fileExtension: 'png' }, fileNew);
});
}
waitAllTasksToFinish() {
return __awaiter(this, void 0, void 0, function* () {
yield Promise.all(allTasks)
.then(() => {
log(`All tasks completed (${allTasks.length})`);
})
.catch(err => {
(0, common_1.logWithPackage)('error', `Some of tasks (${allTasks.length}) failed:`);
// eslint-disable-next-line no-console
console.log(err);
});
log('All files / video copying tasks finished!');
});
}
/**
* Attach video to parent suite
* @param arg {path: string}
*/
attachVideoToContainers(arg) {
// this happens after test and suites have already finished
const { path: videoPath } = arg;
log(`attachVideoToTests: ${videoPath}`);
const ext = '.mp4';
const specname = (0, path_1.basename)(videoPath, ext);
log(specname);
// when video uploads everything is uploaded already (TestOps) except containers
const res = (0, allure_js_parser_1.parseAllure)(this.allureResults);
const tests = res
.filter(t => (this.allureAddVideoOnPass ? true : t.status !== 'passed' && t.status !== 'skipped'))
.map(t => {
var _a;
return ({
path: (_a = t.labels.find((l) => l.name === 'path')) === null || _a === void 0 ? void 0 : _a.value,
id: t.uuid,
fullName: t.fullName,
parent: t.parent,
});
});
const testsAttach = tests.filter(t => t.path && t.path.indexOf(specname) !== -1);
try {
(0, fs_1.readFileSync)(videoPath);
}
catch (errVideo) {
(0, common_1.logWithPackage)('error', `Could not read video: ${errVideo}`);
return;
}
allTasks.push(...testsAttach.map(test => {
if (!test.parent) {
(0, common_1.logWithPackage)('error', `not writing videos since test has no parent suite: ${test.fullName}`);
return Promise.resolve();
}
const containerFile = `${this.allureResults}/${test.parent.uuid}-container.json`;
log(`ATTACHING to container: ${containerFile}`);
return (0, promises_1.readFile)(containerFile)
.then(contents => {
const uuid = (0, crypto_1.randomUUID)();
const nameAttAhc = `${uuid}-attachment${ext}`;
const newPath = path_1.default.join(this.allureResults, nameAttAhc);
const newContentJson = createNewContentForContainer(nameAttAhc, contents, ext, specname);
const newContent = JSON.stringify(newContentJson);
const writeContainer = () => {
log(`write result file ${containerFile} `);
return (0, fs_tools_1.writeResultFile)(containerFile, newContent);
};
if ((0, fs_1.existsSync)(newPath)) {
log(`not writing video, file already exists in path ${newPath} `);
return writeContainer();
}
return (0, fs_tools_1.copyFileCp)(videoPath, newPath, false).then(writeContainer);
})
.catch(err => {
log(`error reading container: ${err.message}`);
});
}));
}
endGroup() {
var _a;
this.addGlobalHooks(this.groups.length);
if (this.currentGroup) {
log('END GROUP');
(_a = this.currentGroup) === null || _a === void 0 ? void 0 : _a.endGroup();
this.groups.pop();
}
}
endAllGroups() {
log('endAllGroups');
this.groups.forEach(g => {
g.endGroup();
});
this.allHooks = [];
}
label(arg) {
if (this.currentTest) {
this.currentTest.addLabel(arg.name, arg.value);
}
}
link(arg) {
if (this.currentTest) {
this.currentTest.addLink(arg.url, arg.name, arg.type);
}
}
fullName(arg) {
if (this.currentTest) {
this.currentTest.fullName = arg.value;
// should update history id when updating title
this.currentTest.historyId = (0, uuid_by_string_1.default)(arg.value);
}
}
historyId(arg) {
if (this.currentTest) {
this.currentTest.historyId = arg.value;
}
}
parameter(arg) {
if (this.currentExecutable) {
this.currentExecutable.addParameter(arg.name, arg.value);
}
}
addGroupLabelByUser(label, value) {
if (value === undefined) {
// remove suite labels
this.labels = this.labels.filter(t => t.name !== label);
}
else {
this.labels.push({ name: label, value: value });
}
}
suite(arg) {
if (!this.currentTest) {
return;
}
this.addGroupLabelByUser(allure_types_1.LabelName.SUITE, arg.name);
}
parentSuite(arg) {
if (!this.currentTest) {
return;
}
this.addGroupLabelByUser(allure_types_1.LabelName.PARENT_SUITE, arg.name);
}
subSuite(arg) {
if (!this.currentTest) {
return;
}
this.addGroupLabelByUser(allure_types_1.LabelName.SUB_SUITE, arg.name);
}
testParameter(arg) {
if (this.currentTest) {
this.currentTest.addParameter(arg.name, arg.value);
}
}
testFileAttachment(arg) {
this.executableFileAttachment(this.currentTest, arg);
}
fileAttachment(arg) {
this.executableFileAttachment(this.currentExecutable, arg);
}
testAttachment(arg) {
this.executableAttachment(this.currentTest, arg);
}
attachment(arg) {
this.executableAttachment(this.currentExecutable, arg);
}
addGroupLabels() {
var _a, _b;
const [parentSuite, suite, subSuite] = this.groups;
if (this.currentSpec) {
const paths = (_a = this.currentSpec.relative) === null || _a === void 0 ? void 0 : _a.split('/');
(_b = this.currentTest) === null || _b === void 0 ? void 0 : _b.addLabel(allure_types_1.LabelName.PACKAGE, paths.join('.'));
}
if (this.groups.length > 0) {
this.labels.push({ name: allure_types_1.LabelName.PARENT_SUITE, value: parentSuite.name });
}
if (this.groups.length > 1) {
this.labels.push({ name: allure_types_1.LabelName.SUITE, value: suite.name });
}
if (this.groups.length > 2) {
this.labels.push({ name: allure_types_1.LabelName.SUB_SUITE, value: subSuite.name });
}
}
startTest(arg) {
var _a, _b, _c;
const { title, fullTitle, id, currentRetry } = arg;
if (this.currentTest) {
// temp fix of defect with wrong event sequence
log(`will not start already started test: ${fullTitle}`);
return;
}
const duplicates = allTests.filter(t => t.fullTitle === fullTitle);
const warn = 'Starting test with the same fullName as already exist, will be shown as ' +
`retried: ${fullTitle}\nTo solve this rename the test. Spec ${(_a = this.currentSpec) === null || _a === void 0 ? void 0 : _a.relative}, ` +
`test full title: ${fullTitle}`;
if (duplicates.length > 0 && currentRetry === 0 && this.showDuplicateWarn) {
(0, common_1.logWithPackage)('warn', warn);
}
if (!this.currentGroup) {
// fallback
this.suiteStarted({ title: 'Root suite', fullTitle: 'Root suite' });
}
const group = this.currentGroup;
const test = group.startTest(title);
allTests.push({
retryIndex: currentRetry,
specRelative: (_b = this.currentSpec) === null || _b === void 0 ? void 0 : _b.relative,
fullTitle,
mochaId: id,
uuid: test.uuid,
});
this.tests.push(test);
test.fullName = fullTitle;
test.historyId = (0, uuid_by_string_1.default)(fullTitle);
this.addGroupLabels();
if ((_c = this.currentSpec) === null || _c === void 0 ? void 0 : _c.relative) {
test.addLabel('path', this.currentSpec.relative);
}
this.globalHooks.processForTest();
}
endTests() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const _tst of this.tests) {
this.endTest({ result: allure_types_1.UNKNOWN, details: undefined });
}
}
endGroups() {
this.endTests();
this.groups.forEach(() => {
this.endGroup();
});
}
endAll() {
this.endAllSteps({ status: allure_types_1.UNKNOWN, details: undefined });
this.endHooks(allure_js_commons_1.Status.BROKEN);
this.endGroups();
}
addDescriptionHtml(arg) {
this.descriptionHtml.push(arg.value);
this.applyDescriptionHtml();
}
applyDescriptionHtml() {
if (this.currentTest) {
this.currentTest.descriptionHtml = this.descriptionHtml.join('');
}
}
testStatus(arg) {
if (!this.currentTest) {
return;
}
this.testStatusStored = arg;
}
testDetails(arg) {
if (!this.currentTest) {
return;
}
this.testDetailsStored = arg;
}
applyGroupLabels() {
// apply labels
const applyLabel = (name) => {
if (!this.currentTest) {
return;
}
const lb = this.labels.filter(l => l.name == name);
// return last added
const lastLabel = lb[lb.length - 1];
if (lastLabel) {
this.currentTest.addLabel(lastLabel.name, lastLabel.value);
}
};
applyLabel(allure_types_1.LabelName.PARENT_SUITE);
applyLabel(allure_types_1.LabelName.SUITE);
applyLabel(allure_types_1.LabelName.SUB_SUITE);
}
filterSteps(result, skipSteps) {
if (result && result.steps.length > 0) {
result.steps = result.steps.filter(t => !skipSteps.some(x => { var _a; return x.test((_a = t.name) !== null && _a !== void 0 ? _a : ''); }));
result.steps.forEach(res => {
this.filterSteps(res, skipSteps);
});
}
}
endTest(arg) {
const { result, details } = arg;
const storedStatus = this.testStatusStored;
const storedDetails = this.testDetailsStored;
/*
todo case when all steps finished but test failed
if (this.steps.length === 0) {
*/
// all ended already
/*this.currentExecutable?.wrappedItem.steps && this.currentExecutable.wrappedItem.steps.length > 0) {
this.startStep({ name: 'some of previous steps failed' });
this.endStep({ status: arg.result, details: arg.details });
}
}
*/
this.endAllSteps({ status: result, details });
if (!this.currentTest) {
return;
}
(0, helper_1.removeFirstStepWhenSame)(this.currentTest.wrappedItem.steps);
(0, helper_1.mergeStepsWithSingleChild)(this.currentTest.wrappedItem.steps);
if (this.currentTestAll) {
this.currentTestAll.status = result;
}
// filter steps here
this.filterSteps(this.currentTest.wrappedItem, this.allureSkipSteps);
this.currentTest.wrappedItem.steps = (0, helper_1.wrapHooks)('"before each" hook', this.currentTest.wrappedItem.steps);
this.currentTest.wrappedItem.steps = (0, helper_1.wrapHooks)('"after each" hook', this.currentTest.wrappedItem.steps);
this.setExecutableStatus(this.currentTest, result, details);
if (storedDetails) {
this.setExecutableStatus(this.currentTest, result, storedDetails.details);
}
if (storedStatus) {
this.setExecutableStatus(this.currentTest, storedStatus.result, storedStatus.details);
}
this.applyGroupLabels();
const uid = this.currentTest.uuid;
//const resAtt: Attachment[] = [...this.currentTest.wrappedItem.attachments];
const attachments = getAllAttachments(this.currentTest.wrappedItem);
this.currentTest.endTest();
this.tests.pop();
this.descriptionHtml = [];
this.testStatusStored = undefined;
this.testDetailsStored = undefined;
this.labels = [];
const testFile = `${this.allureResults}/${uid}-result.json`;
if (!(0, fs_1.existsSync)(testFile)) {
(0, common_1.logWithPackage)('error', ` Result file doesn't exist: ${testFile}`);
}
log('testEnded: will move result to watch folder');
copyFileToWatch({ test: testFile, attachments }, this.allureResultsWatch);
}
startStep(arg) {
const { name, date } = arg;
if (!this.currentExecutable || this.globalHooks.currentHook) {
log('will start step for global hook');
this.globalHooks.startStep(name);
return;
}
log('start step for current executable');
const step = this.currentExecutable.startStep(name, date);
this.steps.push(step);
}
endAllSteps(arg) {
while (this.steps.length !== 0) {
this.endStep(arg);
}
}
// set status to last step recursively when unknown or passed statuses
setLastStepStatus(steps, status, details) {
const stepsCount = steps.length;
const step = steps[stepsCount - 1];
const stepStatus = step === null || step === void 0 ? void 0 : step.status;
if (stepsCount > 0) {
this.setLastStepStatus(step.steps, status, details);
}
if (!stepStatus || ![allure_js_commons_1.Status.FAILED, allure_js_commons_1.Status.SKIPPED, allure_js_commons_1.Status.BROKEN].includes(stepStatus)) {
this.setExecutableItemStatus(step, status, details);
}
}
hasChildrenWith(steps, statuses) {
const stepsCount = steps.length;
let hasError = false;
steps.forEach(step => {
const stepStatus = step.status;
if (stepStatus && stepsCount > 0 && statuses.includes(stepStatus)) {
hasError = true;
}
if (stepsCount > 0) {
return this.hasChildrenWith(step.steps, statuses);
}
});
return hasError;
}
endStep(arg) {
const { status, date, details } = arg;
if (!this.currentExecutable) {
log('No current executable, test or hook - will end step for global hook');
this.globalHooks.endStep(arg.status, details);
return;
}
if (!this.currentStep) {
//this.setLastStepStatus(this.currentExecutable.wrappedItem.steps, status, details);
return;
}
const markBrokenStatuses = ['failed', 'broken'];
const passedStatuses = ['passed'];
// when unknown or passed
this.setLastStepStatus(this.currentStep.wrappedItem.steps, status, details);
if (passedStatuses.includes(status) &&
this.hasChildrenWith(this.currentStep.wrappedItem.steps, markBrokenStatuses)) {
this.setExecutableStatus(this.currentStep, allure_js_commons_1.Status.BROKEN);
}
else {
this.setExecutableStatus(this.currentStep, status, details);
}
this.currentStep.endStep(date !== null && date !== void 0 ? date : Date.now());
this.steps.pop();
}
executableAttachment(exec, arg) {
var _a;
if (!exec) {
log('No current executable - will not attach');
return;
}
const file = this.allureRuntime.writeAttachment((_a = arg.content) !== null && _a !== void 0 ? _a : `Could not create attachment: no content for ${arg.name} received`, arg.type);
exec.addAttachment(arg.name, arg.type, file);
}
setAttached(file) {
const screen = this.screenshotsTest.find(t => t.path === file);
if (screen) {
screen.attached = true;
}
}
executableFileAttachment(exec, arg) {
if (!this.currentExecutable && this.globalHooks.currentHook) {
log('No current executable, test or hook - add to global hook');
this.globalHooks.attachment(arg.name, arg.file, arg.type);
return;
}
if (!exec && !this.currentExecutable) {
return;
}
if (!(0, fs_1.existsSync)(arg.file)) {
(0, common_1.logWithPackage)('error', `Attaching file: file ${arg.file} doesnt exist`);
return;
}
try {
const uuid = (0, crypto_1.randomUUID)();
// to have it in allure-results directory
const fileNew = `${uuid}-attachment${(0, common_1.extname)(arg.file)}`;
if (!(0, fs_1.existsSync)(this.allureResults)) {
(0, fs_1.mkdirSync)(this.allureResults, { recursive: true });
}
const currExec = exec !== null && exec !== void 0 ? exec : this.currentExecutable;
if (currExec) {
(0, fs_1.copyFileSync)(arg.file, `${this.allureResults}/${fileNew}`);
currExec.addAttachment(arg.name, arg.type, fileNew);
log(`added attachment: ${fileNew} ${arg.file}`);
this.setAttached(arg.file);
}
}
catch (err) {
(0, common_1.logWithPackage)('error', `Could not attach ${arg.file}`);
}
}
getEnvInfo(resultsFolder) {
var _a;
const fileName = 'environment.properties';
const envPropsFile = `${resultsFolder}/${fileName}`;
if (!(0, fs_1.existsSync)(envPropsFile)) {
return {};
}
if ((0, fs_1.existsSync)(envPropsFile)) {
try {
const env = (_a = (0, fs_1.readFileSync)(envPropsFile)) === null || _a === void 0 ? void 0 : _a.toString();
const res = {};
env === null || env === void 0 ? void 0 : env.split('\n').forEach(line => {
const keyValue = line.split(' = ');
res[keyValue[0]] = keyValue[1];
});
return res;
}
catch (err) {
(0, common_1.logWithPackage)('error', 'could not get exisitng environemnt info');
return {};
}
}
return {};
}
}
exports.AllureReporter = AllureReporter;