@allurereport/core 3.0.0-beta.15 → 3.0.0-beta.17

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/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { DefaultLabelsConfig, EnvironmentsConfig, HistoryDataPoint, KnownTestFailure, ReportVariables } from "@allurereport/core-api";
1
+ import type { DefaultLabelsConfig, EnvironmentsConfig, KnownTestFailure, ReportVariables } from "@allurereport/core-api";
2
2
  import type { Plugin, QualityGateConfig, ReportFiles } from "@allurereport/plugin-api";
3
3
  import type { ResultsReader } from "@allurereport/reader-api";
4
4
  export interface PluginInstance {
@@ -19,8 +19,12 @@ export interface FullConfig {
19
19
  reportFiles: ReportFiles;
20
20
  readers?: ResultsReader[];
21
21
  plugins?: PluginInstance[];
22
- history: HistoryDataPoint[];
23
22
  appendHistory?: boolean;
24
23
  known?: KnownTestFailure[];
25
24
  realTime?: any;
25
+ allureService?: {
26
+ url?: string;
27
+ project?: string;
28
+ accessToken?: string;
29
+ };
26
30
  }
package/dist/config.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import type { Config } from "@allurereport/plugin-api";
2
- import type { FullConfig } from "./api.js";
2
+ import type { FullConfig, PluginInstance } from "./api.js";
3
3
  export declare const getPluginId: (key: string) => string;
4
4
  export declare const findConfig: (cwd: string, configPath?: string) => Promise<string | undefined>;
5
5
  export interface ConfigOverride {
6
- name?: string;
7
- output?: string;
8
- historyPath?: string;
9
- knownIssuesPath?: string;
6
+ name?: Config["name"];
7
+ output?: Config["output"];
8
+ historyPath?: Config["historyPath"];
9
+ knownIssuesPath?: Config["knownIssuesPath"];
10
+ plugins?: Config["plugins"];
10
11
  }
11
12
  export declare const validateConfig: (config: Config) => {
12
13
  valid: boolean;
@@ -15,4 +16,5 @@ export declare const validateConfig: (config: Config) => {
15
16
  export declare const loadConfig: (configPath: string) => Promise<Config>;
16
17
  export declare const resolveConfig: (config: Config, override?: ConfigOverride) => Promise<FullConfig>;
17
18
  export declare const readConfig: (cwd?: string, configPath?: string, override?: ConfigOverride) => Promise<FullConfig>;
19
+ export declare const getPluginInstance: (config: FullConfig, predicate: (plugin: PluginInstance) => boolean) => PluginInstance | undefined;
18
20
  export declare const resolvePlugin: (path: string) => Promise<any>;
package/dist/config.js CHANGED
@@ -2,7 +2,6 @@ import * as console from "node:console";
2
2
  import { stat } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import * as process from "node:process";
5
- import { readHistory } from "./history.js";
6
5
  import { readKnownIssues } from "./known.js";
7
6
  import { FileSystemReportFiles } from "./plugin.js";
8
7
  import { importWrapper } from "./utils/module.js";
@@ -50,6 +49,7 @@ export const validateConfig = (config) => {
50
49
  "variables",
51
50
  "environments",
52
51
  "appendHistory",
52
+ "allureService",
53
53
  ];
54
54
  const unsupportedFields = Object.keys(config).filter((key) => !supportedFields.includes(key));
55
55
  return {
@@ -70,11 +70,10 @@ export const resolveConfig = async (config, override = {}) => {
70
70
  const appendHistory = config.appendHistory ?? true;
71
71
  const knownIssuesPath = resolve(override.knownIssuesPath ?? config.knownIssuesPath ?? "./allure/known.json");
72
72
  const output = resolve(override.output ?? config.output ?? "./allure-report");
73
- const history = await readHistory(historyPath);
74
73
  const known = await readKnownIssues(knownIssuesPath);
75
74
  const variables = config.variables ?? {};
76
75
  const environments = config.environments ?? {};
77
- const plugins = Object.keys(config?.plugins ?? {}).length === 0
76
+ const plugins = Object.keys(override?.plugins ?? config?.plugins ?? {}).length === 0
78
77
  ? {
79
78
  awesome: {
80
79
  options: {},
@@ -85,7 +84,6 @@ export const resolveConfig = async (config, override = {}) => {
85
84
  return {
86
85
  name,
87
86
  output,
88
- history,
89
87
  historyPath,
90
88
  knownIssuesPath,
91
89
  known,
@@ -95,6 +93,7 @@ export const resolveConfig = async (config, override = {}) => {
95
93
  reportFiles: new FileSystemReportFiles(output),
96
94
  plugins: pluginInstances,
97
95
  qualityGate: config.qualityGate,
96
+ allureService: config.allureService,
98
97
  };
99
98
  };
100
99
  export const readConfig = async (cwd = process.cwd(), configPath, override) => {
@@ -102,6 +101,9 @@ export const readConfig = async (cwd = process.cwd(), configPath, override) => {
102
101
  const config = cfg ? await loadConfig(cfg) : { ...defaultConfig };
103
102
  return await resolveConfig(config, override);
104
103
  };
104
+ export const getPluginInstance = (config, predicate) => {
105
+ return config?.plugins?.find(predicate);
106
+ };
105
107
  export const resolvePlugin = async (path) => {
106
108
  if (!path.startsWith("@allurereport/plugin-")) {
107
109
  try {
@@ -122,9 +124,10 @@ const resolvePlugins = async (plugins) => {
122
124
  const pluginInstances = [];
123
125
  for (const id in plugins) {
124
126
  const pluginConfig = plugins[id];
127
+ const pluginId = getPluginId(id);
125
128
  const Plugin = await resolvePlugin(pluginConfig.import ?? id);
126
129
  pluginInstances.push({
127
- id: getPluginId(id),
130
+ id: pluginId,
128
131
  enabled: pluginConfig.enabled ?? true,
129
132
  options: pluginConfig.options ?? {},
130
133
  plugin: new Plugin(pluginConfig.options),
package/dist/history.d.ts CHANGED
@@ -1,4 +1,9 @@
1
- import type { HistoryDataPoint, TestCase, TestResult } from "@allurereport/core-api";
2
- export declare const createHistory: (reportUuid: string, reportName: string | undefined, testCases: TestCase[], testResults: TestResult[]) => HistoryDataPoint;
3
- export declare const readHistory: (historyPath: string) => Promise<HistoryDataPoint[]>;
1
+ import type { AllureHistory, HistoryDataPoint, TestCase, TestResult } from "@allurereport/core-api";
2
+ export declare const createHistory: (reportUuid: string, reportName: string | undefined, testCases: TestCase[], testResults: TestResult[], remoteUrl?: string) => HistoryDataPoint;
4
3
  export declare const writeHistory: (historyPath: string, data: HistoryDataPoint) => Promise<void>;
4
+ export declare class AllureLocalHistory implements AllureHistory {
5
+ private readonly historyPath;
6
+ constructor(historyPath: string);
7
+ readHistory(): Promise<HistoryDataPoint[]>;
8
+ appendHistory(data: HistoryDataPoint): Promise<void>;
9
+ }
package/dist/history.js CHANGED
@@ -20,12 +20,15 @@ const createHistoryItems = (testResults) => {
20
20
  reportLinks: [],
21
21
  };
22
22
  })
23
- .reduce((previousValue, currentValue) => {
24
- previousValue[currentValue.historyId] = currentValue;
25
- return previousValue;
23
+ .reduce((acc, item) => {
24
+ acc[item.historyId] = {
25
+ ...item,
26
+ url: "",
27
+ };
28
+ return acc;
26
29
  }, {});
27
30
  };
28
- export const createHistory = (reportUuid, reportName = "Allure Report", testCases, testResults) => {
31
+ export const createHistory = (reportUuid, reportName = "Allure Report", testCases, testResults, remoteUrl = "") => {
29
32
  const knownTestCaseIds = testCases.map((tc) => tc.id);
30
33
  return {
31
34
  uuid: reportUuid,
@@ -34,26 +37,38 @@ export const createHistory = (reportUuid, reportName = "Allure Report", testCase
34
37
  knownTestCaseIds,
35
38
  testResults: createHistoryItems(testResults),
36
39
  metrics: {},
40
+ url: remoteUrl,
37
41
  };
38
42
  };
39
- export const readHistory = async (historyPath) => {
40
- const path = resolve(historyPath);
41
- try {
42
- return (await readFile(path, { encoding: "utf-8" }))
43
- .split("\n")
44
- .filter((line) => line && line.trim() !== "")
45
- .map((line) => JSON.parse(line));
46
- }
47
- catch (e) {
48
- if (isFileNotFoundError(e)) {
49
- return [];
50
- }
51
- throw e;
52
- }
53
- };
54
43
  export const writeHistory = async (historyPath, data) => {
55
44
  const path = resolve(historyPath);
56
45
  const parentDir = dirname(path);
57
46
  await mkdir(parentDir, { recursive: true });
58
47
  await writeFile(path, `${JSON.stringify(data)}\n`, { encoding: "utf-8", flag: "a+" });
59
48
  };
49
+ export class AllureLocalHistory {
50
+ constructor(historyPath) {
51
+ this.historyPath = historyPath;
52
+ }
53
+ async readHistory() {
54
+ const path = resolve(this.historyPath);
55
+ try {
56
+ return (await readFile(path, { encoding: "utf-8" }))
57
+ .split("\n")
58
+ .filter((line) => line && line.trim() !== "")
59
+ .map((line) => JSON.parse(line));
60
+ }
61
+ catch (e) {
62
+ if (isFileNotFoundError(e)) {
63
+ return [];
64
+ }
65
+ throw e;
66
+ }
67
+ }
68
+ async appendHistory(data) {
69
+ const path = resolve(this.historyPath);
70
+ const parentDir = dirname(path);
71
+ await mkdir(parentDir, { recursive: true });
72
+ await writeFile(path, `${JSON.stringify(data)}\n`, { encoding: "utf-8", flag: "a+" });
73
+ }
74
+ }
package/dist/index.d.ts CHANGED
@@ -3,9 +3,12 @@ export * from "./utils/misc.js";
3
3
  export * from "./utils/crypto.js";
4
4
  export * from "./utils/path.js";
5
5
  export * from "./utils/stats.js";
6
+ export * from "./utils/git.js";
7
+ export * from "./utils/new.js";
8
+ export * from "./utils/flaky.js";
6
9
  export * from "./history.js";
7
10
  export * from "./known.js";
8
- export { resolveConfig, readConfig } from "./config.js";
11
+ export { resolveConfig, readConfig, getPluginInstance } from "./config.js";
9
12
  export * from "./report.js";
10
13
  export * from "./plugin.js";
11
14
  export * from "./qualityGate.js";
package/dist/index.js CHANGED
@@ -2,9 +2,12 @@ export * from "./utils/misc.js";
2
2
  export * from "./utils/crypto.js";
3
3
  export * from "./utils/path.js";
4
4
  export * from "./utils/stats.js";
5
+ export * from "./utils/git.js";
6
+ export * from "./utils/new.js";
7
+ export * from "./utils/flaky.js";
5
8
  export * from "./history.js";
6
9
  export * from "./known.js";
7
- export { resolveConfig, readConfig } from "./config.js";
10
+ export { resolveConfig, readConfig, getPluginInstance } from "./config.js";
8
11
  export * from "./report.js";
9
12
  export * from "./plugin.js";
10
13
  export * from "./qualityGate.js";
package/dist/plugin.d.ts CHANGED
@@ -3,20 +3,21 @@ export declare class DefaultPluginState implements PluginState {
3
3
  #private;
4
4
  constructor(state: Record<string, any>);
5
5
  set: (key: string, value: any) => Promise<void>;
6
- get: (key: string) => Promise<void>;
6
+ get: <T>(key: string) => Promise<T>;
7
7
  unset: (key: string) => Promise<void>;
8
8
  }
9
9
  export declare class PluginFiles implements ReportFiles {
10
10
  #private;
11
- constructor(parent: ReportFiles, pluginId: string);
12
- addFile: (key: string, data: Buffer) => Promise<void>;
11
+ readonly callback?: ((key: string, path: string) => void) | undefined;
12
+ constructor(parent: ReportFiles, pluginId: string, callback?: ((key: string, path: string) => void) | undefined);
13
+ addFile: (key: string, data: Buffer) => Promise<string>;
13
14
  }
14
15
  export declare class InMemoryReportFiles implements ReportFiles {
15
16
  #private;
16
- addFile: (path: string, data: Buffer) => Promise<void>;
17
+ addFile: (path: string, data: Buffer) => Promise<string>;
17
18
  }
18
19
  export declare class FileSystemReportFiles implements ReportFiles {
19
20
  #private;
20
21
  constructor(output: string);
21
- addFile: (path: string, data: Buffer) => Promise<void>;
22
+ addFile: (path: string, data: Buffer) => Promise<string>;
22
23
  }
package/dist/plugin.js CHANGED
@@ -30,11 +30,14 @@ export class DefaultPluginState {
30
30
  }
31
31
  _DefaultPluginState_state = new WeakMap();
32
32
  export class PluginFiles {
33
- constructor(parent, pluginId) {
33
+ constructor(parent, pluginId, callback) {
34
+ this.callback = callback;
34
35
  _PluginFiles_parent.set(this, void 0);
35
36
  _PluginFiles_pluginId.set(this, void 0);
36
37
  this.addFile = async (key, data) => {
37
- await __classPrivateFieldGet(this, _PluginFiles_parent, "f").addFile(joinPosix(__classPrivateFieldGet(this, _PluginFiles_pluginId, "f"), key), data);
38
+ const filepath = await __classPrivateFieldGet(this, _PluginFiles_parent, "f").addFile(joinPosix(__classPrivateFieldGet(this, _PluginFiles_pluginId, "f"), key), data);
39
+ this.callback?.(key, filepath);
40
+ return filepath;
38
41
  };
39
42
  __classPrivateFieldSet(this, _PluginFiles_parent, parent, "f");
40
43
  __classPrivateFieldSet(this, _PluginFiles_pluginId, pluginId, "f");
@@ -46,6 +49,7 @@ export class InMemoryReportFiles {
46
49
  _InMemoryReportFiles_state.set(this, {});
47
50
  this.addFile = async (path, data) => {
48
51
  __classPrivateFieldGet(this, _InMemoryReportFiles_state, "f")[path] = data;
52
+ return path;
49
53
  };
50
54
  }
51
55
  }
@@ -58,6 +62,7 @@ export class FileSystemReportFiles {
58
62
  const targetDirPath = dirname(targetPath);
59
63
  await mkdir(targetDirPath, { recursive: true });
60
64
  await writeFile(targetPath, data, { encoding: "utf-8" });
65
+ return targetPath;
61
66
  };
62
67
  __classPrivateFieldSet(this, _FileSystemReportFiles_output, resolve(output), "f");
63
68
  }
package/dist/report.d.ts CHANGED
@@ -3,6 +3,8 @@ import type { FullConfig } from "./api.js";
3
3
  import { DefaultAllureStore } from "./store/store.js";
4
4
  export declare class AllureReport {
5
5
  #private;
6
+ readonly reportUuid: string;
7
+ reportUrl?: string;
6
8
  constructor(opts: FullConfig);
7
9
  get store(): DefaultAllureStore;
8
10
  get exitCode(): 0 | 1;
package/dist/report.js CHANGED
@@ -9,9 +9,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _AllureReport_instances, _AllureReport_reportUuid, _AllureReport_reportName, _AllureReport_store, _AllureReport_readers, _AllureReport_plugins, _AllureReport_reportFiles, _AllureReport_eventEmitter, _AllureReport_events, _AllureReport_qualityGate, _AllureReport_appendHistory, _AllureReport_historyPath, _AllureReport_realTime, _AllureReport_output, _AllureReport_state, _AllureReport_stage, _AllureReport_update, _AllureReport_eachPlugin, _AllureReport_getPluginState;
12
+ var _AllureReport_instances, _AllureReport_reportName, _AllureReport_ci, _AllureReport_store, _AllureReport_readers, _AllureReport_plugins, _AllureReport_reportFiles, _AllureReport_eventEmitter, _AllureReport_events, _AllureReport_qualityGate, _AllureReport_realTime, _AllureReport_output, _AllureReport_history, _AllureReport_allureServiceClient, _AllureReport_state, _AllureReport_stage, _AllureReport_publish_get, _AllureReport_update, _AllureReport_eachPlugin, _AllureReport_getPluginState;
13
+ import { detect } from "@allurereport/ci";
13
14
  import { allure1, allure2, attachments, cucumberjson, junitXml, readXcResultBundle } from "@allurereport/reader";
14
15
  import { PathResultFile } from "@allurereport/reader-api";
16
+ import { AllureRemoteHistory, AllureServiceClient, KnownError, UnknownError } from "@allurereport/service";
15
17
  import { generateSummary } from "@allurereport/summary";
16
18
  import console from "node:console";
17
19
  import { randomUUID } from "node:crypto";
@@ -19,7 +21,7 @@ import { EventEmitter } from "node:events";
19
21
  import { readFileSync } from "node:fs";
20
22
  import { lstat, opendir, readdir, realpath, rename, rm } from "node:fs/promises";
21
23
  import { dirname, join, resolve } from "node:path";
22
- import { createHistory, writeHistory } from "./history.js";
24
+ import { AllureLocalHistory, createHistory } from "./history.js";
23
25
  import { DefaultPluginState, PluginFiles } from "./plugin.js";
24
26
  import { QualityGate } from "./qualityGate.js";
25
27
  import { DefaultAllureStore } from "./store/store.js";
@@ -29,8 +31,8 @@ const initRequired = "report is not initialised. Call the start() method first."
29
31
  export class AllureReport {
30
32
  constructor(opts) {
31
33
  _AllureReport_instances.add(this);
32
- _AllureReport_reportUuid.set(this, void 0);
33
34
  _AllureReport_reportName.set(this, void 0);
35
+ _AllureReport_ci.set(this, void 0);
34
36
  _AllureReport_store.set(this, void 0);
35
37
  _AllureReport_readers.set(this, void 0);
36
38
  _AllureReport_plugins.set(this, void 0);
@@ -38,10 +40,10 @@ export class AllureReport {
38
40
  _AllureReport_eventEmitter.set(this, void 0);
39
41
  _AllureReport_events.set(this, void 0);
40
42
  _AllureReport_qualityGate.set(this, void 0);
41
- _AllureReport_appendHistory.set(this, void 0);
42
- _AllureReport_historyPath.set(this, void 0);
43
43
  _AllureReport_realTime.set(this, void 0);
44
44
  _AllureReport_output.set(this, void 0);
45
+ _AllureReport_history.set(this, void 0);
46
+ _AllureReport_allureServiceClient.set(this, void 0);
45
47
  _AllureReport_state.set(this, void 0);
46
48
  _AllureReport_stage.set(this, "init");
47
49
  this.readDirectory = async (resultsDir) => {
@@ -86,6 +88,7 @@ export class AllureReport {
86
88
  }
87
89
  };
88
90
  this.start = async () => {
91
+ await __classPrivateFieldGet(this, _AllureReport_store, "f").readHistory();
89
92
  if (__classPrivateFieldGet(this, _AllureReport_stage, "f") === "running") {
90
93
  throw new Error("the report is already started");
91
94
  }
@@ -93,6 +96,13 @@ export class AllureReport {
93
96
  throw new Error("the report is already stopped, the restart isn't supported at the moment");
94
97
  }
95
98
  __classPrivateFieldSet(this, _AllureReport_stage, "running", "f");
99
+ if (__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f") && __classPrivateFieldGet(this, _AllureReport_instances, "a", _AllureReport_publish_get)) {
100
+ const { url } = await __classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f").createReport({
101
+ reportUuid: this.reportUuid,
102
+ reportName: __classPrivateFieldGet(this, _AllureReport_reportName, "f"),
103
+ });
104
+ this.reportUrl = url;
105
+ }
96
106
  await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, true, async (plugin, context) => {
97
107
  await plugin.start?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"), __classPrivateFieldGet(this, _AllureReport_events, "f"));
98
108
  });
@@ -113,27 +123,53 @@ export class AllureReport {
113
123
  });
114
124
  this.done = async () => {
115
125
  const summaries = [];
126
+ const remoteHrefs = [];
116
127
  if (__classPrivateFieldGet(this, _AllureReport_stage, "f") !== "running") {
117
128
  throw new Error(initRequired);
118
129
  }
119
130
  __classPrivateFieldGet(this, _AllureReport_events, "f").offAll();
120
131
  __classPrivateFieldSet(this, _AllureReport_stage, "done", "f");
121
- await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context, id) => {
132
+ await __classPrivateFieldGet(this, _AllureReport_eachPlugin, "f").call(this, false, async (plugin, context) => {
122
133
  await plugin.done?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
134
+ if (__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f") && context.publish) {
135
+ const pluginFiles = (await context.state.get("files")) ?? {};
136
+ for (const [filename, filepath] of Object.entries(pluginFiles)) {
137
+ if (/^(data|widgets|index\.html$)/.test(filename)) {
138
+ await __classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f").addReportFile({
139
+ reportUuid: this.reportUuid,
140
+ pluginId: context.id,
141
+ filename,
142
+ filepath,
143
+ });
144
+ }
145
+ else {
146
+ await __classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f").addReportAsset({
147
+ filename,
148
+ filepath,
149
+ });
150
+ }
151
+ }
152
+ }
123
153
  const summary = await plugin?.info?.(context, __classPrivateFieldGet(this, _AllureReport_store, "f"));
124
154
  if (!summary) {
125
155
  return;
126
156
  }
157
+ summary.pullRequestHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestUrl;
158
+ summary.jobHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobUrl;
159
+ if (context.publish) {
160
+ summary.remoteHref = `${this.reportUrl}/${context.id}/`;
161
+ remoteHrefs.push(summary.remoteHref);
162
+ }
127
163
  summaries.push({
128
164
  ...summary,
129
- href: `${id}/`,
165
+ href: `${context.id}/`,
130
166
  });
167
+ await context.reportFiles.addFile("summary.json", Buffer.from(JSON.stringify(summary)));
131
168
  });
132
- if (__classPrivateFieldGet(this, _AllureReport_appendHistory, "f")) {
133
- const testResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults();
134
- const testCases = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestCases();
135
- const historyDataPoint = createHistory(__classPrivateFieldGet(this, _AllureReport_reportUuid, "f"), __classPrivateFieldGet(this, _AllureReport_reportName, "f"), testCases, testResults);
136
- await writeHistory(__classPrivateFieldGet(this, _AllureReport_historyPath, "f"), historyDataPoint);
169
+ if (__classPrivateFieldGet(this, _AllureReport_instances, "a", _AllureReport_publish_get)) {
170
+ await __classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")?.completeReport({
171
+ reportUuid: this.reportUuid,
172
+ });
137
173
  }
138
174
  const outputDirFiles = await readdir(__classPrivateFieldGet(this, _AllureReport_output, "f"));
139
175
  if (outputDirFiles.length === 0) {
@@ -150,37 +186,72 @@ export class AllureReport {
150
186
  await rename(currentFilePath, newFilePath);
151
187
  }
152
188
  await rm(reportPath, { recursive: true });
153
- return;
189
+ }
190
+ if (__classPrivateFieldGet(this, _AllureReport_history, "f")) {
191
+ const testResults = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestResults();
192
+ const testCases = await __classPrivateFieldGet(this, _AllureReport_store, "f").allTestCases();
193
+ const historyDataPoint = createHistory(this.reportUuid, __classPrivateFieldGet(this, _AllureReport_reportName, "f"), testCases, testResults, this.reportUrl);
194
+ try {
195
+ await __classPrivateFieldGet(this, _AllureReport_store, "f").appendHistory(historyDataPoint);
196
+ }
197
+ catch (err) {
198
+ if (err instanceof KnownError) {
199
+ console.error("Failed to append history", err.message);
200
+ }
201
+ else if (err instanceof UnknownError) {
202
+ console.error("Failed to append history due to unexpected error", err.message);
203
+ }
204
+ else {
205
+ throw err;
206
+ }
207
+ }
154
208
  }
155
209
  if (summaries.length > 1) {
156
210
  await generateSummary(__classPrivateFieldGet(this, _AllureReport_output, "f"), summaries);
157
211
  }
212
+ if (remoteHrefs.length > 0) {
213
+ console.info("Next reports have been published:");
214
+ remoteHrefs.forEach((href) => {
215
+ console.info(`- ${href}`);
216
+ });
217
+ }
158
218
  };
159
219
  _AllureReport_eachPlugin.set(this, async (initState, consumer) => {
160
220
  if (initState) {
161
221
  __classPrivateFieldSet(this, _AllureReport_state, {}, "f");
162
222
  }
163
- for (const descriptor of __classPrivateFieldGet(this, _AllureReport_plugins, "f")) {
164
- if (!descriptor.enabled) {
223
+ for (const { enabled, id, plugin, options } of __classPrivateFieldGet(this, _AllureReport_plugins, "f")) {
224
+ if (!enabled) {
165
225
  continue;
166
226
  }
167
- const id = descriptor.id;
168
- const plugin = descriptor.plugin;
169
227
  const pluginState = __classPrivateFieldGet(this, _AllureReport_instances, "m", _AllureReport_getPluginState).call(this, initState, id);
170
228
  if (!pluginState) {
171
229
  console.error("plugin error: state is empty");
172
230
  continue;
173
231
  }
174
- const pluginFiles = new PluginFiles(__classPrivateFieldGet(this, _AllureReport_reportFiles, "f"), id);
232
+ if (initState) {
233
+ await pluginState.set("files", {});
234
+ }
235
+ const pluginFiles = new PluginFiles(__classPrivateFieldGet(this, _AllureReport_reportFiles, "f"), id, async (key, filepath) => {
236
+ const currentPluginState = __classPrivateFieldGet(this, _AllureReport_instances, "m", _AllureReport_getPluginState).call(this, false, id);
237
+ const files = await currentPluginState?.get("files");
238
+ if (!files) {
239
+ return;
240
+ }
241
+ files[key] = filepath;
242
+ });
175
243
  const pluginContext = {
244
+ id,
245
+ publish: !!options?.publish,
176
246
  allureVersion: version,
177
- reportUuid: __classPrivateFieldGet(this, _AllureReport_reportUuid, "f"),
247
+ reportUuid: this.reportUuid,
178
248
  reportName: __classPrivateFieldGet(this, _AllureReport_reportName, "f"),
179
249
  state: pluginState,
180
250
  reportFiles: pluginFiles,
251
+ ci: __classPrivateFieldGet(this, _AllureReport_ci, "f"),
181
252
  };
182
253
  try {
183
- await consumer.call(this, plugin, pluginContext, id);
254
+ await consumer.call(this, plugin, pluginContext);
184
255
  if (initState) {
185
256
  __classPrivateFieldGet(this, _AllureReport_state, "f")[id] = pluginState;
186
257
  }
@@ -193,19 +264,26 @@ export class AllureReport {
193
264
  this.validate = async () => {
194
265
  await __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").validate(__classPrivateFieldGet(this, _AllureReport_store, "f"));
195
266
  };
196
- const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], history, known, reportFiles, qualityGate, realTime, appendHistory, historyPath, defaultLabels = {}, variables = {}, environments, output, } = opts;
197
- __classPrivateFieldSet(this, _AllureReport_reportUuid, randomUUID(), "f");
198
- __classPrivateFieldSet(this, _AllureReport_reportName, name, "f");
267
+ const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], known, reportFiles, qualityGate, realTime, historyPath, defaultLabels = {}, variables = {}, environments, output, allureService: allureServiceConfig, } = opts;
268
+ __classPrivateFieldSet(this, _AllureReport_allureServiceClient, allureServiceConfig?.url ? new AllureServiceClient(allureServiceConfig) : undefined, "f");
269
+ this.reportUuid = randomUUID();
270
+ __classPrivateFieldSet(this, _AllureReport_ci, detect(), "f");
271
+ const reportTitleSuffix = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestName ?? __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobRunName;
272
+ __classPrivateFieldSet(this, _AllureReport_reportName, [name, reportTitleSuffix].filter(Boolean).join(" – "), "f");
199
273
  __classPrivateFieldSet(this, _AllureReport_eventEmitter, new EventEmitter(), "f");
200
274
  __classPrivateFieldSet(this, _AllureReport_events, new Events(__classPrivateFieldGet(this, _AllureReport_eventEmitter, "f")), "f");
201
275
  __classPrivateFieldSet(this, _AllureReport_realTime, realTime, "f");
202
- __classPrivateFieldSet(this, _AllureReport_appendHistory, appendHistory ?? true, "f");
203
- __classPrivateFieldSet(this, _AllureReport_historyPath, historyPath, "f");
276
+ if (__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")) {
277
+ __classPrivateFieldSet(this, _AllureReport_history, new AllureRemoteHistory(__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")), "f");
278
+ }
279
+ else if (historyPath) {
280
+ __classPrivateFieldSet(this, _AllureReport_history, new AllureLocalHistory(historyPath), "f");
281
+ }
204
282
  __classPrivateFieldSet(this, _AllureReport_store, new DefaultAllureStore({
205
283
  eventEmitter: __classPrivateFieldGet(this, _AllureReport_eventEmitter, "f"),
206
284
  reportVariables: variables,
207
285
  environmentsConfig: environments,
208
- history,
286
+ history: __classPrivateFieldGet(this, _AllureReport_history, "f"),
209
287
  known,
210
288
  defaultLabels,
211
289
  }), "f");
@@ -214,6 +292,9 @@ export class AllureReport {
214
292
  __classPrivateFieldSet(this, _AllureReport_reportFiles, reportFiles, "f");
215
293
  __classPrivateFieldSet(this, _AllureReport_output, output, "f");
216
294
  __classPrivateFieldSet(this, _AllureReport_qualityGate, new QualityGate(qualityGate), "f");
295
+ __classPrivateFieldSet(this, _AllureReport_history, __classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f")
296
+ ? new AllureRemoteHistory(__classPrivateFieldGet(this, _AllureReport_allureServiceClient, "f"))
297
+ : new AllureLocalHistory(historyPath), "f");
217
298
  }
218
299
  get store() {
219
300
  return __classPrivateFieldGet(this, _AllureReport_store, "f");
@@ -225,6 +306,8 @@ export class AllureReport {
225
306
  return __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").result;
226
307
  }
227
308
  }
228
- _AllureReport_reportUuid = new WeakMap(), _AllureReport_reportName = new WeakMap(), _AllureReport_store = new WeakMap(), _AllureReport_readers = new WeakMap(), _AllureReport_plugins = new WeakMap(), _AllureReport_reportFiles = new WeakMap(), _AllureReport_eventEmitter = new WeakMap(), _AllureReport_events = new WeakMap(), _AllureReport_qualityGate = new WeakMap(), _AllureReport_appendHistory = new WeakMap(), _AllureReport_historyPath = new WeakMap(), _AllureReport_realTime = new WeakMap(), _AllureReport_output = new WeakMap(), _AllureReport_state = new WeakMap(), _AllureReport_stage = new WeakMap(), _AllureReport_update = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_getPluginState = function _AllureReport_getPluginState(init, id) {
309
+ _AllureReport_reportName = new WeakMap(), _AllureReport_ci = new WeakMap(), _AllureReport_store = new WeakMap(), _AllureReport_readers = new WeakMap(), _AllureReport_plugins = new WeakMap(), _AllureReport_reportFiles = new WeakMap(), _AllureReport_eventEmitter = new WeakMap(), _AllureReport_events = new WeakMap(), _AllureReport_qualityGate = new WeakMap(), _AllureReport_realTime = new WeakMap(), _AllureReport_output = new WeakMap(), _AllureReport_history = new WeakMap(), _AllureReport_allureServiceClient = new WeakMap(), _AllureReport_state = new WeakMap(), _AllureReport_stage = new WeakMap(), _AllureReport_update = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_publish_get = function _AllureReport_publish_get() {
310
+ return __classPrivateFieldGet(this, _AllureReport_plugins, "f").some(({ enabled, options }) => enabled && options.publish);
311
+ }, _AllureReport_getPluginState = function _AllureReport_getPluginState(init, id) {
229
312
  return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
230
313
  };
@@ -19,7 +19,7 @@ export const testFixtureResultRawToState = (stateData, raw, context) => {
19
19
  ...(raw.actual && raw.expected ? { expected: raw.expected, actual: raw.actual } : {}),
20
20
  },
21
21
  ...processTimings(raw),
22
- steps: convertSteps(stateData, raw.steps),
22
+ steps: convertSteps(stateData, raw.steps).convertedSteps,
23
23
  sourceMetadata: {
24
24
  readerId: context.readerId,
25
25
  metadata: context.metadata ?? {},
@@ -57,7 +57,7 @@ export const testResultRawToState = (stateData, raw, context) => {
57
57
  known: raw.known ?? false,
58
58
  hidden: false,
59
59
  labels,
60
- steps: convertSteps(stateData, raw.steps),
60
+ steps: convertSteps(stateData, raw.steps).convertedSteps,
61
61
  parameters,
62
62
  links: convertLinks(raw.links),
63
63
  hostId,
@@ -66,6 +66,7 @@ export const testResultRawToState = (stateData, raw, context) => {
66
66
  readerId: context.readerId,
67
67
  metadata: context.metadata ?? {},
68
68
  },
69
+ titlePath: raw.titlePath ?? [],
69
70
  };
70
71
  };
71
72
  const processTestCase = ({ testCases }, raw) => {
@@ -172,26 +173,46 @@ const convertLabel = (label) => {
172
173
  value: label.value,
173
174
  };
174
175
  };
175
- const convertSteps = (stateData, steps) => steps?.filter(notNull)?.map((step) => convertStep(stateData, step)) ?? [];
176
+ const convertSteps = (stateData, steps) => {
177
+ const convertedStepData = steps?.filter(notNull)?.map((step) => convertStep(stateData, step)) ?? [];
178
+ return {
179
+ convertedSteps: convertedStepData.map(({ convertedStep }) => convertedStep),
180
+ messages: new Set(convertedStepData.reduce((acc, { messages }) => {
181
+ if (messages) {
182
+ acc.push(...messages);
183
+ }
184
+ return acc;
185
+ }, [])),
186
+ };
187
+ };
176
188
  const convertStep = (stateData, step) => {
177
189
  if (step.type === "step") {
178
- const subSteps = convertSteps(stateData, step.steps);
179
- const isMessageInSubSteps = !!step.message && subSteps.some((s) => s.type === "step" && s?.message === step.message);
190
+ const { message } = step;
191
+ const { convertedSteps: subSteps, messages } = convertSteps(stateData, step.steps);
192
+ const hasSimilarErrorInSubSteps = !!message && messages.has(message);
193
+ if (message && !hasSimilarErrorInSubSteps) {
194
+ messages.add(message);
195
+ }
180
196
  return {
181
- stepId: md5(`${step.name}${step.start}`),
182
- name: step.name ?? __unknown,
183
- status: step.status ?? defaultStatus,
184
- steps: subSteps,
185
- parameters: convertParameters(step.parameters),
186
- ...processTimings(step),
187
- type: "step",
188
- hasSimilarErrorInSubSteps: isMessageInSubSteps ?? undefined,
189
- message: isMessageInSubSteps ? undefined : step.message,
190
- trace: isMessageInSubSteps ? undefined : step.trace,
197
+ convertedStep: {
198
+ stepId: md5(`${step.name}${step.start}`),
199
+ name: step.name ?? __unknown,
200
+ status: step.status ?? defaultStatus,
201
+ steps: subSteps,
202
+ parameters: convertParameters(step.parameters),
203
+ ...processTimings(step),
204
+ type: "step",
205
+ message,
206
+ trace: step.trace,
207
+ hasSimilarErrorInSubSteps,
208
+ },
209
+ messages,
191
210
  };
192
211
  }
193
212
  return {
194
- ...processAttachmentLink(stateData, step),
213
+ convertedStep: {
214
+ ...processAttachmentLink(stateData, step),
215
+ },
195
216
  };
196
217
  };
197
218
  const convertParameters = (parameters) => parameters
@@ -1,4 +1,4 @@
1
- import { type AttachmentLink, type DefaultLabelsConfig, type EnvironmentsConfig, type HistoryDataPoint, type HistoryTestResult, type KnownTestFailure, type ReportVariables, type TestCase, type TestEnvGroup, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
1
+ import { type AllureHistory, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentsConfig, type HistoryDataPoint, type HistoryTestResult, type KnownTestFailure, type RepoData, type ReportVariables, type TestCase, type TestEnvGroup, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
2
2
  import { type AllureStore, type ResultFile, type TestResultFilter } from "@allurereport/plugin-api";
3
3
  import type { RawFixtureResult, RawMetadata, RawTestResult, ReaderContext, ResultsVisitor } from "@allurereport/reader-api";
4
4
  import type { EventEmitter } from "node:events";
@@ -13,13 +13,16 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
13
13
  readonly indexFixturesByTestResult: Map<string, TestFixtureResult[]>;
14
14
  readonly indexKnownByHistoryId: Map<string, KnownTestFailure[]>;
15
15
  constructor(params?: {
16
- history?: HistoryDataPoint[];
16
+ history?: AllureHistory;
17
17
  known?: KnownTestFailure[];
18
18
  eventEmitter?: EventEmitter<AllureStoreEvents>;
19
19
  defaultLabels?: DefaultLabelsConfig;
20
20
  environmentsConfig?: EnvironmentsConfig;
21
21
  reportVariables?: ReportVariables;
22
22
  });
23
+ readHistory(): Promise<HistoryDataPoint[]>;
24
+ appendHistory(history: HistoryDataPoint): Promise<void>;
25
+ repoData(): Promise<RepoData | undefined>;
23
26
  visitTestResult(raw: RawTestResult, context: ReaderContext): Promise<void>;
24
27
  visitTestFixtureResult(result: RawFixtureResult, context: ReaderContext): Promise<void>;
25
28
  visitAttachmentFile(resultFile: ResultFile, context: ReaderContext): Promise<void>;
@@ -36,6 +39,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
36
39
  allFixtures(): Promise<TestFixtureResult[]>;
37
40
  allHistoryDataPoints(): Promise<HistoryDataPoint[]>;
38
41
  allKnownIssues(): Promise<KnownTestFailure[]>;
42
+ allNewTestResults(): Promise<TestResult[]>;
39
43
  testCaseById(tcId: string): Promise<TestCase | undefined>;
40
44
  testResultById(trId: string): Promise<TestResult | undefined>;
41
45
  attachmentById(attachmentId: string): Promise<AttachmentLink | undefined>;
@@ -9,10 +9,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _DefaultAllureStore_testResults, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_known, _DefaultAllureStore_fixtures, _DefaultAllureStore_defaultLabels, _DefaultAllureStore_environmentsConfig, _DefaultAllureStore_reportVariables, _DefaultAllureStore_eventEmitter;
12
+ var _DefaultAllureStore_testResults, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_known, _DefaultAllureStore_fixtures, _DefaultAllureStore_defaultLabels, _DefaultAllureStore_environmentsConfig, _DefaultAllureStore_reportVariables, _DefaultAllureStore_eventEmitter, _DefaultAllureStore_historyPoints, _DefaultAllureStore_repoData;
13
13
  import { compareBy, getWorstStatus, matchEnvironment, nullsLast, ordinal, reverse, } from "@allurereport/core-api";
14
14
  import { md5 } from "@allurereport/plugin-api";
15
15
  import { isFlaky } from "../utils/flaky.js";
16
+ import { getGitBranch, getGitRepoName } from "../utils/git.js";
17
+ import { getStatusTransition } from "../utils/new.js";
16
18
  import { getTestResultsStats } from "../utils/stats.js";
17
19
  import { testFixtureResultRawToState, testResultRawToState } from "./convert.js";
18
20
  const index = (indexMap, key, ...items) => {
@@ -44,14 +46,16 @@ export class DefaultAllureStore {
44
46
  this.indexAttachmentByFixture = new Map();
45
47
  this.indexFixturesByTestResult = new Map();
46
48
  this.indexKnownByHistoryId = new Map();
47
- const { history = [], known = [], eventEmitter, defaultLabels = {}, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
49
+ _DefaultAllureStore_historyPoints.set(this, []);
50
+ _DefaultAllureStore_repoData.set(this, void 0);
51
+ const { history, known = [], eventEmitter, defaultLabels = {}, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
48
52
  __classPrivateFieldSet(this, _DefaultAllureStore_testResults, new Map(), "f");
49
53
  __classPrivateFieldSet(this, _DefaultAllureStore_attachments, new Map(), "f");
50
54
  __classPrivateFieldSet(this, _DefaultAllureStore_attachmentContents, new Map(), "f");
51
55
  __classPrivateFieldSet(this, _DefaultAllureStore_testCases, new Map(), "f");
52
56
  __classPrivateFieldSet(this, _DefaultAllureStore_metadata, new Map(), "f");
53
57
  __classPrivateFieldSet(this, _DefaultAllureStore_fixtures, new Map(), "f");
54
- __classPrivateFieldSet(this, _DefaultAllureStore_history, [...history].sort(compareBy("timestamp", reverse(ordinal()))), "f");
58
+ __classPrivateFieldSet(this, _DefaultAllureStore_history, history, "f");
55
59
  __classPrivateFieldSet(this, _DefaultAllureStore_known, [...known], "f");
56
60
  __classPrivateFieldGet(this, _DefaultAllureStore_known, "f").forEach((ktf) => index(this.indexKnownByHistoryId, ktf.historyId, ktf));
57
61
  __classPrivateFieldSet(this, _DefaultAllureStore_eventEmitter, eventEmitter, "f");
@@ -65,6 +69,38 @@ export class DefaultAllureStore {
65
69
  this.indexLatestEnvTestResultByHistoryId.set(key, new Map());
66
70
  });
67
71
  }
72
+ async readHistory() {
73
+ if (!__classPrivateFieldGet(this, _DefaultAllureStore_history, "f")) {
74
+ return [];
75
+ }
76
+ const repoData = await this.repoData();
77
+ __classPrivateFieldSet(this, _DefaultAllureStore_historyPoints, (await __classPrivateFieldGet(this, _DefaultAllureStore_history, "f").readHistory(repoData?.branch)) ?? [], "f");
78
+ __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f").sort(compareBy("timestamp", reverse(ordinal())));
79
+ return __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f");
80
+ }
81
+ async appendHistory(history) {
82
+ if (!__classPrivateFieldGet(this, _DefaultAllureStore_history, "f")) {
83
+ return;
84
+ }
85
+ const repoData = await this.repoData();
86
+ __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f").push(history);
87
+ await __classPrivateFieldGet(this, _DefaultAllureStore_history, "f").appendHistory(history, repoData?.branch);
88
+ }
89
+ async repoData() {
90
+ if (__classPrivateFieldGet(this, _DefaultAllureStore_repoData, "f")) {
91
+ return __classPrivateFieldGet(this, _DefaultAllureStore_repoData, "f");
92
+ }
93
+ try {
94
+ __classPrivateFieldSet(this, _DefaultAllureStore_repoData, {
95
+ name: await getGitRepoName(),
96
+ branch: await getGitBranch(),
97
+ }, "f");
98
+ return __classPrivateFieldGet(this, _DefaultAllureStore_repoData, "f");
99
+ }
100
+ catch (err) {
101
+ return undefined;
102
+ }
103
+ }
68
104
  async visitTestResult(raw, context) {
69
105
  const attachmentLinks = [];
70
106
  const testResult = testResultRawToState({
@@ -88,6 +124,7 @@ export class DefaultAllureStore {
88
124
  }
89
125
  testResult.environment = matchEnvironment(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f"), testResult);
90
126
  const trHistory = await this.historyByTr(testResult);
127
+ testResult.transition = getStatusTransition(testResult, trHistory);
91
128
  testResult.flaky = isFlaky(testResult, trHistory);
92
129
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
93
130
  if (testResult.historyId) {
@@ -177,11 +214,21 @@ export class DefaultAllureStore {
177
214
  return Array.from(__classPrivateFieldGet(this, _DefaultAllureStore_fixtures, "f").values());
178
215
  }
179
216
  async allHistoryDataPoints() {
180
- return __classPrivateFieldGet(this, _DefaultAllureStore_history, "f");
217
+ return __classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f");
181
218
  }
182
219
  async allKnownIssues() {
183
220
  return __classPrivateFieldGet(this, _DefaultAllureStore_known, "f");
184
221
  }
222
+ async allNewTestResults() {
223
+ const allTrs = await this.allTestResults();
224
+ const allHistoryDps = await this.allHistoryDataPoints();
225
+ return allTrs.filter((tr) => {
226
+ if (!tr.historyId) {
227
+ return true;
228
+ }
229
+ return !allHistoryDps.some((dp) => dp.testResults[tr.historyId]);
230
+ });
231
+ }
185
232
  async testCaseById(tcId) {
186
233
  return __classPrivateFieldGet(this, _DefaultAllureStore_testCases, "f").get(tcId);
187
234
  }
@@ -219,9 +266,19 @@ export class DefaultAllureStore {
219
266
  if (!tr?.historyId) {
220
267
  return [];
221
268
  }
222
- return [...__classPrivateFieldGet(this, _DefaultAllureStore_history, "f")]
269
+ return [...__classPrivateFieldGet(this, _DefaultAllureStore_historyPoints, "f")]
223
270
  .filter((dp) => !!dp.testResults[tr.historyId])
224
- .map((dp) => ({ ...dp.testResults[tr.historyId] }));
271
+ .map((dp) => {
272
+ if (!dp.url) {
273
+ return dp.testResults[tr.historyId];
274
+ }
275
+ const url = new URL(dp.url);
276
+ url.hash = tr.id;
277
+ return {
278
+ ...dp.testResults[tr.historyId],
279
+ url: url.toString(),
280
+ };
281
+ });
225
282
  }
226
283
  async historyByTrId(trId) {
227
284
  const tr = await this.testResultById(trId);
@@ -265,11 +322,9 @@ export class DefaultAllureStore {
265
322
  async testsStatistic(filter) {
266
323
  const all = await this.allTestResults();
267
324
  const allWithStats = await Promise.all(all.map(async (tr) => {
268
- const trHistory = await this.historyByTr(tr);
269
325
  const retries = await this.retriesByTr(tr);
270
326
  return {
271
327
  ...tr,
272
- flaky: isFlaky(tr, trHistory),
273
328
  retries,
274
329
  };
275
330
  }));
@@ -327,4 +382,4 @@ export class DefaultAllureStore {
327
382
  };
328
383
  }
329
384
  }
330
- _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments = new WeakMap(), _DefaultAllureStore_attachmentContents = new WeakMap(), _DefaultAllureStore_testCases = new WeakMap(), _DefaultAllureStore_metadata = new WeakMap(), _DefaultAllureStore_history = new WeakMap(), _DefaultAllureStore_known = new WeakMap(), _DefaultAllureStore_fixtures = new WeakMap(), _DefaultAllureStore_defaultLabels = new WeakMap(), _DefaultAllureStore_environmentsConfig = new WeakMap(), _DefaultAllureStore_reportVariables = new WeakMap(), _DefaultAllureStore_eventEmitter = new WeakMap();
385
+ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments = new WeakMap(), _DefaultAllureStore_attachmentContents = new WeakMap(), _DefaultAllureStore_testCases = new WeakMap(), _DefaultAllureStore_metadata = new WeakMap(), _DefaultAllureStore_history = new WeakMap(), _DefaultAllureStore_known = new WeakMap(), _DefaultAllureStore_fixtures = new WeakMap(), _DefaultAllureStore_defaultLabels = new WeakMap(), _DefaultAllureStore_environmentsConfig = new WeakMap(), _DefaultAllureStore_reportVariables = new WeakMap(), _DefaultAllureStore_eventEmitter = new WeakMap(), _DefaultAllureStore_historyPoints = new WeakMap(), _DefaultAllureStore_repoData = new WeakMap();
@@ -4,7 +4,7 @@ const isAllureClassicFlaky = (tr, history) => {
4
4
  if (history.length === 0 || !badStatuses.includes(tr.status)) {
5
5
  return false;
6
6
  }
7
- const limitedLastHistory = history.slice(-MAX_LAST_HISTORY_SIZE);
7
+ const limitedLastHistory = history.slice(0, MAX_LAST_HISTORY_SIZE);
8
8
  const limitedLastHistoryStatuses = limitedLastHistory.map((h) => h.status);
9
9
  return (limitedLastHistoryStatuses.includes("passed") &&
10
10
  limitedLastHistoryStatuses.indexOf("passed") < limitedLastHistoryStatuses.lastIndexOf("failed"));
@@ -0,0 +1,2 @@
1
+ export declare const getGitBranch: (cwd?: string) => Promise<string>;
2
+ export declare const getGitRepoName: (cwd?: string) => Promise<string>;
@@ -0,0 +1,46 @@
1
+ import { spawn } from "node:child_process";
2
+ import { basename } from "node:path";
3
+ export const getGitBranch = (cwd) => {
4
+ return new Promise((resolve, reject) => {
5
+ const git = spawn("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
6
+ let output = "";
7
+ let errorOutput = "";
8
+ git.stdout.on("data", (data) => {
9
+ output += data;
10
+ });
11
+ git.stderr.on("data", (data) => {
12
+ errorOutput += data;
13
+ });
14
+ git.on("close", (code) => {
15
+ if (code !== 0) {
16
+ return reject(new Error(errorOutput || "Git command failed"));
17
+ }
18
+ return resolve(output.trim());
19
+ });
20
+ git.on("error", (err) => {
21
+ return reject(err);
22
+ });
23
+ });
24
+ };
25
+ export const getGitRepoName = (cwd) => {
26
+ return new Promise((resolve, reject) => {
27
+ const git = spawn("git", ["rev-parse", "--show-toplevel"], { cwd });
28
+ let output = "";
29
+ let errorOutput = "";
30
+ git.stdout.on("data", (data) => {
31
+ output += data;
32
+ });
33
+ git.stderr.on("data", (data) => {
34
+ errorOutput += data;
35
+ });
36
+ git.on("close", (code) => {
37
+ if (code !== 0) {
38
+ return reject(new Error(errorOutput || "Git command failed"));
39
+ }
40
+ return resolve(basename(output.trim()));
41
+ });
42
+ git.on("error", (err) => {
43
+ return reject(err);
44
+ });
45
+ });
46
+ };
@@ -0,0 +1,4 @@
1
+ import type { HistoryTestResult, TestResult, TestStatusTransition } from "@allurereport/core-api";
2
+ export declare const isNew: (history?: HistoryTestResult[]) => boolean;
3
+ export declare const getLastSignificantStatus: (history?: HistoryTestResult[]) => string | undefined;
4
+ export declare const getStatusTransition: (tr: TestResult, history?: HistoryTestResult[]) => TestStatusTransition | undefined;
@@ -0,0 +1,23 @@
1
+ const NON_SIGNIFICANT_HISTORY_STATUSES = ["unknown", "skipped"];
2
+ export const isNew = (history = []) => history.length === 0;
3
+ const hasSignificantStatus = (htr) => !NON_SIGNIFICANT_HISTORY_STATUSES.includes(htr.status);
4
+ export const getLastSignificantStatus = (history = []) => {
5
+ const significantHtr = history.find(hasSignificantStatus);
6
+ return significantHtr?.status;
7
+ };
8
+ export const getStatusTransition = (tr, history = []) => {
9
+ if (isNew(history)) {
10
+ return "new";
11
+ }
12
+ const lastStatus = getLastSignificantStatus(history);
13
+ if (lastStatus !== tr.status) {
14
+ switch (tr.status) {
15
+ case "passed":
16
+ return "fixed";
17
+ case "failed":
18
+ return "regressed";
19
+ case "broken":
20
+ return "malfunctioned";
21
+ }
22
+ }
23
+ };
@@ -10,6 +10,9 @@ export const getTestResultsStats = (trs, filter = () => true) => {
10
10
  if (tr.flaky) {
11
11
  acc.flaky = (acc.flaky ?? 0) + 1;
12
12
  }
13
+ if (tr.transition === "new") {
14
+ acc.new = (acc.new ?? 0) + 1;
15
+ }
13
16
  if (!acc[tr.status]) {
14
17
  acc[tr.status] = 0;
15
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.0.0-beta.15",
3
+ "version": "3.0.0-beta.17",
4
4
  "description": "Collection of generic Allure utilities used across the entire project",
5
5
  "keywords": [
6
6
  "allure"
@@ -25,20 +25,22 @@
25
25
  "test": "vitest run"
26
26
  },
27
27
  "dependencies": {
28
- "@allurereport/core-api": "3.0.0-beta.15",
29
- "@allurereport/plugin-allure2": "3.0.0-beta.15",
30
- "@allurereport/plugin-api": "3.0.0-beta.15",
31
- "@allurereport/plugin-awesome": "3.0.0-beta.15",
32
- "@allurereport/plugin-classic": "3.0.0-beta.15",
33
- "@allurereport/plugin-csv": "3.0.0-beta.15",
34
- "@allurereport/plugin-dashboard": "3.0.0-beta.15",
35
- "@allurereport/plugin-log": "3.0.0-beta.15",
36
- "@allurereport/plugin-progress": "3.0.0-beta.15",
37
- "@allurereport/plugin-slack": "3.0.0-beta.15",
38
- "@allurereport/plugin-testplan": "3.0.0-beta.15",
39
- "@allurereport/reader": "3.0.0-beta.15",
40
- "@allurereport/reader-api": "3.0.0-beta.15",
41
- "@allurereport/summary": "3.0.0-beta.15",
28
+ "@allurereport/ci": "3.0.0-beta.17",
29
+ "@allurereport/core-api": "3.0.0-beta.17",
30
+ "@allurereport/plugin-allure2": "3.0.0-beta.17",
31
+ "@allurereport/plugin-api": "3.0.0-beta.17",
32
+ "@allurereport/plugin-awesome": "3.0.0-beta.17",
33
+ "@allurereport/plugin-classic": "3.0.0-beta.17",
34
+ "@allurereport/plugin-csv": "3.0.0-beta.17",
35
+ "@allurereport/plugin-dashboard": "3.0.0-beta.17",
36
+ "@allurereport/plugin-log": "3.0.0-beta.17",
37
+ "@allurereport/plugin-progress": "3.0.0-beta.17",
38
+ "@allurereport/plugin-slack": "3.0.0-beta.17",
39
+ "@allurereport/plugin-testplan": "3.0.0-beta.17",
40
+ "@allurereport/reader": "3.0.0-beta.17",
41
+ "@allurereport/reader-api": "3.0.0-beta.17",
42
+ "@allurereport/service": "3.0.0-beta.17",
43
+ "@allurereport/summary": "3.0.0-beta.17",
42
44
  "handlebars": "^4.7.8",
43
45
  "markdown-it": "^14.1.0"
44
46
  },
@@ -50,10 +52,10 @@
50
52
  "@types/node": "^20.17.9",
51
53
  "@typescript-eslint/eslint-plugin": "^8.0.0",
52
54
  "@typescript-eslint/parser": "^8.0.0",
53
- "@vitest/runner": "^2.1.8",
54
- "@vitest/snapshot": "^2.1.8",
55
- "allure-js-commons": "^3.0.9",
56
- "allure-vitest": "^3.0.9",
55
+ "@vitest/runner": "^2.1.9",
56
+ "@vitest/snapshot": "^2.1.9",
57
+ "allure-js-commons": "^3.3.0",
58
+ "allure-vitest": "^3.3.0",
57
59
  "eslint": "^8.57.0",
58
60
  "eslint-config-prettier": "^9.1.0",
59
61
  "eslint-plugin-import": "^2.29.1",
@@ -64,6 +66,6 @@
64
66
  "rimraf": "^6.0.1",
65
67
  "tslib": "^2.7.0",
66
68
  "typescript": "^5.6.3",
67
- "vitest": "^2.1.8"
69
+ "vitest": "^2.1.9"
68
70
  }
69
71
  }