@allurereport/plugin-awesome 3.20.1 → 3.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -198,6 +198,7 @@ export const generateCategories = async (writer, { tests, categories, filename =
198
198
  duration: tr.duration,
199
199
  flaky: tr.flaky,
200
200
  retriesCount: tr.retriesCount,
201
+ retriesStatusChange: tr.retriesStatusChange,
201
202
  transition: tr.transition,
202
203
  tooltips: tr.tooltips,
203
204
  });
@@ -50,6 +50,7 @@ export declare const generateStaticFiles: (payload: AwesomeOptions & {
50
50
  reportDataFiles: ReportFile[];
51
51
  reportUuid: string;
52
52
  reportName: string;
53
+ overviewHref?: string;
53
54
  executor?: ReportExecutorInfo;
54
55
  runSummary?: ReportRunSummary;
55
56
  runSummaryByEnv?: Record<string, ReportRunSummary>;
@@ -1,11 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { defaultChartsConfig } from "@allurereport/charts-api";
3
- import { appLoaderFaviconDataUri, appLoaderStyles, compareBy, createAppLoaderMarkup, createBaseUrlScript, createFaviconLinkTag, createFontLinkTag, createReportDataScript, stringifyForInlineScript, createScriptTag, createStylesLinkTag, incrementStatistic, joinPosixPath, nullsLast, ordinal, redactParameters, severityLabelName, } from "@allurereport/core-api";
3
+ import { appLoaderFaviconDataUri, appLoaderStyles, compareBy, createAppLoaderMarkup, createBaseUrlScript, createFaviconLinkTag, createFontLinkTag, injectReportDataScript, reportDataScriptPlaceholder, stringifyForInlineScript, createScriptTag, createStylesLinkTag, hasRetriesStatusChange, incrementStatistic, joinPosixPath, nullsLast, ordinal, redactParameters, severityLabelName, } from "@allurereport/core-api";
4
4
  import { collapseTreeGroups, createTreeByLabels, createTreeByLabelsAndTitlePath, createTreeByTitlePath, preciseTreeLabels, processTree, } from "@allurereport/plugin-api";
5
5
  import { copyReportStaticAssets, getReportStaticAsset, readReportStaticAssets, } from "@allurereport/plugin-api/static-assets";
6
6
  import { generateCharts, getPieChartValues } from "@allurereport/web-commons";
7
7
  import Handlebars from "handlebars";
8
8
  import { convertFixtureResult, convertTestResult } from "./converters.js";
9
+ import { createSingleFileScripts } from "./singleFileAssets.js";
9
10
  const reportStaticArchive = new URL("../dist/static/report.tar", import.meta.url);
10
11
  const template = `<!DOCTYPE html>
11
12
  <html dir="ltr" lang="en">
@@ -104,6 +105,7 @@ export const generateTestResults = async (_writer, store, trs, options) => {
104
105
  convertedTr.retries = related.retriesByTrId.get(tr.id) ?? [];
105
106
  convertedTr.retriesCount = convertedTr.retries.length;
106
107
  convertedTr.retry = convertedTr.retriesCount > 0;
108
+ convertedTr.retriesStatusChange = hasRetriesStatusChange(tr, convertedTr.retries);
107
109
  convertedTr.isRetry = tr.isRetry;
108
110
  convertedTr.setup = convertedTrFixtures.filter((f) => f.type === "before");
109
111
  convertedTr.teardown = convertedTrFixtures.filter((f) => f.type === "after");
@@ -137,7 +139,9 @@ export const generateNav = async (writer, trs, filename = "nav.json") => {
137
139
  };
138
140
  const SEARCHABLE_LABELS = new Set([
139
141
  "owner",
142
+ "parentSuite",
140
143
  "suite",
144
+ "subSuite",
141
145
  "package",
142
146
  "testClass",
143
147
  "testMethod",
@@ -175,6 +179,7 @@ const searchDocumentFactory = (test) => {
175
179
  id: test.id,
176
180
  nodeId: test.id,
177
181
  name: test.name,
182
+ titlePath: joinSearchValues(test.titlePath ?? []),
178
183
  fullName: test.fullName,
179
184
  retryHash: test.retryHash,
180
185
  labels: joinSearchValues(labels),
@@ -282,7 +287,7 @@ const buildTreeByTitlePath = (tests) => {
282
287
  };
283
288
  };
284
289
  const buildTreeByLabelsAndTitlePathCombined = (tests, labels) => collapseTreeGroups(createTreeByLabelsAndTitlePath(tests, labels, leafFactory, undefined, (group, leaf) => incrementStatistic(group.statistic, leaf.status)));
285
- const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesCount, resolution, transition, tooltips, retryHash, groupedLabels, categories, parameters, }) => {
290
+ const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesCount, retriesStatusChange, resolution, transition, tooltips, retryHash, groupedLabels, categories, parameters, }) => {
286
291
  const unresolvedFailure = (status === "failed" || status === "broken") && !resolution;
287
292
  const parameterValues = redactParameters(parameters).flatMap(({ value }) => (value ? [value] : []));
288
293
  const leaf = {
@@ -295,6 +300,7 @@ const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesC
295
300
  start,
296
301
  retry,
297
302
  retriesCount,
303
+ retriesStatusChange,
298
304
  resolution,
299
305
  resolutionStatus: resolution ?? (unresolvedFailure ? "none" : undefined),
300
306
  transition,
@@ -332,6 +338,7 @@ const resolutionTestResultFactory = (test, index) => ({
332
338
  transition: test.transition,
333
339
  retry: test.retry,
334
340
  retriesCount: test.retriesCount,
341
+ retriesStatusChange: test.retriesStatusChange,
335
342
  resolution: test.resolution,
336
343
  groupOrder: index + 1,
337
344
  tooltips: test.tooltips,
@@ -478,7 +485,7 @@ export const generateQualityGateResults = async (writer, qualityGateResults = {}
478
485
  await writer.writeWidget("quality-gate.json", resultsWithTrees);
479
486
  };
480
487
  export const generateStaticFiles = async (payload) => {
481
- const { id, reportName = "Allure Report", reportLanguage = "en", logo = "", theme = "auto", groupBy, reportFiles, reportDataFiles, reportUuid, allureVersion, layout = "base", defaultSection = "", ci, executor, runSummary, runSummaryByEnv, stepTreeExpansion, defaultSortBy, } = payload;
488
+ const { id, reportName = "Allure Report", reportLanguage = "en", logo = "", theme = "auto", groupBy, reportFiles, reportDataFiles, reportUuid, allureVersion, layout = "base", defaultSection = "", ci, executor, runSummary, runSummaryByEnv, stepTreeExpansion, defaultSortBy, overviewHref, } = payload;
482
489
  const staticAssets = await readReportStaticAssets(reportStaticArchive);
483
490
  const { manifest } = staticAssets;
484
491
  const headTags = [];
@@ -506,8 +513,7 @@ export const generateStaticFiles = async (payload) => {
506
513
  const mainCssContent = getReportStaticAsset(staticAssets, mainCss);
507
514
  headTags.push(createStylesLinkTag(`data:text/css;base64,${mainCssContent.toString("base64")}`));
508
515
  }
509
- const mainJsContent = getReportStaticAsset(staticAssets, mainJs);
510
- bodyTags.push(createScriptTag(`data:text/javascript;base64,${mainJsContent.toString("base64")}`));
516
+ bodyTags.push(createSingleFileScripts(staticAssets.files, mainJs));
511
517
  }
512
518
  const now = Date.now();
513
519
  const reportOptions = {
@@ -530,12 +536,13 @@ export const generateStaticFiles = async (payload) => {
530
536
  defaultSection,
531
537
  stepTreeExpansion,
532
538
  defaultSortBy,
539
+ overviewHref,
533
540
  };
534
541
  try {
535
542
  const html = compiledTemplate({
536
543
  headTags: headTags.join("\n"),
537
544
  bodyTags: bodyTags.join("\n"),
538
- reportFilesScript: createReportDataScript(reportDataFiles),
545
+ reportFilesScript: reportDataScriptPlaceholder,
539
546
  reportOptions: stringifyForInlineScript(reportOptions),
540
547
  analyticsEnable: true,
541
548
  allureVersion,
@@ -543,7 +550,7 @@ export const generateStaticFiles = async (payload) => {
543
550
  reportName,
544
551
  singleFile: payload.singleFile,
545
552
  });
546
- await reportFiles.addFile("index.html", Buffer.from(html, "utf8"));
553
+ await reportFiles.addFile("index.html", injectReportDataScript(html, reportDataFiles));
547
554
  }
548
555
  catch (err) {
549
556
  if (err instanceof RangeError) {
package/dist/plugin.js CHANGED
@@ -10,14 +10,14 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
12
  var _AwesomePlugin_writer, _AwesomePlugin_generateAfterStart, _AwesomePlugin_generate;
13
- import { incrementStatistic, validateEnvironmentId, joinPosixPath, } from "@allurereport/core-api";
13
+ import { incrementStatistic, hasRetriesStatusChange, validateEnvironmentId, joinPosixPath, } from "@allurereport/core-api";
14
14
  import { createPluginSummary, } from "@allurereport/plugin-api";
15
15
  import { preciseTreeLabels } from "@allurereport/plugin-api";
16
16
  import { applyCategoriesToTestResults, generateCategories } from "./categories.js";
17
17
  import { generateTimeline } from "./generateTimeline.js";
18
18
  import { generateAllCharts, generateAttachmentsFiles, generateEnvironmentJson, generateEnvirontmentsList, generateGlobals, generateHistoryDataPoints, generateMetricsWidget, generateNav, generateQualityGateResults, generateResolutionCategories, generateSearchIndex, generateStaticFiles, generateStatistic, generateTestCases, generateTestEnvGroups, generateTestResults, generateTree, generateTreeFilters, generateVariables, getRunSummary, } from "./generators.js";
19
19
  import { InMemoryReportDataWriter, ReportFileDataWriter } from "./writer.js";
20
- const statisticByTestResults = (testResults, trIdsWithRetries) => {
20
+ const statisticByTestResults = (testResults, retriesByTrId) => {
21
21
  const statistic = { total: 0 };
22
22
  const incrementResolution = (testResult) => {
23
23
  if (testResult.resolution === "issue") {
@@ -38,9 +38,13 @@ const statisticByTestResults = (testResults, trIdsWithRetries) => {
38
38
  continue;
39
39
  }
40
40
  incrementStatistic(statistic, testResult.status);
41
- if (trIdsWithRetries.has(testResult.id)) {
41
+ const retries = retriesByTrId.get(testResult.id) ?? [];
42
+ if (retries.length > 0) {
42
43
  statistic.retries = (statistic.retries ?? 0) + 1;
43
44
  }
45
+ if (hasRetriesStatusChange(testResult, retries)) {
46
+ statistic.retriesStatusChange = (statistic.retriesStatusChange ?? 0) + 1;
47
+ }
44
48
  if (testResult.flaky) {
45
49
  statistic.flaky = (statistic.flaky ?? 0) + 1;
46
50
  }
@@ -117,15 +121,9 @@ export class AwesomePlugin {
117
121
  });
118
122
  const trsByEnvId = new Map();
119
123
  await measure("stats", async () => {
120
- const trIdsWithRetries = new Set();
121
124
  const envStatistics = new Map();
122
125
  const pieEnvStatistics = new Map();
123
- related.retriesByTrId.forEach((retries, trId) => {
124
- if (retries.length > 0) {
125
- trIdsWithRetries.add(trId);
126
- }
127
- });
128
- const pieStatistics = statisticByTestResults(allTrs.filter(isActiveStatisticTestResult), trIdsWithRetries);
126
+ const pieStatistics = statisticByTestResults(allTrs.filter(isActiveStatisticTestResult), related.retriesByTrId);
129
127
  for (const tr of allTrs) {
130
128
  const environmentId = envIdByTrId.get(tr.id);
131
129
  if (!environmentId) {
@@ -141,8 +139,8 @@ export class AwesomePlugin {
141
139
  }
142
140
  await Promise.all(environments.map(async ({ id }) => {
143
141
  const envTrs = trsByEnvId.get(id) ?? [];
144
- envStatistics.set(id, statisticByTestResults(envTrs, trIdsWithRetries));
145
- pieEnvStatistics.set(id, statisticByTestResults(envTrs.filter(isActiveStatisticTestResult), trIdsWithRetries));
142
+ envStatistics.set(id, statisticByTestResults(envTrs, related.retriesByTrId));
143
+ pieEnvStatistics.set(id, statisticByTestResults(envTrs.filter(isActiveStatisticTestResult), related.retriesByTrId));
146
144
  }));
147
145
  await generateStatistic(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), {
148
146
  stats: statistics,
@@ -263,6 +261,7 @@ export class AwesomePlugin {
263
261
  reportFiles: context.reportFiles,
264
262
  reportUuid: context.reportUuid,
265
263
  reportName: context.reportName,
264
+ overviewHref: context.overviewHref,
266
265
  ci: context.ci,
267
266
  executor,
268
267
  runSummary,
@@ -0,0 +1 @@
1
+ export declare const createSingleFileScripts: (files: ReadonlyMap<string, Buffer>, mainJs: string) => string;
@@ -0,0 +1,81 @@
1
+ import { gzipSync } from "node:zlib";
2
+ import { stringifyForInlineScript } from "@allurereport/core-api";
3
+ const embeddedAssetsElementId = "allure-single-file-assets";
4
+ export const createSingleFileScripts = (files, mainJs) => {
5
+ const compressedScripts = Object.fromEntries([...files]
6
+ .filter(([fileName]) => fileName.endsWith(".js"))
7
+ .map(([fileName, content]) => [fileName, gzipSync(content, { level: 9 }).toString("base64")]));
8
+ if (!compressedScripts[mainJs]) {
9
+ throw new Error(`The report static assets do not contain the main script: ${mainJs}`);
10
+ }
11
+ return `
12
+ <script id="${embeddedAssetsElementId}" type="application/json">${stringifyForInlineScript(compressedScripts)}</script>
13
+ <script>
14
+ (function () {
15
+ const assetsElement = document.getElementById(${stringifyForInlineScript(embeddedAssetsElementId)});
16
+ const assets = JSON.parse(assetsElement.textContent || "{}");
17
+ const appendChild = document.head.appendChild;
18
+
19
+ assetsElement.remove();
20
+
21
+ function embeddedAssetName(script) {
22
+ const source = script.getAttribute("src");
23
+
24
+ if (!source) {
25
+ return undefined;
26
+ }
27
+
28
+ const path = source.split(/[?#]/, 1)[0];
29
+
30
+ return path.slice(path.lastIndexOf("/") + 1);
31
+ }
32
+
33
+ async function decompressAsset(encoded) {
34
+ const binary = atob(encoded);
35
+ const compressed = new Uint8Array(binary.length);
36
+
37
+ for (let index = 0; index < binary.length; index++) {
38
+ compressed[index] = binary.charCodeAt(index);
39
+ }
40
+
41
+ const stream = new Blob([compressed]).stream().pipeThrough(new DecompressionStream("gzip"));
42
+
43
+ return new Response(stream).text();
44
+ }
45
+
46
+ document.head.appendChild = function (node) {
47
+ if (node.tagName !== "SCRIPT") {
48
+ return appendChild.call(this, node);
49
+ }
50
+
51
+ const fileName = embeddedAssetName(node);
52
+ const encoded = fileName && Object.hasOwn(assets, fileName) ? assets[fileName] : undefined;
53
+
54
+ if (!encoded) {
55
+ return appendChild.call(this, node);
56
+ }
57
+
58
+ void decompressAsset(encoded)
59
+ .then(function (source) {
60
+ node.removeAttribute("src");
61
+ node.text = source;
62
+ delete assets[fileName];
63
+ appendChild.call(document.head, node);
64
+ node.dispatchEvent(new Event("load"));
65
+ })
66
+ .catch(function (error) {
67
+ console.error("Failed to load embedded report asset " + fileName, error);
68
+ node.dispatchEvent(new Event("error"));
69
+ });
70
+
71
+ return node;
72
+ };
73
+
74
+ const mainScript = document.createElement("script");
75
+
76
+ mainScript.src = ${stringifyForInlineScript(mainJs)};
77
+ document.head.appendChild(mainScript);
78
+ })();
79
+ </script>
80
+ `;
81
+ };
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/plugin-awesome",
3
- "version": "3.20.1",
3
+ "version": "3.21.0",
4
4
  "description": "Allure Awesome Plugin – brand new HTML report with modern design and new features",
5
5
  "keywords": [
6
6
  "allure",
@@ -23,26 +23,29 @@
23
23
  ".": "./dist/index.js"
24
24
  },
25
25
  "scripts": {
26
- "build": "run clean && tsc --project ./tsconfig.json && node ../../scripts/pack-report-static.mjs ../web-awesome/dist/multi ./dist/static/report.tar",
26
+ "build": "run clean && tsc --project ./tsconfig.json && node ../../scripts/pack-report-static.mjs ../web-awesome/dist/multi ./dist/static/report.tar --allow-chunks",
27
27
  "clean": "rimraf ./dist",
28
28
  "test": "rimraf ./out && vitest run",
29
+ "test:unit": "yarn test",
29
30
  "lint": "oxlint --import-plugin src test features stories",
30
31
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
31
32
  },
32
33
  "dependencies": {
33
- "@allurereport/charts-api": "3.20.1",
34
- "@allurereport/core-api": "3.20.1",
35
- "@allurereport/plugin-api": "3.20.1",
36
- "@allurereport/web-commons": "3.20.1",
34
+ "@allurereport/charts-api": "3.21.0",
35
+ "@allurereport/core-api": "3.21.0",
36
+ "@allurereport/plugin-api": "3.21.0",
37
+ "@allurereport/web-commons": "3.21.0",
37
38
  "d3-shape": "^3.2.0",
38
- "handlebars": "^4.7.9",
39
- "markdown-it": "^14.2.0"
39
+ "handlebars": "4.7.10",
40
+ "markdown-it": "^15.0.2"
40
41
  },
41
42
  "devDependencies": {
42
- "@allurereport/web-awesome": "3.20.1",
43
+ "@allurereport/test-config": "3.21.0",
44
+ "@allurereport/web-awesome": "3.21.0",
43
45
  "@types/d3-shape": "^3.1.8",
44
46
  "@types/markdown-it": "^14.1.2",
45
47
  "@types/node": "^20",
48
+ "@vitest/coverage-istanbul": "^4.1.0",
46
49
  "@vitest/runner": "^2",
47
50
  "allure-js-commons": "^3",
48
51
  "allure-vitest": "^3",