@mmisty/cypress-allure-adapter
Version:
cypress allure adapter to generate allure results during tests execution (Allure TestOps compatible)
945 lines (944 loc) • 39 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 __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;