@allurereport/plugin-awesome 3.2.0 → 3.3.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.
@@ -0,0 +1,13 @@
1
+ import type { CategoryDefinition } from "@allurereport/core-api";
2
+ import type { AwesomeTestResult } from "@allurereport/web-awesome";
3
+ import type { AwesomeDataWriter } from "./writer.js";
4
+ export declare const applyCategoriesToTestResults: (tests: AwesomeTestResult[], categories: CategoryDefinition[]) => void;
5
+ export declare const generateCategories: (writer: AwesomeDataWriter, { tests, categories, filename, environmentCount, selectedEnvironmentCount, environments, defaultEnvironment, }: {
6
+ tests: AwesomeTestResult[];
7
+ categories: CategoryDefinition[];
8
+ filename?: string;
9
+ environmentCount?: number;
10
+ selectedEnvironmentCount?: number;
11
+ environments?: string[];
12
+ defaultEnvironment?: string;
13
+ }) => Promise<void>;
@@ -0,0 +1,213 @@
1
+ import { EMPTY_VALUE, buildEnvironmentSortOrder, compareChildNodes, extractErrorMatchingData, findLastByLabelName, incrementStatistic, matchCategory, } from "@allurereport/core-api";
2
+ import { md5 } from "@allurereport/plugin-api";
3
+ const emptyStat = () => ({
4
+ total: 0,
5
+ });
6
+ const msgKey = (m) => (m && m.trim().length ? m : EMPTY_VALUE);
7
+ const envKey = (m) => (m && m.trim().length ? m : EMPTY_VALUE);
8
+ const formatEmptyValue = (key) => {
9
+ if (key === "message") {
10
+ return "No message";
11
+ }
12
+ if (key === "transition") {
13
+ return "No transition";
14
+ }
15
+ if (key === "environment") {
16
+ return "No environment";
17
+ }
18
+ return `No ${key}`;
19
+ };
20
+ const hasEnvironmentSelector = (category) => category.groupBy.some((selector) => selector === "environment");
21
+ const computeGroupEnvironments = (category, environmentCount, isSingleEnvironmentSelected) => {
22
+ if (isSingleEnvironmentSelected) {
23
+ return false;
24
+ }
25
+ if (category.groupEnvironments !== undefined) {
26
+ return category.groupEnvironments;
27
+ }
28
+ if (environmentCount > 1 && !hasEnvironmentSelector(category)) {
29
+ return true;
30
+ }
31
+ return false;
32
+ };
33
+ const displayGroupValue = (key, value) => (value === EMPTY_VALUE ? formatEmptyValue(key) : value);
34
+ const formatGroupName = (key, value) => `${key}: ${displayGroupValue(key, value)}`;
35
+ export const applyCategoriesToTestResults = (tests, categories) => {
36
+ for (const tr of tests) {
37
+ const matchingData = extractErrorMatchingData(tr);
38
+ const matched = matchCategory(categories, matchingData);
39
+ if (!matched) {
40
+ tr.categories = [];
41
+ continue;
42
+ }
43
+ tr.categories = [{ name: matched.name }];
44
+ }
45
+ };
46
+ const extractGroupValue = (selector, testResult) => {
47
+ if (selector === "flaky") {
48
+ const flakyValue = testResult.flaky ? "true" : "false";
49
+ return { key: "flaky", value: flakyValue, name: formatGroupName("flaky", flakyValue) };
50
+ }
51
+ if (selector === "transition") {
52
+ const transitionValue = testResult.transition ?? EMPTY_VALUE;
53
+ return { key: "transition", value: transitionValue, name: formatGroupName("transition", transitionValue) };
54
+ }
55
+ if (selector === "status") {
56
+ const statusValue = testResult.status ?? "unknown";
57
+ return { key: "status", value: statusValue, name: formatGroupName("status", statusValue) };
58
+ }
59
+ if (selector === "environment") {
60
+ const environmentValue = envKey(testResult.environment);
61
+ return { key: "environment", value: environmentValue, name: formatGroupName("environment", environmentValue) };
62
+ }
63
+ if (selector === "owner") {
64
+ const ownerValue = findLastByLabelName(testResult.labels, "owner") ?? EMPTY_VALUE;
65
+ return { key: "owner", value: ownerValue, name: formatGroupName("owner", ownerValue) };
66
+ }
67
+ if (selector === "severity") {
68
+ const fallbackValue = selector === "severity" ? "normal" : EMPTY_VALUE;
69
+ const builtInValue = findLastByLabelName(testResult.labels, selector) ?? fallbackValue;
70
+ return {
71
+ key: selector,
72
+ value: builtInValue,
73
+ name: formatGroupName(selector, builtInValue),
74
+ };
75
+ }
76
+ if (selector === "layer") {
77
+ const layerValue = findLastByLabelName(testResult.labels, "layer") ?? EMPTY_VALUE;
78
+ return { key: "layer", value: layerValue, name: formatGroupName("layer", layerValue) };
79
+ }
80
+ const labelName = selector.label;
81
+ const labelValue = findLastByLabelName(testResult.labels, labelName) ?? EMPTY_VALUE;
82
+ return { key: labelName, value: labelValue, name: formatGroupName(labelName, labelValue) };
83
+ };
84
+ const buildGroupLevels = (category, testResult, matchingData, environmentCount, isSingleEnvironmentSelected) => {
85
+ const levels = [];
86
+ for (const selector of category.groupBy) {
87
+ const groupValue = extractGroupValue(selector, testResult);
88
+ levels.push({ type: "group", key: groupValue.key, value: groupValue.value, name: groupValue.name });
89
+ }
90
+ if (category.groupByMessage) {
91
+ const messageValue = msgKey(matchingData.message);
92
+ levels.push({
93
+ type: "message",
94
+ key: "message",
95
+ value: messageValue,
96
+ name: displayGroupValue("message", messageValue),
97
+ });
98
+ }
99
+ const groupEnvironments = computeGroupEnvironments(category, environmentCount, isSingleEnvironmentSelected);
100
+ if (groupEnvironments) {
101
+ const testKeyValue = testResult.historyId ?? testResult.id;
102
+ const testDisplayName = testResult.name ?? testKeyValue;
103
+ levels.push({
104
+ type: "history",
105
+ key: "historyId",
106
+ value: testKeyValue,
107
+ name: testDisplayName,
108
+ });
109
+ }
110
+ return levels;
111
+ };
112
+ export const generateCategories = async (writer, { tests, categories, filename = "categories.json", environmentCount = 0, selectedEnvironmentCount, environments = [], defaultEnvironment = "default", }) => {
113
+ const visible = tests.filter((t) => !t.hidden);
114
+ const environmentOrderMap = buildEnvironmentSortOrder(environments, defaultEnvironment);
115
+ const nodes = {};
116
+ const roots = [];
117
+ const childrenMap = new Map();
118
+ const categoryOrder = categories.filter((cat) => !cat.hide).map((cat) => cat.name);
119
+ const categoryIds = new Map();
120
+ const categoryTouched = new Set();
121
+ const duplicateChecker = (node) => {
122
+ var _a;
123
+ nodes[_a = node.id] ?? (nodes[_a] = node);
124
+ return nodes[node.id];
125
+ };
126
+ const attachChild = (parentId, childId) => {
127
+ const set = childrenMap.get(parentId) ?? new Set();
128
+ set.add(childId);
129
+ childrenMap.set(parentId, set);
130
+ };
131
+ const bumpStat = (nodeId, status) => {
132
+ const node = nodes[nodeId];
133
+ node.statistic ?? (node.statistic = emptyStat());
134
+ incrementStatistic(node.statistic, status);
135
+ };
136
+ const ensureCategoryNode = (category) => {
137
+ const catId = categoryIds.get(category.name) ?? `cat:${md5(category.name)}`;
138
+ if (!categoryIds.has(category.name)) {
139
+ categoryIds.set(category.name, catId);
140
+ }
141
+ duplicateChecker({
142
+ id: catId,
143
+ type: "category",
144
+ name: category.name,
145
+ statistic: emptyStat(),
146
+ childrenIds: [],
147
+ expand: category.expand,
148
+ });
149
+ return catId;
150
+ };
151
+ for (const tr of visible) {
152
+ const matchingData = extractErrorMatchingData(tr);
153
+ const matchedCategory = matchCategory(categories, matchingData);
154
+ if (!matchedCategory || matchedCategory.hide) {
155
+ continue;
156
+ }
157
+ const catId = ensureCategoryNode(matchedCategory);
158
+ categoryTouched.add(matchedCategory.name);
159
+ bumpStat(catId, tr.status);
160
+ const environmentValue = envKey(tr.environment);
161
+ const isSingleEnvironmentSelected = selectedEnvironmentCount === 1;
162
+ const groupEnvironments = computeGroupEnvironments(matchedCategory, environmentCount, isSingleEnvironmentSelected);
163
+ const levels = buildGroupLevels(matchedCategory, tr, matchingData, environmentCount, isSingleEnvironmentSelected);
164
+ const leafName = groupEnvironments ? formatGroupName("environment", environmentValue) : tr.name;
165
+ let parentId = catId;
166
+ for (const level of levels) {
167
+ const levelId = `${level.type}:${md5(`${parentId}\n${level.key}\n${level.value}`)}`;
168
+ const historyId = level.type === "history" ? level.value : undefined;
169
+ duplicateChecker({
170
+ id: levelId,
171
+ type: level.type,
172
+ name: level.name,
173
+ key: level.key,
174
+ value: level.value,
175
+ historyId,
176
+ statistic: emptyStat(),
177
+ childrenIds: [],
178
+ });
179
+ bumpStat(levelId, tr.status);
180
+ attachChild(parentId, levelId);
181
+ parentId = levelId;
182
+ }
183
+ duplicateChecker({
184
+ id: tr.id,
185
+ type: "tr",
186
+ name: leafName,
187
+ key: groupEnvironments ? "environment" : undefined,
188
+ value: groupEnvironments ? environmentValue : undefined,
189
+ status: tr.status,
190
+ duration: tr.duration,
191
+ flaky: tr.flaky,
192
+ retriesCount: tr.retriesCount,
193
+ transition: tr.transition,
194
+ tooltips: tr.tooltips,
195
+ });
196
+ attachChild(parentId, tr.id);
197
+ }
198
+ for (const [parentNodeId, childNodeIds] of childrenMap.entries()) {
199
+ const sortedChildIds = Array.from(childNodeIds).sort((leftChildId, rightChildId) => compareChildNodes(leftChildId, rightChildId, nodes, environmentOrderMap));
200
+ nodes[parentNodeId].childrenIds = sortedChildIds;
201
+ }
202
+ for (const catName of categoryOrder) {
203
+ if (!categoryTouched.has(catName)) {
204
+ continue;
205
+ }
206
+ const id = categoryIds.get(catName);
207
+ if (id) {
208
+ roots.push(id);
209
+ }
210
+ }
211
+ const store = { roots, nodes };
212
+ await writer.writeWidget(filename, store);
213
+ };
@@ -1,4 +1,4 @@
1
- import type { TestFixtureResult, TestResult, TestStepResult } from "@allurereport/core-api";
1
+ import { type TestFixtureResult, type TestResult, type TestStepResult } from "@allurereport/core-api";
2
2
  import type { AwesomeFixtureResult, AwesomeTestResult, AwesomeTestStepResult } from "@allurereport/web-awesome";
