@allurereport/plugin-classic
Version:
The classic version of Allure HTML report
247 lines (246 loc) • 9.81 kB
JavaScript
import { compareBy, incrementStatistic, nullsLast, ordinal, } from "@allurereport/core-api";
import { createTreeByCategories, createTreeByLabels, filterTree, sortTree, transformTree, } from "@allurereport/plugin-api";
import { createBaseUrlScript, createFontLinkTag, createReportDataScript, createScriptTag, createStylesLinkTag, } from "@allurereport/web-commons";
import Handlebars from "handlebars";
import { readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { basename, join } from "node:path";
import { matchCategories } from "./categories.js";
import { getChartData } from "./charts.js";
import { convertFixtureResult, convertTestResult } from "./converters.js";
const require = createRequire(import.meta.url);
const template = `<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<title> {{ reportName }} </title>
<link rel="icon" href="favicon.ico">
{{{ headTags }}}
</head>
<body>
<div id="app"></div>
${createBaseUrlScript()}
<script>
window.allure = window.allure || {};
</script>
{{{ bodyTags }}}
{{#if analyticsEnable}}
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LNDJ3J7WT0"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LNDJ3J7WT0', {
"report": "classic",
"allureVersion": "{{ allureVersion }}",
"reportUuid": "{{ reportUuid }}",
"single_file": "{{singleFile}}"
});
</script>
{{/if}}
<script>
window.allureReportOptions = {{{ reportOptions }}}
</script>
{{{ reportFilesScript }}}
</body>
</html>
`;
export const readTemplateManifest = async (singleFileMode) => {
const templateManifestSource = require.resolve(`@allurereport/web-classic/dist/${singleFileMode ? "single" : "multi"}/manifest.json`);
const templateManifest = await readFile(templateManifestSource, { encoding: "utf-8" });
return JSON.parse(templateManifest);
};
const createBreadcrumbs = (convertedTr) => {
const labelsByType = convertedTr.labels.reduce((acc, label) => {
if (!acc[label.name]) {
acc[label.name] = [];
}
acc[label.name].push(label.value || "");
return acc;
}, {});
const parentSuites = labelsByType.parentSuite || [""];
const suites = labelsByType.suite || [""];
const subSuites = labelsByType.subSuite || [""];
return parentSuites.reduce((acc, parentSuite) => {
suites.forEach((suite) => {
subSuites.forEach((subSuite) => {
const path = [parentSuite, suite, subSuite].filter(Boolean);
if (path.length > 0) {
acc.push(path);
}
});
});
return acc;
}, []);
};
export const generateTestResults = async (writer, store) => {
const allTr = await store.allTestResults({ includeHidden: true });
let convertedTrs = [];
for (const tr of allTr) {
const trFixtures = await store.fixturesByTrId(tr.id);
const convertedTrFixtures = trFixtures.map(convertFixtureResult);
const convertedTr = convertTestResult(tr);
const { error, status, flaky } = convertedTr;
const categories = (await store.metadataByKey("allure2_categories")) ?? [];
const matchedCategories = matchCategories(categories, {
message: error?.message,
trace: error?.trace,
status,
flaky,
});
convertedTr.categories = matchedCategories;
convertedTr.history = await store.historyByTrId(tr.id);
convertedTr.retries = await store.retriesByTrId(tr.id);
convertedTr.retry = convertedTr.retries.length > 0;
convertedTr.setup = convertedTrFixtures.filter((f) => f.type === "before");
convertedTr.teardown = convertedTrFixtures.filter((f) => f.type === "after");
convertedTr.attachments = (await store.attachmentsByTrId(tr.id)).map((attachment) => ({
link: attachment,
type: "attachment",
}));
convertedTr.breadcrumbs = createBreadcrumbs(convertedTr);
convertedTrs.push(convertedTr);
}
convertedTrs = convertedTrs.sort(nullsLast(compareBy("start", ordinal()))).map((tr, idx) => ({
...tr,
order: idx + 1,
}));
for (const convertedTr of convertedTrs) {
await writer.writeTestCase(convertedTr);
}
await writer.writeWidget("nav.json", convertedTrs.filter(({ hidden }) => !hidden).map(({ id }) => id));
return convertedTrs;
};
export const generateTree = async (writer, treeName, labels, tests) => {
const visibleTests = tests.filter((test) => !test.hidden);
const tree = createTreeByLabels(visibleTests, labels, ({ id, name, status, duration, flaky, start, retries }) => {
return {
nodeId: id,
retry: !!retries?.length,
name,
status,
duration,
flaky,
start,
};
}, undefined, (group, leaf) => {
incrementStatistic(group.statistic, leaf.status);
});
filterTree(tree, (leaf) => !leaf.hidden);
sortTree(tree, nullsLast(compareBy("start", ordinal())));
transformTree(tree, (leaf, idx) => ({ ...leaf, groupOrder: idx + 1 }));
await writer.writeWidget(`${treeName}.json`, tree);
};
export const generateEnvironmentJson = async (writer, env) => {
await writer.writeWidget("allure_environment.json", env);
};
export const generateStatistic = async (writer, statistic) => {
await writer.writeWidget("allure_statistic.json", statistic);
};
export const generatePieChart = async (writer, statistic) => {
const chartData = getChartData(statistic);
await writer.writeWidget("allure_pie_chart.json", chartData);
};
export const generateAttachmentsFiles = async (writer, attachmentLinks, contentFunction) => {
const result = new Map();
for (const { id, ext, ...link } of attachmentLinks) {
if (link.missed) {
return;
}
const content = await contentFunction(id);
if (!content) {
continue;
}
const src = `${id}${ext}`;
await writer.writeAttachment(src, content);
result.set(id, src);
}
return result;
};
export const generateHistoryDataPoints = async (writer, store) => {
const result = new Map();
const allHistoryPoints = await store.allHistoryDataPoints();
for (const historyPoint of allHistoryPoints.slice(0, 6)) {
const src = `history/${historyPoint.uuid}.json`;
await writer.writeData(src, historyPoint);
}
return result;
};
export const generateStaticFiles = async (payload) => {
const { reportName = "Allure Report", reportLanguage = "en", singleFile, logo = "", theme = "light", groupBy, reportFiles, reportDataFiles, reportUuid, allureVersion, } = payload;
const compile = Handlebars.compile(template);
const manifest = await readTemplateManifest(payload.singleFile);
const headTags = [];
const bodyTags = [];
if (!payload.singleFile) {
for (const key in manifest) {
const fileName = manifest[key];
const filePath = require.resolve(join("@allurereport/web-classic/dist", singleFile ? "single" : "multi", fileName));
if (key.includes(".woff")) {
headTags.push(createFontLinkTag(fileName));
}
if (key === "main.css") {
headTags.push(createStylesLinkTag(fileName));
}
if (key === "main.js") {
bodyTags.push(createScriptTag(fileName));
}
if (singleFile) {
continue;
}
const fileContent = await readFile(filePath);
await reportFiles.addFile(basename(filePath), fileContent);
}
}
else {
const mainJs = manifest["main.js"];
const mainJsSource = require.resolve(`@allurereport/web-classic/dist/single/${mainJs}`);
const mainJsContentBuffer = await readFile(mainJsSource);
bodyTags.push(createScriptTag(`data:text/javascript;base64,${mainJsContentBuffer.toString("base64")}`));
}
const reportOptions = {
reportName,
logo,
theme,
reportLanguage,
createdAt: Date.now(),
reportUuid,
groupBy: groupBy?.length ? groupBy : ["parentSuite", "suite", "subSuite"],
allureVersion,
};
const html = compile({
headTags: headTags.join("\n"),
bodyTags: bodyTags.join("\n"),
reportFilesScript: createReportDataScript(reportDataFiles),
reportOptions: JSON.stringify(reportOptions),
analyticsEnable: true,
allureVersion,
reportUuid,
reportName,
singleFile: payload.singleFile,
});
await reportFiles.addFile("index.html", Buffer.from(html, "utf8"));
};
export const generateTreeByCategories = async (writer, treeName, tests) => {
const visibleTests = tests.filter((test) => !test.hidden);
const tree = createTreeByCategories(visibleTests, ({ id, name, status, duration, flaky, start, retries }) => {
return {
nodeId: id,
retry: !!retries?.length,
name,
status,
duration,
flaky,
start,
};
}, undefined, (group, leaf) => {
incrementStatistic(group.statistic, leaf.status);
});
filterTree(tree, (leaf) => !leaf.hidden);
sortTree(tree, nullsLast(compareBy("start", ordinal())));
transformTree(tree, (leaf, idx) => ({
...leaf,
groupOrder: idx + 1,
}));
await writer.writeWidget(`${treeName}.json`, tree);
};