@allurereport/plugin-api 3.8.1 → 3.9.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.
package/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CategoriesConfig, DefaultLabelsConfig, EnvironmentsConfig, ReportVariables } from "@allurereport/core-api";
1
+ import type { AllureServiceConfig, CategoriesConfig, DefaultLabelsConfig, EnvironmentsConfig, ReportVariables } from "@allurereport/core-api";
2
2
  import type { PluginDescriptor } from "./plugin.js";
3
3
  import type { QualityGateConfig } from "./qualityGate.js";
4
4
  export interface Config {
@@ -19,10 +19,7 @@ export interface Config {
19
19
  plugins?: Record<string, PluginDescriptor>;
20
20
  appendHistory?: boolean;
21
21
  qualityGate?: QualityGateConfig;
22
- allureService?: {
23
- accessToken?: string;
24
- legacy?: boolean;
25
- };
22
+ allureService?: AllureServiceConfig;
26
23
  categories?: CategoriesConfig;
27
24
  globalAttachments?: string[];
28
25
  }
package/dist/plugin.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AllureHistory, AttachmentLink, CategoryDefinition, CiDescriptor, Statistic, TestError, TestResult, TestStatus } from "@allurereport/core-api";
1
+ import type { AllureHistory, AttachmentLink, CategoryDefinition, CiDescriptor, HistoryDataPoint, Statistic, TestError, TestResult, TestStatus } from "@allurereport/core-api";
2
2
  import type { QualityGateValidationResult } from "./qualityGate.js";
3
3
  import type { ResultFile } from "./resultFile.js";
4
4
  import type { AllureStore } from "./store.js";