3
3
  export declare const convertTestResult: (tr: TestResult) => AwesomeTestResult;
4
4
  export declare const convertTestStepResult: (tsr: TestStepResult) => AwesomeTestStepResult;
@@ -1,3 +1,4 @@
1
+ import { createDictionary, } from "@allurereport/core-api";
1
2
  import MarkdownIt from "markdown-it";
2
3
  const md = new MarkdownIt();
3
4
  const markdownToHtml = (value) => (value ? md.render(value) : undefined);
@@ -8,7 +9,7 @@ const mapLabelsByName = (labels) => {
8
9
  acc[name].push(value);
9
10
  }
10
11
  return acc;
11
- }, {});
12
+ }, createDictionary());
12
13
  };
13
14
  export const convertTestResult = (tr) => {
14
15
  return {
@@ -58,9 +58,9 @@ const createBreadcrumbs = (convertedTr) => {
58
58
  acc[label.name].push(label.value || "");
59
59
  return acc;
60
60
  }, {});
61
- const parentSuites = labelsByType.parentSuite || [""];
62
- const suites = labelsByType.suite || [""];
63
- const subSuites = labelsByType.subSuite || [""];
61
+ const parentSuites = labelsByType.parentSuite ?? [""];
62
+ const suites = labelsByType.suite ?? [""];
63
+ const subSuites = labelsByType.subSuite ?? [""];
64
64
  return parentSuites.reduce((acc, parentSuite) => {
65
65
  suites.forEach((suite) => {
66
66
  subSuites.forEach((subSuite) => {
@@ -189,7 +189,7 @@ const buildTreeByTitlePath = (tests) => {
189
189
  };
190
190
  };
191
191
  const buildTreeByLabelsAndTitlePathCombined = (tests, labels) => createTreeByLabelsAndTitlePath(tests, labels, leafFactory, undefined, (group, leaf) => incrementStatistic(group.statistic, leaf.status));
192
- const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesCount, transition, tooltips, historyId, groupedLabels, }) => {
192
+ const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesCount, transition, tooltips, historyId, groupedLabels, categories, }) => {
193
193
  const leaf = {
194
194
  nodeId: id,
195
195
  id: historyId ?? id,
@@ -206,6 +206,9 @@ const leafFactory = ({ id, name, status, duration, flaky, start, retry, retriesC
206
206
  if (groupedLabels.tag && groupedLabels.tag.length > 0) {
207
207
  leaf.tags = groupedLabels.tag;
208
208
  }
209
+ if (categories?.length) {
210
+ leaf.categories = categories.map((category) => category.name).filter(Boolean);
211
+ }
209
212
  return leaf;
210
213
  };
211
214
  export const generateEnvironmentJson = async (writer, env) => {
@@ -366,17 +369,21 @@ export const generateAllCharts = async (writer, store, options, context) => {
366
369
  };
367
370
  export const generateTreeFilters = async (writer, testResults) => {
368
371
  const trTags = new Set();
372
+ const trCategories = new Set();
369
373
  for (const tr of testResults) {
370
- if (tr.labels.length === 0) {
371
- continue;
372
- }
373
- Object.values(tr.groupedLabels.tag ?? []).forEach((tag) => {
374
+ for (const tag of tr.groupedLabels.tag ?? []) {
374
375
  trTags.add(tag);
376
+ }
377
+ tr.categories?.forEach((category) => {
378
+ if (category.name) {
379
+ trCategories.add(category.name);
380
+ }
375
381
  });
376
382
  }
377
- if (trTags.size === 0) {
383
+ if (trTags.size === 0 && trCategories.size === 0) {
378
384
  return Promise.resolve();
379
385
  }
380
386
  const tags = Array.from(trTags).sort((a, b) => a.localeCompare(b));
381
- await writer.writeWidget("tree-filters.json", { tags });
387
+ const categories = Array.from(trCategories).sort((a, b) => a.localeCompare(b));
388
+ await writer.writeWidget("tree-filters.json", { tags, categories });
382
389
  };
package/dist/plugin.js CHANGED
@@ -10,10 +10,10 @@ 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_generate;
13
- import { getWorstStatus } from "@allurereport/core-api";
14
- import { convertToSummaryTestResult, } from "@allurereport/plugin-api";
13
+ import { createPluginSummary, } from "@allurereport/plugin-api";
15
14
  import { preciseTreeLabels } from "@allurereport/plugin-api";
16
15
  import { join } from "node:path";
16
+ import { applyCategoriesToTestResults, generateCategories } from "./categories.js";
17
17
  import { filterEnv } from "./environments.js";
18
18
  import { generateTimeline } from "./generateTimeline.js";
19
19
  import { generateAllCharts, generateAttachmentsFiles, generateEnvironmentJson, generateEnvirontmentsList, generateGlobals, generateHistoryDataPoints, generateNav, generateQualityGateResults, generateStaticFiles, generateStatistic, generateTestCases, generateTestEnvGroups, generateTestResults, generateTree, generateTreeFilters, generateVariables, } from "./generators.js";
@@ -24,6 +24,7 @@ export class AwesomePlugin {
24
24
  _AwesomePlugin_writer.set(this, void 0);
25
25
  _AwesomePlugin_generate.set(this, async (context, store) => {
26
26
  const { singleFile, groupBy = [], filter, appendTitlePath } = this.options ?? {};
27
+ const categories = context.categories ?? [];
27
28
  const environmentItems = await store.metadataByKey("allure_environment");
28
29
  const reportEnvironments = await store.allEnvironments();
29
30
  const attachments = await store.allAttachments();
@@ -46,6 +47,15 @@ export class AwesomePlugin {
46
47
  });
47
48
  await generateAllCharts(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, this.options, context);
48
49
  const convertedTrs = await generateTestResults(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, allTrs);
50
+ applyCategoriesToTestResults(convertedTrs, categories);
51
+ await generateCategories(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), {
52
+ tests: convertedTrs,
53
+ categories,
54
+ environmentCount: environments.length,
55
+ environments,
56
+ defaultEnvironment: "default",
57
+ selectedEnvironmentCount: environments.length,
58
+ });
49
59
  const hasGroupBy = groupBy.length > 0;
50
60
  await generateTimeline(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), convertedTrs, this.options);
51
61
  const treeLabels = hasGroupBy
@@ -62,6 +72,14 @@ export class AwesomePlugin {
62
72
  appendTitlePath,
63
73
  });
64
74
  await generateNav(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), envConvertedTrs, join(reportEnvironment, "nav.json"));
75
+ await generateCategories(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), {
76
+ tests: envConvertedTrs,
77
+ categories,
78
+ environmentCount: 1,
79
+ defaultEnvironment: "default",
80
+ selectedEnvironmentCount: 1,
81
+ filename: join(reportEnvironment, "categories.json"),
82
+ });
65
83
  }
66
84
  await generateTreeFilters(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), convertedTrs);
67
85
  await generateEnvirontmentsList(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store);
@@ -114,29 +132,19 @@ export class AwesomePlugin {
114
132
  };
115
133
  }
116
134
  async info(context, store) {
117
- const allTrs = await store.allTestResults({ filter: this.options.filter });
118
- const newTrs = await store.allNewTestResults(this.options.filter);
119
- const retryTrs = allTrs.filter((tr) => !!tr?.retries?.length);
120
- const flakyTrs = allTrs.filter((tr) => !!tr?.flaky);
121
- const duration = allTrs.reduce((acc, { duration: trDuration = 0 }) => acc + trDuration, 0);
122
- const worstStatus = getWorstStatus(allTrs.map(({ status }) => status));
123
- const createdAt = allTrs.reduce((acc, { stop }) => Math.max(acc, stop || 0), 0);
124
- return {
135
+ return createPluginSummary({
125
136
  name: this.options.reportName || context.reportName,
126
- stats: await store.testsStatistic(this.options.filter),
127
- status: worstStatus ?? "passed",
128
- duration,
129
- createdAt,
130
137
  plugin: "Awesome",
131
- newTests: newTrs.map(convertToSummaryTestResult),
132
- flakyTests: flakyTrs.map(convertToSummaryTestResult),
133
- retryTests: retryTrs.map(convertToSummaryTestResult),
134
138
  meta: {
135
139
  reportId: context.reportUuid,
136
140
  singleFile: this.options.singleFile ?? false,
137
141
  withTestResultsLinks: true,
138
142
  },
139
- };
143
+ filter: this.options.filter,
144
+ ci: context.ci,
145
+ history: context.history,
146
+ store,
147
+ });
140
148
  }
141
149
  }
142
150
  _AwesomePlugin_writer = new WeakMap(), _AwesomePlugin_generate = new WeakMap();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/plugin-awesome",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "Allure Awesome Plugin – brand new HTML report with modern design and new features",
5
5
  "keywords": [
6
6
  "allure",
@@ -30,11 +30,11 @@
30
30
  "test": "rimraf ./out && vitest run"
31
31
  },
32
32
  "dependencies": {
33
- "@allurereport/charts-api": "3.2.0",
34
- "@allurereport/core-api": "3.2.0",
35
- "@allurereport/plugin-api": "3.2.0",
36
- "@allurereport/web-awesome": "3.2.0",
37
- "@allurereport/web-commons": "3.2.0",
33
+ "@allurereport/charts-api": "3.3.1",
34
+ "@allurereport/core-api": "3.3.1",
35
+ "@allurereport/plugin-api": "3.3.1",
36
+ "@allurereport/web-awesome": "3.3.1",
37
+ "@allurereport/web-commons": "3.3.1",
38
38
  "d3-shape": "^3.2.0",
39
39
  "handlebars": "^4.7.8",
40
40
  "markdown-it": "^14.1.0"