@allurereport/plugin-awesome 3.19.1 → 3.20.1

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.
@@ -1,11 +1,12 @@
1
1
  import { type AttachmentLink, type EnvironmentIdentity, type EnvironmentItem, type HistoryTestResultUrlResolver, type MetricSample, type Statistic, type TestEnvGroup, type TestResult } from "@allurereport/core-api";
2
- import type { AllureStore, ReportExecutorInfo, ReportRunSummary, ReportTestResult, GlobalAttachmentLink, ExitCode, PluginContext, PluginGlobalError, QualityGateValidationResult, ReportFiles, ResultFile } from "@allurereport/plugin-api";
2
+ import type { AllureStore, ReportExecutorInfo, ReportRunSummary, ReportTestResult, GlobalAttachmentLink, ExitCode, PluginContext, PluginGlobalError, QualityGateValidationResult, ReportFiles, ResultFile, TestResultRelatedData } from "@allurereport/plugin-api";
3
3
  import type { AwesomeOptions, TemplateManifest } from "./model.js";
4
4
  import type { AwesomeDataWriter, ReportFile } from "./writer.js";
5
5
  export declare const readTemplateManifest: (_singleFileMode?: boolean) => Promise<TemplateManifest>;
6
6
  export declare const generateTestResults: (_writer: AwesomeDataWriter, store: AllureStore, trs: TestResult[], options: {
7
7
  pluginId: string;
8
8
  hideLabels?: readonly (string | RegExp)[];
9
+ related?: TestResultRelatedData;
9
10
  resolveHistoryUrl?: HistoryTestResultUrlResolver;
10
11
  }) => Promise<ReportTestResult[]>;
11
12
  export declare const generateTestCases: (writer: AwesomeDataWriter, trs: ReportTestResult[]) => Promise<void>;
@@ -1,6 +1,6 @@
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, severityLabelName, } from "@allurereport/core-api";
3
+ import { appLoaderFaviconDataUri, appLoaderStyles, compareBy, createAppLoaderMarkup, createBaseUrlScript, createFaviconLinkTag, createFontLinkTag, createReportDataScript, stringifyForInlineScript, createScriptTag, createStylesLinkTag, 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";
@@ -78,9 +78,17 @@ const writeConcurrently = async (items, write, concurrency = 64) => {
78
78
  await Promise.all(items.slice(i, i + concurrency).map(write));
79
79
  }
80
80
  };
