@reportportal/agent-js-cypress
Version:
This agent helps Cypress to communicate with Report Portal
275 lines (240 loc) • 7.93 kB
JavaScript
/*
* Copyright 2022 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const minimatch = require('minimatch');
const { entityType, hookTypesMap, testItemStatuses } = require('./constants');
const pjson = require('./../package.json');
const { FAILED, PASSED, SKIPPED } = testItemStatuses;
const base64Encode = (file) => {
const bitmap = fs.readFileSync(file);
return Buffer.from(bitmap).toString('base64');
};
const getScreenshotAttachment = (absolutePath) => {
if (!absolutePath) return absolutePath;
const name = absolutePath.split(path.sep).pop();
return {
name,
type: 'image/png',
content: base64Encode(absolutePath),
};
};
const getCodeRef = (testItemPath, testFileName) =>
`${testFileName.replace(/\\/g, '/')}/${testItemPath.join('/')}`;
const getAgentInfo = () => ({
version: pjson.version,
name: pjson.name,
});
const getSystemAttributes = (config) => {
const agentInfo = getAgentInfo();
const systemAttributes = [
{
key: 'agent',
value: `${agentInfo.name}|${agentInfo.version}`,
system: true,
},
];
if (config.reporterOptions.skippedIssue === false) {
const skippedIssueAttribute = {
key: 'skippedIssue',
value: 'false',
system: true,
};
systemAttributes.push(skippedIssueAttribute);
}
return systemAttributes;
};
const getConfig = (initialConfig) => {
const attributes = initialConfig.reporterOptions.attributes || [];
if (
initialConfig.reporterOptions.parallel &&
initialConfig.reporterOptions.autoMerge &&
process.env.CI_BUILD_ID
) {
attributes.push({ value: process.env.CI_BUILD_ID });
}
const { apiKey, token, ...reporterOptions } = initialConfig.reporterOptions;
let calculatedApiKey = process.env.RP_API_KEY || apiKey;
if (!calculatedApiKey) {
calculatedApiKey = process.env.RP_TOKEN || token;
if (calculatedApiKey) {
console.warn('ReportPortal warning. Option "token" is deprecated. Use "apiKey" instead.');
}
}
return {
...initialConfig,
reporterOptions: {
...reporterOptions,
attributes,
apiKey: calculatedApiKey,
},
};
};
const getLaunchStartObject = (config) => {
const launchAttributes = (config.reporterOptions.attributes || []).concat(
getSystemAttributes(config),
);
return {
launch: config.reporterOptions.launch,
description: config.reporterOptions.description,
attributes: launchAttributes,
rerun: config.reporterOptions.rerun,
rerunOf: config.reporterOptions.rerunOf,
mode: config.reporterOptions.mode,
startTime: new Date().valueOf(),
id: config.reporterOptions.launchId,
};
};
const getSuiteStartObject = (suite, testFileName) => ({
id: suite.id,
type: entityType.SUITE,
name: suite.title.slice(0, 255).toString(),
startTime: new Date().valueOf(),
description: suite.description,
attributes: [],
codeRef: getCodeRef(suite.titlePath(), testFileName),
parentId: !suite.root ? suite.parent.id : undefined,
});
const getSuiteEndObject = (suite) => ({
id: suite.id,
title: suite.title,
endTime: new Date().valueOf(),
});
// TODO: update/split to not return the redundant and confusing data for items start
const getTestInfo = (test, testFileName, status, err) => ({
id: test.id,
status: status || (test.state === 'pending' ? testItemStatuses.SKIPPED : test.state),
title: test.title,
codeRef: getCodeRef(test.titlePath(), testFileName),
parentId: test.parent.id,
err: err || test.err,
testFileName,
});
const getTestStartObject = (test) => ({
type: entityType.STEP,
name: test.title.slice(0, 255).toString(),
startTime: new Date().valueOf(),
codeRef: test.codeRef,
attributes: [],
});
const getTestEndObject = (testInfo, skippedIssue) => {
const testEndObj = Object.assign(
{
endTime: new Date().valueOf(),
status: testInfo.status,
attributes: testInfo.attributes,
description: testInfo.description,
},
testInfo.testCaseId && { testCaseId: testInfo.testCaseId },
);
if (testInfo.status === SKIPPED && skippedIssue === false) {
testEndObj.issue = {
issueType: 'NOT_ISSUE',
};
}
return testEndObj;
};
const getHookInfo = (hook, testFileName, status, err) => {
const hookRPType = hookTypesMap[hook.hookName];
let parentId = hook.parent.id;
if ([entityType.BEFORE_SUITE, entityType.AFTER_SUITE].includes(hookRPType)) {
parentId = hook.parent.parent && hook.parent.parent.title ? hook.parent.parent.id : undefined;
}
return {
id: hook.failedFromHookId ? `${hook.failedFromHookId}_${hook.id}` : `${hook.hookId}_${hook.id}`,
hookName: hook.hookName,
title: hook.title,
status: status || (hook.state === FAILED ? FAILED : PASSED),
parentId,
codeRef: getCodeRef(hook.titlePath(), testFileName),
err: (err && err.message) || err || (hook.err && hook.err.message),
testFileName,
};
};
const getHookStartObject = (hook) => {
const hookRPType = hookTypesMap[hook.hookName];
const hookName = hook.title.replace(`"${hook.hookName}" hook:`, '').trim();
return {
name: hookName,
startTime: new Date().valueOf(),
type: hookRPType,
codeRef: hook.codeRef,
};
};
const getFixtureFolderPattern = (config) => {
return [].concat(config.fixturesFolder ? path.join(config.fixturesFolder, '**', '*') : []);
};
const getExcludeSpecPattern = (config) => {
// Return cypress >= 10 pattern.
if (config.excludeSpecPattern) {
const excludePattern = Array.isArray(config.excludeSpecPattern)
? config.excludeSpecPattern
: [config.excludeSpecPattern];
return [...excludePattern];
}
// Return cypress <= 9 pattern
const ignoreTestFilesPattern = Array.isArray(config.ignoreTestFiles)
? config.ignoreTestFiles
: [config.ignoreTestFiles] || [];
return [...ignoreTestFilesPattern];
};
const getSpecPattern = (config) => {
if (config.specPattern) return [].concat(config.specPattern);
return Array.isArray(config.testFiles)
? config.testFiles.map((file) => path.join(config.integrationFolder, file))
: [].concat(path.join(config.integrationFolder, config.testFiles));
};
const getTotalSpecs = (config) => {
if (!config.testFiles && !config.specPattern)
throw new Error('Configuration property not set! Neither for cypress <= 9 nor cypress >= 10');
const specPattern = getSpecPattern(config);
const excludeSpecPattern = getExcludeSpecPattern(config);
const options = {
sort: true,
absolute: true,
nodir: true,
ignore: [config.supportFile].concat(getFixtureFolderPattern(config)),
};
const doesNotMatchAllIgnoredPatterns = (file) =>
excludeSpecPattern.every(
(pattern) => !minimatch(file, pattern, { dot: true, matchBase: true }),
);
const globResult = specPattern.reduce(
(files, pattern) => files.concat(glob.sync(pattern, options) || []),
[],
);
return globResult.filter(doesNotMatchAllIgnoredPatterns).length;
};
module.exports = {
getScreenshotAttachment,
getAgentInfo,
getCodeRef,
getSystemAttributes,
getLaunchStartObject,
getSuiteStartObject,
getSuiteEndObject,
getTestStartObject,
getTestInfo,
getTestEndObject,
getHookInfo,
getHookStartObject,
getTotalSpecs,
getConfig,
getExcludeSpecPattern,
getFixtureFolderPattern,
getSpecPattern,
};