UNPKG

@mmisty/cypress-allure-adapter

Version:

cypress allure adapter to generate allure results during tests execution (Allure TestOps compatible)

945 lines (944 loc) 39 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 __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 reporter_1 = require("allure-js-commons/sdk/reporter"); const uuid_by_string_1 = __importDefault(require("uuid-by-string")); const fs_1 = require("fs"); 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 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'); // all tests for session const allTests = []; class AllureReporter { constructor(opts, taskManager, client) { var _a, _b; this.taskManager = taskManager; this.client = client; this.groups = []; this.tests = []; this.steps = []; this.labels = []; this.globalHooks = new allure_global_hook_1.GlobalHooks(this); this.hooks = []; this.allHooks = []; this.taskQueueId = '__no_spec__'; this.descriptionHtml = []; this.screenshotsTest = []; this.allureResultsDirEnsured = false; 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); // Ensure allure results directory exists this.taskManager.addOperation('__init__', { type: 'fs:mkdir', path: this.allureResults, options: { recursive: true }, }); // Initialize with new API this.writer = new reporter_1.FileSystemWriter({ resultsDir: this.allureResults }); this.allureRuntime = new reporter_1.ReporterRuntime({ writer: this.writer }); } 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]; } get currentStep() { if (this.steps.length === 0) { return undefined; } log('current step'); return this.steps[this.steps.length - 1]; } get currentExecutable() { if (this.currentStep) { return { uuid: this.currentStep.uuid, result: this.currentStep.result, rootUuid: this.currentStep.rootUuid }; } if (this.currentHook) { return { uuid: this.currentHook.uuid, result: this.currentHook.result }; } if (this.currentTest) { return { uuid: this.currentTest.uuid, result: this.currentTest.result }; } return undefined; } /** * Get all tests for external use (e.g., for server operations) */ getAllTests() { return allTests.map(t => ({ specRelative: t.specRelative, fullTitle: t.fullTitle, uuid: t.uuid, mochaId: t.mochaId, retryIndex: t.retryIndex, status: t.status, })); } /** * Get screenshots for spec to send to server */ getScreenshotsForSpec(arg) { const { screenshots } = arg; if (!screenshots) { return []; } 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)(path_1.default.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; return uniqueScreenshotsArr; } addGlobalHooks(_nestedLevel) { log('>>> add Global Hooks'); if (!this.globalHooks.hasHooks()) { log('not root hooks'); return; } log('add root hooks'); this.globalHooks.process(); } suiteStarted(arg) { const { title } = arg; log(`start group: ${title}`); const scopeUuid = this.allureRuntime.startScope(); const groupUuid = (0, crypto_1.randomUUID)(); if (this.currentGroup) { this.currentGroup.childUuids.push(groupUuid); log(`Added nested suite ${groupUuid} (${title}) to parent ${this.currentGroup.name}`); } this.groups.push({ uuid: groupUuid, name: title, scopeUuid, childUuids: [], nestedLevel: this.groups.length + 1, }); 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 fixtureType = isBeforeAllHook(hk.name) ? 'before' : 'after'; const fixtureUuid = this.allureRuntime.startFixture(this.currentGroup.scopeUuid, fixtureType, { name: hk.name, start: hk.result.start, }); if (fixtureUuid) { this.allureRuntime.updateFixture(fixtureUuid, (fixture) => { fixture.status = hk.result.status; fixture.stop = hk.result.stop; fixture.attachments = hk.result.attachments; fixture.statusDetails = hk.result.statusDetails; fixture.parameters = hk.result.parameters; }); this.allureRuntime.stopFixture(fixtureUuid); } }); } } specStarted(args) { log('SPEC started'); log(JSON.stringify(args)); this.currentSpec = args.spec; this.taskQueueId = `${this.currentSpec.relative}`; // Ensure directory exists (only once per spec) this.taskManager.addOperation(this.taskQueueId, { type: 'fs:mkdir', path: this.allureResults, options: { recursive: true }, }); this.allureResultsDirEnsured = true; } hookStarted(arg) { 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; } if (this.currentTest && (isBeforeEachHook(title) || isAfterEachHook(title))) { log(`${title} will not be added to suite:${hookId} ${title}`); this.endAllSteps({ status: allure_types_1.UNKNOWN }); this.startStep({ name: title }); return; } const fixtureResult = { name: title, status: undefined, statusDetails: { message: '', trace: '' }, stage: allure_js_commons_1.Stage.RUNNING, steps: [], attachments: [], parameters: [], start: date !== null && date !== void 0 ? date : Date.now(), }; if (this.allureSkipSteps.every(t => !t.test(title))) { const fixtureType = isBeforeAllHook(title) ? 'before' : 'after'; const fixtureUuid = this.allureRuntime.startFixture(this.currentGroup.scopeUuid, fixtureType, fixtureResult); if (fixtureUuid) { const hookInfo = { id: hookId, uuid: fixtureUuid, result: fixtureResult, nested: this.groups.length, name: title, scopeUuid: this.currentGroup.scopeUuid, }; this.hooks.push(hookInfo); this.allHooks.push(hookInfo); } } else { const hookInfo = { id: hookId, uuid: (0, crypto_1.randomUUID)(), result: fixtureResult, nested: this.groups.length, name: title, scopeUuid: this.currentGroup.scopeUuid, }; this.hooks.push(hookInfo); this.allHooks.push(hookInfo); } } setExecutableStatus(executableItem, res, dtls) { if (!executableItem) { return; } if (res === allure_js_commons_1.Status.PASSED) { executableItem.status = allure_js_commons_1.Status.PASSED; executableItem.stage = allure_js_commons_1.Stage.FINISHED; } if (res === allure_js_commons_1.Status.BROKEN) { executableItem.status = allure_js_commons_1.Status.BROKEN; executableItem.stage = allure_js_commons_1.Stage.FINISHED; } if (res === allure_js_commons_1.Status.FAILED) { executableItem.status = allure_js_commons_1.Status.FAILED; executableItem.stage = allure_js_commons_1.Stage.FINISHED; if (dtls) { executableItem.statusDetails.message = dtls.message; executableItem.statusDetails.trace = dtls.trace; } } if (res === allure_js_commons_1.Status.SKIPPED) { executableItem.status = allure_js_commons_1.Status.SKIPPED; executableItem.stage = allure_js_commons_1.Stage.PENDING; executableItem.statusDetails.message = (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) { executableItem.status = allure_types_1.UNKNOWN; executableItem.stage = allure_js_commons_1.Stage.PENDING; executableItem.statusDetails.message = (dtls === null || dtls === void 0 ? void 0 : dtls.message) || `Result: ${res !== null && res !== void 0 ? res : '<no>'}`; } if (dtls) { executableItem.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; } 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)) { const hasFailedStep = (_a = this.currentStep) === null || _a === void 0 ? void 0 : _a.result.steps.some(s => s.status === allure_js_commons_1.Status.FAILED || s.status === allure_js_commons_1.Status.BROKEN); this.endStep({ status: hasFailedStep ? 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.result, this.allureSkipSteps); this.currentHook.result.stop = date !== null && date !== void 0 ? date : Date.now(); this.setExecutableStatus(this.currentHook.result, result, details); (0, helper_1.mergeStepsWithSingleChild)(this.currentHook.result.steps); this.allureRuntime.updateFixture(this.currentHook.uuid, (fixture) => { Object.assign(fixture, this.currentHook.result); }); this.allureRuntime.stopFixture(this.currentHook.uuid); this.hooks.pop(); return; } } endHooks(status = allure_js_commons_1.Status.PASSED) { this.hooks.forEach(h => { this.hookEnded({ title: h.name, result: status }); }); } screenshotAttachment(arg) { const { testId, path: screenshotPath, testAttemptIndex, specName, testFailure } = arg; this.screenshotsTest.push({ testId, path: screenshotPath, 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; const newUuid = (0, crypto_1.randomUUID)(); const fileNew = `${newUuid}-attachment.png`; // Queue the copy operation this.taskManager.addOperation(this.taskQueueId, { type: 'allure:copyScreenshot', allureResults: this.allureResults, screenshotPath: file, targetName: fileNew, }); if (attachTo) { attachTo.result.attachments.push({ name: (0, path_1.basename)(file), type: 'image/png', source: fileNew, }); } }); } endGroup() { this.addGlobalHooks(this.groups.length); if (this.currentGroup) { log('END GROUP'); const scopeUuid = this.currentGroup.scopeUuid; const nestedLevel = this.currentGroup.nestedLevel; const shouldIncludeHook = (hookName) => this.allureSkipSteps.every(t => !t.test(hookName)); const scopeHooks = this.allHooks.filter(h => h.scopeUuid === scopeUuid && shouldIncludeHook(h.name)); const inheritedBeforeHooks = this.allHooks.filter(h => h.nested < nestedLevel && h.name.includes('"before all" hook') && shouldIncludeHook(h.name)); const befores = [ ...inheritedBeforeHooks.map(h => h.result), ...scopeHooks.filter(h => h.name.includes('"before all" hook')).map(h => h.result), ]; const afters = scopeHooks.filter(h => !h.name.includes('"before all" hook')).map(h => h.result); const container = { uuid: this.currentGroup.uuid, name: this.currentGroup.name, children: [...new Set(this.currentGroup.childUuids)], befores, afters, }; if (container.children.length > 0 || befores.length > 0 || afters.length > 0) { this.writer.writeGroup(container); } this.groups.pop(); } } endAllGroups() { log('endAllGroups'); const shouldIncludeHook = (hookName) => this.allureSkipSteps.every(t => !t.test(hookName)); this.groups.forEach(g => { const scopeHooks = this.allHooks.filter(h => h.scopeUuid === g.scopeUuid && shouldIncludeHook(h.name)); const befores = scopeHooks.filter(h => h.name.includes('"before all" hook')).map(h => h.result); const afters = scopeHooks.filter(h => !h.name.includes('"before all" hook')).map(h => h.result); const container = { uuid: g.uuid, name: g.name, children: [...new Set(g.childUuids)], befores, afters, }; if (container.children.length > 0 || befores.length > 0 || afters.length > 0) { this.writer.writeGroup(container); } }); this.groups = []; this.allHooks = []; } label(arg) { if (this.currentTest) { this.currentTest.result.labels.push({ name: arg.name, value: arg.value }); } } link(arg) { if (this.currentTest) { this.currentTest.result.links.push({ url: arg.url, name: arg.name, type: arg.type }); } } fullName(arg) { if (this.currentTest) { this.currentTest.result.fullName = arg.value; this.currentTest.result.historyId = (0, uuid_by_string_1.default)(arg.value); } } historyId(arg) { if (this.currentTest) { this.currentTest.result.historyId = arg.value; } } parameter(arg) { if (this.currentExecutable) { const value = typeof arg.value === 'object' ? JSON.stringify(arg.value) : arg.value; this.currentExecutable.result.parameters.push({ name: arg.name, value }); } } addGroupLabelByUser(label, value) { if (value === undefined) { 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.result.parameters.push({ name: arg.name, value: 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.result.labels.push({ name: allure_types_1.LabelName.PACKAGE, value: 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 }); } } writeEnvironmentInfo(arg) { try { if (!(0, fs_1.existsSync)(this.allureResults)) { (0, fs_1.mkdirSync)(this.allureResults, { recursive: true }); } const content = Object.entries(arg.info) .filter(([key, value]) => key && value !== undefined) .map(([key, value]) => `${key} = ${value}`) .join('\n'); (0, fs_1.writeFileSync)(`${this.allureResults}/environment.properties`, content); } catch (err) { (0, common_1.logWithPackage)('error', `Could not write environment info ${err.message}`); } } addEnvironmentInfo(arg) { try { const additionalInfo = arg.info; const existing = this.getEnvInfo(this.allureResults); for (const key in existing) { if (additionalInfo[key] && additionalInfo[key] !== existing[key]) { additionalInfo[key] += `,${existing[key]}`; } } const newInfo = Object.assign(Object.assign({}, existing), additionalInfo); (0, fs_1.mkdirSync)(this.allureResults, { recursive: true }); const content = Object.entries(newInfo) .filter(([key, value]) => key && value !== undefined) .map(([key, value]) => `${key} = ${value}`) .join('\n'); (0, fs_1.writeFileSync)(`${this.allureResults}/environment.properties`, content); } catch (err) { (0, common_1.logWithPackage)('error', `Could not add environment info ${err.message}`); } } startTest(arg) { var _a, _b, _c; const { title, fullTitle, id, currentRetry } = arg; if (this.currentTest) { 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) { this.suiteStarted({ title: 'Root suite', fullTitle: 'Root suite' }); } const testResult = { name: title, fullName: fullTitle, historyId: (0, uuid_by_string_1.default)(fullTitle), labels: [], links: [], start: Date.now(), }; const scopeUuids = this.groups.map(g => g.scopeUuid); const testUuid = this.allureRuntime.startTest(testResult, scopeUuids); if (this.currentGroup) { this.currentGroup.childUuids.push(testUuid); log(`Added test ${testUuid} to group ${this.currentGroup.name}`); } const testInfo = { uuid: testUuid, result: { uuid: testUuid, name: title, fullName: fullTitle, historyId: (0, uuid_by_string_1.default)(fullTitle), labels: [], links: [], status: undefined, statusDetails: {}, stage: allure_js_commons_1.Stage.RUNNING, steps: [], attachments: [], parameters: [], start: Date.now(), }, }; allTests.push({ retryIndex: currentRetry, specRelative: (_b = this.currentSpec) === null || _b === void 0 ? void 0 : _b.relative, fullTitle, mochaId: id, uuid: testUuid, }); this.tests.push(testInfo); this.addGroupLabels(); if ((_c = this.currentSpec) === null || _c === void 0 ? void 0 : _c.relative) { testInfo.result.labels.push({ name: 'path', value: this.currentSpec.relative }); } this.globalHooks.processForTest(); } endTests() { for (let i = 0; i < this.tests.length; i++) { 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.result.descriptionHtml = this.descriptionHtml.join(''); } } testStatus(arg) { if (!this.currentTest) { return; } this.testStatusStored = arg; } testDetails(arg) { if (!this.currentTest) { return; } this.testDetailsStored = arg; } applyGroupLabels() { const applyLabel = (name) => { if (!this.currentTest) { return; } const lb = this.labels.filter(l => l.name == name); const lastLabel = lb[lb.length - 1]; if (lastLabel) { this.currentTest.result.labels.push({ name: lastLabel.name, value: 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; this.endAllSteps({ status: result, details }); if (!this.currentTest) { return; } (0, helper_1.removeFirstStepWhenSame)(this.currentTest.result.steps); (0, helper_1.mergeStepsWithSingleChild)(this.currentTest.result.steps); if (this.currentTestAll) { this.currentTestAll.status = result; } this.filterSteps(this.currentTest.result, this.allureSkipSteps); this.currentTest.result.steps = (0, helper_1.wrapHooks)('"before each" hook', this.currentTest.result.steps); this.currentTest.result.steps = (0, helper_1.wrapHooks)('"after each" hook', this.currentTest.result.steps); this.setExecutableStatus(this.currentTest.result, result, details); if (storedDetails) { this.setExecutableStatus(this.currentTest.result, result, storedDetails.details); } if (storedStatus) { this.setExecutableStatus(this.currentTest.result, storedStatus.result, storedStatus.details); } this.applyGroupLabels(); const uid = this.currentTest.uuid; this.allureRuntime.updateTest(uid, (test) => { Object.assign(test, this.currentTest.result); }); this.allureRuntime.stopTest(uid); this.allureRuntime.writeTest(uid); 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'); } startStep(arg) { var _a, _b, _c; 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 stepResult = { name, status: undefined, statusDetails: {}, stage: allure_js_commons_1.Stage.RUNNING, steps: [], attachments: [], parameters: [], start: date !== null && date !== void 0 ? date : Date.now(), }; const rootUuid = (_b = (_a = this.currentTest) === null || _a === void 0 ? void 0 : _a.uuid) !== null && _b !== void 0 ? _b : (_c = this.currentHook) === null || _c === void 0 ? void 0 : _c.uuid; const stepUuid = (0, crypto_1.randomUUID)(); if (this.currentStep) { this.currentStep.result.steps.push(stepResult); } else if (this.currentHook) { this.currentHook.result.steps.push(stepResult); } else if (this.currentTest) { this.currentTest.result.steps.push(stepResult); } this.steps.push({ uuid: stepUuid, result: stepResult, rootUuid: rootUuid !== null && rootUuid !== void 0 ? rootUuid : '', }); } endAllSteps(arg) { while (this.steps.length !== 0) { this.endStep(arg); } } 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) { return; } const markBrokenStatuses = ['failed', 'broken']; const passedStatuses = ['passed']; this.setLastStepStatus(this.currentStep.result.steps, status, details); if (passedStatuses.includes(status) && this.hasChildrenWith(this.currentStep.result.steps, markBrokenStatuses)) { this.setExecutableStatus(this.currentStep.result, allure_js_commons_1.Status.BROKEN); } else { this.setExecutableStatus(this.currentStep.result, status, details); } this.currentStep.result.stop = date !== null && date !== void 0 ? date : Date.now(); this.currentStep.result.stage = allure_js_commons_1.Stage.FINISHED; this.steps.pop(); } executableAttachment(exec, arg) { var _a; if (!exec) { log('No current executable - will not attach'); return; } const content = (_a = arg.content) !== null && _a !== void 0 ? _a : `Could not create attachment: no content for ${arg.name} received`; const fileExt = arg.type.split('/')[1] || 'txt'; const fileName = `${(0, crypto_1.randomUUID)()}-attachment.${fileExt}`; // Queue the write operation const contentStr = Buffer.isBuffer(content) ? content.toString('base64') : content.toString(); const encoding = Buffer.isBuffer(content) ? 'base64' : undefined; // Only ensure directory exists once per spec (reduces redundant operations) if (!this.allureResultsDirEnsured) { this.taskManager.addOperation(this.taskQueueId, { type: 'fs:mkdir', path: this.allureResults, options: { recursive: true }, }); this.allureResultsDirEnsured = true; } this.taskManager.addOperation(this.taskQueueId, { type: 'fs:writeFile', path: `${this.allureResults}/${fileName}`, content: contentStr, encoding, }); exec.result.attachments.push({ name: arg.name, type: arg.type, source: fileName, }); } 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; } const uuid = (0, crypto_1.randomUUID)(); const fileNew = `${uuid}-attachment${(0, common_1.extname)(arg.file)}`; const currExec = exec !== null && exec !== void 0 ? exec : this.currentExecutable; if (currExec) { currExec.result.attachments.push({ name: arg.name, type: arg.type, source: fileNew, }); log(`added attachment: ${fileNew} ${arg.file}`); this.setAttached(arg.file); // Queue the copy operation this.taskManager.addOperation(this.taskQueueId, { type: 'allure:copyScreenshot', allureResults: this.allureResults, screenshotPath: arg.file, targetName: fileNew, }); } } getEnvInfo(resultsFolder) { const fileName = 'environment.properties'; const envPropsFile = `${resultsFolder}/${fileName}`; if (!(0, fs_1.existsSync)(envPropsFile)) { return {}; } try { const envBuffer = (0, fs_1.readFileSync)(envPropsFile); const env = envBuffer.toString(); const res = {}; env === null || env === void 0 ? void 0 : env.split('\n').forEach(line => { const separatorIndex = line.indexOf('='); if (separatorIndex > 0) { let key = line.substring(0, separatorIndex); let value = line.substring(separatorIndex + 1); key = key.trim(); value = value.trim(); if (key) { res[key] = value; } } }); return res; } catch (err) { (0, common_1.logWithPackage)('error', 'could not get existing environment info'); return {}; } } } exports.AllureReporter = AllureReporter;