81
+ const mapConcurrently = async (items, map, concurrency = 8) => {
82
+ const results = [];
83
+ for (let i = 0; i < items.length; i += concurrency) {
84
+ results.push(...(await Promise.all(items.slice(i, i + concurrency).map(map))));
85
+ }
86
+ return results;
87
+ };
81
88
  export const generateTestResults = async (_writer, store, trs, options) => {
82
89
  let convertedTrs = [];
83
- const related = await store.relatedByTestResultIds(trs.map(({ id }) => id));
90
+ const related = options.related ?? (await store.relatedByTestResultIds(trs.map(({ id }) => id)));
91
+ const resolutionIssuesByTrId = related.resolutionIssuesByTrId;
84
92
  for (const tr of trs) {
85
93
  const trFixtures = related.fixturesByTrId.get(tr.id) ?? [];
86
94
  const convertedTrFixtures = [...trFixtures]
@@ -89,7 +97,6 @@ export const generateTestResults = async (_writer, store, trs, options) => {
89
97
  const convertedTr = convertTestResult(tr, {
90
98
  hideLabels: options.hideLabels,
91
99
  });
92
- const resolutionIssue = await store.resolutionIssueByTestResultId(tr.id);
93
100
  convertedTr.history = (related.historyByTrId.get(tr.id) ?? []).map((item) => ({
94
101
  ...item,
95
102
  url: options.resolveHistoryUrl?.(item.url, options.pluginId, item.id) ?? "",
@@ -105,7 +112,9 @@ export const generateTestResults = async (_writer, store, trs, options) => {
105
112
  type: "attachment",
106
113
  }));
107
114
  convertedTr.breadcrumbs = createBreadcrumbs(convertedTr);
108
- convertedTr.resolutionIssue = resolutionIssue;
115
+ convertedTr.resolutionIssue = resolutionIssuesByTrId
116
+ ? resolutionIssuesByTrId.get(tr.id)
117
+ : await store.resolutionIssueByTestResultId(tr.id);
109
118
  convertedTrs.push(convertedTr);
110
119
  }
111
120
  convertedTrs = convertedTrs.sort(nullsLast(compareBy("start", ordinal()))).map((tr, idx) => ({
@@ -273,7 +282,9 @@ const buildTreeByTitlePath = (tests) => {
273
282
  };
274
283
  };
275
284
  const buildTreeByLabelsAndTitlePathCombined = (tests, labels) => collapseTreeGroups(createTreeByLabelsAndTitlePath(tests, labels, leafFactory, undefined, (group, leaf) => incrementStatistic(group.statistic, leaf.status)));
276
- const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesCount, resolution, transition, tooltips, retryHash, groupedLabels, categories, }) => {
285
+ const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesCount, resolution, transition, tooltips, retryHash, groupedLabels, categories, parameters, }) => {
286
+ const unresolvedFailure = (status === "failed" || status === "broken") && !resolution;
287
+ const parameterValues = redactParameters(parameters).flatMap(({ value }) => (value ? [value] : []));
277
288
  const leaf = {
278
289
  nodeId: id,
279
290
  id: retryHash ?? id,
@@ -285,9 +296,13 @@ const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesC
285
296
  retry,
286
297
  retriesCount,
287
298
  resolution,
299
+ resolutionStatus: resolution ?? (unresolvedFailure ? "none" : undefined),
288
300
  transition,
289
301
  tooltips,
290
302
  };
303
+ if (parameterValues.length) {
304
+ leaf.parameters = parameterValues;
305
+ }
291
306
  const severity = groupedLabels[severityLabelName]?.[0];
292
307
  if (severity) {
293
308
  leaf.severity = severity;
@@ -377,20 +392,19 @@ export const generateStatistic = async (writer, data) => {
377
392
  }
378
393
  };
379
394
  export const generateAttachmentsFiles = async (writer, attachmentLinks, contentFunction) => {
380
- const result = new Map();
381
- for (const { id, ext, ...link } of attachmentLinks) {
395
+ const writtenAttachments = await mapConcurrently(attachmentLinks, async ({ id, ext, ...link }) => {
382
396
  if (link.missed) {
383
- continue;
397
+ return;
384
398
  }
385
399
  const content = await contentFunction(id);
386
400
  if (!content) {
387
- continue;
401
+ return;
388
402
  }
389
403
  const src = `${id}${ext}`;
390
404
  await writer.writeAttachment(src, content);
391
- result.set(id, src);
392
- }
393
- return result;
405
+ return [id, src];
406
+ });
407
+ return new Map(writtenAttachments.filter((attachment) => attachment !== undefined));
394
408
  };
395
409
  export const generateHistoryDataPoints = async (writer, store) => {
396
410
  const result = new Map();
@@ -411,15 +425,22 @@ export const generateGlobals = async (writer, payload) => {
411
425
  if (globalExitCode) {
412
426
  globals.exitCode = globalExitCode;
413
427
  }
414
- for (const attachment of globalAttachments) {
428
+ const attachmentWrites = new Map();
429
+ const writtenAttachments = await mapConcurrently(globalAttachments, async (attachment) => {
415
430
  const src = `${attachment.id}${attachment.ext}`;
416
431
  const content = await contentFunction(attachment.id);
417
432
  if (!content) {
418
- continue;
433
+ return;
419
434
  }
420
- await writer.writeAttachment(src, content);
421
- globals.attachments.push(attachment);
422
- }
435
+ let write = attachmentWrites.get(src);
436
+ if (!write) {
437
+ write = writer.writeAttachment(src, content);
438
+ attachmentWrites.set(src, write);
439
+ }
440
+ await write;
441
+ return attachment;
442
+ });
443
+ globals.attachments = writtenAttachments.filter((attachment) => attachment !== undefined);
423
444
  Object.entries(globalAttachmentsByEnv).forEach(([environmentId, attachments]) => {
424
445
  const attachmentIds = new Set(globals.attachments.map(({ id }) => id));
425
446
  const writtenAttachments = attachments.filter(({ id }) => attachmentIds.has(id));
package/dist/plugin.js CHANGED
@@ -10,16 +10,15 @@ 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, joinPosixPath, } from "@allurereport/core-api";
13
+ import { incrementStatistic, 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 = async (store, testResults) => {
20
+ const statisticByTestResults = (testResults, trIdsWithRetries) => {
21
21
  const statistic = { total: 0 };
22
- const related = await store.relatedByTestResultIds(testResults.map(({ id }) => id));
23
22
  const incrementResolution = (testResult) => {
24
23
  if (testResult.resolution === "issue") {
25
24
  statistic.resolutions ?? (statistic.resolutions = {});
@@ -39,7 +38,7 @@ const statisticByTestResults = async (store, testResults) => {
39
38
  continue;
40
39
  }
41
40
  incrementStatistic(statistic, testResult.status);
42
- if ((related.retriesByTrId.get(testResult.id)?.length ?? 0) > 0) {
41
+ if (trIdsWithRetries.has(testResult.id)) {
43
42
  statistic.retries = (statistic.retries ?? 0) + 1;
44
43
  }
45
44
  if (testResult.flaky) {
@@ -69,13 +68,15 @@ export class AwesomePlugin {
69
68
  const measure = (name, fn) => perf?.measure(name, fn) ?? fn();
70
69
  const hideLabels = context.hideLabels;
71
70
  const categories = context.categories ?? [];
72
- const { environmentItems, executor, attachments, allTrs, runSummary, statistics, environments, allTestEnvGroups, globalAttachments, globalAttachmentsByEnv, globalExitCode, globalErrors, globalErrorsByEnv, qualityGateResults, } = await measure("readData", async () => {
71
+ const { environmentItems, executor, attachments, allTrs, related, runSummary, statistics, environments, allTestEnvGroups, globalAttachments, globalAttachmentsByEnv, globalExitCode, globalErrors, globalErrorsByEnv, qualityGateResults, } = await measure("readData", async () => {
73
72
  const testResults = await store.allTestResults({ includeRetries: true, filter });
73
+ const relatedData = await store.relatedByTestResultIds(testResults.map(({ id }) => id));
74
74
  return {
75
75
  environmentItems: await store.metadataByKey("allure_environment"),
76
76
  executor: await store.metadataByKey("allure2_executor"),
77
77
  attachments: await store.allAttachments(),
78
78
  allTrs: testResults,
79
+ related: relatedData,
79
80
  runSummary: getRunSummary(testResults),
80
81
  statistics: await store.testsStatistic(filter),
81
82
  environments: await store.allEnvironmentIdentities(),
@@ -116,9 +117,15 @@ export class AwesomePlugin {
116
117
  });
117
118
  const trsByEnvId = new Map();
118
119
  await measure("stats", async () => {
120
+ const trIdsWithRetries = new Set();
119
121
  const envStatistics = new Map();
120
- const pieStatistics = await statisticByTestResults(store, allTrs.filter(isActiveStatisticTestResult));
121
122
  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);
122
129
  for (const tr of allTrs) {
123
130
  const environmentId = envIdByTrId.get(tr.id);
124
131
  if (!environmentId) {
@@ -134,8 +141,8 @@ export class AwesomePlugin {
134
141
  }
135
142
  await Promise.all(environments.map(async ({ id }) => {
136
143
  const envTrs = trsByEnvId.get(id) ?? [];
137
- envStatistics.set(id, await statisticByTestResults(store, envTrs));
138
- pieEnvStatistics.set(id, await statisticByTestResults(store, envTrs.filter(isActiveStatisticTestResult)));
144
+ envStatistics.set(id, statisticByTestResults(envTrs, trIdsWithRetries));
145
+ pieEnvStatistics.set(id, statisticByTestResults(envTrs.filter(isActiveStatisticTestResult), trIdsWithRetries));
139
146
  }));
140
147
  await generateStatistic(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), {
141
148
  stats: statistics,
@@ -157,6 +164,7 @@ export class AwesomePlugin {
157
164
  const convertedTrs = await measure("convert", () => generateTestResults(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, allTrs, {
158
165
  pluginId: context.id,
159
166
  hideLabels,
167
+ related,
160
168
  resolveHistoryUrl: context.history?.resolveTestResultUrl,
161
169
  }));
162
170
  if (perf) {
@@ -194,7 +202,11 @@ export class AwesomePlugin {
194
202
  const convertedTrsById = new Map(convertedTrs.map((tr) => [tr.id, tr]));
195
203
  await measure("environmentsOutput", async () => {
196
204
  for (const reportEnvironment of environments) {
197
- const envTrs = await store.testResultsByEnvironmentId(reportEnvironment.id, { includeRetries: true });
205
+ const environmentIdValidation = validateEnvironmentId(reportEnvironment.id);
206
+ if (!environmentIdValidation.valid) {
207
+ throw new Error(`Invalid environmentId ${JSON.stringify(reportEnvironment.id)}: ${environmentIdValidation.reason}`);
208
+ }
209
+ const envTrs = trsByEnvId.get(reportEnvironment.id) ?? [];
198
210
  const envConvertedTrs = envTrs
199
211
  .map((tr) => convertedTrsById.get(tr.id))
200
212
  .filter((tr) => Boolean(tr));
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/plugin-awesome",
3
- "version": "3.19.1",
3
+ "version": "3.20.1",
4
4
  "description": "Allure Awesome Plugin – brand new HTML report with modern design and new features",
5
5
  "keywords": [
6
6
  "allure",
@@ -30,16 +30,16 @@
30
30
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
31
31
  },
32
32
  "dependencies": {
33
- "@allurereport/charts-api": "3.19.1",
34
- "@allurereport/core-api": "3.19.1",
35
- "@allurereport/plugin-api": "3.19.1",
36
- "@allurereport/web-commons": "3.19.1",
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",
37
37
  "d3-shape": "^3.2.0",
38
38
  "handlebars": "^4.7.9",
39
39
  "markdown-it": "^14.2.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@allurereport/web-awesome": "3.19.1",
42
+ "@allurereport/web-awesome": "3.20.1",
43
43
  "@types/d3-shape": "^3.1.8",
44
44
  "@types/markdown-it": "^14.1.2",
45
45
  "@types/node": "^20",