@allurereport/core 3.19.0 → 3.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/qualityGate/rules.d.ts +2 -1
- package/dist/qualityGate/rules.js +20 -0
- package/dist/report.js +38 -14
- package/dist/resolutions.d.ts +2 -1
- package/dist/resolutions.js +16 -1
- package/dist/store/retrySubstore.js +6 -3
- package/dist/store/store.js +88 -69
- package/dist/utils/perf.d.ts +23 -12
- package/dist/utils/perf.js +115 -16
- package/package.json +22 -22
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,4 @@ export * from "./resolutions.js";
|
|
|
11
11
|
export { resolveConfig, readConfig, readRawConfig, getPluginInstance } from "./config.js";
|
|
12
12
|
export * from "./report.js";
|
|
13
13
|
export * from "./plugin.js";
|
|
14
|
-
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, metricMaxRule, metricMinRule, metricMaxDeltaRule, metricMaxDeltaPercentRule, convertQualityGateResultsToTestErrors, filterFailedQualityGateResults, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
|
14
|
+
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, newTestsRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, metricMaxRule, metricMinRule, metricMaxDeltaRule, metricMaxDeltaPercentRule, convertQualityGateResultsToTestErrors, filterFailedQualityGateResults, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,4 @@ export * from "./resolutions.js";
|
|
|
10
10
|
export { resolveConfig, readConfig, readRawConfig, getPluginInstance } from "./config.js";
|
|
11
11
|
export * from "./report.js";
|
|
12
12
|
export * from "./plugin.js";
|
|
13
|
-
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, metricMaxRule, metricMinRule, metricMaxDeltaRule, metricMaxDeltaPercentRule, convertQualityGateResultsToTestErrors, filterFailedQualityGateResults, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
|
13
|
+
export { QualityGateState, qualityGateDefaultRules, maxFailuresRule, minTestsCountRule, newTestsRule, successRateRule, maxDurationRule, allTestsContainEnvRule, environmentsTestedRule, metricMaxRule, metricMinRule, metricMaxDeltaRule, metricMaxDeltaPercentRule, convertQualityGateResultsToTestErrors, filterFailedQualityGateResults, stringifyQualityGateResults, } from "./qualityGate/index.js";
|
|
@@ -7,6 +7,7 @@ type MetricRuleConfig = {
|
|
|
7
7
|
};
|
|
8
8
|
export declare const maxFailuresRule: QualityGateRule<number>;
|
|
9
9
|
export declare const minTestsCountRule: QualityGateRule<number>;
|
|
10
|
+
export declare const newTestsRule: QualityGateRule<boolean, number>;
|
|
10
11
|
export declare const successRateRule: QualityGateRule<number>;
|
|
11
12
|
export declare const maxDurationRule: QualityGateRule<number>;
|
|
12
13
|
export declare const allTestsContainEnvRule: QualityGateRule<string, number>;
|
|
@@ -15,5 +16,5 @@ export declare const metricMaxRule: QualityGateRule<MetricRuleConfig>;
|
|
|
15
16
|
export declare const metricMinRule: QualityGateRule<MetricRuleConfig>;
|
|
16
17
|
export declare const metricMaxDeltaRule: QualityGateRule<MetricRuleConfig>;
|
|
17
18
|
export declare const metricMaxDeltaPercentRule: QualityGateRule<MetricRuleConfig>;
|
|
18
|
-
export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<string, number> | QualityGateRule<string[]> | QualityGateRule<MetricRuleConfig>)[];
|
|
19
|
+
export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<boolean, number> | QualityGateRule<string, number> | QualityGateRule<string[]> | QualityGateRule<MetricRuleConfig>)[];
|
|
19
20
|
export {};
|
|
@@ -106,6 +106,25 @@ export const minTestsCountRule = {
|
|
|
106
106
|
};
|
|
107
107
|
},
|
|
108
108
|
};
|
|
109
|
+
export const newTestsRule = {
|
|
110
|
+
rule: "newTests",
|
|
111
|
+
message: ({ actual, expected }) => expected
|
|
112
|
+
? `No new tests were found; expected at least one new test`
|
|
113
|
+
: `The number of new tests ${bold(String(actual))} does not match the expected policy`,
|
|
114
|
+
successMessage: ({ actual }) => `New tests were found: ${bold(String(actual))}`,
|
|
115
|
+
validate: async ({ trs, expected, state }) => {
|
|
116
|
+
const previous = numberStateValue(state.getResult());
|
|
117
|
+
const newTrs = trs.filter((tr) => tr.transition === "new");
|
|
118
|
+
const testResults = newTrs.map((tr) => tr.id);
|
|
119
|
+
const actual = previous + newTrs.length;
|
|
120
|
+
state.setResult(actual, testResults);
|
|
121
|
+
return {
|
|
122
|
+
success: !expected || actual > 0,
|
|
123
|
+
actual,
|
|
124
|
+
testResults,
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
109
128
|
export const successRateRule = {
|
|
110
129
|
rule: "successRate",
|
|
111
130
|
message: ({ actual, expected }) => `Success rate ${bold(formatQualityGateNumber(actual))} is less, than expected ${bold(formatQualityGateNumber(expected))}`,
|
|
@@ -237,6 +256,7 @@ export const metricMaxDeltaPercentRule = {
|
|
|
237
256
|
export const qualityGateDefaultRules = [
|
|
238
257
|
maxFailuresRule,
|
|
239
258
|
minTestsCountRule,
|
|
259
|
+
newTestsRule,
|
|
240
260
|
successRateRule,
|
|
241
261
|
maxDurationRule,
|
|
242
262
|
allTestsContainEnvRule,
|
package/dist/report.js
CHANGED
|
@@ -14,7 +14,7 @@ import console from "node:console";
|
|
|
14
14
|
import { randomUUID } from "node:crypto";
|
|
15
15
|
import { once } from "node:events";
|
|
16
16
|
import { createReadStream, createWriteStream, existsSync, readFileSync } from "node:fs";
|
|
17
|
-
import { lstat, mkdtemp, readdir,
|
|
17
|
+
import { lstat, mkdtemp, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
18
18
|
import { tmpdir } from "node:os";
|
|
19
19
|
import { basename, dirname, join, resolve } from "node:path";
|
|
20
20
|
import { promisify } from "node:util";
|
|
@@ -22,7 +22,7 @@ import { detect } from "@allurereport/ci";
|
|
|
22
22
|
import { createProgressLogger } from "@allurereport/cli-commons";
|
|
23
23
|
import { normalizeCategoriesConfig, parseIntegerConfigValue } from "@allurereport/core-api";
|
|
24
24
|
import { AllureStoreDumpFiles, createTestResultRegistry, } from "@allurereport/plugin-api";
|
|
25
|
-
import { allure1, allure2, attachments, cucumberjson, junitXml, readXcResultBundle } from "@allurereport/reader";
|
|
25
|
+
import { allure1, allure2, attachments, cucumberjson, cucumberMessages, junitXml, readXcResultBundle, } from "@allurereport/reader";
|
|
26
26
|
import { PathResultFile } from "@allurereport/reader-api";
|
|
27
27
|
import { AllureRemoteHistory, AllureServiceClient, AllureTestOpsClient, KnownError, UnknownError, } from "@allurereport/service";
|
|
28
28
|
import { generateSummary } from "@allurereport/summary";
|
|
@@ -37,7 +37,7 @@ import { writeKnownIssues } from "./resolutions.js";
|
|
|
37
37
|
import { DefaultAllureStore } from "./store/store.js";
|
|
38
38
|
import { ARTIFACTS_MANIFEST_FILENAME, createReportArtifact, deduplicateDumpInputs, } from "./utils/artifacts.js";
|
|
39
39
|
import { environmentIdentityById, environmentIdentityByName } from "./utils/environment.js";
|
|
40
|
-
import { getPerfMetricsResults, isPerfMetricsEnabled, measurePerf, PERF_METRIC_NAMES, PERF_METRIC_PREFIXES, perfMetricsFileName, startPerfSpan, writePerfMetrics, } from "./utils/perf.js";
|
|
40
|
+
import { getPerfMetricsResults, incrementPerfCounter, isPerfMetricsEnabled, measurePerfAggregate, measurePerf, PERF_METRIC_NAMES, PERF_METRIC_PREFIXES, perfMetricsFileName, startPerfSpan, writePerfMetrics, } from "./utils/perf.js";
|
|
41
41
|
import { RealtimeChannel } from "./utils/realtimeChannel.js";
|
|
42
42
|
import { RealtimeUpdateScheduler } from "./utils/realtimeUpdateScheduler.js";
|
|
43
43
|
import { resolveDumpAttachmentPath, UnsafeDumpPathError } from "./utils/safeDumpPath.js";
|
|
@@ -53,6 +53,11 @@ const ROOT_INTEGRATION_FILENAMES = new Set([
|
|
|
53
53
|
QUALITY_GATE_RESULTS_FILENAME,
|
|
54
54
|
ARTIFACTS_MANIFEST_FILENAME,
|
|
55
55
|
]);
|
|
56
|
+
const WORKLOAD_PERF_METRIC = {
|
|
57
|
+
group: "workload",
|
|
58
|
+
groupTitle: "Workload",
|
|
59
|
+
better: "neutral",
|
|
60
|
+
};
|
|
56
61
|
const readConcurrency = () => {
|
|
57
62
|
const parsed = Number.parseInt(process.env.ALLURE_READ_CONCURRENCY ?? "", 10);
|
|
58
63
|
if (!Number.isFinite(parsed)) {
|
|
@@ -76,9 +81,7 @@ const getExecutorReportUrl = (executor) => {
|
|
|
76
81
|
return undefined;
|
|
77
82
|
}
|
|
78
83
|
try {
|
|
79
|
-
|
|
80
|
-
navUrl.pathname = navUrl.pathname.endsWith("/") ? navUrl.pathname : `${navUrl.pathname}/`;
|
|
81
|
-
return navUrl.toString();
|
|
84
|
+
return new URL(reportUrl).toString();
|
|
82
85
|
}
|
|
83
86
|
catch {
|
|
84
87
|
return undefined;
|
|
@@ -298,19 +301,23 @@ export class AllureReport {
|
|
|
298
301
|
throw new Error(INIT_REQUIRED_ERROR_MESSAGE);
|
|
299
302
|
}
|
|
300
303
|
const resultsDirPath = resolve(resultsDir);
|
|
301
|
-
if (await readXcResultBundle(__classPrivateFieldGet(this, _AllureReport_store, "f"), resultsDirPath)) {
|
|
304
|
+
if (await measurePerf(PERF_METRIC_NAMES.generateReadResultsXcresultCheck, () => readXcResultBundle(__classPrivateFieldGet(this, _AllureReport_store, "f"), resultsDirPath))) {
|
|
302
305
|
__classPrivateFieldGet(this, _AllureReport_store, "f").updateHistoryFlags();
|
|
303
306
|
return;
|
|
304
307
|
}
|
|
305
308
|
try {
|
|
306
|
-
const entries = (await readdir(resultsDirPath, { withFileTypes: true }))
|
|
309
|
+
const entries = await measurePerf(PERF_METRIC_NAMES.generateReadResultsReaddir, async () => (await readdir(resultsDirPath, { withFileTypes: true }))
|
|
307
310
|
.filter((dirent) => dirent.isFile() && !dirent.name.endsWith(".tmp"))
|
|
308
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
311
|
+
.sort((a, b) => a.name.localeCompare(b.name)));
|
|
309
312
|
const limit = pLimit(readConcurrency());
|
|
313
|
+
incrementPerfCounter(PERF_METRIC_NAMES.generateReadResultsFiles, entries.length, {
|
|
314
|
+
...WORKLOAD_PERF_METRIC,
|
|
315
|
+
title: "Input files",
|
|
316
|
+
unit: "files",
|
|
317
|
+
});
|
|
310
318
|
await Promise.all(entries.map((dirent) => limit(async () => {
|
|
311
319
|
try {
|
|
312
|
-
|
|
313
|
-
await this.readResult(new PathResultFile(path, dirent.name));
|
|
320
|
+
await this.readResult(new PathResultFile(join(resultsDirPath, dirent.name), dirent.name));
|
|
314
321
|
}
|
|
315
322
|
catch (e) {
|
|
316
323
|
console.error(`can't read result file ${dirent.name}`, e);
|
|
@@ -338,7 +345,7 @@ export class AllureReport {
|
|
|
338
345
|
if (reader.matches && !(await reader.matches(data))) {
|
|
339
346
|
continue;
|
|
340
347
|
}
|
|
341
|
-
const processed = await reader.read(__classPrivateFieldGet(this, _AllureReport_store, "f"), data);
|
|
348
|
+
const processed = await measurePerfAggregate(PERF_METRIC_NAMES.generateReadResultsReaderRead, () => reader.read(__classPrivateFieldGet(this, _AllureReport_store, "f"), data));
|
|
342
349
|
if (processed) {
|
|
343
350
|
return;
|
|
344
351
|
}
|
|
@@ -457,7 +464,7 @@ export class AllureReport {
|
|
|
457
464
|
const addJsonDumpEntry = async (entryName, value) => {
|
|
458
465
|
await addRequiredDumpEntry(Buffer.from(JSON.stringify(value)), entryName);
|
|
459
466
|
};
|
|
460
|
-
const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByRetryHash = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], metrics = [], }) => [
|
|
467
|
+
const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, metadata = {}, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByRetryHash = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], metrics = [], }) => [
|
|
461
468
|
[AllureStoreDumpFiles.TestResults, testResults],
|
|
462
469
|
[AllureStoreDumpFiles.TestCases, testCases],
|
|
463
470
|
[AllureStoreDumpFiles.Fixtures, fixtures],
|
|
@@ -465,6 +472,7 @@ export class AllureReport {
|
|
|
465
472
|
[AllureStoreDumpFiles.CheckResults, checkResults],
|
|
466
473
|
[AllureStoreDumpFiles.Environments, environments],
|
|
467
474
|
[AllureStoreDumpFiles.ReportVariables, reportVariables],
|
|
475
|
+
[AllureStoreDumpFiles.Metadata, metadata],
|
|
468
476
|
[AllureStoreDumpFiles.ResolutionIssues, resolutionIssues],
|
|
469
477
|
[AllureStoreDumpFiles.GlobalAttachments, globalAttachmentIds],
|
|
470
478
|
[AllureStoreDumpFiles.GlobalErrors, globalErrors],
|
|
@@ -613,6 +621,7 @@ export class AllureReport {
|
|
|
613
621
|
const checkResultsEntry = await optionalEntryData(AllureStoreDumpFiles.CheckResults);
|
|
614
622
|
const environmentsEntry = await requiredEntryData(AllureStoreDumpFiles.Environments);
|
|
615
623
|
const reportVariablesEntry = await requiredEntryData(AllureStoreDumpFiles.ReportVariables);
|
|
624
|
+
const metadataEntry = await optionalEntryData(AllureStoreDumpFiles.Metadata);
|
|
616
625
|
const globalAttachmentsEntry = await requiredEntryData(AllureStoreDumpFiles.GlobalAttachments);
|
|
617
626
|
const globalErrorsEntry = await requiredEntryData(AllureStoreDumpFiles.GlobalErrors);
|
|
618
627
|
const indexAttachmentsEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByTestResults);
|
|
@@ -633,6 +642,7 @@ export class AllureReport {
|
|
|
633
642
|
case AllureStoreDumpFiles.Fixtures:
|
|
634
643
|
case AllureStoreDumpFiles.Environments:
|
|
635
644
|
case AllureStoreDumpFiles.ReportVariables:
|
|
645
|
+
case AllureStoreDumpFiles.Metadata:
|
|
636
646
|
case AllureStoreDumpFiles.ResolutionIssues:
|
|
637
647
|
case AllureStoreDumpFiles.GlobalAttachments:
|
|
638
648
|
case AllureStoreDumpFiles.GlobalErrors:
|
|
@@ -663,6 +673,7 @@ export class AllureReport {
|
|
|
663
673
|
checkResults: checkResultsEntry ? JSON.parse(checkResultsEntry.toString("utf8")) : [],
|
|
664
674
|
environments: JSON.parse(environmentsEntry.toString("utf8")),
|
|
665
675
|
reportVariables: JSON.parse(reportVariablesEntry.toString("utf8")),
|
|
676
|
+
metadata: metadataEntry ? JSON.parse(metadataEntry.toString("utf8")) : {},
|
|
666
677
|
globalAttachmentIds: JSON.parse(globalAttachmentsEntry.toString("utf8")),
|
|
667
678
|
globalErrors: JSON.parse(globalErrorsEntry.toString("utf8")),
|
|
668
679
|
indexAttachmentByTestResult: JSON.parse(indexAttachmentsEntry.toString("utf8")),
|
|
@@ -978,6 +989,11 @@ export class AllureReport {
|
|
|
978
989
|
await pluginState.set("files", {});
|
|
979
990
|
}
|
|
980
991
|
const pluginFiles = new PluginFiles(__classPrivateFieldGet(this, _AllureReport_reportFiles, "f"), id, async (key, filepath) => {
|
|
992
|
+
incrementPerfCounter(`${PERF_METRIC_PREFIXES.generatePlugin}${id}.generatedFiles`, 1, {
|
|
993
|
+
...WORKLOAD_PERF_METRIC,
|
|
994
|
+
title: `Generated ${id} files`,
|
|
995
|
+
unit: "files",
|
|
996
|
+
});
|
|
981
997
|
const currentPluginState = __classPrivateFieldGet(this, _AllureReport_instances, "m", _AllureReport_getPluginState).call(this, false, id);
|
|
982
998
|
const files = await currentPluginState?.get("files");
|
|
983
999
|
if (!files) {
|
|
@@ -994,6 +1010,14 @@ export class AllureReport {
|
|
|
994
1010
|
hideLabels: __classPrivateFieldGet(this, _AllureReport_hideLabels, "f"),
|
|
995
1011
|
state: pluginState,
|
|
996
1012
|
reportFiles: pluginFiles,
|
|
1013
|
+
...(isPerfMetricsEnabled()
|
|
1014
|
+
? {
|
|
1015
|
+
perf: {
|
|
1016
|
+
measure: (name, fn) => measurePerf(`${PERF_METRIC_PREFIXES.generatePlugin}${id}.${name}`, fn),
|
|
1017
|
+
count: (name, value, metadata) => incrementPerfCounter(`${PERF_METRIC_PREFIXES.generatePlugin}${id}.${name}`, value, metadata),
|
|
1018
|
+
},
|
|
1019
|
+
}
|
|
1020
|
+
: {}),
|
|
997
1021
|
reportUrl: this.reportUrl,
|
|
998
1022
|
realTime: !!__classPrivateFieldGet(this, _AllureReport_realTime, "f"),
|
|
999
1023
|
output: __classPrivateFieldGet(this, _AllureReport_output, "f"),
|
|
@@ -1013,7 +1037,7 @@ export class AllureReport {
|
|
|
1013
1037
|
}
|
|
1014
1038
|
}
|
|
1015
1039
|
});
|
|
1016
|
-
const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyBaseUrl, historyLimit, flakyDetection, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, performance, dump, categories, allureService, globalAttachments, cwd, } = opts;
|
|
1040
|
+
const { name, readers = [allure1, allure2, cucumberjson, cucumberMessages, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyBaseUrl, historyLimit, flakyDetection, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, performance, dump, categories, allureService, globalAttachments, cwd, } = opts;
|
|
1017
1041
|
const allureServiceAccessToken = allureService?.accessToken;
|
|
1018
1042
|
if (allureServiceAccessToken) {
|
|
1019
1043
|
const allureServiceClientConfig = {
|
package/dist/resolutions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResolutionRule, ResolutionsConfig, TestResult } from "@allurereport/core-api";
|
|
1
|
+
import type { IssueResolutionRule, ResolutionIssue, ResolutionRule, ResolutionsConfig, TestResult } from "@allurereport/core-api";
|
|
2
2
|
import type { AllureStore } from "@allurereport/plugin-api";
|
|
3
3
|
export declare const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
|
|
4
4
|
export declare const validateResolutionsConfig: (config?: ResolutionsConfig) => void;
|
|
@@ -12,5 +12,6 @@ export declare const KNOWN_FROM_RESULT_RESOLUTION: {
|
|
|
12
12
|
};
|
|
13
13
|
export declare const getResolutionByRules: (testResult: TestResult, config?: ResolutionsConfig) => ResolutionRule | undefined;
|
|
14
14
|
export declare const isIgnoredFailure: (testResult: TestResult) => boolean;
|
|
15
|
+
export declare const createResolutionIssue: (rule: IssueResolutionRule, config?: ResolutionsConfig) => ResolutionIssue;
|
|
15
16
|
export declare const resolveExactIssuesFilePath: (pathOrDir: string | undefined, label: string) => Promise<string | undefined>;
|
|
16
17
|
export declare const writeKnownIssues: (store: AllureStore, knownIssuesPath?: string) => Promise<void>;
|
package/dist/resolutions.js
CHANGED
|
@@ -52,7 +52,7 @@ const validateIssueUrl = (rule, template, errors) => {
|
|
|
52
52
|
}
|
|
53
53
|
let url;
|
|
54
54
|
try {
|
|
55
|
-
url = new URL(template.urlTemplate.replace("%s", encodeURIComponent(rule.issue.id)));
|
|
55
|
+
url = new URL(template.urlTemplate.replace("%s", () => encodeURIComponent(rule.issue.id)));
|
|
56
56
|
}
|
|
57
57
|
catch {
|
|
58
58
|
errors.push(`resolutions.links.${rule.issue.type}.urlTemplate must resolve to an absolute URL`);
|
|
@@ -155,6 +155,21 @@ export const getResolutionByRules = (testResult, config) => {
|
|
|
155
155
|
return undefined;
|
|
156
156
|
};
|
|
157
157
|
export const isIgnoredFailure = (testResult) => testResult.resolution === "muted" || testResult.resolution === "accepted";
|
|
158
|
+
export const createResolutionIssue = (rule, config) => {
|
|
159
|
+
const template = config?.links?.[rule.issue.type];
|
|
160
|
+
const issue = { ...rule.issue, comment: rule.comment };
|
|
161
|
+
if (!template) {
|
|
162
|
+
return issue;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
...issue,
|
|
166
|
+
link: {
|
|
167
|
+
name: template.nameTemplate?.replace("%s", () => rule.issue.id) ?? rule.issue.id,
|
|
168
|
+
url: template.urlTemplate.replace("%s", () => encodeURIComponent(rule.issue.id)),
|
|
169
|
+
type: rule.issue.type,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
};
|
|
158
173
|
export const resolveExactIssuesFilePath = async (pathOrDir, label) => {
|
|
159
174
|
if (!pathOrDir)
|
|
160
175
|
return undefined;
|
|
@@ -48,15 +48,18 @@ export class RetrySubstore {
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
retriesByTr(testResult) {
|
|
51
|
-
if (!testResult.retryHash
|
|
51
|
+
if (!testResult.retryHash) {
|
|
52
52
|
return NO_RETRIES;
|
|
53
53
|
}
|
|
54
54
|
const attempts = __classPrivateFieldGet(this, _RetrySubstore_testResultsByRetryHash, "f").get(testResult.retryHash) ?? [];
|
|
55
|
+
if (attempts.length <= 1) {
|
|
56
|
+
return NO_RETRIES;
|
|
57
|
+
}
|
|
55
58
|
const index = attempts.findIndex((attempt) => attempt.id === testResult.id);
|
|
56
|
-
if (index
|
|
59
|
+
if (index === -1) {
|
|
57
60
|
return NO_RETRIES;
|
|
58
61
|
}
|
|
59
|
-
return attempts.
|
|
62
|
+
return attempts.filter((attempt) => attempt.id !== testResult.id);
|
|
60
63
|
}
|
|
61
64
|
reset() {
|
|
62
65
|
__classPrivateFieldGet(this, _RetrySubstore_testResultsByRetryHash, "f").clear();
|
package/dist/store/store.js
CHANGED
|
@@ -13,10 +13,11 @@ var _DefaultAllureStore_instances, _DefaultAllureStore_testResults, _DefaultAllu
|
|
|
13
13
|
import { extname } from "node:path";
|
|
14
14
|
import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, calculateEnvironmentHash, calculateParametersHash, calculateRetryHash, calculateTestCaseHash, compareBy, createDictionary, fallbackTestCaseIdLabelName, getWorstStatus, createHistoryTestResultLookup, normalizeHistoryDataPoint, ordinal, reverse, resolveMetricSamples, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
|
|
15
15
|
import { md5, } from "@allurereport/plugin-api";
|
|
16
|
-
import { getResolutionByRules, isIgnoredFailure } from "../resolutions.js";
|
|
16
|
+
import { createResolutionIssue, getResolutionByRules, isIgnoredFailure } from "../resolutions.js";
|
|
17
17
|
import { environmentIdentityById, normalizeEnvironmentDescriptorMap, resolveEnvironmentIdentity, resolveStoredEnvironmentIdentity, validateAllowedEnvironmentId, } from "../utils/environment.js";
|
|
18
18
|
import { createFlakyDetector } from "../utils/flaky.js";
|
|
19
19
|
import { getStatusTransition } from "../utils/new.js";
|
|
20
|
+
import { measurePerfAggregateSync, PERF_METRIC_NAMES } from "../utils/perf.js";
|
|
20
21
|
import { testFixtureResultRawToState, testResultRawToState } from "./convert.js";
|
|
21
22
|
import { RetrySubstore } from "./retrySubstore.js";
|
|
22
23
|
const index = (indexMap, key, ...items) => {
|
|
@@ -348,58 +349,71 @@ export class DefaultAllureStore {
|
|
|
348
349
|
}
|
|
349
350
|
async visitTestResult(raw, context) {
|
|
350
351
|
const attachmentLinks = [];
|
|
351
|
-
const testResult = testResultRawToState({
|
|
352
|
+
const testResult = measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultConvert, () => testResultRawToState({
|
|
352
353
|
testCases: __classPrivateFieldGet(this, _DefaultAllureStore_testCases, "f"),
|
|
353
354
|
attachments: __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f"),
|
|
354
355
|
visitAttachmentLink: (link) => attachmentLinks.push(link),
|
|
355
|
-
}, raw, context);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
defaultLabelsNames.
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
356
|
+
}, raw, context));
|
|
357
|
+
measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultDefaultLabels, () => {
|
|
358
|
+
const defaultLabelsNames = Object.keys(__classPrivateFieldGet(this, _DefaultAllureStore_defaultLabels, "f"));
|
|
359
|
+
if (defaultLabelsNames.length) {
|
|
360
|
+
defaultLabelsNames.forEach((labelName) => {
|
|
361
|
+
if (!testResult.labels.find((label) => label.name === labelName)) {
|
|
362
|
+
const defaultLabelValue = __classPrivateFieldGet(this, _DefaultAllureStore_defaultLabels, "f")[labelName];
|
|
363
|
+
[].concat(defaultLabelValue).forEach((labelValue) => {
|
|
364
|
+
testResult.labels.push({
|
|
365
|
+
name: labelName,
|
|
366
|
+
value: labelValue,
|
|
367
|
+
});
|
|
365
368
|
});
|
|
366
|
-
}
|
|
367
|
-
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
const environmentIdentity = measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultEnvironment, () => {
|
|
374
|
+
const environmentMatcherLabels = testResult.labels.filter(({ name }) => name !== "ALLURE_ID" && name !== fallbackTestCaseIdLabelName);
|
|
375
|
+
const environmentMatch = __classPrivateFieldGet(this, _DefaultAllureStore_environment, "f")
|
|
376
|
+
? undefined
|
|
377
|
+
: Object.entries(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f")).find(([, { matcher }]) => matcher({ labels: environmentMatcherLabels }));
|
|
378
|
+
const namedEnvironmentIdentity = __classPrivateFieldGet(this, _DefaultAllureStore_environment, "f") ??
|
|
379
|
+
(environmentMatch
|
|
380
|
+
? {
|
|
381
|
+
id: environmentMatch[0],
|
|
382
|
+
name: environmentMatch[1].name ?? environmentMatch[0],
|
|
383
|
+
}
|
|
384
|
+
: undefined);
|
|
385
|
+
const identity = namedEnvironmentIdentity ?? DEFAULT_ENVIRONMENT_IDENTITY;
|
|
386
|
+
testResult.environment = identity.id;
|
|
387
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addEnvironments).call(this, [identity]);
|
|
388
|
+
return identity;
|
|
389
|
+
});
|
|
390
|
+
measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultRetry, () => {
|
|
391
|
+
testResult.environmentHash = calculateEnvironmentHash(environmentIdentity.id);
|
|
392
|
+
testResult.retryHash = calculateRetryHash({
|
|
393
|
+
testCaseHash: testResult.testCaseHash,
|
|
394
|
+
parametersHash: testResult.parametersHash,
|
|
395
|
+
environmentHash: testResult.environmentHash,
|
|
368
396
|
});
|
|
369
|
-
}
|
|
370
|
-
const environmentMatcherLabels = testResult.labels.filter(({ name }) => name !== "ALLURE_ID" && name !== fallbackTestCaseIdLabelName);
|
|
371
|
-
const environmentMatch = __classPrivateFieldGet(this, _DefaultAllureStore_environment, "f")
|
|
372
|
-
? undefined
|
|
373
|
-
: Object.entries(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f")).find(([, { matcher }]) => matcher({ labels: environmentMatcherLabels }));
|
|
374
|
-
const namedEnvironmentIdentity = __classPrivateFieldGet(this, _DefaultAllureStore_environment, "f") ??
|
|
375
|
-
(environmentMatch
|
|
376
|
-
? {
|
|
377
|
-
id: environmentMatch[0],
|
|
378
|
-
name: environmentMatch[1].name ?? environmentMatch[0],
|
|
379
|
-
}
|
|
380
|
-
: undefined);
|
|
381
|
-
const environmentIdentity = namedEnvironmentIdentity ?? DEFAULT_ENVIRONMENT_IDENTITY;
|
|
382
|
-
testResult.environment = environmentIdentity.id;
|
|
383
|
-
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_addEnvironments).call(this, [environmentIdentity]);
|
|
384
|
-
testResult.environmentHash = calculateEnvironmentHash(environmentIdentity.id);
|
|
385
|
-
testResult.retryHash = calculateRetryHash({
|
|
386
|
-
testCaseHash: testResult.testCaseHash,
|
|
387
|
-
parametersHash: testResult.parametersHash,
|
|
388
|
-
environmentHash: testResult.environmentHash,
|
|
389
397
|
});
|
|
390
398
|
if (__classPrivateFieldGet(this, _DefaultAllureStore_history, "f")) {
|
|
391
|
-
|
|
392
|
-
|
|
399
|
+
measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultHistory, () => {
|
|
400
|
+
const lookup = createHistoryTestResultLookup([testResult]);
|
|
401
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_applyHistoryFlags).call(this, testResult, __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_historyFor).call(this, testResult, lookup));
|
|
402
|
+
});
|
|
393
403
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
404
|
+
measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultResolution, () => {
|
|
405
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyResolution).call(this, testResult);
|
|
406
|
+
});
|
|
407
|
+
measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitTestResultIndexes, () => {
|
|
408
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
|
|
409
|
+
__classPrivateFieldSet(this, _DefaultAllureStore_historyLookup, undefined, "f");
|
|
410
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_setTestResultEnvironmentId).call(this, testResult, environmentIdentity.id);
|
|
411
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").recordIngestOrder(testResult.id);
|
|
412
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").upsert(testResult);
|
|
413
|
+
index(this.indexTestResultByTestCase, testResult.testCase?.id, testResult);
|
|
414
|
+
index(this.indexTestResultByRetryHash, testResult.retryHash, testResult);
|
|
415
|
+
index(this.indexAttachmentByTestResult, testResult.id, ...attachmentLinks);
|
|
416
|
+
});
|
|
403
417
|
__classPrivateFieldGet(this, _DefaultAllureStore_realtimeDispatcher, "f")?.sendTestResult(testResult.id);
|
|
404
418
|
}
|
|
405
419
|
async visitTestFixtureResult(result, context) {
|
|
@@ -416,28 +430,31 @@ export class DefaultAllureStore {
|
|
|
416
430
|
__classPrivateFieldGet(this, _DefaultAllureStore_realtimeDispatcher, "f")?.sendTestFixtureResult(testFixtureResult.id);
|
|
417
431
|
}
|
|
418
432
|
async visitAttachmentFile(resultFile) {
|
|
419
|
-
const originalFileName =
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
433
|
+
const { id, originalFileName } = measurePerfAggregateSync(PERF_METRIC_NAMES.storeVisitAttachmentFileMetadata, () => {
|
|
434
|
+
const originalFileName = resultFile.getOriginalFileName();
|
|
435
|
+
const id = md5(originalFileName);
|
|
436
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_attachmentContents, "f").set(id, resultFile);
|
|
437
|
+
const maybeLink = __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").get(id);
|
|
438
|
+
if (maybeLink) {
|
|
439
|
+
const link = maybeLink;
|
|
440
|
+
link.missed = false;
|
|
441
|
+
link.ext = link.ext === undefined || link.ext === "" ? resultFile.getExtension() : link.ext;
|
|
442
|
+
link.contentType = link.contentType ?? resultFile.getContentType();
|
|
443
|
+
link.contentLength = resultFile.getContentLength();
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").set(id, {
|
|
447
|
+
used: false,
|
|
448
|
+
missed: false,
|
|
449
|
+
id,
|
|
450
|
+
originalFileName,
|
|
451
|
+
ext: resultFile.getExtension(),
|
|
452
|
+
contentType: resultFile.getContentType(),
|
|
453
|
+
contentLength: resultFile.getContentLength(),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return { id, originalFileName };
|
|
457
|
+
});
|
|
441
458
|
for (const globalAttachmentId of [...__classPrivateFieldGet(this, _DefaultAllureStore_globalAttachmentIds, "f"), ...__classPrivateFieldGet(this, _DefaultAllureStore_processGlobalAttachmentIds, "f")]) {
|
|
442
459
|
const globalAttachment = __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").get(globalAttachmentId);
|
|
443
460
|
if (!globalAttachment || globalAttachment.originalFileName !== originalFileName) {
|
|
@@ -874,6 +891,7 @@ export class DefaultAllureStore {
|
|
|
874
891
|
fixtures: mapToObject(__classPrivateFieldGet(this, _DefaultAllureStore_fixtures, "f")),
|
|
875
892
|
environments: __classPrivateFieldGet(this, _DefaultAllureStore_environments, "f"),
|
|
876
893
|
reportVariables: __classPrivateFieldGet(this, _DefaultAllureStore_reportVariables, "f"),
|
|
894
|
+
metadata: mapToObject(__classPrivateFieldGet(this, _DefaultAllureStore_metadata, "f")),
|
|
877
895
|
globalAttachmentIds: [...__classPrivateFieldGet(this, _DefaultAllureStore_globalAttachmentIds, "f"), ...__classPrivateFieldGet(this, _DefaultAllureStore_processGlobalAttachmentIds, "f")],
|
|
878
896
|
globalErrors: [...__classPrivateFieldGet(this, _DefaultAllureStore_globalErrors, "f"), ...__classPrivateFieldGet(this, _DefaultAllureStore_processGlobalErrors, "f")],
|
|
879
897
|
checkResults: mapToObject(__classPrivateFieldGet(this, _DefaultAllureStore_checkResultsById, "f")),
|
|
@@ -910,7 +928,7 @@ export class DefaultAllureStore {
|
|
|
910
928
|
}
|
|
911
929
|
async restoreState(stateDump, attachmentsContents = {}) {
|
|
912
930
|
__classPrivateFieldSet(this, _DefaultAllureStore_historyLookup, undefined, "f");
|
|
913
|
-
const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, qualityGateResults = [], metrics = [], testResultIdsIngestOrder = [], } = stateDump;
|
|
931
|
+
const { testResults, attachments, testCases, fixtures, reportVariables, metadata = {}, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, qualityGateResults = [], metrics = [], testResultIdsIngestOrder = [], } = stateDump;
|
|
914
932
|
__classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").clear();
|
|
915
933
|
__classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").clear();
|
|
916
934
|
__classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssueIdByTestResultId, "f").clear();
|
|
@@ -1021,6 +1039,7 @@ export class DefaultAllureStore {
|
|
|
1021
1039
|
__classPrivateFieldGet(this, _DefaultAllureStore_globalErrors, "f").push(__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_indexGlobalError).call(this, error));
|
|
1022
1040
|
});
|
|
1023
1041
|
Object.assign(__classPrivateFieldGet(this, _DefaultAllureStore_reportVariables, "f"), reportVariables);
|
|
1042
|
+
updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_metadata, "f"), metadata);
|
|
1024
1043
|
Object.entries(indexAttachmentByTestResult).forEach(([trId, links]) => {
|
|
1025
1044
|
const attachmentsLinks = links.map((id) => __classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f").get(id)).filter(Boolean);
|
|
1026
1045
|
if (attachmentsLinks.length === 0) {
|
|
@@ -1176,7 +1195,7 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_attachments
|
|
|
1176
1195
|
testResult.resolution = rule.resolution;
|
|
1177
1196
|
testResult.resolutionComment = rule.comment;
|
|
1178
1197
|
if (rule.resolution === "issue") {
|
|
1179
|
-
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_associateResolutionIssue).call(this,
|
|
1198
|
+
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_associateResolutionIssue).call(this, createResolutionIssue(rule, __classPrivateFieldGet(this, _DefaultAllureStore_resolutionsConfig, "f")), testResult.id);
|
|
1180
1199
|
}
|
|
1181
1200
|
}, _DefaultAllureStore_associateResolutionIssue = function _DefaultAllureStore_associateResolutionIssue(resolutionIssue, testResultId) {
|
|
1182
1201
|
__classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResultId);
|
package/dist/utils/perf.d.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import type { AllurePerformanceResult } from "@allurereport/core-api";
|
|
2
2
|
export declare const PERF_METRICS_FILE = "performance.json";
|
|
3
3
|
export declare const perfMetricsFileName: (reportUuid: string) => string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
name: string;
|
|
11
|
-
count: number;
|
|
12
|
-
totalMs: number;
|
|
13
|
-
minMs: number;
|
|
14
|
-
maxMs: number;
|
|
15
|
-
avgMs: number;
|
|
4
|
+
type PerfMetricMetadata = {
|
|
5
|
+
title?: string;
|
|
6
|
+
unit?: string;
|
|
7
|
+
group?: string;
|
|
8
|
+
groupTitle?: string;
|
|
9
|
+
better?: "neutral";
|
|
16
10
|
};
|
|
17
11
|
export declare const PERF_METRIC_NAMES: {
|
|
18
12
|
readonly allureTotal: "allure.total";
|
|
@@ -22,17 +16,34 @@ export declare const PERF_METRIC_NAMES: {
|
|
|
22
16
|
readonly restoreStateStoreRestore: "restoreState.storeRestore";
|
|
23
17
|
readonly generateTotal: "generate.total";
|
|
24
18
|
readonly generateReadResults: "generate.readResults";
|
|
19
|
+
readonly generateReadResultsXcresultCheck: "generate.readResults.xcresultCheck";
|
|
20
|
+
readonly generateReadResultsReaddir: "generate.readResults.readdir";
|
|
21
|
+
readonly generateReadResultsFiles: "generate.readResults.files";
|
|
22
|
+
readonly generateReadResultsReaderRead: "generate.readResults.reader.read";
|
|
25
23
|
readonly generatePluginsDone: "generate.plugins.done";
|
|
24
|
+
readonly storeVisitTestResultConvert: "store.visitTestResult.convert";
|
|
25
|
+
readonly storeVisitTestResultDefaultLabels: "store.visitTestResult.defaultLabels";
|
|
26
|
+
readonly storeVisitTestResultEnvironment: "store.visitTestResult.environment";
|
|
27
|
+
readonly storeVisitTestResultRetry: "store.visitTestResult.retry";
|
|
28
|
+
readonly storeVisitTestResultHistory: "store.visitTestResult.history";
|
|
29
|
+
readonly storeVisitTestResultResolution: "store.visitTestResult.resolution";
|
|
30
|
+
readonly storeVisitTestResultIndexes: "store.visitTestResult.indexes";
|
|
31
|
+
readonly storeVisitAttachmentFileMetadata: "store.visitAttachmentFile.metadata";
|
|
26
32
|
readonly publishUploadTotal: "publish.upload.total";
|
|
27
33
|
readonly summaryGenerate: "summary.generate";
|
|
28
34
|
};
|
|
29
35
|
export declare const PERF_METRIC_PREFIXES: {
|
|
36
|
+
readonly generatePlugin: "generate.plugin.";
|
|
30
37
|
readonly generatePluginDone: "generate.plugin.done.";
|
|
31
38
|
readonly publishUploadPlugin: "publish.upload.plugin.";
|
|
32
39
|
};
|
|
33
40
|
export declare const isPerfMetricsEnabled: () => boolean;
|
|
34
41
|
export declare const startPerfSpan: (name: string) => (() => void);
|
|
35
42
|
export declare const measurePerf: <T>(name: string, fn: () => Promise<T>) => Promise<T>;
|
|
43
|
+
export declare const measurePerfAggregate: <T>(name: string, fn: () => Promise<T>) => Promise<T>;
|
|
44
|
+
export declare const measurePerfAggregateSync: <T>(name: string, fn: () => T) => T;
|
|
45
|
+
export declare const incrementPerfCounter: (name: string, value?: number, metadata?: PerfMetricMetadata) => void;
|
|
36
46
|
export declare const getPerfMetricsResults: () => AllurePerformanceResult[];
|
|
37
47
|
export declare const writePerfMetrics: (output: string, fileName?: string) => Promise<boolean>;
|
|
38
48
|
export declare const resetPerfMetrics: () => void;
|
|
49
|
+
export {};
|
package/dist/utils/perf.js
CHANGED
|
@@ -7,6 +7,8 @@ export const perfMetricsFileName = (reportUuid) => `${reportUuid}-perf.json`;
|
|
|
7
7
|
const MARK_PREFIX = "allure:perf:";
|
|
8
8
|
const ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
9
9
|
const SPANS = [];
|
|
10
|
+
const AGGREGATES = new Map();
|
|
11
|
+
const COUNTERS = new Map();
|
|
10
12
|
const MARKS = new Set();
|
|
11
13
|
const MEASURES = new Set();
|
|
12
14
|
let sequence = 0;
|
|
@@ -19,28 +21,45 @@ export const PERF_METRIC_NAMES = {
|
|
|
19
21
|
restoreStateStoreRestore: "restoreState.storeRestore",
|
|
20
22
|
generateTotal: "generate.total",
|
|
21
23
|
generateReadResults: "generate.readResults",
|
|
24
|
+
generateReadResultsXcresultCheck: "generate.readResults.xcresultCheck",
|
|
25
|
+
generateReadResultsReaddir: "generate.readResults.readdir",
|
|
26
|
+
generateReadResultsFiles: "generate.readResults.files",
|
|
27
|
+
generateReadResultsReaderRead: "generate.readResults.reader.read",
|
|
22
28
|
generatePluginsDone: "generate.plugins.done",
|
|
29
|
+
storeVisitTestResultConvert: "store.visitTestResult.convert",
|
|
30
|
+
storeVisitTestResultDefaultLabels: "store.visitTestResult.defaultLabels",
|
|
31
|
+
storeVisitTestResultEnvironment: "store.visitTestResult.environment",
|
|
32
|
+
storeVisitTestResultRetry: "store.visitTestResult.retry",
|
|
33
|
+
storeVisitTestResultHistory: "store.visitTestResult.history",
|
|
34
|
+
storeVisitTestResultResolution: "store.visitTestResult.resolution",
|
|
35
|
+
storeVisitTestResultIndexes: "store.visitTestResult.indexes",
|
|
36
|
+
storeVisitAttachmentFileMetadata: "store.visitAttachmentFile.metadata",
|
|
23
37
|
publishUploadTotal: "publish.upload.total",
|
|
24
38
|
summaryGenerate: "summary.generate",
|
|
25
39
|
};
|
|
26
40
|
export const PERF_METRIC_PREFIXES = {
|
|
41
|
+
generatePlugin: "generate.plugin.",
|
|
27
42
|
generatePluginDone: "generate.plugin.done.",
|
|
28
43
|
publishUploadPlugin: "publish.upload.plugin.",
|
|
29
44
|
};
|
|
30
|
-
const
|
|
31
|
-
|
|
45
|
+
const getCoveredTiming = () => {
|
|
46
|
+
const timings = [
|
|
47
|
+
...SPANS.map(({ startTimeMs, durationMs }) => ({
|
|
48
|
+
startTimeMs,
|
|
49
|
+
stopTimeMs: startTimeMs + durationMs,
|
|
50
|
+
})),
|
|
51
|
+
...AGGREGATES.values(),
|
|
52
|
+
...COUNTERS.values(),
|
|
53
|
+
];
|
|
54
|
+
if (timings.length === 0) {
|
|
32
55
|
return undefined;
|
|
33
56
|
}
|
|
34
|
-
const startTimeMs = Math.min(...
|
|
35
|
-
const
|
|
36
|
-
const durationMs = round(endTimeMs - startTimeMs);
|
|
57
|
+
const startTimeMs = Math.min(...timings.map(({ startTimeMs }) => startTimeMs));
|
|
58
|
+
const stopTimeMs = Math.max(...timings.map(({ stopTimeMs }) => stopTimeMs));
|
|
37
59
|
return {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
minMs: durationMs,
|
|
42
|
-
maxMs: durationMs,
|
|
43
|
-
avgMs: durationMs,
|
|
60
|
+
startTimeMs,
|
|
61
|
+
stopTimeMs,
|
|
62
|
+
durationMs: round(stopTimeMs - startTimeMs),
|
|
44
63
|
};
|
|
45
64
|
};
|
|
46
65
|
export const isPerfMetricsEnabled = () => ENABLED_VALUES.has((process.env.ALLURE_PERF_METRICS ?? "").toLowerCase());
|
|
@@ -91,8 +110,80 @@ export const measurePerf = async (name, fn) => {
|
|
|
91
110
|
end();
|
|
92
111
|
}
|
|
93
112
|
};
|
|
113
|
+
const recordPerfTiming = (name, durationMs, startTimeMs) => {
|
|
114
|
+
if (!isPerfMetricsEnabled()) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const current = AGGREGATES.get(name);
|
|
118
|
+
if (!current) {
|
|
119
|
+
AGGREGATES.set(name, {
|
|
120
|
+
name,
|
|
121
|
+
count: 1,
|
|
122
|
+
totalMs: durationMs,
|
|
123
|
+
startTimeMs,
|
|
124
|
+
stopTimeMs: startTimeMs + durationMs,
|
|
125
|
+
});
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
current.count += 1;
|
|
129
|
+
current.totalMs += durationMs;
|
|
130
|
+
current.startTimeMs = Math.min(current.startTimeMs, startTimeMs);
|
|
131
|
+
current.stopTimeMs = Math.max(current.stopTimeMs, startTimeMs + durationMs);
|
|
132
|
+
};
|
|
133
|
+
export const measurePerfAggregate = async (name, fn) => {
|
|
134
|
+
if (!isPerfMetricsEnabled()) {
|
|
135
|
+
return fn();
|
|
136
|
+
}
|
|
137
|
+
const startTimeMs = performance.now();
|
|
138
|
+
try {
|
|
139
|
+
return await fn();
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
recordPerfTiming(name, performance.now() - startTimeMs, startTimeMs);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
export const measurePerfAggregateSync = (name, fn) => {
|
|
146
|
+
if (!isPerfMetricsEnabled()) {
|
|
147
|
+
return fn();
|
|
148
|
+
}
|
|
149
|
+
const startTimeMs = performance.now();
|
|
150
|
+
try {
|
|
151
|
+
return fn();
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
recordPerfTiming(name, performance.now() - startTimeMs, startTimeMs);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
export const incrementPerfCounter = (name, value = 1, metadata) => {
|
|
158
|
+
if (!isPerfMetricsEnabled()) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const timeMs = performance.now();
|
|
162
|
+
const current = COUNTERS.get(name);
|
|
163
|
+
if (!current) {
|
|
164
|
+
COUNTERS.set(name, {
|
|
165
|
+
name,
|
|
166
|
+
value,
|
|
167
|
+
startTimeMs: timeMs,
|
|
168
|
+
stopTimeMs: timeMs,
|
|
169
|
+
...(metadata ? { metadata } : {}),
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
current.value += value;
|
|
174
|
+
current.stopTimeMs = timeMs;
|
|
175
|
+
current.metadata ?? (current.metadata = metadata);
|
|
176
|
+
};
|
|
177
|
+
const metricResult = (key, value, startTimeMs, stopTimeMs, metadata) => ({
|
|
178
|
+
id: randomUUID(),
|
|
179
|
+
key,
|
|
180
|
+
value: round(value),
|
|
181
|
+
start: round(performance.timeOrigin + startTimeMs),
|
|
182
|
+
stop: round(performance.timeOrigin + stopTimeMs),
|
|
183
|
+
...metadata,
|
|
184
|
+
});
|
|
94
185
|
export const getPerfMetricsResults = () => {
|
|
95
|
-
const
|
|
186
|
+
const coveredTiming = getCoveredTiming();
|
|
96
187
|
const results = SPANS.map((span) => ({
|
|
97
188
|
id: randomUUID(),
|
|
98
189
|
key: span.name,
|
|
@@ -100,15 +191,21 @@ export const getPerfMetricsResults = () => {
|
|
|
100
191
|
start: round(performance.timeOrigin + span.startTimeMs),
|
|
101
192
|
stop: round(performance.timeOrigin + span.startTimeMs + span.durationMs),
|
|
102
193
|
}));
|
|
103
|
-
if (
|
|
194
|
+
if (coveredTiming) {
|
|
104
195
|
results.unshift({
|
|
105
196
|
id: randomUUID(),
|
|
106
197
|
key: PERF_METRIC_NAMES.allureTotal,
|
|
107
|
-
value:
|
|
108
|
-
start: round(performance.timeOrigin +
|
|
109
|
-
stop: round(performance.timeOrigin +
|
|
198
|
+
value: coveredTiming.durationMs,
|
|
199
|
+
start: round(performance.timeOrigin + coveredTiming.startTimeMs),
|
|
200
|
+
stop: round(performance.timeOrigin + coveredTiming.stopTimeMs),
|
|
110
201
|
});
|
|
111
202
|
}
|
|
203
|
+
for (const aggregate of AGGREGATES.values()) {
|
|
204
|
+
results.push(metricResult(`${aggregate.name}.totalMs`, aggregate.totalMs, aggregate.startTimeMs, aggregate.stopTimeMs), metricResult(`${aggregate.name}.avgMs`, aggregate.totalMs / aggregate.count, aggregate.startTimeMs, aggregate.stopTimeMs));
|
|
205
|
+
}
|
|
206
|
+
for (const counter of COUNTERS.values()) {
|
|
207
|
+
results.push(metricResult(counter.name, counter.value, counter.startTimeMs, counter.stopTimeMs, counter.metadata));
|
|
208
|
+
}
|
|
112
209
|
return results;
|
|
113
210
|
};
|
|
114
211
|
export const writePerfMetrics = async (output, fileName = PERF_METRICS_FILE) => {
|
|
@@ -123,6 +220,8 @@ export const writePerfMetrics = async (output, fileName = PERF_METRICS_FILE) =>
|
|
|
123
220
|
};
|
|
124
221
|
export const resetPerfMetrics = () => {
|
|
125
222
|
SPANS.length = 0;
|
|
223
|
+
AGGREGATES.clear();
|
|
224
|
+
COUNTERS.clear();
|
|
126
225
|
sequence = 0;
|
|
127
226
|
for (const mark of MARKS) {
|
|
128
227
|
performance.clearMarks(mark);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allurereport/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.0",
|
|
4
4
|
"description": "Collection of generic Allure utilities used across the entire project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"allure"
|
|
@@ -25,26 +25,26 @@
|
|
|
25
25
|
"lint:fix": "oxlint --import-plugin --fix src test features stories"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@allurereport/ci": "3.
|
|
29
|
-
"@allurereport/cli-commons": "3.
|
|
30
|
-
"@allurereport/core-api": "3.
|
|
31
|
-
"@allurereport/plugin-agent": "3.
|
|
32
|
-
"@allurereport/plugin-allure2": "3.
|
|
33
|
-
"@allurereport/plugin-api": "3.
|
|
34
|
-
"@allurereport/plugin-awesome": "3.
|
|
35
|
-
"@allurereport/plugin-classic": "3.
|
|
36
|
-
"@allurereport/plugin-csv": "3.
|
|
37
|
-
"@allurereport/plugin-dashboard": "3.
|
|
38
|
-
"@allurereport/plugin-jira": "3.
|
|
39
|
-
"@allurereport/plugin-log": "3.
|
|
40
|
-
"@allurereport/plugin-progress": "3.
|
|
41
|
-
"@allurereport/plugin-slack": "3.
|
|
42
|
-
"@allurereport/plugin-testops": "3.
|
|
43
|
-
"@allurereport/plugin-testplan": "3.
|
|
44
|
-
"@allurereport/reader": "3.
|
|
45
|
-
"@allurereport/reader-api": "3.
|
|
46
|
-
"@allurereport/service": "3.
|
|
47
|
-
"@allurereport/summary": "3.
|
|
28
|
+
"@allurereport/ci": "3.20.0",
|
|
29
|
+
"@allurereport/cli-commons": "3.20.0",
|
|
30
|
+
"@allurereport/core-api": "3.20.0",
|
|
31
|
+
"@allurereport/plugin-agent": "3.20.0",
|
|
32
|
+
"@allurereport/plugin-allure2": "3.20.0",
|
|
33
|
+
"@allurereport/plugin-api": "3.20.0",
|
|
34
|
+
"@allurereport/plugin-awesome": "3.20.0",
|
|
35
|
+
"@allurereport/plugin-classic": "3.20.0",
|
|
36
|
+
"@allurereport/plugin-csv": "3.20.0",
|
|
37
|
+
"@allurereport/plugin-dashboard": "3.20.0",
|
|
38
|
+
"@allurereport/plugin-jira": "3.20.0",
|
|
39
|
+
"@allurereport/plugin-log": "3.20.0",
|
|
40
|
+
"@allurereport/plugin-progress": "3.20.0",
|
|
41
|
+
"@allurereport/plugin-slack": "3.20.0",
|
|
42
|
+
"@allurereport/plugin-testops": "3.20.0",
|
|
43
|
+
"@allurereport/plugin-testplan": "3.20.0",
|
|
44
|
+
"@allurereport/reader": "3.20.0",
|
|
45
|
+
"@allurereport/reader-api": "3.20.0",
|
|
46
|
+
"@allurereport/service": "3.20.0",
|
|
47
|
+
"@allurereport/summary": "3.20.0",
|
|
48
48
|
"glob": "^13.0.6",
|
|
49
49
|
"handlebars": "^4.7.9",
|
|
50
50
|
"jiti": "^2.5.1",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@types/node": "^20",
|
|
59
59
|
"@types/zip-stream": "^7.0.0",
|
|
60
60
|
"@vitest/runner": "^2",
|
|
61
|
-
"@vitest/snapshot": "^
|
|
61
|
+
"@vitest/snapshot": "^5.0.1",
|
|
62
62
|
"allure-js-commons": "^3",
|
|
63
63
|
"allure-vitest": "^3",
|
|
64
64
|
"rimraf": "^6",
|