@allurereport/plugin-allure2 3.16.0 → 3.17.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.
Files changed (53) hide show
  1. package/README.md +12 -11
  2. package/dist/categories.js +9 -6
  3. package/dist/converters.d.ts +2 -1
  4. package/dist/converters.js +111 -43
  5. package/dist/generators.d.ts +10 -15
  6. package/dist/generators.js +163 -123
  7. package/dist/model.d.ts +40 -3
  8. package/dist/model.js +1 -1
  9. package/dist/plugin.js +18 -3
  10. package/dist/static/report.tar +0 -0
  11. package/dist/tree.d.ts +1 -0
  12. package/dist/tree.js +1 -0
  13. package/dist/utils.d.ts +1 -0
  14. package/dist/utils.js +11 -1
  15. package/package.json +7 -8
  16. package/static/multi/154.app-fb0942f23106767a264c.js +0 -1
  17. package/static/multi/216.app-fb0942f23106767a264c.js +0 -1
  18. package/static/multi/232.app-fb0942f23106767a264c.js +0 -1
  19. package/static/multi/250.app-fb0942f23106767a264c.js +0 -1
  20. package/static/multi/256.app-fb0942f23106767a264c.js +0 -1
  21. package/static/multi/301.app-fb0942f23106767a264c.js +0 -1
  22. package/static/multi/323.app-fb0942f23106767a264c.js +0 -1
  23. package/static/multi/353.app-fb0942f23106767a264c.js +0 -1
  24. package/static/multi/411d15fc5bb62472eea6ead4fb276ca9.ico +0 -0
  25. package/static/multi/564.app-fb0942f23106767a264c.js +0 -1
  26. package/static/multi/594.app-fb0942f23106767a264c.js +0 -1
  27. package/static/multi/623.app-fb0942f23106767a264c.js +0 -1
  28. package/static/multi/647.app-fb0942f23106767a264c.js +0 -1
  29. package/static/multi/7.app-fb0942f23106767a264c.js +0 -1
  30. package/static/multi/729.app-fb0942f23106767a264c.js +0 -1
  31. package/static/multi/783.app-fb0942f23106767a264c.js +0 -1
  32. package/static/multi/826.app-fb0942f23106767a264c.js +0 -1
  33. package/static/multi/925.app-fb0942f23106767a264c.js +0 -1
  34. package/static/multi/app-fb0942f23106767a264c.js +0 -2
  35. package/static/multi/app-fb0942f23106767a264c.js.LICENSE.txt +0 -22
  36. package/static/multi/checkmark.svg +0 -8
  37. package/static/multi/manifest.json +0 -28
  38. package/static/multi/sort-down-icon.svg +0 -8
  39. package/static/multi/sort-icon.svg +0 -8
  40. package/static/multi/sort-up-icon.svg +0 -8
  41. package/static/multi/sprite.svg +0 -4
  42. package/static/multi/styles-aa1ab4909f318c34044b.css +0 -558
  43. package/static/multi/vertical.png +0 -0
  44. package/static/single/411d15fc5bb62472eea6ead4fb276ca9.ico +0 -0
  45. package/static/single/app-1fe2002078c21c1001aa.js +0 -2
  46. package/static/single/app-1fe2002078c21c1001aa.js.LICENSE.txt +0 -22
  47. package/static/single/checkmark.svg +0 -8
  48. package/static/single/manifest.json +0 -10
  49. package/static/single/sort-down-icon.svg +0 -8
  50. package/static/single/sort-icon.svg +0 -8
  51. package/static/single/sort-up-icon.svg +0 -8
  52. package/static/single/sprite.svg +0 -4
  53. package/static/single/styles-0bf6b43f9cd1a3e65872.css +0 -558
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Classic Plugin
1
+ # Allure 2 Plugin
2
2
 
