@allurereport/core 3.3.1 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +71 -6
- package/dist/history.d.ts +1 -1
- package/dist/history.js +8 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/plugin.js +2 -1
- package/dist/qualityGate/qualityGate.js +2 -2
- package/dist/qualityGate/rules.d.ts +3 -1
- package/dist/qualityGate/rules.js +40 -6
- package/dist/report.js +32 -13
- package/dist/store/convert.js +1 -1
- package/dist/store/store.d.ts +14 -3
- package/dist/store/store.js +366 -61
- package/dist/utils/environment.d.ts +24 -0
- package/dist/utils/environment.js +160 -0
- package/dist/utils/event.d.ts +1 -1
- package/dist/utils/safeDumpPath.d.ts +4 -0
- package/dist/utils/safeDumpPath.js +22 -0
- package/dist/utils/safeOutputPath.d.ts +5 -0
- package/dist/utils/safeOutputPath.js +31 -0
- package/dist/utils/windows.d.ts +2 -0
- package/dist/utils/windows.js +51 -0
- package/package.json +30 -42
package/dist/api.d.ts
CHANGED
package/dist/config.d.ts
CHANGED
|
@@ -5,11 +5,13 @@ export interface ConfigOverride {
|
|
|
5
5
|
output?: Config["output"];
|
|
6
6
|
open?: Config["open"];
|
|
7
7
|
port?: Config["port"];
|
|
8
|
+
hideLabels?: Config["hideLabels"];
|
|
8
9
|
historyPath?: Config["historyPath"];
|
|
9
10
|
historyLimit?: Config["historyLimit"];
|
|
10
11
|
knownIssuesPath?: Config["knownIssuesPath"];
|
|
11
12
|
plugins?: Config["plugins"];
|
|
12
13
|
}
|
|
14
|
+
export declare const assertValidPluginId: (id: string) => void;
|
|
13
15
|
export declare const getPluginId: (key: string) => string;
|
|
14
16
|
export declare const findConfig: (cwd: string, configPath?: string) => Promise<string | undefined>;
|
|
15
17
|
export declare const validateConfig: (config: Config) => {
|
package/dist/config.js
CHANGED
|
@@ -2,11 +2,14 @@ import * as console from "node:console";
|
|
|
2
2
|
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
import { extname, resolve } from "node:path";
|
|
4
4
|
import * as process from "node:process";
|
|
5
|
+
import { validateEnvironmentName } from "@allurereport/core-api";
|
|
5
6
|
import { parse } from "yaml";
|
|
6
7
|
import { readKnownIssues } from "./known.js";
|
|
7
8
|
import { FileSystemReportFiles } from "./plugin.js";
|
|
9
|
+
import { environmentIdentityById, environmentIdentityByName, normalizeEnvironmentDescriptorMap, } from "./utils/environment.js";
|
|
8
10
|
import { importWrapper } from "./utils/module.js";
|
|
9
11
|
import { normalizeImportPath } from "./utils/path.js";
|
|
12
|
+
import { assertValidPluginIdForWindows, isWindows } from "./utils/windows.js";
|
|
10
13
|
const CONFIG_FILENAMES = [
|
|
11
14
|
"allurerc.js",
|
|
12
15
|
"allurerc.mjs",
|
|
@@ -16,8 +19,31 @@ const CONFIG_FILENAMES = [
|
|
|
16
19
|
"allurerc.yml",
|
|
17
20
|
];
|
|
18
21
|
const DEFAULT_CONFIG = {};
|
|
22
|
+
export const assertValidPluginId = (id) => {
|
|
23
|
+
if (id.length === 0) {
|
|
24
|
+
throw new Error("Invalid plugin id: must not be empty");
|
|
25
|
+
}
|
|
26
|
+
if (id === "." || id === "..") {
|
|
27
|
+
throw new Error(`Invalid plugin id ${JSON.stringify(id)}: must not be "." or ".."`);
|
|
28
|
+
}
|
|
29
|
+
if (id.includes("..")) {
|
|
30
|
+
throw new Error(`Invalid plugin id ${JSON.stringify(id)}: must not contain ".."`);
|
|
31
|
+
}
|
|
32
|
+
if (/[/\\]/.test(id)) {
|
|
33
|
+
throw new Error(`Invalid plugin id ${JSON.stringify(id)}: must not contain path separators`);
|
|
34
|
+
}
|
|
35
|
+
if (isWindows()) {
|
|
36
|
+
assertValidPluginIdForWindows(id);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
19
39
|
export const getPluginId = (key) => {
|
|
20
|
-
|
|
40
|
+
const trimmed = key.trim();
|
|
41
|
+
if (trimmed.length === 0) {
|
|
42
|
+
throw new Error(`Invalid plugin key ${JSON.stringify(key)}: must not be empty or whitespace-only`);
|
|
43
|
+
}
|
|
44
|
+
const id = trimmed.replace(/^@.*\//, "").replace(/[/\\]/g, "-");
|
|
45
|
+
assertValidPluginId(id);
|
|
46
|
+
return id;
|
|
21
47
|
};
|
|
22
48
|
export const findConfig = async (cwd, configPath) => {
|
|
23
49
|
if (configPath) {
|
|
@@ -51,12 +77,14 @@ export const validateConfig = (config) => {
|
|
|
51
77
|
"output",
|
|
52
78
|
"open",
|
|
53
79
|
"port",
|
|
80
|
+
"hideLabels",
|
|
54
81
|
"historyPath",
|
|
55
82
|
"historyLimit",
|
|
56
83
|
"knownIssuesPath",
|
|
57
84
|
"plugins",
|
|
58
85
|
"defaultLabels",
|
|
59
86
|
"variables",
|
|
87
|
+
"environment",
|
|
60
88
|
"environments",
|
|
61
89
|
"appendHistory",
|
|
62
90
|
"qualityGate",
|
|
@@ -98,14 +126,42 @@ export const loadJsonConfig = async (configPath) => {
|
|
|
98
126
|
export const loadJsConfig = async (configPath) => {
|
|
99
127
|
return (await import(normalizeImportPath(configPath))).default;
|
|
100
128
|
};
|
|
129
|
+
const resolveConfigEnvironments = (config) => {
|
|
130
|
+
const errors = [];
|
|
131
|
+
const { normalized: environments, errors: environmentErrors } = normalizeEnvironmentDescriptorMap(config.environments ?? {}, "config.environments");
|
|
132
|
+
let environment;
|
|
133
|
+
errors.push(...environmentErrors);
|
|
134
|
+
if (config.environment !== undefined) {
|
|
135
|
+
const environmentResult = validateEnvironmentName(config.environment);
|
|
136
|
+
if (!environmentResult.valid) {
|
|
137
|
+
errors.push(`environment ${environmentResult.reason}`);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const normalizedEnvironment = environmentResult.normalized;
|
|
141
|
+
environment =
|
|
142
|
+
environmentIdentityById(environments, normalizedEnvironment)?.id ??
|
|
143
|
+
environmentIdentityByName(environments, normalizedEnvironment)?.id ??
|
|
144
|
+
normalizedEnvironment;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (errors.length > 0) {
|
|
148
|
+
throw new Error(`The provided Allure config contains invalid environments: ${errors.join("; ")}`);
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
environments,
|
|
152
|
+
environment,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
101
155
|
export const resolveConfig = async (config, override = {}) => {
|
|
102
156
|
const validationResult = validateConfig(config);
|
|
103
157
|
if (!validationResult.valid) {
|
|
104
158
|
throw new Error(`The provided Allure config contains unsupported fields: ${validationResult.fields.join(", ")}`);
|
|
105
159
|
}
|
|
160
|
+
const { environments, environment } = resolveConfigEnvironments(config);
|
|
106
161
|
const name = override.name ?? config.name ?? "Allure Report";
|
|
107
162
|
const open = override.open ?? config.open ?? false;
|
|
108
163
|
const port = override.port ?? config.port ?? undefined;
|
|
164
|
+
const hideLabels = override.hideLabels ?? config.hideLabels;
|
|
109
165
|
const historyPath = override.historyPath ?? config.historyPath;
|
|
110
166
|
const historyLimit = override.historyLimit ?? config.historyLimit;
|
|
111
167
|
const appendHistory = config.appendHistory ?? true;
|
|
@@ -113,22 +169,24 @@ export const resolveConfig = async (config, override = {}) => {
|
|
|
113
169
|
const output = resolve(override.output ?? config.output ?? "./allure-report");
|
|
114
170
|
const known = await readKnownIssues(knownIssuesPath);
|
|
115
171
|
const variables = config.variables ?? {};
|
|
116
|
-
const
|
|
117
|
-
const plugins = Object.keys(
|
|
172
|
+
const configuredPlugins = override.plugins ?? config.plugins;
|
|
173
|
+
const plugins = Object.keys(configuredPlugins ?? {}).length === 0
|
|
118
174
|
? {
|
|
119
175
|
awesome: {
|
|
120
176
|
options: {},
|
|
121
177
|
},
|
|
122
178
|
}
|
|
123
|
-
:
|
|
179
|
+
: configuredPlugins;
|
|
124
180
|
const pluginInstances = await resolvePlugins(plugins);
|
|
125
181
|
return {
|
|
126
182
|
name,
|
|
127
183
|
output,
|
|
128
184
|
open,
|
|
129
185
|
port,
|
|
186
|
+
hideLabels,
|
|
130
187
|
knownIssuesPath,
|
|
131
188
|
known,
|
|
189
|
+
environment,
|
|
132
190
|
variables,
|
|
133
191
|
environments,
|
|
134
192
|
appendHistory,
|
|
@@ -167,19 +225,26 @@ export const readConfig = async (cwd = process.cwd(), configPath, override) => {
|
|
|
167
225
|
export const getPluginInstance = (config, predicate) => {
|
|
168
226
|
return config?.plugins?.find(predicate);
|
|
169
227
|
};
|
|
228
|
+
const isModuleNotFoundError = (err) => {
|
|
229
|
+
return err instanceof Error && "code" in err && err.code === "ERR_MODULE_NOT_FOUND";
|
|
230
|
+
};
|
|
170
231
|
export const resolvePlugin = async (path) => {
|
|
171
232
|
if (!path.startsWith("@allurereport/plugin-")) {
|
|
172
233
|
try {
|
|
173
234
|
const module = await importWrapper(`@allurereport/plugin-${path}`);
|
|
174
235
|
return module.default;
|
|
175
236
|
}
|
|
176
|
-
catch (err) {
|
|
237
|
+
catch (err) {
|
|
238
|
+
if (!isModuleNotFoundError(err)) {
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
177
242
|
}
|
|
178
243
|
try {
|
|
179
244
|
const module = await importWrapper(path);
|
|
180
245
|
return module.default;
|
|
181
246
|
}
|
|
182
|
-
catch
|
|
247
|
+
catch {
|
|
183
248
|
throw new Error(`Cannot resolve plugin: ${path}`);
|
|
184
249
|
}
|
|
185
250
|
};
|
package/dist/history.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AllureHistory, type HistoryDataPoint, type TestCase, type TestResult } from "@allurereport/core-api";
|
|
2
2
|
export declare const createHistory: (reportUuid: string, reportName: string | undefined, testCases: TestCase[], testResults: TestResult[], remoteUrl?: string) => HistoryDataPoint;
|
|
3
3
|
export declare class AllureLocalHistory implements AllureHistory {
|
|
4
4
|
#private;
|
package/dist/history.js
CHANGED
|
@@ -15,8 +15,9 @@ import { mkdir, open } from "node:fs/promises";
|
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
import readline from "node:readline/promises";
|
|
17
17
|
import { pipeline } from "node:stream/promises";
|
|
18
|
+
import { normalizeHistoryDataPointUrls, } from "@allurereport/core-api";
|
|
18
19
|
import { isFileNotFoundError } from "./utils/misc.js";
|
|
19
|
-
const createHistoryItems = (testResults) => {
|
|
20
|
+
const createHistoryItems = (testResults, remoteUrl) => {
|
|
20
21
|
return testResults
|
|
21
22
|
.filter((tr) => tr.historyId)
|
|
22
23
|
.map(({ id, name, fullName, environment, historyId, status, error: { message, trace } = {}, start, stop, duration, labels, }) => {
|
|
@@ -32,7 +33,7 @@ const createHistoryItems = (testResults) => {
|
|
|
32
33
|
stop,
|
|
33
34
|
duration,
|
|
34
35
|
labels,
|
|
35
|
-
url:
|
|
36
|
+
url: remoteUrl,
|
|
36
37
|
historyId: historyId,
|
|
37
38
|
reportLinks: [],
|
|
38
39
|
};
|
|
@@ -49,7 +50,7 @@ export const createHistory = (reportUuid, reportName = "Allure Report", testCase
|
|
|
49
50
|
name: reportName,
|
|
50
51
|
timestamp: new Date().getTime(),
|
|
51
52
|
knownTestCaseIds,
|
|
52
|
-
testResults: createHistoryItems(testResults),
|
|
53
|
+
testResults: createHistoryItems(testResults, remoteUrl),
|
|
53
54
|
metrics: {},
|
|
54
55
|
url: remoteUrl,
|
|
55
56
|
};
|
|
@@ -144,7 +145,7 @@ export class AllureLocalHistory {
|
|
|
144
145
|
.createInterface({ input: stream, terminal: false, crlfDelay: Infinity })
|
|
145
146
|
.on("line", (line) => {
|
|
146
147
|
if (line && line.trim().length) {
|
|
147
|
-
const historyEntry = JSON.parse(line);
|
|
148
|
+
const historyEntry = normalizeHistoryDataPointUrls(JSON.parse(line));
|
|
148
149
|
historyPoints.push(historyEntry);
|
|
149
150
|
}
|
|
150
151
|
});
|
|
@@ -157,6 +158,7 @@ export class AllureLocalHistory {
|
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
async appendHistory(data) {
|
|
161
|
+
const normalizedData = normalizeHistoryDataPointUrls(data);
|
|
160
162
|
const fullPath = path.resolve(this.params.historyPath);
|
|
161
163
|
const parentDir = path.dirname(fullPath);
|
|
162
164
|
const { limit } = this.params;
|
|
@@ -176,7 +178,7 @@ export class AllureLocalHistory {
|
|
|
176
178
|
const src = historyFile.createReadStream({ start, autoClose: false });
|
|
177
179
|
await pipeline(src, dst, { end: false });
|
|
178
180
|
}
|
|
179
|
-
const sources = [JSON.stringify(
|
|
181
|
+
const sources = [JSON.stringify(normalizedData), Buffer.from([0x0a])];
|
|
180
182
|
await pipeline(sources, dst);
|
|
181
183
|
if (historyExists) {
|
|
182
184
|
await historyFile.truncate(dst.bytesWritten);
|
|
@@ -185,7 +187,7 @@ export class AllureLocalHistory {
|
|
|
185
187
|
finally {
|
|
186
188
|
await historyFile.close();
|
|
187
189
|
if (limit !== 0) {
|
|
188
|
-
__classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f").push(
|
|
190
|
+
__classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f").push(normalizedData);
|
|
189
191
|
}
|
|
190
192
|
if (limit) {
|
|
191
193
|
__classPrivateFieldGet(this, _AllureLocalHistory_cachedHistory, "f").splice(limit);
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ export * from "./utils/crypto.js";
|
|
|
4
4
|
export * from "./utils/path.js";
|
|
5
5
|
export * from "./utils/new.js";
|
|
6
6
|
export * from "./utils/flaky.js";
|
|
7
|
+
export * from "./utils/environment.js";
|
|
7
8
|
export * from "./history.js";
|
|
8
9
|
export * from "./known.js";
|
|
9
10
|
export { resolveConfig, readConfig, getPluginInstance } from "./config.js";
|
|
10
11
|
export * from "./report.js";
|
|
11
12
|
export * from "./plugin.js";
|
|
12
|
-
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
|
13
|
+
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,10 @@ export * from "./utils/crypto.js";
|
|
|
3
3
|
export * from "./utils/path.js";
|
|
4
4
|
export * from "./utils/new.js";
|
|
5
5
|
export * from "./utils/flaky.js";
|
|
6
|
+
export * from "./utils/environment.js";
|
|
6
7
|
export * from "./history.js";
|
|
7
8
|
export * from "./known.js";
|
|
8
9
|
export { resolveConfig, readConfig, getPluginInstance } from "./config.js";
|
|
9
10
|
export * from "./report.js";
|
|
10
11
|
export * from "./plugin.js";
|
|
11
|
-
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
|
12
|
+
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, convertQualityGateResultsToTestErrors, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
package/dist/plugin.js
CHANGED
|
@@ -13,6 +13,7 @@ var _DefaultPluginState_state, _PluginFiles_parent, _PluginFiles_pluginId, _InMe
|
|
|
13
13
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
14
14
|
import { dirname, resolve } from "node:path";
|
|
15
15
|
import { join as joinPosix } from "node:path/posix";
|
|
16
|
+
import { resolvePathUnderOutputRoot } from "./utils/safeOutputPath.js";
|
|
16
17
|
export class DefaultPluginState {
|
|
17
18
|
constructor(state) {
|
|
18
19
|
_DefaultPluginState_state.set(this, void 0);
|
|
@@ -58,7 +59,7 @@ export class FileSystemReportFiles {
|
|
|
58
59
|
constructor(output) {
|
|
59
60
|
_FileSystemReportFiles_output.set(this, void 0);
|
|
60
61
|
this.addFile = async (path, data) => {
|
|
61
|
-
const targetPath =
|
|
62
|
+
const targetPath = resolvePathUnderOutputRoot(__classPrivateFieldGet(this, _FileSystemReportFiles_output, "f"), path);
|
|
62
63
|
const targetDirPath = dirname(targetPath);
|
|
63
64
|
await mkdir(targetDirPath, { recursive: true });
|
|
64
65
|
await writeFile(targetPath, data, { encoding: "utf-8" });
|
|
@@ -4,7 +4,6 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
6
|
var _QualityGateState_state;
|
|
7
|
-
import { DEFAULT_ENVIRONMENT } from "@allurereport/core-api";
|
|
8
7
|
import { gray, red } from "yoctocolors";
|
|
9
8
|
import { qualityGateDefaultRules } from "./rules.js";
|
|
10
9
|
export const stringifyQualityGateResults = (results) => {
|
|
@@ -77,6 +76,7 @@ export class QualityGate {
|
|
|
77
76
|
},
|
|
78
77
|
expected,
|
|
79
78
|
knownIssues,
|
|
79
|
+
environment,
|
|
80
80
|
});
|
|
81
81
|
if (result.success) {
|
|
82
82
|
continue;
|
|
@@ -89,7 +89,7 @@ export class QualityGate {
|
|
|
89
89
|
actual: result.actual,
|
|
90
90
|
expected,
|
|
91
91
|
}),
|
|
92
|
-
environment
|
|
92
|
+
environment,
|
|
93
93
|
});
|
|
94
94
|
if (ruleset.fastFail) {
|
|
95
95
|
fastFailed = true;
|
|
@@ -3,4 +3,6 @@ export declare const maxFailuresRule: QualityGateRule<number>;
|
|
|
3
3
|
export declare const minTestsCountRule: QualityGateRule<number>;
|
|
4
4
|
export declare const successRateRule: QualityGateRule<number>;
|
|
5
5
|
export declare const maxDurationRule: QualityGateRule<number>;
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const allTestsContainEnvRule: QualityGateRule<string>;
|
|
7
|
+
export declare const environmentsTestedRule: QualityGateRule<string[]>;
|
|
8
|
+
export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<string> | QualityGateRule<string[]>)[];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { filterSuccessful, filterUnsuccessful } from "@allurereport/core-api";
|
|
1
|
+
import { filterSuccessful, filterUnknownByKnownIssues, filterUnsuccessful } from "@allurereport/core-api";
|
|
2
2
|
import { bold } from "yoctocolors";
|
|
3
3
|
export const maxFailuresRule = {
|
|
4
4
|
rule: "maxFailures",
|
|
5
5
|
message: ({ actual, expected }) => `The number of failed tests ${bold(String(actual))} exceeds the allowed threshold value ${bold(String(expected))}`,
|
|
6
6
|
validate: async ({ trs, knownIssues, expected, state }) => {
|
|
7
|
-
const knownIssuesHistoryIds = knownIssues.map(({ historyId }) => historyId);
|
|
8
|
-
const unknown = trs
|
|
7
|
+
const knownIssuesHistoryIds = new Set(knownIssues.map(({ historyId }) => historyId));
|
|
8
|
+
const unknown = filterUnknownByKnownIssues(trs, knownIssuesHistoryIds);
|
|
9
9
|
const failedTrs = unknown.filter(filterUnsuccessful);
|
|
10
10
|
const actual = failedTrs.length + (state.getResult() ?? 0);
|
|
11
11
|
state.setResult(actual);
|
|
@@ -31,8 +31,8 @@ export const successRateRule = {
|
|
|
31
31
|
rule: "successRate",
|
|
32
32
|
message: ({ actual, expected }) => `Success rate ${bold(String(actual))} is less, than expected ${bold(String(expected))}`,
|
|
33
33
|
validate: async ({ trs, knownIssues, expected }) => {
|
|
34
|
-
const knownIssuesHistoryIds = knownIssues.map(({ historyId }) => historyId);
|
|
35
|
-
const unknown = trs
|
|
34
|
+
const knownIssuesHistoryIds = new Set(knownIssues.map(({ historyId }) => historyId));
|
|
35
|
+
const unknown = filterUnknownByKnownIssues(trs, knownIssuesHistoryIds);
|
|
36
36
|
const passedTrs = unknown.filter(filterSuccessful);
|
|
37
37
|
const rate = passedTrs.length === 0 ? 0 : passedTrs.length / unknown.length;
|
|
38
38
|
return {
|
|
@@ -52,4 +52,38 @@ export const maxDurationRule = {
|
|
|
52
52
|
};
|
|
53
53
|
},
|
|
54
54
|
};
|
|
55
|
-
export const
|
|
55
|
+
export const allTestsContainEnvRule = {
|
|
56
|
+
rule: "allTestsContainEnv",
|
|
57
|
+
message: ({ actual, expected }) => `Not all tests contain the required environment, ${bold(`"${expected}"`)}; ${bold(actual.length === 1 ? "one" : String(actual))} ${actual.length === 1 ? "test has" : "tests have"} different or missing environment`,
|
|
58
|
+
validate: async ({ trs, expected }) => {
|
|
59
|
+
const testsWithoutEnv = trs.filter((tr) => (tr.environment ?? "") !== expected);
|
|
60
|
+
const actual = testsWithoutEnv.length;
|
|
61
|
+
return {
|
|
62
|
+
success: actual === 0,
|
|
63
|
+
actual,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
export const environmentsTestedRule = {
|
|
68
|
+
rule: "environmentsTested",
|
|
69
|
+
message: ({ actual, expected }) => `The following environments were not tested: "${actual.join('", "')}"; expected all of: "${expected.join('", "')}"`,
|
|
70
|
+
validate: async ({ trs, expected, state }) => {
|
|
71
|
+
const previouslyTested = new Set(state.getResult() ?? []);
|
|
72
|
+
const batchTested = trs.map((tr) => tr.environment).filter((env) => env != null && env !== "");
|
|
73
|
+
const testedEnvs = new Set([...previouslyTested, ...batchTested]);
|
|
74
|
+
state.setResult([...testedEnvs]);
|
|
75
|
+
const missing = expected.filter((env) => !testedEnvs.has(env));
|
|
76
|
+
return {
|
|
77
|
+
success: missing.length === 0,
|
|
78
|
+
actual: missing,
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
export const qualityGateDefaultRules = [
|
|
83
|
+
maxFailuresRule,
|
|
84
|
+
minTestsCountRule,
|
|
85
|
+
successRateRule,
|
|
86
|
+
maxDurationRule,
|
|
87
|
+
allTestsContainEnvRule,
|
|
88
|
+
environmentsTestedRule,
|
|
89
|
+
];
|
package/dist/report.js
CHANGED
|
@@ -9,15 +9,7 @@ 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_reportName, _AllureReport_reportVariables, _AllureReport_ci, _AllureReport_store, _AllureReport_readers, _AllureReport_plugins, _AllureReport_reportFiles, _AllureReport_eventEmitter, _AllureReport_realtimeSubscriber, _AllureReport_realtimeDispatcher, _AllureReport_realTime, _AllureReport_output, _AllureReport_history, _AllureReport_allureServiceClient, _AllureReport_qualityGate, _AllureReport_dump, _AllureReport_categories, _AllureReport_dumpTempDirs, _AllureReport_state, _AllureReport_executionStage, _AllureReport_publish_get, _AllureReport_update, _AllureReport_eachPlugin, _AllureReport_getPluginState;
|
|
13
|
-
import { detect } from "@allurereport/ci";
|
|
14
|
-
import { normalizeCategoriesConfig } from "@allurereport/core-api";
|
|
15
|
-
import { AllureStoreDumpFiles, } from "@allurereport/plugin-api";
|
|
16
|
-
import { allure1, allure2, attachments, cucumberjson, junitXml, readXcResultBundle } from "@allurereport/reader";
|
|
17
|
-
import { PathResultFile } from "@allurereport/reader-api";
|
|
18
|
-
import { AllureRemoteHistory, AllureServiceClient, KnownError, UnknownError } from "@allurereport/service";
|
|
19
|
-
import { generateSummary } from "@allurereport/summary";
|
|
20
|
-
import ZipReadStream from "node-stream-zip";
|
|
12
|
+
var _AllureReport_instances, _AllureReport_reportName, _AllureReport_reportVariables, _AllureReport_ci, _AllureReport_store, _AllureReport_readers, _AllureReport_plugins, _AllureReport_reportFiles, _AllureReport_eventEmitter, _AllureReport_realtimeSubscriber, _AllureReport_realtimeDispatcher, _AllureReport_realTime, _AllureReport_hideLabels, _AllureReport_output, _AllureReport_history, _AllureReport_allureServiceClient, _AllureReport_qualityGate, _AllureReport_dump, _AllureReport_categories, _AllureReport_environments, _AllureReport_dumpTempDirs, _AllureReport_state, _AllureReport_executionStage, _AllureReport_publish_get, _AllureReport_update, _AllureReport_eachPlugin, _AllureReport_getPluginState;
|
|
21
13
|
import console from "node:console";
|
|
22
14
|
import { randomUUID } from "node:crypto";
|
|
23
15
|
import { EventEmitter } from "node:events";
|
|
@@ -26,6 +18,14 @@ import { lstat, mkdtemp, opendir, readdir, realpath, rename, rm, writeFile } fro
|
|
|
26
18
|
import { tmpdir } from "node:os";
|
|
27
19
|
import { basename, dirname, join, resolve } from "node:path";
|
|
28
20
|
import { promisify } from "node:util";
|
|
21
|
+
import { detect } from "@allurereport/ci";
|
|
22
|
+
import { normalizeCategoriesConfig } from "@allurereport/core-api";
|
|
23
|
+
import { AllureStoreDumpFiles, } from "@allurereport/plugin-api";
|
|
24
|
+
import { allure1, allure2, attachments, cucumberjson, junitXml, readXcResultBundle } from "@allurereport/reader";
|
|
25
|
+
import { PathResultFile } from "@allurereport/reader-api";
|
|
26
|
+
import { AllureRemoteHistory, AllureServiceClient, KnownError, UnknownError } from "@allurereport/service";
|
|
27
|
+
import { generateSummary } from "@allurereport/summary";
|
|
28
|
+
import ZipReadStream from "node-stream-zip";
|
|
29
29
|
import pLimit from "p-limit";
|
|
30
30
|
import ProgressBar from "progress";
|
|
31
31
|
import ZipWriteStream from "zip-stream";
|
|
@@ -33,7 +33,9 @@ import { AllureLocalHistory, createHistory } from "./history.js";
|
|
|
33
33
|
import { DefaultPluginState, PluginFiles } from "./plugin.js";
|
|
34
34
|
import { QualityGate } from "./qualityGate/index.js";
|
|
35
35
|
import { DefaultAllureStore } from "./store/store.js";
|
|
36
|
+
import { environmentIdentityById, environmentIdentityByName } from "./utils/environment.js";
|
|
36
37
|
import { RealtimeEventsDispatcher, RealtimeSubscriber } from "./utils/event.js";
|
|
38
|
+
import { resolveDumpAttachmentPath, UnsafeDumpPathError } from "./utils/safeDumpPath.js";
|
|
37
39
|
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
38
40
|
const initRequired = "report is not initialised. Call the start() method first.";
|
|
39
41
|
export class AllureReport {
|
|
@@ -50,12 +52,14 @@ export class AllureReport {
|
|
|
50
52
|
_AllureReport_realtimeSubscriber.set(this, void 0);
|
|
51
53
|
_AllureReport_realtimeDispatcher.set(this, void 0);
|
|
52
54
|
_AllureReport_realTime.set(this, void 0);
|
|
55
|
+
_AllureReport_hideLabels.set(this, void 0);
|
|
53
56
|
_AllureReport_output.set(this, void 0);
|
|
54
57
|
_AllureReport_history.set(this, void 0);
|
|
55
58
|
_AllureReport_allureServiceClient.set(this, void 0);
|
|
56
59
|
_AllureReport_qualityGate.set(this, void 0);
|
|
57
60
|
_AllureReport_dump.set(this, void 0);
|
|
58
61
|
_AllureReport_categories.set(this, void 0);
|
|
62
|
+
_AllureReport_environments.set(this, void 0);
|
|
59
63
|
_AllureReport_dumpTempDirs.set(this, []);
|
|
60
64
|
_AllureReport_state.set(this, void 0);
|
|
61
65
|
_AllureReport_executionStage.set(this, "init");
|
|
@@ -102,11 +106,16 @@ export class AllureReport {
|
|
|
102
106
|
};
|
|
103
107
|
this.validate = async (params) => {
|
|
104
108
|
const { trs, knownIssues, state, environment } = params;
|
|
109
|
+
const qualityGateEnvironment = environment === undefined
|
|
110
|
+
? undefined
|
|
111
|
+
: (environmentIdentityById(__classPrivateFieldGet(this, _AllureReport_environments, "f"), environment)?.name ??
|
|
112
|
+
environmentIdentityByName(__classPrivateFieldGet(this, _AllureReport_environments, "f"), environment)?.name ??
|
|
113
|
+
environment);
|
|
105
114
|
return __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").validate({
|
|
106
115
|
trs: trs.filter(Boolean),
|
|
107
116
|
knownIssues,
|
|
108
117
|
state,
|
|
109
|
-
environment,
|
|
118
|
+
environment: qualityGateEnvironment,
|
|
110
119
|
});
|
|
111
120
|
};
|
|
112
121
|
this.start = async () => {
|
|
@@ -299,12 +308,18 @@ export class AllureReport {
|
|
|
299
308
|
try {
|
|
300
309
|
for (const [attachmentId] of Object.entries(attachmentsEntries)) {
|
|
301
310
|
const attachmentContentEntry = await dumpArchive.entryData(attachmentId);
|
|
302
|
-
const attachmentFilePath =
|
|
311
|
+
const attachmentFilePath = resolveDumpAttachmentPath(dumpTempDir, attachmentId);
|
|
303
312
|
await writeFile(attachmentFilePath, attachmentContentEntry);
|
|
304
313
|
resultsAttachments[attachmentId] = new PathResultFile(attachmentFilePath, attachmentId);
|
|
305
314
|
}
|
|
306
315
|
}
|
|
307
316
|
catch (err) {
|
|
317
|
+
if (err instanceof UnsafeDumpPathError) {
|
|
318
|
+
console.error(`Cannot restore dump from "${dump}": the archive lists attachment paths that would write outside the extract directory (unsafe zip paths such as "../" or absolute names).`);
|
|
319
|
+
console.error(err.message);
|
|
320
|
+
console.error("Only use dump archives produced by this tool; do not load untrusted or third-party --dump zip files.");
|
|
321
|
+
throw err;
|
|
322
|
+
}
|
|
308
323
|
console.error(`Can't restore state from "${dump}", continuing without it`);
|
|
309
324
|
console.error(err);
|
|
310
325
|
}
|
|
@@ -380,6 +395,7 @@ export class AllureReport {
|
|
|
380
395
|
if (!summary) {
|
|
381
396
|
return;
|
|
382
397
|
}
|
|
398
|
+
summary.pluginId = context.id;
|
|
383
399
|
summary.pullRequestHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.pullRequestUrl;
|
|
384
400
|
summary.jobHref = __classPrivateFieldGet(this, _AllureReport_ci, "f")?.jobRunUrl;
|
|
385
401
|
if (context.publish && this.reportUrl && !cancelledPluginsIds.has(context.id)) {
|
|
@@ -496,6 +512,7 @@ export class AllureReport {
|
|
|
496
512
|
allureVersion: version,
|
|
497
513
|
reportUuid: this.reportUuid,
|
|
498
514
|
reportName: __classPrivateFieldGet(this, _AllureReport_reportName, "f"),
|
|
515
|
+
hideLabels: __classPrivateFieldGet(this, _AllureReport_hideLabels, "f"),
|
|
499
516
|
state: pluginState,
|
|
500
517
|
reportFiles: pluginFiles,
|
|
501
518
|
reportUrl: this.reportUrl,
|
|
@@ -515,7 +532,7 @@ export class AllureReport {
|
|
|
515
532
|
}
|
|
516
533
|
}
|
|
517
534
|
});
|
|
518
|
-
const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], known, reportFiles, realTime, historyPath, historyLimit, defaultLabels = {}, variables = {}, environment, environments, output, qualityGate, dump, categories, allureService: allureServiceConfig, } = opts;
|
|
535
|
+
const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], known, reportFiles, realTime, historyPath, historyLimit, defaultLabels = {}, variables = {}, environment, environments, output, hideLabels, qualityGate, dump, categories, allureService: allureServiceConfig, } = opts;
|
|
519
536
|
__classPrivateFieldSet(this, _AllureReport_allureServiceClient, allureServiceConfig?.accessToken
|
|
520
537
|
? new AllureServiceClient(allureServiceConfig)
|
|
521
538
|
: undefined, "f");
|
|
@@ -529,6 +546,8 @@ export class AllureReport {
|
|
|
529
546
|
__classPrivateFieldSet(this, _AllureReport_realtimeSubscriber, new RealtimeSubscriber(__classPrivateFieldGet(this, _AllureReport_eventEmitter, "f")), "f");
|
|
530
547
|
__classPrivateFieldSet(this, _AllureReport_realTime, realTime, "f");
|
|
531
548
|
__classPrivateFieldSet(this, _AllureReport_dump, dump, "f");
|
|
549
|
+
__classPrivateFieldSet(this, _AllureReport_hideLabels, hideLabels, "f");
|
|
550
|
+
__classPrivateFieldSet(this, _AllureReport_environments, environments ?? {}, "f");
|
|
532
551
|
if (qualityGate) {
|
|
533
552
|
__classPrivateFieldSet(this, _AllureReport_qualityGate, new QualityGate(qualityGate), "f");
|
|
534
553
|
}
|
|
@@ -574,7 +593,7 @@ export class AllureReport {
|
|
|
574
593
|
return __classPrivateFieldGet(this, _AllureReport_realtimeDispatcher, "f");
|
|
575
594
|
}
|
|
576
595
|
}
|
|
577
|
-
_AllureReport_reportName = new WeakMap(), _AllureReport_reportVariables = 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_realtimeSubscriber = new WeakMap(), _AllureReport_realtimeDispatcher = new WeakMap(), _AllureReport_realTime = new WeakMap(), _AllureReport_output = new WeakMap(), _AllureReport_history = new WeakMap(), _AllureReport_allureServiceClient = new WeakMap(), _AllureReport_qualityGate = new WeakMap(), _AllureReport_dump = new WeakMap(), _AllureReport_categories = new WeakMap(), _AllureReport_dumpTempDirs = new WeakMap(), _AllureReport_state = new WeakMap(), _AllureReport_executionStage = new WeakMap(), _AllureReport_update = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_publish_get = function _AllureReport_publish_get() {
|
|
596
|
+
_AllureReport_reportName = new WeakMap(), _AllureReport_reportVariables = 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_realtimeSubscriber = new WeakMap(), _AllureReport_realtimeDispatcher = new WeakMap(), _AllureReport_realTime = new WeakMap(), _AllureReport_hideLabels = new WeakMap(), _AllureReport_output = new WeakMap(), _AllureReport_history = new WeakMap(), _AllureReport_allureServiceClient = new WeakMap(), _AllureReport_qualityGate = new WeakMap(), _AllureReport_dump = new WeakMap(), _AllureReport_categories = new WeakMap(), _AllureReport_environments = new WeakMap(), _AllureReport_dumpTempDirs = new WeakMap(), _AllureReport_state = new WeakMap(), _AllureReport_executionStage = new WeakMap(), _AllureReport_update = new WeakMap(), _AllureReport_eachPlugin = new WeakMap(), _AllureReport_instances = new WeakSet(), _AllureReport_publish_get = function _AllureReport_publish_get() {
|
|
578
597
|
return __classPrivateFieldGet(this, _AllureReport_plugins, "f").some(({ enabled, options }) => enabled && options.publish);
|
|
579
598
|
}, _AllureReport_getPluginState = function _AllureReport_getPluginState(init, id) {
|
|
580
599
|
return init ? new DefaultPluginState({}) : __classPrivateFieldGet(this, _AllureReport_state, "f")?.[id];
|
package/dist/store/convert.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { findByLabelName, notNull } from "@allurereport/core-api";
|
|
2
3
|
import { md5 } from "@allurereport/plugin-api";
|
|
3
4
|
import { extension, lookupContentType } from "@allurereport/reader-api";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
5
|
const defaultStatus = "unknown";
|
|
6
6
|
export const __unknown = "#___unknown_value___#";
|
|
7
7
|
export const testFixtureResultRawToState = (stateData, raw, context) => {
|
package/dist/store/store.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AllureHistory, type AttachmentLink, type AttachmentLinkLinked, type DefaultLabelsConfig, type EnvironmentsConfig, type HistoryDataPoint, type HistoryTestResult, type KnownTestFailure, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
|
|
1
|
+
import { type AllureHistory, type AttachmentLink, type AttachmentLinkLinked, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type HistoryDataPoint, type HistoryTestResult, type KnownTestFailure, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
|
|
2
2
|
import { type AllureStore, type AllureStoreDump, type ExitCode, type QualityGateValidationResult, type RealtimeEventsDispatcher, type RealtimeSubscriber, type ResultFile, type TestResultFilter } from "@allurereport/plugin-api";
|
|
3
3
|
import type { RawFixtureResult, RawGlobals, RawMetadata, RawTestResult, ReaderContext, ResultsVisitor } from "@allurereport/reader-api";
|
|
4
4
|
export declare const mapToObject: <K extends string | number | symbol, T = any>(map: Map<K, T>) => Record<K, T>;
|
|
@@ -6,6 +6,7 @@ export declare const updateMapWithRecord: <K extends string | number | symbol, T
|
|
|
6
6
|
export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
|
|
7
7
|
#private;
|
|
8
8
|
readonly indexTestResultByTestCase: Map<string, TestResult[]>;
|
|
9
|
+
readonly indexTestResultByEnvironmentId: Map<string, TestResult[]>;
|
|
9
10
|
readonly indexLatestEnvTestResultByHistoryId: Map<string, Map<string, TestResult>>;
|
|
10
11
|
readonly indexTestResultByHistoryId: Map<string, TestResult[]>;
|
|
11
12
|
readonly indexAttachmentByTestResult: Map<string, AttachmentLink[]>;
|
|
@@ -26,6 +27,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
|
|
|
26
27
|
appendHistory(history: HistoryDataPoint): Promise<void>;
|
|
27
28
|
qualityGateResults(): Promise<QualityGateValidationResult[]>;
|
|
28
29
|
qualityGateResultsByEnv(): Promise<Record<string, QualityGateValidationResult[]>>;
|
|
30
|
+
qualityGateResultsByEnvironmentId(): Promise<Record<string, QualityGateValidationResult[]>>;
|
|
29
31
|
globalExitCode(): Promise<ExitCode | undefined>;
|
|
30
32
|
allGlobalErrors(): Promise<TestError[]>;
|
|
31
33
|
allGlobalAttachments(): Promise<AttachmentLinkLinked[]>;
|
|
@@ -47,6 +49,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
|
|
|
47
49
|
allFixtures(): Promise<TestFixtureResult[]>;
|
|
48
50
|
allHistoryDataPoints(): Promise<HistoryDataPoint[]>;
|
|
49
51
|
allHistoryDataPointsByEnvironment(environment: string): Promise<HistoryDataPoint[]>;
|
|
52
|
+
allHistoryDataPointsByEnvironmentId(environmentId: string): Promise<HistoryDataPoint[]>;
|
|
50
53
|
allKnownIssues(): Promise<KnownTestFailure[]>;
|
|
51
54
|
allNewTestResults(filter?: TestResultFilter, history?: HistoryDataPoint[]): Promise<TestResult[]>;
|
|
52
55
|
testCaseById(tcId: string): Promise<TestCase | undefined>;
|
|
@@ -55,8 +58,9 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
|
|
|
55
58
|
attachmentContentById(attachmentId: string): Promise<ResultFile | undefined>;
|
|
56
59
|
metadataByKey<T>(key: string): Promise<T | undefined>;
|
|
57
60
|
testResultsByTcId(tcId: string): Promise<TestResult[]>;
|
|
61
|
+
environmentIdByTrId(trId: string): Promise<string | undefined>;
|
|
58
62
|
attachmentsByTrId(trId: string): Promise<AttachmentLink[]>;
|
|
59
|
-
retriesByTr(tr
|
|
63
|
+
retriesByTr(tr?: TestResult): Promise<TestResult[]>;
|
|
60
64
|
retriesByTrId(trId: string): Promise<TestResult[]>;
|
|
61
65
|
historyByTr(tr: TestResult): Promise<HistoryTestResult[] | undefined>;
|
|
62
66
|
historyByTrId(trId: string): Promise<HistoryTestResult[] | undefined>;
|
|
@@ -69,14 +73,21 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
|
|
|
69
73
|
}>;
|
|
70
74
|
testsStatistic(filter?: TestResultFilter): Promise<Statistic>;
|
|
71
75
|
allEnvironments(): Promise<string[]>;
|
|
76
|
+
allEnvironmentIdentities(): Promise<EnvironmentIdentity[]>;
|
|
72
77
|
testResultsByEnvironment(env: string, options?: {
|
|
73
|
-
includeHidden
|
|
78
|
+
includeHidden?: boolean;
|
|
79
|
+
}): Promise<TestResult[]>;
|
|
80
|
+
testResultsByEnvironmentId(envId: string, options?: {
|
|
81
|
+
includeHidden?: boolean;
|
|
74
82
|
}): Promise<TestResult[]>;
|
|
75
83
|
allTestEnvGroups(): Promise<TestEnvGroup[]>;
|
|
76
84
|
allVariables(): Promise<ReportVariables>;
|
|
77
85
|
envVariables(env: string): Promise<{
|
|
78
86
|
[x: string]: string;
|
|
79
87
|
}>;
|
|
88
|
+
envVariablesByEnvironmentId(envId: string): Promise<{
|
|
89
|
+
[x: string]: string;
|
|
90
|
+
}>;
|
|
80
91
|
dumpState(): AllureStoreDump;
|
|
81
92
|
restoreState(stateDump: AllureStoreDump, attachmentsContents?: Record<string, ResultFile>): Promise<void>;
|
|
82
93
|
}
|