@@ -25,6 +25,7 @@ export interface PluginContext {
25
25
  hideLabels?: (string | RegExp)[];
26
26
  reportFiles: ReportFiles;
27
27
  reportUrl?: string;
28
+ realTime?: boolean;
28
29
  output: string;
29
30
  ci?: CiDescriptor;
30
31
  categories?: CategoryDefinition[];
@@ -48,6 +49,26 @@ export interface PluginSummary {
48
49
  createdAt?: number;
49
50
  meta?: Record<string, any>;
50
51
  }
52
+ export interface PluginReportFile {
53
+ pluginId: string;
54
+ publish: boolean;
55
+ files: Record<string, string>;
56
+ }
57
+ export interface PluginPublishContext {
58
+ reportUuid: string;
59
+ reportName: string;
60
+ ci?: CiDescriptor;
61
+ historyPoint?: HistoryDataPoint;
62
+ reports: PluginReportFile[];
63
+ summary?: {
64
+ filepath: string;
65
+ summaries?: PluginSummary[];
66
+ };
67
+ }
68
+ export interface PluginPublishResult {
69
+ linksByPluginId: Record<string, string>;
70
+ remoteHref?: string;
71
+ }
51
72
  export interface ExitCode {
52
73
  actual?: number;
53
74
  original: number;
package/dist/store.d.ts CHANGED
@@ -3,10 +3,16 @@ import type { ExitCode, PluginGlobalAttachment, PluginGlobalError } from "./plug
3
3
  import type { QualityGateValidationResult } from "./qualityGate.js";
4
4
  import type { ResultFile } from "./resultFile.js";
5
5
  export type TestResultFilter = (testResult: TestResult) => boolean;
6
+ export interface TestResultRelatedData {
7
+ attachmentsByTrId: Map<string, AttachmentLink[]>;
8
+ fixturesByTrId: Map<string, TestFixtureResult[]>;
9
+ historyByTrId: Map<string, HistoryTestResult[] | undefined>;
10
+ retriesByTrId: Map<string, TestResult[]>;
11
+ }
6
12
  export interface AllureStore {
7
13
  allTestCases: () => Promise<TestCase[]>;
8
14
  allTestResults: (options?: {
9
- includeHidden?: boolean;
15
+ includeRetries?: boolean;
10
16
  filter?: TestResultFilter;
11
17
  }) => Promise<TestResult[]>;
12
18
  allAttachments: () => Promise<AttachmentLink[]>;
@@ -39,6 +45,7 @@ export interface AllureStore {
39
45
  retriesByTrId: (trId: string) => Promise<TestResult[]>;
40
46
  historyByTrId: (trId: string) => Promise<HistoryTestResult[] | undefined>;
41
47
  fixturesByTrId: (trId: string) => Promise<TestFixtureResult[]>;
48
+ relatedByTestResultIds: (trIds: readonly string[]) => Promise<TestResultRelatedData>;
42
49
  failedTestResults: () => Promise<TestResult[]>;
43
50
  unknownFailedTestResults: () => Promise<TestResult[]>;
44
51
  testResultsByLabel: (labelName: string) => Promise<{
@@ -49,10 +56,10 @@ export interface AllureStore {
49
56
  allEnvironments: () => Promise<string[]>;
50
57
  allEnvironmentIdentities: () => Promise<EnvironmentIdentity[]>;
51
58
  testResultsByEnvironment: (env: string, options?: {
52
- includeHidden?: boolean;
59
+ includeRetries?: boolean;
53
60
  }) => Promise<TestResult[]>;
54
61
  testResultsByEnvironmentId: (envId: string, options?: {
55
- includeHidden?: boolean;
62
+ includeRetries?: boolean;
56
63
  }) => Promise<TestResult[]>;
57
64
  allTestEnvGroups: () => Promise<TestEnvGroup[]>;
58
65
  allVariables: () => Promise<Record<string, any>>;
@@ -73,10 +80,10 @@ export interface AllureStoreDump {
73
80
  indexAttachmentByTestResult: Record<string, string[]>;
74
81
  indexTestResultByHistoryId: Record<string, string[]>;
75
82
  indexTestResultByTestCase: Record<string, string[]>;
76
- indexLatestEnvTestResultByHistoryId: Record<string, string> | Record<string, Record<string, string>>;
77
83
  indexAttachmentByFixture: Record<string, string[]>;
78
84
  indexFixturesByTestResult: Record<string, string[]>;
79
85
  indexKnownByHistoryId: Record<string, KnownTestFailure[]>;
86
+ testResultIdsIngestOrder: string[];
80
87
  }
81
88
  export declare enum AllureStoreDumpFiles {
82
89
  TestResults = "test-results.json",
@@ -91,9 +98,9 @@ export declare enum AllureStoreDumpFiles {
91
98
  IndexAttachmentsByTestResults = "index-attachments-by-test-results.json",
92
99
  IndexTestResultsByHistoryId = "index-test-results-by-history-id.json",
93
100
  IndexTestResultsByTestCase = "index-test-results-by-test-case.json",
94
- IndexLatestEnvTestResultsByHistoryId = "index-latest-env-test-results-by-history-id.json",
95
101
  IndexAttachmentsByFixture = "index-attachments-by-fixture.json",
96
102
  IndexFixturesByTestResult = "index-fixtures-by-test-result.json",
97
103
  IndexKnownByHistoryId = "index-known-by-history-id.json",
98
- QualityGateResults = "quality-gate-results.json"
104
+ QualityGateResults = "quality-gate-results.json",
105
+ TestResultIngestOrder = "test-result-ingest-order.json"
99
106
  }
package/dist/store.js CHANGED
@@ -12,9 +12,9 @@ export var AllureStoreDumpFiles;
12
12
  AllureStoreDumpFiles["IndexAttachmentsByTestResults"] = "index-attachments-by-test-results.json";
13
13
  AllureStoreDumpFiles["IndexTestResultsByHistoryId"] = "index-test-results-by-history-id.json";
14
14
  AllureStoreDumpFiles["IndexTestResultsByTestCase"] = "index-test-results-by-test-case.json";
15
- AllureStoreDumpFiles["IndexLatestEnvTestResultsByHistoryId"] = "index-latest-env-test-results-by-history-id.json";
16
15
  AllureStoreDumpFiles["IndexAttachmentsByFixture"] = "index-attachments-by-fixture.json";
17
16
  AllureStoreDumpFiles["IndexFixturesByTestResult"] = "index-fixtures-by-test-result.json";
18
17
  AllureStoreDumpFiles["IndexKnownByHistoryId"] = "index-known-by-history-id.json";
19
18
  AllureStoreDumpFiles["QualityGateResults"] = "quality-gate-results.json";
19
+ AllureStoreDumpFiles["TestResultIngestOrder"] = "test-result-ingest-order.json";
20
20
  })(AllureStoreDumpFiles || (AllureStoreDumpFiles = {}));
@@ -5,6 +5,11 @@ export declare const createTreeByLabels: <T = TestResult, L = DefaultTreeLeaf, G
5
5
  export declare const createTreeByCategories: <T = TestResult, L = DefaultTreeLeaf, G = DefaultTreeGroup>(data: T[], leafFactory?: (item: T) => TreeLeaf<L>, groupFactory?: (parentGroup: string | undefined, groupClassifier: string) => TreeGroup<G>, addLeafToGroup?: (group: TreeGroup<G>, leaf: TreeLeaf<L>) => void) => TreeData<L, G>;
6
6
  export declare const byCategories: (item: TestResult) => string[][];
7
7
  export declare const preciseTreeLabels: <T = TestResult>(labelNames: string[], trs: T[], labelNamesAccessor?: (tr: T) => string[]) => string[];
8
+ export declare const processTree: <L, G>(tree: TreeData<L, G>, options: {
9
+ filter?: (leaf: TreeLeaf<L>) => boolean;
10
+ sort?: Comparator<TreeLeaf<L>>;
11
+ transform?: (leaf: TreeLeaf<L>, idx: number) => TreeLeaf<L>;
12
+ }) => TreeData<L, G>;
8
13
  export declare const filterTree: <L, G>(tree: TreeData<L, G>, predicate: (leaf: TreeLeaf<L>) => boolean) => TreeData<L, G>;
9
14
  export declare const sortTree: <L, G>(tree: TreeData<L, G>, comparator: Comparator<TreeLeaf<L>>) => TreeData<L, G>;
10
15
  export declare const transformTree: <L, G>(tree: TreeData<L, G>, transformer: (leaf: TreeLeaf<L>, idx: number) => TreeLeaf<L>) => TreeData<L, G>;
@@ -1,28 +1,41 @@
1
- import { createDictionary, findByLabelName, } from "@allurereport/core-api";
2
1
  import { emptyStatistic } from "@allurereport/core-api";
3
2
  import { md5 } from "./misc.js";
4
- const addLeaf = (node, nodeId) => {
3
+ const addLeaf = (childLeaves, node, nodeId) => {
5
4
  if (node.leaves === undefined) {
6
5
  node.leaves = [];
7
6
  }
8
- if (node.leaves.find((value) => value === nodeId)) {
7
+ let leaves = childLeaves.get(node);
8
+ if (!leaves) {
9
+ leaves = new Set(node.leaves);
10
+ childLeaves.set(node, leaves);
11
+ }
12
+ if (leaves.has(nodeId)) {
9
13
  return;
10
14
  }
15
+ leaves.add(nodeId);
11
16
  node.leaves.push(nodeId);
12
17
  };
13
- const addGroup = (node, nodeId) => {
18
+ const addGroup = (childGroups, node, nodeId) => {
14
19
  if (node.groups === undefined) {
15
20
  node.groups = [];
16
21
  }
17
- if (node.groups.find((value) => value === nodeId)) {
22
+ let groups = childGroups.get(node);
23
+ if (!groups) {
24
+ groups = new Set(node.groups);
25
+ childGroups.set(node, groups);
26
+ }
27
+ if (groups.has(nodeId)) {
18
28
  return;
19
29
  }
30
+ groups.add(nodeId);
20
31
  node.groups.push(nodeId);
21
32
  };
22
33
  const createTree = (data, classifier, leafFactory, groupFactory, addLeafToGroup = () => { }) => {
23
- const groupsByClassifier = createDictionary();
34
+ const groupsByClassifier = new Map();
24
35
  const leavesById = {};
25
36
  const groupsById = {};
37
+ const childLeaves = new WeakMap();
38
+ const childGroups = new WeakMap();
26
39
  const root = { groups: [], leaves: [] };
27
40
  for (const item of data) {
28
41
  const leaf = leafFactory(item);
@@ -33,32 +46,40 @@ const createTree = (data, classifier, leafFactory, groupFactory, addLeafToGroup
33
46
  if (layer.length === 0) {
34
47
  break;
35
48
  }
36
- parentGroups = layer.flatMap((group) => {
37
- return parentGroups.flatMap((parentGroup) => {
49
+ const nextParentGroups = [];
50
+ const nextParentGroupIds = new Set();
51
+ for (const group of layer) {
52
+ for (const parentGroup of parentGroups) {
38
53
  const parentId = "nodeId" in parentGroup ? parentGroup.nodeId : "";
39
- if (groupsByClassifier[parentId] === undefined) {
40
- groupsByClassifier[parentId] = createDictionary();
54
+ let groupsByParent = groupsByClassifier.get(parentId);
55
+ if (groupsByParent === undefined) {
56
+ groupsByParent = new Map();
57
+ groupsByClassifier.set(parentId, groupsByParent);
41
58
  }
42
- if (groupsByClassifier[parentId][group] === undefined) {
59
+ if (groupsByParent.get(group) === undefined) {
43
60
  const newGroup = groupFactory(parentId, group);
44
61
  if (!newGroup || typeof newGroup !== "object" || typeof newGroup.nodeId !== "string") {
45
- return [];
62
+ continue;
46
63
  }
47
- groupsByClassifier[parentId][group] = newGroup;
64
+ groupsByParent.set(group, newGroup);
48
65
  groupsById[newGroup.nodeId] = newGroup;
49
66
  }
50
- const currentGroup = groupsByClassifier[parentId][group];
67
+ const currentGroup = groupsByParent.get(group);
51
68
  if (!currentGroup || typeof currentGroup !== "object") {
52
- return [];
69
+ continue;
53
70
  }
54
- addGroup(parentGroup, currentGroup.nodeId);
71
+ addGroup(childGroups, parentGroup, currentGroup.nodeId);
55
72
  addLeafToGroup(currentGroup, leaf);
56
- return [currentGroup];
57
- });
58
- });
73
+ if (!nextParentGroupIds.has(currentGroup.nodeId)) {
74
+ nextParentGroupIds.add(currentGroup.nodeId);
75
+ nextParentGroups.push(currentGroup);
76
+ }
77
+ }
78
+ }
79
+ parentGroups = nextParentGroups;
59
80
  }
60
81
  parentGroups.forEach((parentGroup) => {
61
- addLeaf(parentGroup, leaf.nodeId);
82
+ addLeaf(childLeaves, parentGroup, leaf.nodeId);
62
83
  });
63
84
  }
64
85
  return {
@@ -67,16 +88,30 @@ const createTree = (data, classifier, leafFactory, groupFactory, addLeafToGroup
67
88
  leavesById,
68
89
  };
69
90
  };
70
- export const byLabels = (item, labelNames) => {
71
- return labelNames
72
- .map((labelName) => item.labels.filter((label) => labelName === label.name).map((label) => label.value ?? "__unknown") ?? [])
73
- .filter((layer) => layer.length > 0);
91
+ const byLabelsWithFallback = (item, labelNames, fallbackValue) => {
92
+ const labelsByName = new Map();
93
+ for (const label of item.labels) {
94
+ const values = labelsByName.get(label.name) ?? [];
95
+ values.push(label.value ?? fallbackValue);
96
+ labelsByName.set(label.name, values);
97
+ }
98
+ return labelNames.map((labelName) => labelsByName.get(labelName) ?? []).filter((layer) => layer.length > 0);
74
99
  };
100
+ export const byLabels = (item, labelNames) => byLabelsWithFallback(item, labelNames, "__unknown");
75
101
  export const filterTreeLabels = (data, labelNames) => {
76
- return [...labelNames]
77
- .reverse()
78
- .filter((labelName) => data.find((item) => findByLabelName(item.labels, labelName)))
79
- .reverse();
102
+ const requested = new Set(labelNames);
103
+ const found = new Set();
104
+ for (const item of data) {
105
+ for (const label of item.labels) {
106
+ if (requested.has(label.name)) {
107
+ found.add(label.name);
108
+ }
109
+ }
110
+ if (found.size === requested.size) {
111
+ break;
112
+ }
113
+ }
114
+ return labelNames.filter((labelName) => found.has(labelName));
80
115
  };
81
116
  export const createTreeByLabels = (data, labelNames, leafFactory, groupFactory, addLeafToGroup = () => { }) => {
82
117
  const leafFactoryFn = leafFactory ??
@@ -128,92 +163,71 @@ export const byCategories = (item) => {
128
163
  return result;
129
164
  };
130
165
  export const preciseTreeLabels = (labelNames, trs, labelNamesAccessor = (tr) => tr.labels.map(({ name }) => name)) => {
131
- const result = new Set();
132
- for (const labelName of labelNames) {
133
- if (trs.some((tr) => labelNamesAccessor(tr).includes(labelName))) {
134
- result.add(labelName);
166
+ const requested = new Set(labelNames);
167
+ const found = new Set();
168
+ for (const tr of trs) {
169
+ for (const labelName of labelNamesAccessor(tr)) {
170
+ if (requested.has(labelName)) {
171
+ found.add(labelName);
172
+ }
173
+ }
174
+ if (found.size === requested.size) {
175
+ break;
135
176
  }
136
177
  }
137
- return Array.from(result);
178
+ const emitted = new Set();
179
+ return labelNames.filter((labelName) => {
180
+ if (!found.has(labelName) || emitted.has(labelName)) {
181
+ return false;
182
+ }
183
+ emitted.add(labelName);
184
+ return true;
185
+ });
138
186
  };
139
- export const filterTree = (tree, predicate) => {
187
+ export const processTree = (tree, options) => {
140
188
  const visitedGroups = new Set();
141
189
  const { root, leavesById, groupsById } = tree;
142
- const filterGroupLeaves = (group) => {
143
- if (!predicate) {
144
- return group;
145
- }
190
+ const processGroup = (group) => {
146
191
  if (group.groups?.length) {
147
192
  group.groups.forEach((groupId) => {
148
193
  const subGroup = groupsById[groupId];
149
194
  if (!subGroup || visitedGroups.has(groupId)) {
150
195
  return;
151
196
  }
152
- filterGroupLeaves(subGroup);
197
+ processGroup(subGroup);
153
198
  visitedGroups.add(groupId);
154
199
  });
155
200
  }
156
201
  if (group.leaves?.length) {
157
- group.leaves = group.leaves.filter((leaveId) => predicate(leavesById[leaveId]));
202
+ if (options.filter) {
203
+ group.leaves = group.leaves.filter((leaveId) => options.filter(leavesById[leaveId]));
204
+ }
205
+ if (options.sort) {
206
+ group.leaves = group.leaves.sort((a, b) => {
207
+ const leafA = leavesById[a];
208
+ const leafB = leavesById[b];
209
+ return options.sort(leafA, leafB);
210
+ });
211
+ }
212
+ if (options.transform) {
213
+ group.leaves.forEach((leafId, i) => {
214
+ leavesById[leafId] = options.transform(leavesById[leafId], i);
215
+ });
216
+ }
158
217
  }
159
218
  return group;
160
219
  };
161
- filterGroupLeaves(root);
220
+ processGroup(root);
162
221
  return tree;
163
222
  };
223
+ export const filterTree = (tree, predicate) => {
224
+ return processTree(tree, { filter: predicate });
225
+ };
164
226
  export const sortTree = (tree, comparator) => {
165
- const visitedGroups = new Set();
166
- const { root, leavesById, groupsById } = tree;
167
- const sortGroupLeaves = (group) => {
168
- if (!comparator) {
169
- return group;
170
- }
171
- if (group.groups?.length) {
172
- group.groups.forEach((groupId) => {
173
- if (visitedGroups.has(groupId)) {
174
- return;
175
- }
176
- sortGroupLeaves(groupsById[groupId]);
177
- visitedGroups.add(groupId);
178
- });
179
- }
180
- if (group.leaves?.length) {
181
- group.leaves = group.leaves.sort((a, b) => {
182
- const leafA = leavesById[a];
183
- const leafB = leavesById[b];
184
- return comparator(leafA, leafB);
185
- });
186
- }
187
- return group;
188
- };
189
- sortGroupLeaves(root);
190
- return tree;
227
+ return processTree(tree, { sort: comparator });
191
228
  };
192
229
  export const transformTree = (tree, transformer) => {
193
- const visitedGroups = new Set();
194
- const { root, leavesById, groupsById } = tree;
195
- const transformGroupLeaves = (group) => {
196
- if (!transformer) {
197
- return group;
198
- }
199
- if (group.groups?.length) {
200
- group.groups.forEach((groupId) => {
201
- if (visitedGroups.has(groupId)) {
202
- return;
203
- }
204
- transformGroupLeaves(groupsById[groupId]);
205
- visitedGroups.add(groupId);
206
- });
207
- }
208
- if (group.leaves?.length) {
209
- group.leaves.forEach((leaf, i) => {
210
- leavesById[leaf] = transformer(leavesById[leaf], i);
211
- });
212
- }
213
- return group;
214
- };
215
- transformGroupLeaves(root);
216
- return tree;
230
+ return processTree(tree, { transform: transformer });
217
231
  };
218
232
  export const createTreeByTitlePath = (data, leafFactory, groupFactory, addLeafToGroup = () => { }) => {
219
233
  const leafFactoryFn = leafFactory ??
@@ -235,14 +249,7 @@ export const createTreeByTitlePath = (data, leafFactory, groupFactory, addLeafTo
235
249
  return createTree(data, (item) => (item.titlePath ?? []).map((segment) => [segment]), leafFactoryFn, groupFactoryFn, addLeafToGroup);
236
250
  };
237
251
  const byLabelsAndTitlePath = (item, labelNames) => {
238
- const leaves = [];
239
- for (const labelName of labelNames) {
240
- const values = item.labels.filter((label) => label.name === labelName).map((label) => label.value ?? "");
241
- if (!values.length) {
242
- continue;
243
- }
244
- leaves.push(values);
245
- }
252
+ const leaves = byLabelsWithFallback(item, labelNames, "");
246
253
  const titlePath = item.titlePath;
247
254
  if (Array.isArray(titlePath) && titlePath.length > 0) {
248
255
  for (const segment of titlePath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/plugin-api",
3
- "version": "3.8.1",
3
+ "version": "3.9.0",
4
4
  "description": "Allure Plugin API",
5
5
  "keywords": [
6
6
  "allure"
@@ -26,12 +26,13 @@
26
26
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
27
27
  },
28
28
  "dependencies": {
29
- "@allurereport/core-api": "3.8.1"
29
+ "@allurereport/core-api": "3.9.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^20.17.9",
33
33
  "@vitest/runner": "^2.1.9",
34
34
  "@vitest/snapshot": "^2.1.9",
35
+ "allure-js-commons": "^3.3.3",
35
36
  "allure-vitest": "^3.3.3",
36
37
  "rimraf": "^6.0.1",
37
38
  "typescript": "^5.6.3",