3
3
  [<img src="https://allurereport.org/public/img/allure-report.svg" height="85px" alt="Allure Report logo" align="right" />](https://allurereport.org "Allure Report")
4
4
 
@@ -12,16 +12,17 @@
12
12
 
13
13
  ## Overview
14
14
 
15
- The plugin generates a classic Allure Report.
15
+ The plugin generates an Allure 2-compatible report using the Allure 2.46.0 UI built from
16
+ the checked-in `@allurereport/web-allure2` sources and embedded in the plugin package.
16
17
 
17
18
  ## Install
18
19
 
19
20
  Use your favorite package manager to install the package:
20
21
 
21
22
  ```shell
22
- npm add @allurereport/plugin-classic
23
- yarn add @allurereport/plugin-classic
24
- pnpm add @allurereport/plugin-classic
23
+ npm add @allurereport/plugin-allure2
24
+ yarn add @allurereport/plugin-allure2
25
+ pnpm add @allurereport/plugin-allure2
25
26
  ```
26
27
 
27
28
  Then, add the plugin to the Allure configuration file:
@@ -34,7 +35,7 @@ export default defineConfig({
34
35
  output: "./allure-report",
35
36
  historyPath: "./history.jsonl",
36
37
  plugins: {
37
- + classic: {
38
+ + allure2: {
38
39
  + options: {
39
40
  + reportName: "HelloWorld",
40
41
  + },
@@ -47,8 +48,8 @@ export default defineConfig({
47
48
 
48
49
  The plugin accepts the following options:
49
50
 
50
- | Option | Description | Type | Default |
51
- |------------------|-------------------------------------------------|--------------------------------------------------------------|-----------------|
52
- | `reportName` | Name of the report | `string` | `Allure Report` |
53
- | `singleFile` | Writes the report as a single `index.html` file | `boolean` | `false` |
54
- | `reportLanguage` | Default language of the report | `string` | OS language |
51
+ | Option | Description | Type | Default |
52
+ | ---------------- | ----------------------------------------------- | --------- | --------------- |
53
+ | `reportName` | Name of the report | `string` | `Allure Report` |
54
+ | `singleFile` | Writes the report as a single `index.html` file | `boolean` | `false` |
55
+ | `reportLanguage` | Default language of the report | `string` | OS language |
@@ -7,14 +7,17 @@ const testDefects = {
7
7
  matchedStatuses: ["broken"],
8
8
  };
9
9
  export const matchCategories = (categories, result) => {
10
- const matched = categories.filter((category) => categoryMatch(category, result));
11
- if (matched.length === 0 && categoryMatch(productDefects, result)) {
12
- matched.push(productDefects);
10
+ const matched = categories.find((category) => categoryMatch(category, result));
11
+ if (matched) {
12
+ return [matched];
13
13
  }
14
- if (matched.length === 0 && categoryMatch(testDefects, result)) {
15
- matched.push(testDefects);
14
+ if (categoryMatch(productDefects, result)) {
15
+ return [productDefects];
16
16
  }
17
- return matched;
17
+ if (categoryMatch(testDefects, result)) {
18
+ return [testDefects];
19
+ }
20
+ return [];
18
21
  };
19
22
  const categoryMatch = (category, result) => {
20
23
  const { status, statusMessage, statusTrace, flaky } = result;
@@ -1,11 +1,12 @@
1
1
  import type { HistoryTestResult, TestFixtureResult, TestResult } from "@allurereport/core-api";
2
- import type { Allure2Category, Allure2TestResult } from "./model.js";
2
+ import type { Allure2Category, Allure2HistoryData, Allure2TestResult } from "./model.js";
3
3
  type ConvertContext = {
4
4
  attachmentMap: Map<string, string>;
5
5
  fixtures: TestFixtureResult[];
6
6
  categories: Allure2Category[];
7
7
  retries: TestResult[];
8
8
  history: HistoryTestResult[];
9
+ legacyHistory?: Allure2HistoryData;
9
10
  };
10
11
  export declare const convertTestResult: (context: ConvertContext, test: TestResult) => Allure2TestResult;
11
12
  export {};
@@ -1,31 +1,70 @@
1
- import { filterIncludedInSuccessRate, isStep, redactParameters } from "@allurereport/core-api";
1
+ import { isStep, redactParameters } from "@allurereport/core-api";
2
2
  import { matchCategories } from "./categories.js";
3
3
  import { severityValues } from "./model.js";
4
- import { updateStatistic } from "./utils.js";
4
+ import { createStatistic, updateStatistic } from "./utils.js";
5
5
  const sortByTime = (a, b) => a.time.start !== undefined && b.time.start !== undefined ? a.time.start - b.time.start : 0;
6
6
  const convertStatus = (status) => status;
7
+ const getTopLevelAttachmentCount = (result) => {
8
+ if (result.sourceMetadata.readerId !== "allure2") {
9
+ return 0;
10
+ }
11
+ const value = result.sourceMetadata.metadata.allure2_top_level_attachment_count;
12
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : 0;
13
+ };
14
+ const hasMessage = (step, message) => step.statusMessage === message || step.steps.some((child) => hasMessage(child, message));
15
+ const summarize = (name, statusMessage, steps, attachments, parametersCount) => {
16
+ const stepsCount = steps.reduce((count, step) => count + 1 + step.stepsCount, 0);
17
+ const attachmentsCount = attachments.length + steps.reduce((count, step) => count + step.attachmentsCount, 0);
18
+ const shouldDisplayMessage = !!statusMessage && !steps.some((step) => hasMessage(step, statusMessage));
19
+ return {
20
+ stepsCount,
21
+ attachmentsCount,
22
+ shouldDisplayMessage,
23
+ hasContent: steps.length + attachments.length + parametersCount > 0 || shouldDisplayMessage,
24
+ attachmentStep: stepsCount === 0 && attachmentsCount === 1 && name !== undefined && name === attachments[0]?.name,
25
+ };
26
+ };
7
27
  const convertStageResult = (context, result) => {
8
- const { name: _name, ...testStage } = convertStep(context, {
9
- name: "test",
10
- steps: result.steps,
11
- start: result.start,
12
- stop: result.stop,
13
- duration: result.duration,
28
+ const topLevelAttachmentCount = Math.min(getTopLevelAttachmentCount(result), result.steps.length);
29
+ const attachmentStart = result.steps.length - topLevelAttachmentCount;
30
+ const attachmentCandidates = result.steps.slice(attachmentStart);
31
+ const stageSteps = [
32
+ ...result.steps.slice(0, attachmentStart),
33
+ ...attachmentCandidates.filter((step) => isStep(step)),
34
+ ];
35
+ const attachments = attachmentCandidates
36
+ .filter((step) => !isStep(step))
37
+ .map((step) => convertAttachment(context, step));
38
+ const steps = stageSteps.map((step) => convertStep(context, step));
39
+ const name = "type" in result ? result.name : undefined;
40
+ const statusMessage = result.error?.message;
41
+ const summary = summarize(name, statusMessage, steps, attachments, 0);
42
+ return {
43
+ ...(name === undefined ? {} : { name }),
44
+ time: {
45
+ start: result.start,
46
+ stop: result.stop,
47
+ duration: result.duration,
48
+ },
14
49
  status: result.status,
50
+ statusMessage,
51
+ statusTrace: result.error?.trace,
52
+ steps,
53
+ attachments,
15
54
  parameters: [],
16
- type: "step",
17
- });
18
- return testStage;
55
+ ...summary,
56
+ };
19
57
  };
20
58
  const convertStep = (context, step) => {
21
59
  if (isStep(step)) {
22
60
  const name = step.name;
23
- const steps = step.steps.map((child) => convertStep(context, child));
24
- const stepsCount = steps.length;
61
+ const steps = step.steps.filter((child) => isStep(child)).map((child) => convertStep(context, child));
62
+ const attachments = step.steps
63
+ .filter((child) => !isStep(child))
64
+ .map((child) => convertAttachment(context, child));
25
65
  const parameters = redactParameters(step.parameters);
26
- const parametersCount = parameters.length;
27
- const statusMessage = step.error?.message;
28
- const shouldDisplayMessage = !!statusMessage || steps.findIndex((s) => s.statusMessage === statusMessage) > 0;
66
+ const statusMessage = step.error?.message ?? step.message;
67
+ const summary = summarize(name, statusMessage, steps, attachments, parameters.length);
29
68
  return {
30
69
  name,
31
70
  time: {
@@ -35,15 +74,11 @@ const convertStep = (context, step) => {
35
74
  },
36
75
  status: step.status,
37
76
  statusMessage,
38
- statusTrace: step.error?.trace,
77
+ statusTrace: step.error?.trace ?? step.trace,
39
78
  steps,
40
- attachments: [],
79
+ attachments,
41
80
  parameters,
42
- stepsCount,
43
- attachmentsCount: 0,
44
- hasContent: stepsCount + parametersCount > 0 || shouldDisplayMessage,
45
- shouldDisplayMessage,
46
- attachmentStep: false,
81
+ ...summary,
47
82
  };
48
83
  }
49
84
  const stepName = step.link.name;
@@ -87,8 +122,30 @@ const extractSeverity = (test) => {
87
122
  return (maybeSeverity ? severityValues.find((value) => value === maybeSeverity) : undefined) ?? "normal";
88
123
  };
89
124
  const importantStatuses = ["failed", "broken", "passed"];
125
+ const isImportantStatus = (status) => importantStatuses.includes(status);
126
+ const getAllure2Links = (test) => {
127
+ if (test.sourceMetadata.readerId !== "allure2") {
128
+ return undefined;
129
+ }
130
+ const links = test.sourceMetadata.metadata.allure2_links;
131
+ if (!Array.isArray(links)) {
132
+ return undefined;
133
+ }
134
+ return links.map((link) => ({
135
+ name: typeof link?.name === "string" ? link.name : undefined,
136
+ url: typeof link?.url === "string" ? link.url : undefined,
137
+ type: typeof link?.type === "string" ? link.type : undefined,
138
+ }));
139
+ };
140
+ const isFlakyFromHistory = (currentStatus, historyItems) => {
141
+ if (currentStatus !== "failed" && currentStatus !== "broken") {
142
+ return false;
143
+ }
144
+ const statuses = historyItems.slice(0, 5).map(({ status }) => status);
145
+ return statuses.includes("passed") && statuses.indexOf("passed") < statuses.lastIndexOf("failed");
146
+ };
90
147
  export const convertTestResult = (context, test) => {
91
- const testStage = convertStageResult(context, test);
148
+ const testStage = test.steps.length > 0 ? convertStageResult(context, test) : null;
92
149
  const beforeStages = context.fixtures
93
150
  .filter((value) => value.type === "before")
94
151
  .map((fixture) => convertStageResult(context, fixture))
@@ -103,8 +160,6 @@ export const convertTestResult = (context, test) => {
103
160
  const status = convertStatus(test.status);
104
161
  const statusMessage = test.error?.message;
105
162
  const statusTrace = test.error?.trace;
106
- const flaky = false;
107
- const categories = matchCategories(context.categories, { statusMessage, statusTrace, status, flaky });
108
163
  const retries = context.retries.map((retry) => ({
109
164
  uid: retry.id,
110
165
  status: convertStatus(retry.status),
@@ -115,25 +170,38 @@ export const convertTestResult = (context, test) => {
115
170
  duration: retry.duration,
116
171
  },
117
172
  }));
118
- const retriesStatusChange = status in importantStatuses &&
119
- retries.find((tr) => tr.status in importantStatuses && tr.status !== status) !== undefined;
120
- const lastHistoryStatus = context.history.find(filterIncludedInSuccessRate)?.status;
173
+ const retriesStatusChange = isImportantStatus(status) &&
174
+ retries.find((retry) => isImportantStatus(retry.status) && retry.status !== status) !== undefined;
175
+ const historyItems = context.legacyHistory
176
+ ? context.legacyHistory.items.map((item) => ({
177
+ uid: item.uid,
178
+ status: item.status,
179
+ reportUrl: item.reportUrl,
180
+ statusDetails: item.statusDetails,
181
+ time: { ...item.time },
182
+ }))
183
+ : context.history.map((historyTestResult) => ({
184
+ uid: historyTestResult.id,
185
+ status: convertStatus(historyTestResult.status),
186
+ reportUrl: historyTestResult.url,
187
+ statusDetails: historyTestResult.error?.message,
188
+ time: {
189
+ start: historyTestResult.start,
190
+ stop: historyTestResult.stop,
191
+ duration: historyTestResult.duration,
192
+ },
193
+ }));
194
+ const lastHistoryStatus = historyItems.find(({ status: historyStatus }) => isImportantStatus(historyStatus))?.status;
121
195
  const newFailed = lastHistoryStatus === "passed" && test.status === "failed";
122
196
  const newBroken = lastHistoryStatus === "passed" && test.status === "broken";
123
197
  const newPassed = lastHistoryStatus !== undefined && lastHistoryStatus !== "passed" && test.status === "passed";
124
- const historyItems = context.history.map((htr) => ({
125
- uid: htr.id,
126
- status: convertStatus(htr.status),
127
- reportUrl: "unsupported",
128
- statusDetails: htr.error?.message,
129
- time: {
130
- start: htr.start,
131
- stop: htr.stop,
132
- duration: htr.duration,
133
- },
134
- }));
135
- const statistic = { total: 0 };
136
- historyItems.forEach((historyItem) => updateStatistic(statistic, historyItem));
198
+ const flaky = test.flaky || isFlakyFromHistory(status, historyItems);
199
+ const categories = matchCategories(context.categories, { statusMessage, statusTrace, status, flaky });
200
+ const statistic = createStatistic(context.legacyHistory?.statistic);
201
+ if (!context.legacyHistory) {
202
+ historyItems.forEach((historyItem) => updateStatistic(statistic, historyItem));
203
+ }
204
+ updateStatistic(statistic, test);
137
205
  const history = {
138
206
  statistic,
139
207
  items: historyItems,
@@ -155,7 +223,7 @@ export const convertTestResult = (context, test) => {
155
223
  statusMessage,
156
224
  statusTrace,
157
225
  labels: test.labels,
158
- links: test.links,
226
+ links: getAllure2Links(test) ?? test.links,
159
227
  parameters: redactParameters(test.parameters),
160
228
  afterStages,
161
229
  beforeStages,
@@ -1,18 +1,10 @@
1
- import type { AttachmentLink, HistoryDataPoint } from "@allurereport/core-api";
1
+ import type { AttachmentLink, GlobalAttachmentLink, HistoryDataPoint, TestError } from "@allurereport/core-api";
2
2
  import type { ReportFiles, ResultFile } from "@allurereport/plugin-api";
3
- import type { Allure2ExecutorInfo, Allure2TestResult } from "./model.js";
3
+ import type { ReportStaticManifest } from "@allurereport/plugin-api/static-assets";
4
+ import type { Allure2CategoriesTrendItem, Allure2DurationTrendItem, Allure2ExecutorInfo, Allure2LegacyHistoryTrendItem, Allure2RetryTrendItem, Allure2TestResult } from "./model.js";
4
5
  import type { Allure2DataWriter, ReportFile } from "./writer.js";
5
- export type TemplateManifest = Record<string, string>;
6
- export declare const getPackageRoot: () => Promise<string>;
7
- export declare const readTemplateManifest: (packageRoot: string, singleFileMode?: boolean) => Promise<TemplateManifest>;
8
- export declare const readManifestEntry: (options: {
9
- fileName: string;
10
- singleFile?: boolean;
11
- mimeType: string;
12
- reportFiles: ReportFiles;
13
- inserter: (content: string) => string;
14
- packageRoot: string;
15
- }) => Promise<string>;
6
+ export type TemplateManifest = ReportStaticManifest;
7
+ export declare const readTemplateManifest: () => Promise<TemplateManifest>;
16
8
  export declare const generateStaticFiles: (payload: {
17
9
  allureVersion: string;
18
10
  reportName: string;
@@ -34,6 +26,9 @@ export declare const generateEnvironmentJson: (writer: Allure2DataWriter, env: {
34
26
  }[]) => Promise<void>;
35
27
  export declare const generateExecutorJson: (writer: Allure2DataWriter, executor?: Partial<Allure2ExecutorInfo>) => Promise<void>;
36
28
  export declare const generateDefaultWidgetData: (writer: Allure2DataWriter, tests: Allure2TestResult[], ...fileNames: string[]) => Promise<void>;
37
- export declare const generateEmptyTrendData: (writer: Allure2DataWriter, ...fileNames: string[]) => Promise<void>;
38
- export declare const generateTrendData: (writer: Allure2DataWriter, reportName: string, tests: Allure2TestResult[], historyDataPoints: HistoryDataPoint[]) => Promise<void>;
29
+ export declare const generateHistoryTrendData: (writer: Allure2DataWriter, reportName: string, tests: Allure2TestResult[], historyDataPoints: HistoryDataPoint[], legacyHistory?: Allure2LegacyHistoryTrendItem[], executor?: Partial<Allure2ExecutorInfo>) => Promise<void>;
30
+ export declare const generateDurationTrendData: (writer: Allure2DataWriter, tests: Allure2TestResult[], history?: Allure2DurationTrendItem[], executor?: Partial<Allure2ExecutorInfo>) => Promise<void>;
31
+ export declare const generateRetryTrendData: (writer: Allure2DataWriter, tests: Allure2TestResult[], history?: Allure2RetryTrendItem[], executor?: Partial<Allure2ExecutorInfo>) => Promise<void>;
32
+ export declare const generateCategoriesTrendData: (writer: Allure2DataWriter, tests: Allure2TestResult[], history?: Allure2CategoriesTrendItem[], executor?: Partial<Allure2ExecutorInfo>) => Promise<void>;
39
33
  export declare const generateAttachmentsData: (writer: Allure2DataWriter, attachmentLinks: AttachmentLink[], contentFunction: (id: string) => Promise<ResultFile | undefined>) => Promise<Map<string, string>>;
34
+ export declare const generateGlobalsData: (writer: Allure2DataWriter, globalErrors: TestError[], globalAttachments: GlobalAttachmentLink[], attachmentMap: Map<string, string>, attachmentTimestamps?: Record<string, number>, errorTimestamps?: Array<number | undefined>) => Promise<void>;