UNPKG

@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
"use strict"; 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;