@allurereport/core 3.16.0 → 3.16.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 CHANGED
@@ -1,4 +1,4 @@
1
- import type { CategoriesConfig, KnownTestFailure, ResolvedAllureServiceConfig } from "@allurereport/core-api";
1
+ import type { CategoriesConfig, ResolvedAllureServiceConfig } from "@allurereport/core-api";
2
2
  import type { Plugin, ReportFiles, Config } from "@allurereport/plugin-api";
3
3
  import type { ResultsReader } from "@allurereport/reader-api";
4
4
  export interface PluginInstance {
@@ -8,14 +8,12 @@ export interface PluginInstance {
8
8
  options: Record<string, any>;
9
9
  }
10
10
  type FullConfigRequiredFromConfig = Required<Pick<Config, "name" | "output" | "open">>;
11
- export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "allureService" | "knownIssuesPath" | "plugins" | "port" | "resultsDir">, FullConfigRequiredFromConfig {
11
+ export interface FullConfig extends Omit<Config, "name" | "output" | "open" | "allureService" | "plugins" | "port" | "resultsDir">, FullConfigRequiredFromConfig {
12
12
  port: Config["port"] | undefined;
13
13
  allowedEnvironments?: Config["allowedEnvironments"];
14
14
  reportFiles: ReportFiles;
15
15
  readers?: ResultsReader[];
16
16
  plugins?: PluginInstance[];
17
- known?: KnownTestFailure[];
18
- knownIssuesPath?: Config["knownIssuesPath"];
19
17
  realTime?: any;
20
18
  qualityGate?: Config["qualityGate"];
21
19
  allureService?: ResolvedAllureServiceConfig;
package/dist/config.d.ts CHANGED
@@ -9,7 +9,7 @@ export interface ConfigOverride {
9
9
  hideLabels?: Config["hideLabels"];
10
10
  historyPath?: Config["historyPath"];
11
11
  historyLimit?: Config["historyLimit"];
12
- knownIssuesPath?: Config["knownIssuesPath"];
12
+ resolutions?: Pick<NonNullable<Config["resolutions"]>, "knownIssuesPath">;
13
13
  plugins?: Config["plugins"];
14
14
  }
15
15
  export declare const parseIntegerConfigValue: (value: unknown, defaultValue: number, minValue: number) => number;
package/dist/config.js CHANGED
@@ -4,8 +4,8 @@ import * as process from "node:process";
4
4
  import { validateEnvironmentName } from "@allurereport/core-api";
5
5
  import { createJiti } from "jiti";
6
6
  import { parse } from "yaml";
7
- import { DEFAULT_KNOWN_ISSUES_PATH, hasKnownIssueRules, readKnownIssues, resolveExactIssuesFilePath } from "./known.js";
8
7
  import { FileSystemReportFiles } from "./plugin.js";
8
+ import { DEFAULT_KNOWN_ISSUES_PATH, resolveExactIssuesFilePath, validateResolutionsConfig } from "./resolutions.js";
9
9
  import { environmentIdentityById, environmentIdentityByName, validateAllowedEnvironmentIds, normalizeEnvironmentDescriptorMap, validateAllowedEnvironmentId, } from "./utils/environment.js";
10
10
  import { importWrapper } from "./utils/module.js";
11
11
  import { normalizeImportPath } from "./utils/path.js";
@@ -98,8 +98,7 @@ export const validateConfig = (config) => {
98
98
  "hideLabels",
99
99
  "historyPath",
100
100
  "historyLimit",
101
- "knownIssuesPath",
102
- "knownIssues",
101
+ "resolutions",
103
102
  "plugins",
104
103
  "defaultLabels",
105
104
  "variables",
@@ -195,6 +194,7 @@ export const resolveConfig = async (config, override = {}) => {
195
194
  if (!validationResult.valid) {
196
195
  throw new Error(`The provided Allure config contains unsupported fields: ${validationResult.fields.join(", ")}`);
197
196
  }
197
+ validateResolutionsConfig(config.resolutions);
198
198
  const { environments, environment, allowedEnvironments } = resolveConfigEnvironments(config);
199
199
  const name = override.name ?? config.name ?? "Allure Report";
200
200
  const open = override.open ?? config.open ?? false;
@@ -203,11 +203,13 @@ export const resolveConfig = async (config, override = {}) => {
203
203
  const historyPath = override.historyPath ?? config.historyPath;
204
204
  const historyLimit = override.historyLimit ?? config.historyLimit;
205
205
  const appendHistory = config.appendHistory ?? true;
206
- const configuredKnownIssuesPath = override.knownIssuesPath ?? config.knownIssuesPath;
207
- const knownIssuesPathInput = configuredKnownIssuesPath ?? (hasKnownIssueRules(config.knownIssues) ? DEFAULT_KNOWN_ISSUES_PATH : undefined);
206
+ const configuredKnownIssuesPath = override.resolutions?.knownIssuesPath ?? config.resolutions?.knownIssuesPath;
207
+ const knownIssuesPathInput = configuredKnownIssuesPath ?? (config.resolutions ? DEFAULT_KNOWN_ISSUES_PATH : undefined);
208
208
  const knownIssuesPath = await resolveExactIssuesFilePath(knownIssuesPathInput, "known issues");
209
+ const resolutions = config.resolutions || override.resolutions?.knownIssuesPath !== undefined
210
+ ? { ...(config.resolutions ?? { rules: [] }), knownIssuesPath }
211
+ : undefined;
209
212
  const output = resolve(override.output ?? config.output ?? "./allure-report");
210
- const known = knownIssuesPath ? await readKnownIssues(knownIssuesPath) : undefined;
211
213
  const variables = config.variables ?? {};
212
214
  const resultsDir = normalizeResultsDir(config.resultsDir);
213
215
  let pluginInstances = [];
@@ -237,9 +239,7 @@ export const resolveConfig = async (config, override = {}) => {
237
239
  open,
238
240
  port,
239
241
  hideLabels,
240
- knownIssuesPath,
241
- known,
242
- knownIssues: config.knownIssues,
242
+ resolutions,
243
243
  environment,
244
244
  allowedEnvironments,
245
245
  variables,
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export * from "./utils/flaky.js";
7
7
  export * from "./utils/environment.js";
8
8
  export * from "./utils/resultsDir.js";
9
9
  export * from "./history.js";
10
- export * from "./known.js";
10
+ 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";
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ export * from "./utils/flaky.js";
6
6
  export * from "./utils/environment.js";
7
7
  export * from "./utils/resultsDir.js";
8
8
  export * from "./history.js";
9
- export * from "./known.js";
9
+ 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";
@@ -1,4 +1,4 @@
1
- import { type KnownTestFailure, type TestError, type TestResult } from "@allurereport/core-api";
1
+ import { type TestError, type TestResult } from "@allurereport/core-api";
2
2
  import type { QualityGateConfig, QualityGateValidationResult } from "@allurereport/plugin-api";
3
3
  export declare const stringifyQualityGateResults: (results: QualityGateValidationResult[]) => string;
4
4
  export declare const convertQualityGateResultsToTestErrors: (results: QualityGateValidationResult[]) => TestError[];
@@ -16,7 +16,6 @@ export declare class QualityGate {
16
16
  validate(payload: {
17
17
  state?: QualityGateState;
18
18
  trs: TestResult[];
19
- knownIssues: KnownTestFailure[];
20
19
  environment?: string;
21
20
  }): Promise<{
22
21
  fastFailed: boolean;
@@ -5,6 +5,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
5
5
  };
6
6
  var _QualityGateState_state, _QualityGateState_processedTestResultIds;
7
7
  import { gray, red } from "yoctocolors";
8
+ import { isIgnoredFailure } from "../resolutions.js";
8
9
  import { qualityGateDefaultRules } from "./rules.js";
9
10
  export const stringifyQualityGateResults = (results) => {
10
11
  if (results.length === 0) {
@@ -58,10 +59,10 @@ export class QualityGate {
58
59
  this.config = config;
59
60
  }
60
61
  async validate(payload) {
61
- const { state, trs, knownIssues, environment } = payload;
62
+ const { state, trs, environment } = payload;
62
63
  const trsToValidateById = new Map();
63
64
  for (const tr of trs) {
64
- if (tr.isRetry || state?.isTestResultProcessed(tr.id)) {
65
+ if (tr.isRetry || isIgnoredFailure(tr) || state?.isTestResultProcessed(tr.id)) {
65
66
  continue;
66
67
  }
67
68
  trsToValidateById.set(tr.id, tr);
@@ -97,7 +98,6 @@ export class QualityGate {
97
98
  setResult: (value, testResults) => state?.setResult?.(ruleId, value, testResults),
98
99
  },
99
100
  expected,
100
- knownIssues,
101
101
  environment,
102
102
  });
103
103
  if (result.success) {
@@ -3,7 +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
  totalCount: number;
6
- unknownCount: number;
7
6
  passedCount: number;
8
7
  }>;
9
8
  export declare const maxDurationRule: QualityGateRule<number>;
@@ -11,6 +10,5 @@ export declare const allTestsContainEnvRule: QualityGateRule<string, number>;
11
10
  export declare const environmentsTestedRule: QualityGateRule<string[]>;
12
11
  export declare const qualityGateDefaultRules: (QualityGateRule<number> | QualityGateRule<number, {
13
12
  totalCount: number;
14
- unknownCount: number;
15
13
  passedCount: number;
16
14
  }> | QualityGateRule<string, number> | QualityGateRule<string[]>)[];
@@ -5,8 +5,7 @@ export const maxFailuresRule = {
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, expected, state }) => {
7
7
  const previous = state.getResult() ?? 0;
8
- const unknown = trs.filter((tr) => !tr.known);
9
- const failedTrs = unknown.filter(filterUnsuccessful);
8
+ const failedTrs = trs.filter(filterUnsuccessful);
10
9
  const testResults = failedTrs.map((tr) => tr.id);
11
10
  const actual = previous + failedTrs.length;
12
11
  state.setResult(actual, testResults);
@@ -34,16 +33,14 @@ export const successRateRule = {
34
33
  rule: "successRate",
35
34
  message: ({ actual, expected }) => `Success rate ${bold(String(actual))} is less, than expected ${bold(String(expected))}`,
36
35
  validate: async ({ trs, expected, state }) => {
37
- const previous = state.getResult() ?? { totalCount: 0, unknownCount: 0, passedCount: 0 };
38
- const unknown = trs.filter((tr) => !tr.known);
39
- const passedTrs = unknown.filter(filterSuccessful);
40
- const notPassedTrs = unknown.filter((tr) => !filterSuccessful(tr));
36
+ const previous = state.getResult() ?? { totalCount: 0, passedCount: 0 };
37
+ const passedTrs = trs.filter(filterSuccessful);
38
+ const notPassedTrs = trs.filter((tr) => !filterSuccessful(tr));
41
39
  const totalCount = previous.totalCount + trs.length;
42
- const unknownCount = previous.unknownCount + unknown.length;
43
40
  const passedCount = previous.passedCount + passedTrs.length;
44
41
  const testResults = notPassedTrs.map((tr) => tr.id);
45
- const rate = totalCount === 0 ? 0 : unknownCount === 0 ? 1 : passedCount / unknownCount;
46
- state.setResult({ totalCount, unknownCount, passedCount }, testResults);
42
+ const rate = totalCount === 0 ? 0 : passedCount / totalCount;
43
+ state.setResult({ totalCount, passedCount }, testResults);
47
44
  return {
48
45
  success: rate >= expected,
49
46
  actual: rate,
package/dist/report.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { KnownTestFailure, TestResult } from "@allurereport/core-api";
1
+ import type { TestResult } from "@allurereport/core-api";
2
2
  import { type ResultFile } from "@allurereport/plugin-api";
3
3
  import type { FullConfig } from "./api.js";
4
4
  import { type QualityGateState } from "./qualityGate/index.js";
@@ -19,7 +19,6 @@ export declare class AllureReport {
19
19
  readResult: (data: ResultFile) => Promise<void>;
20
20
  validate: (params: {
21
21
  trs: TestResult[];
22
- knownIssues: KnownTestFailure[];
23
22
  state?: QualityGateState;
24
23
  environment?: string;
25
24
  }) => Promise<{
package/dist/report.js CHANGED
@@ -31,9 +31,9 @@ import ZipReadStream from "node-stream-zip";
31
31
  import pLimit from "p-limit";
32
32
  import ZipWriteStream from "zip-stream";
33
33
  import { AllureLocalHistory, createHistory } from "./history.js";
34
- import { writeKnownIssues } from "./known.js";
35
34
  import { DefaultPluginState, PluginFiles } from "./plugin.js";
36
35
  import { QualityGate } from "./qualityGate/index.js";
36
+ import { writeKnownIssues } from "./resolutions.js";
37
37
  import { DefaultAllureStore } from "./store/store.js";
38
38
  import { environmentIdentityById, environmentIdentityByName } from "./utils/environment.js";
39
39
  import { measurePerf, PERF_METRIC_NAMES, PERF_METRIC_PREFIXES, startPerfSpan, writePerfMetrics } from "./utils/perf.js";
@@ -283,12 +283,7 @@ export class AllureReport {
283
283
  }
284
284
  };
285
285
  this.validate = async (params) => {
286
- const { trs, knownIssues, state, environment } = params;
287
- const currentKnownIssues = await __classPrivateFieldGet(this, _AllureReport_store, "f").allKnownIssues();
288
- const effectiveKnownIssues = new Map();
289
- [...knownIssues, ...currentKnownIssues].forEach((issue) => {
290
- effectiveKnownIssues.set(issue.historyId, issue);
291
- });
286
+ const { trs, state, environment } = params;
292
287
  const qualityGateEnvironment = environment === undefined
293
288
  ? undefined
294
289
  : (environmentIdentityById(__classPrivateFieldGet(this, _AllureReport_environments, "f"), environment)?.name ??
@@ -296,7 +291,6 @@ export class AllureReport {
296
291
  environment);
297
292
  return __classPrivateFieldGet(this, _AllureReport_qualityGate, "f").validate({
298
293
  trs: trs.filter(Boolean),
299
- knownIssues: [...effectiveKnownIssues.values()],
300
294
  state,
301
295
  environment: qualityGateEnvironment,
302
296
  });
@@ -393,7 +387,7 @@ export class AllureReport {
393
387
  const addJsonDumpEntry = async (entryName, value) => {
394
388
  await addRequiredDumpEntry(Buffer.from(JSON.stringify(value)), entryName);
395
389
  };
396
- const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, knownIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
390
+ const dumpJsonEntries = ({ testResults, testCases, fixtures, attachments, environments, reportVariables, checkResults = {}, globalAttachmentIds = [], globalErrors = [], indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], }) => [
397
391
  [AllureStoreDumpFiles.TestResults, testResults],
398
392
  [AllureStoreDumpFiles.TestCases, testCases],
399
393
  [AllureStoreDumpFiles.Fixtures, fixtures],
@@ -401,12 +395,13 @@ export class AllureReport {
401
395
  [AllureStoreDumpFiles.CheckResults, checkResults],
402
396
  [AllureStoreDumpFiles.Environments, environments],
403
397
  [AllureStoreDumpFiles.ReportVariables, reportVariables],
404
- [AllureStoreDumpFiles.KnownIssues, knownIssues],
398
+ [AllureStoreDumpFiles.ResolutionIssues, resolutionIssues],
405
399
  [AllureStoreDumpFiles.GlobalAttachments, globalAttachmentIds],
406
400
  [AllureStoreDumpFiles.GlobalErrors, globalErrors],
407
401
  [AllureStoreDumpFiles.IndexAttachmentsByTestResults, indexAttachmentByTestResult],
408
402
  [AllureStoreDumpFiles.IndexTestResultsByHistoryId, indexTestResultByHistoryId],
409
403
  [AllureStoreDumpFiles.IndexTestResultsByTestCase, indexTestResultByTestCase],
404
+ [AllureStoreDumpFiles.IndexTestResultsByResolutionIssue, indexTestResultByResolutionIssue],
410
405
  [AllureStoreDumpFiles.IndexAttachmentsByFixture, indexAttachmentByFixture],
411
406
  [AllureStoreDumpFiles.IndexFixturesByTestResult, indexFixturesByTestResult],
412
407
  [AllureStoreDumpFiles.QualityGateResults, qualityGateResults],
@@ -533,9 +528,10 @@ export class AllureReport {
533
528
  const indexAttachmentsEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByTestResults);
534
529
  const indexTestResultsByHistoryId = await requiredEntryData(AllureStoreDumpFiles.IndexTestResultsByHistoryId);
535
530
  const indexTestResultsByTestCaseEntry = await requiredEntryData(AllureStoreDumpFiles.IndexTestResultsByTestCase);
531
+ const indexTestResultsByResolutionIssueEntry = await optionalEntryData(AllureStoreDumpFiles.IndexTestResultsByResolutionIssue);
536
532
  const indexAttachmentsByFixtureEntry = await requiredEntryData(AllureStoreDumpFiles.IndexAttachmentsByFixture);
537
533
  const indexFixturesByTestResultEntry = await requiredEntryData(AllureStoreDumpFiles.IndexFixturesByTestResult);
538
- const knownIssuesEntry = await requiredEntryData(AllureStoreDumpFiles.KnownIssues);
534
+ const resolutionIssuesEntry = await optionalEntryData(AllureStoreDumpFiles.ResolutionIssues);
539
535
  const qualityGateResultsEntry = await requiredEntryData(AllureStoreDumpFiles.QualityGateResults);
540
536
  const testResultIngestOrderEntry = await optionalEntryData(AllureStoreDumpFiles.TestResultIngestOrder);
541
537
  const attachmentsLinks = JSON.parse(attachmentsEntry.toString("utf8"));
@@ -548,12 +544,13 @@ export class AllureReport {
548
544
  case AllureStoreDumpFiles.Fixtures:
549
545
  case AllureStoreDumpFiles.Environments:
550
546
  case AllureStoreDumpFiles.ReportVariables:
551
- case AllureStoreDumpFiles.KnownIssues:
547
+ case AllureStoreDumpFiles.ResolutionIssues:
552
548
  case AllureStoreDumpFiles.GlobalAttachments:
553
549
  case AllureStoreDumpFiles.GlobalErrors:
554
550
  case AllureStoreDumpFiles.IndexAttachmentsByTestResults:
555
551
  case AllureStoreDumpFiles.IndexTestResultsByHistoryId:
556
552
  case AllureStoreDumpFiles.IndexTestResultsByTestCase:
553
+ case AllureStoreDumpFiles.IndexTestResultsByResolutionIssue:
557
554
  case AllureStoreDumpFiles.IndexAttachmentsByFixture:
558
555
  case AllureStoreDumpFiles.IndexFixturesByTestResult:
559
556
  case AllureStoreDumpFiles.QualityGateResults:
@@ -581,9 +578,12 @@ export class AllureReport {
581
578
  indexAttachmentByTestResult: JSON.parse(indexAttachmentsEntry.toString("utf8")),
582
579
  indexTestResultByHistoryId: JSON.parse(indexTestResultsByHistoryId.toString("utf8")),
583
580
  indexTestResultByTestCase: JSON.parse(indexTestResultsByTestCaseEntry.toString("utf8")),
581
+ indexTestResultByResolutionIssue: indexTestResultsByResolutionIssueEntry
582
+ ? JSON.parse(indexTestResultsByResolutionIssueEntry.toString("utf8"))
583
+ : {},
584
584
  indexAttachmentByFixture: JSON.parse(indexAttachmentsByFixtureEntry.toString("utf8")),
585
585
  indexFixturesByTestResult: JSON.parse(indexFixturesByTestResultEntry.toString("utf8")),
586
- knownIssues: JSON.parse(knownIssuesEntry.toString("utf8")),
586
+ resolutionIssues: resolutionIssuesEntry ? JSON.parse(resolutionIssuesEntry.toString("utf8")) : {},
587
587
  qualityGateResults: JSON.parse(qualityGateResultsEntry.toString("utf8")),
588
588
  testResultIdsIngestOrder: testResultIngestOrderEntry
589
589
  ? JSON.parse(testResultIngestOrderEntry.toString("utf8"))
@@ -893,7 +893,7 @@ export class AllureReport {
893
893
  }
894
894
  }
895
895
  });
896
- const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], known, knownIssues, knownIssuesPath, reportFiles, realTime, historyPath, historyLimit, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, dump, categories, allureService, globalAttachments, } = opts;
896
+ const { name, readers = [allure1, allure2, cucumberjson, junitXml, attachments], plugins = [], resolutions, reportFiles, realTime, historyPath, historyLimit, appendHistory, defaultLabels = {}, variables = {}, environment, allowedEnvironments, environments, output, hideLabels, qualityGate, dump, categories, allureService, globalAttachments, } = opts;
897
897
  const allureServiceAccessToken = allureService?.accessToken;
898
898
  if (allureServiceAccessToken) {
899
899
  const allureServiceClientConfig = {
@@ -918,7 +918,7 @@ export class AllureReport {
918
918
  __classPrivateFieldSet(this, _AllureReport_environments, environments ?? {}, "f");
919
919
  __classPrivateFieldSet(this, _AllureReport_globalAttachments, globalAttachments, "f");
920
920
  __classPrivateFieldSet(this, _AllureReport_appendHistory, appendHistory ?? true, "f");
921
- __classPrivateFieldSet(this, _AllureReport_knownIssuesPath, knownIssuesPath, "f");
921
+ __classPrivateFieldSet(this, _AllureReport_knownIssuesPath, resolutions?.knownIssuesPath, "f");
922
922
  if (qualityGate) {
923
923
  __classPrivateFieldSet(this, _AllureReport_qualityGate, new QualityGate(qualityGate), "f");
924
924
  }
@@ -942,8 +942,7 @@ export class AllureReport {
942
942
  reportVariables: variables,
943
943
  environmentsConfig: environments,
944
944
  history: __classPrivateFieldGet(this, _AllureReport_history, "f"),
945
- known,
946
- knownIssuesConfig: knownIssues,
945
+ resolutionsConfig: resolutions,
947
946
  defaultLabels,
948
947
  environment,
949
948
  allowedEnvironments,
@@ -0,0 +1,8 @@
1
+ import type { ResolutionRule, ResolutionsConfig, TestResult } from "@allurereport/core-api";
2
+ import type { AllureStore } from "@allurereport/plugin-api";
3
+ export declare const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
4
+ export declare const validateResolutionsConfig: (config?: ResolutionsConfig) => void;
5
+ export declare const getResolutionByRules: (testResult: TestResult, config?: ResolutionsConfig) => ResolutionRule | undefined;
6
+ export declare const isIgnoredFailure: (testResult: TestResult) => boolean;
7
+ export declare const resolveExactIssuesFilePath: (pathOrDir: string | undefined, label: string) => Promise<string | undefined>;
8
+ export declare const writeKnownIssues: (store: AllureStore, knownIssuesPath?: string) => Promise<void>;
@@ -0,0 +1,181 @@
1
+ import { mkdir, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, extname, resolve } from "node:path";
3
+ import { isFileNotFoundError } from "./utils/misc.js";
4
+ export const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
5
+ const nonEmpty = (value) => typeof value === "string" && value.trim().length > 0;
6
+ const validateStringList = (value, path, errors) => {
7
+ if (value === undefined) {
8
+ return;
9
+ }
10
+ if (!Array.isArray(value) || value.length === 0 || value.some((entry) => !nonEmpty(entry))) {
11
+ errors.push(`${path} must be a non-empty array of non-empty strings`);
12
+ }
13
+ };
14
+ const placeholderCount = (value) => (typeof value === "string" ? (value.match(/%s/g)?.length ?? 0) : 0);
15
+ const validateLinkTemplate = (type, template, errors) => {
16
+ if (!nonEmpty(type)) {
17
+ errors.push("resolutions.links keys must be non-empty strings");
18
+ }
19
+ if (!nonEmpty(template?.urlTemplate) || placeholderCount(template.urlTemplate) !== 1) {
20
+ errors.push(`resolutions.links.${type}.urlTemplate must contain exactly one %s`);
21
+ }
22
+ if (template?.nameTemplate === undefined) {
23
+ return;
24
+ }
25
+ if (!nonEmpty(template.nameTemplate) || placeholderCount(template.nameTemplate) !== 1) {
26
+ errors.push(`resolutions.links.${type}.nameTemplate must contain exactly one %s`);
27
+ }
28
+ };
29
+ const validateMessageRegexp = (value, path, errors) => {
30
+ if (value === undefined) {
31
+ return;
32
+ }
33
+ try {
34
+ new RegExp(value);
35
+ }
36
+ catch {
37
+ errors.push(`${path}.messageRegexp must be a valid regular expression`);
38
+ }
39
+ };
40
+ const validateMatcher = (matcher, path, errors) => {
41
+ validateStringList(matcher.testCaseId, `${path}.testCaseId`, errors);
42
+ validateStringList(matcher.retryHash, `${path}.retryHash`, errors);
43
+ validateStringList(matcher.environment, `${path}.environment`, errors);
44
+ if (![matcher.messageRegexp, matcher.testCaseId, matcher.retryHash, matcher.environment].some((value) => value !== undefined)) {
45
+ errors.push(`${path} must contain at least one matcher`);
46
+ }
47
+ validateMessageRegexp(matcher.messageRegexp, path, errors);
48
+ };
49
+ const validateIssueUrl = (rule, template, errors) => {
50
+ if (!nonEmpty(template.urlTemplate) || placeholderCount(template.urlTemplate) !== 1) {
51
+ return;
52
+ }
53
+ let url;
54
+ try {
55
+ url = new URL(template.urlTemplate.replace("%s", encodeURIComponent(rule.issue.id)));
56
+ }
57
+ catch {
58
+ errors.push(`resolutions.links.${rule.issue.type}.urlTemplate must resolve to an absolute URL`);
59
+ return;
60
+ }
61
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
62
+ errors.push(`resolutions.links.${rule.issue.type}.urlTemplate must resolve to an HTTP(S) URL`);
63
+ }
64
+ };
65
+ const validateIssueRule = (rule, path, config, issueIds, errors) => {
66
+ if (!nonEmpty(rule.issue?.id) || !nonEmpty(rule.issue?.type)) {
67
+ errors.push(`${path}.issue.id and ${path}.issue.type must be non-empty strings`);
68
+ return;
69
+ }
70
+ if (issueIds.has(rule.issue.id)) {
71
+ errors.push(`${path}.issue.id must be unique`);
72
+ }
73
+ issueIds.add(rule.issue.id);
74
+ const template = config.links?.[rule.issue.type];
75
+ if (!template) {
76
+ errors.push(`${path}.issue.type must reference resolutions.links`);
77
+ return;
78
+ }
79
+ validateIssueUrl(rule, template, errors);
80
+ };
81
+ const validateIgnoredRule = (rule, path, errors) => {
82
+ if (!nonEmpty(rule.comment)) {
83
+ errors.push(`${path}.comment must be a non-empty string`);
84
+ }
85
+ };
86
+ export const validateResolutionsConfig = (config) => {
87
+ if (!config) {
88
+ return;
89
+ }
90
+ const errors = [];
91
+ if (!Array.isArray(config.rules)) {
92
+ throw new Error("The provided resolutions config is invalid: resolutions.rules must be an array");
93
+ }
94
+ for (const [type, template] of Object.entries(config.links ?? {})) {
95
+ validateLinkTemplate(type, template, errors);
96
+ }
97
+ const issueIds = new Set();
98
+ config.rules.forEach((rule, index) => {
99
+ const path = `resolutions.rules[${index}]`;
100
+ const matcher = rule;
101
+ validateMatcher(matcher, path, errors);
102
+ if (matcher.resolution === "issue") {
103
+ validateIssueRule(matcher, path, config, issueIds, errors);
104
+ return;
105
+ }
106
+ if (matcher.resolution === "muted" || matcher.resolution === "accepted") {
107
+ validateIgnoredRule(matcher, path, errors);
108
+ return;
109
+ }
110
+ errors.push(`${path}.resolution must be issue, muted, or accepted`);
111
+ });
112
+ if (errors.length > 0) {
113
+ throw new Error(`The provided resolutions config is invalid: ${errors.join("; ")}`);
114
+ }
115
+ };
116
+ const matches = (rule, testResult) => {
117
+ const { id, allureId, externalId } = testResult.testCase ?? {};
118
+ const testCaseIds = [id, allureId, externalId].filter((value) => typeof value === "string");
119
+ return ((!rule.messageRegexp || new RegExp(rule.messageRegexp).test(testResult.error?.message ?? "")) &&
120
+ (!rule.testCaseId || rule.testCaseId.some((id) => testCaseIds.includes(id))) &&
121
+ (!rule.retryHash || (!!testResult.retryHash && rule.retryHash.includes(testResult.retryHash))) &&
122
+ (!rule.environment || (!!testResult.environment && rule.environment.includes(testResult.environment))));
123
+ };
124
+ export const getResolutionByRules = (testResult, config) => {
125
+ if ((testResult.status !== "failed" && testResult.status !== "broken") || !config?.rules.length) {
126
+ return undefined;
127
+ }
128
+ for (const resolution of ["issue", "muted", "accepted"]) {
129
+ const rule = config.rules.find((candidate) => candidate.resolution === resolution && matches(candidate, testResult));
130
+ if (rule) {
131
+ return rule;
132
+ }
133
+ }
134
+ return undefined;
135
+ };
136
+ export const isIgnoredFailure = (testResult) => testResult.resolution === "muted" || testResult.resolution === "accepted";
137
+ export const resolveExactIssuesFilePath = async (pathOrDir, label) => {
138
+ if (!pathOrDir)
139
+ return undefined;
140
+ const path = resolve(pathOrDir);
141
+ if (extname(path).toLowerCase() !== ".json") {
142
+ throw new Error(`Invalid ${label} path ${JSON.stringify(pathOrDir)}: expected exact .json file path`);
143
+ }
144
+ try {
145
+ if ((await stat(path)).isDirectory()) {
146
+ throw new Error(`Invalid ${label} path ${JSON.stringify(pathOrDir)}: expected file, got directory`);
147
+ }
148
+ }
149
+ catch (e) {
150
+ if (!isFileNotFoundError(e))
151
+ throw e;
152
+ }
153
+ return path;
154
+ };
155
+ export const writeKnownIssues = async (store, knownIssuesPath) => {
156
+ const path = await resolveExactIssuesFilePath(knownIssuesPath, "known issues");
157
+ if (!path)
158
+ return;
159
+ const records = new Map();
160
+ for (const testResult of await store.allTestResults()) {
161
+ if (testResult.resolution !== "issue" || testResult.isRetry || !testResult.retryHash) {
162
+ continue;
163
+ }
164
+ const issue = await store.resolutionIssueByTestResultId(testResult.id);
165
+ if (!issue || (testResult.status !== "failed" && testResult.status !== "broken")) {
166
+ continue;
167
+ }
168
+ const record = records.get(issue.id) ?? { ...issue, testResults: {} };
169
+ record.testResults[testResult.retryHash] = {
170
+ name: testResult.name,
171
+ fullName: testResult.fullName,
172
+ environment: testResult.environment,
173
+ status: testResult.status,
174
+ error: testResult.error,
175
+ };
176
+ records.set(issue.id, record);
177
+ }
178
+ const file = { resolutionIssues: [...records.values()].sort((a, b) => a.id.localeCompare(b.id)) };
179
+ await mkdir(dirname(path), { recursive: true });
180
+ await writeFile(path, `${JSON.stringify(file)}\n`, "utf-8");
181
+ };
@@ -53,7 +53,6 @@ export const testResultRawToState = (stateData, raw, context) => {
53
53
  expectedResultHtml: raw.expectedResultHtml,
54
54
  flaky: raw.flaky ?? false,
55
55
  muted: raw.muted ?? false,
56
- known: raw.known ?? false,
57
56
  isRetry: false,
58
57
  labels,
59
58
  steps: convertSteps(stateData, raw.steps).convertedSteps,
@@ -1,11 +1,10 @@
1
- import { type AllureCheckResult, type AllureHistory, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type KnownIssuesConfig, type KnownTestFailure, type ReportVariables, type Statistic, type TestCase, type TestEnvGroup, type TestError, type TestFixtureResult, type TestResult } from "@allurereport/core-api";
1
+ import { type AllureCheckResult, type AllureHistory, type AttachmentLink, type DefaultLabelsConfig, type EnvironmentIdentity, type EnvironmentsConfig, type GlobalAttachmentLink, type HistoryDataPoint, type HistoryTestResult, type ResolutionIssue, type ResolutionsConfig, 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 PluginGlobalError, 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>;
5
5
  export declare const updateMapWithRecord: <K extends string | number | symbol, T = any>(map: Map<K, T>, record: Record<K, T>) => Map<K, T>;
6
6
  export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
7
7
  #private;
8
- readonly known: Map<string, KnownTestFailure>;
9
8
  readonly indexTestResultByTestCase: Map<string, TestResult[]>;
10
9
  readonly indexTestResultByEnvironmentId: Map<string, TestResult[]>;
11
10
  readonly indexTestResultByHistoryId: Map<string, TestResult[]>;
@@ -14,8 +13,7 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
14
13
  readonly indexFixturesByTestResult: Map<string, TestFixtureResult[]>;
15
14
  constructor(params?: {
16
15
  history?: AllureHistory;
17
- known?: KnownTestFailure[];
18
- knownIssuesConfig?: KnownIssuesConfig;
16
+ resolutionsConfig?: ResolutionsConfig;
19
17
  realtimeDispatcher?: RealtimeEventsDispatcher;
20
18
  realtimeSubscriber?: RealtimeSubscriber;
21
19
  defaultLabels?: DefaultLabelsConfig;
@@ -56,7 +54,9 @@ export declare class DefaultAllureStore implements AllureStore, ResultsVisitor {
56
54
  allHistoryDataPoints(): Promise<HistoryDataPoint[]>;
57
55
  allHistoryDataPointsByEnvironment(environment: string): Promise<HistoryDataPoint[]>;
58
56
  allHistoryDataPointsByEnvironmentId(environmentId: string): Promise<HistoryDataPoint[]>;
59
- allKnownIssues(): Promise<KnownTestFailure[]>;
57
+ allResolutionIssues(): Promise<ResolutionIssue[]>;
58
+ resolutionIssueByTestResultId(trId: string): Promise<ResolutionIssue | undefined>;
59
+ testResultsByResolutionIssueId(resolutionIssueId: string): Promise<TestResult[]>;
60
60
  allNewTestResults(filter?: TestResultFilter, history?: HistoryDataPoint[]): Promise<TestResult[]>;
61
61
  testCaseById(tcId: string): Promise<TestCase | undefined>;
62
62
  testResultById(trId: string): Promise<TestResult | undefined>;
@@ -9,11 +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 _DefaultAllureStore_instances, _DefaultAllureStore_testResults, _DefaultAllureStore_environmentDisplayNames, _DefaultAllureStore_environmentNameToId, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_knownIssuesConfig, _DefaultAllureStore_fixtures, _DefaultAllureStore_defaultLabels, _DefaultAllureStore_environment, _DefaultAllureStore_environmentsConfig, _DefaultAllureStore_reportVariables, _DefaultAllureStore_realtimeDispatcher, _DefaultAllureStore_realtimeSubscriber, _DefaultAllureStore_allowedEnvironmentIds, _DefaultAllureStore_retrySubstore, _DefaultAllureStore_testResultIdsByEnvironmentId, _DefaultAllureStore_cachedEnvironmentEntries, _DefaultAllureStore_globalAttachmentIds, _DefaultAllureStore_globalAttachmentIdsByEnv, _DefaultAllureStore_globalErrors, _DefaultAllureStore_globalErrorsByEnv, _DefaultAllureStore_globalExitCode, _DefaultAllureStore_checkResultsById, _DefaultAllureStore_qualityGateResults, _DefaultAllureStore_historyPoints, _DefaultAllureStore_environments, _DefaultAllureStore_historyIdMatches, _DefaultAllureStore_mergeEnvironmentIdentity, _DefaultAllureStore_environmentIdForLookup, _DefaultAllureStore_addEnvironments, _DefaultAllureStore_environmentIdByName, _DefaultAllureStore_setTestResultEnvironmentId, _DefaultAllureStore_assertAllowedEnvironmentId, _DefaultAllureStore_assertAllowedStoredEnvironment, _DefaultAllureStore_environmentIdByTestResult, _DefaultAllureStore_assignRetryHash, _DefaultAllureStore_setKnownIssue, _DefaultAllureStore_classifyKnownIssue, _DefaultAllureStore_rebuildRetrySubstore, _DefaultAllureStore_resolveGlobalEnvironmentIdentity, _DefaultAllureStore_globalAttachmentId, _DefaultAllureStore_indexGlobalError, _DefaultAllureStore_addGlobalError, _DefaultAllureStore_indexGlobalAttachment, _DefaultAllureStore_addGlobalAttachment, _DefaultAllureStore_restoreAttachmentContent, _DefaultAllureStore_relinkRestoredAttachmentSteps, _DefaultAllureStore_retriesByTr, _DefaultAllureStore_historyByTr;
12
+ var _DefaultAllureStore_instances, _DefaultAllureStore_testResults, _DefaultAllureStore_environmentDisplayNames, _DefaultAllureStore_environmentNameToId, _DefaultAllureStore_attachments, _DefaultAllureStore_attachmentContents, _DefaultAllureStore_testCases, _DefaultAllureStore_metadata, _DefaultAllureStore_history, _DefaultAllureStore_resolutionsConfig, _DefaultAllureStore_resolutionIssues, _DefaultAllureStore_testResultIdsByResolutionIssueId, _DefaultAllureStore_resolutionIssueIdByTestResultId, _DefaultAllureStore_fixtures, _DefaultAllureStore_defaultLabels, _DefaultAllureStore_environment, _DefaultAllureStore_environmentsConfig, _DefaultAllureStore_reportVariables, _DefaultAllureStore_realtimeDispatcher, _DefaultAllureStore_realtimeSubscriber, _DefaultAllureStore_allowedEnvironmentIds, _DefaultAllureStore_retrySubstore, _DefaultAllureStore_testResultIdsByEnvironmentId, _DefaultAllureStore_cachedEnvironmentEntries, _DefaultAllureStore_globalAttachmentIds, _DefaultAllureStore_globalAttachmentIdsByEnv, _DefaultAllureStore_globalErrors, _DefaultAllureStore_globalErrorsByEnv, _DefaultAllureStore_globalExitCode, _DefaultAllureStore_checkResultsById, _DefaultAllureStore_qualityGateResults, _DefaultAllureStore_historyPoints, _DefaultAllureStore_environments, _DefaultAllureStore_mergeEnvironmentIdentity, _DefaultAllureStore_environmentIdForLookup, _DefaultAllureStore_addEnvironments, _DefaultAllureStore_environmentIdByName, _DefaultAllureStore_setTestResultEnvironmentId, _DefaultAllureStore_assertAllowedEnvironmentId, _DefaultAllureStore_assertAllowedStoredEnvironment, _DefaultAllureStore_environmentIdByTestResult, _DefaultAllureStore_assignRetryHash, _DefaultAllureStore_classifyResolution, _DefaultAllureStore_associateResolutionIssue, _DefaultAllureStore_removeResolutionIssueAssociation, _DefaultAllureStore_rebuildRetrySubstore, _DefaultAllureStore_resolveGlobalEnvironmentIdentity, _DefaultAllureStore_globalAttachmentId, _DefaultAllureStore_indexGlobalError, _DefaultAllureStore_addGlobalError, _DefaultAllureStore_indexGlobalAttachment, _DefaultAllureStore_addGlobalAttachment, _DefaultAllureStore_restoreAttachmentContent, _DefaultAllureStore_relinkRestoredAttachmentSteps, _DefaultAllureStore_retriesByTr, _DefaultAllureStore_historyByTr;
13
13
  import { extname } from "node:path";
14
14
  import { DEFAULT_ENVIRONMENT, DEFAULT_ENVIRONMENT_IDENTITY, compareBy, createDictionary, getHistoryIdCandidates, getWorstStatus, normalizeHistoryDataPoint, ordinal, reverse, selectHistoryTestResults, validateEnvironmentId, validateEnvironmentName, } from "@allurereport/core-api";
15
15
  import { md5, } from "@allurereport/plugin-api";
16
- import { getKnownIssueByRules } from "../known.js";
16
+ import { getResolutionByRules, isIgnoredFailure } from "../resolutions.js";
17
17
  import { environmentIdentityById, normalizeEnvironmentDescriptorMap, resolveEnvironmentIdentity, resolveStoredEnvironmentIdentity, validateAllowedEnvironmentId, } from "../utils/environment.js";
18
18
  import { isFlaky } from "../utils/flaky.js";
19
19
  import { getStatusTransition } from "../utils/new.js";
@@ -64,8 +64,10 @@ export class DefaultAllureStore {
64
64
  _DefaultAllureStore_testCases.set(this, void 0);
65
65
  _DefaultAllureStore_metadata.set(this, void 0);
66
66
  _DefaultAllureStore_history.set(this, void 0);
67
- _DefaultAllureStore_knownIssuesConfig.set(this, void 0);
68
- this.known = new Map();
67
+ _DefaultAllureStore_resolutionsConfig.set(this, void 0);
68
+ _DefaultAllureStore_resolutionIssues.set(this, new Map());
69
+ _DefaultAllureStore_testResultIdsByResolutionIssueId.set(this, new Map());
70
+ _DefaultAllureStore_resolutionIssueIdByTestResultId.set(this, new Map());
69
71
  _DefaultAllureStore_fixtures.set(this, void 0);
70
72
  _DefaultAllureStore_defaultLabels.set(this, {});
71
73
  _DefaultAllureStore_environment.set(this, void 0);
@@ -92,7 +94,7 @@ export class DefaultAllureStore {
92
94
  _DefaultAllureStore_qualityGateResults.set(this, []);
93
95
  _DefaultAllureStore_historyPoints.set(this, []);
94
96
  _DefaultAllureStore_environments.set(this, []);
95
- const { history, known = [], knownIssuesConfig, realtimeDispatcher, realtimeSubscriber, defaultLabels = {}, environment, allowedEnvironments, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
97
+ const { history, resolutionsConfig, realtimeDispatcher, realtimeSubscriber, defaultLabels = {}, environment, allowedEnvironments, environmentsConfig = {}, reportVariables = {}, } = params ?? {};
96
98
  const errors = [];
97
99
  const { normalized: normalizedEnvironmentsConfig, identities, errors: configErrors, } = normalizeEnvironmentDescriptorMap(environmentsConfig, "environmentsConfig");
98
100
  errors.push(...configErrors);
@@ -117,8 +119,7 @@ export class DefaultAllureStore {
117
119
  __classPrivateFieldSet(this, _DefaultAllureStore_metadata, new Map(), "f");
118
120
  __classPrivateFieldSet(this, _DefaultAllureStore_fixtures, new Map(), "f");
119
121
  __classPrivateFieldSet(this, _DefaultAllureStore_history, history, "f");
120
- __classPrivateFieldSet(this, _DefaultAllureStore_knownIssuesConfig, knownIssuesConfig, "f");
121
- known.forEach((ktf) => this.known.set(ktf.historyId, ktf));
122
+ __classPrivateFieldSet(this, _DefaultAllureStore_resolutionsConfig, resolutionsConfig, "f");
122
123
  __classPrivateFieldSet(this, _DefaultAllureStore_realtimeDispatcher, realtimeDispatcher, "f");
123
124
  __classPrivateFieldSet(this, _DefaultAllureStore_realtimeSubscriber, realtimeSubscriber, "f");
124
125
  __classPrivateFieldSet(this, _DefaultAllureStore_defaultLabels, defaultLabels, "f");
@@ -330,7 +331,7 @@ export class DefaultAllureStore {
330
331
  testResult.transition = getStatusTransition(testResult, trHistory);
331
332
  testResult.flaky = isFlaky(testResult, trHistory);
332
333
  }
333
- testResult.known = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyKnownIssue).call(this, testResult, environmentIdentity.id);
334
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyResolution).call(this, testResult);
334
335
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
335
336
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_setTestResultEnvironmentId).call(this, testResult, environmentIdentity.id);
336
337
  __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").recordIngestOrder(testResult.id);
@@ -512,8 +513,21 @@ export class DefaultAllureStore {
512
513
  return result;
513
514
  }, []);
514
515
  }
515
- async allKnownIssues() {
516
- return [...this.known.values()].flat();
516
+ async allResolutionIssues() {
517
+ return [...__classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").values()];
518
+ }
519
+ async resolutionIssueByTestResultId(trId) {
520
+ const issueId = __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssueIdByTestResultId, "f").get(trId);
521
+ return issueId ? __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").get(issueId) : undefined;
522
+ }
523
+ async testResultsByResolutionIssueId(resolutionIssueId) {
524
+ const testResultIds = __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").get(resolutionIssueId);
525
+ if (!testResultIds) {
526
+ return [];
527
+ }
528
+ return [...testResultIds]
529
+ .map((testResultId) => __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").get(testResultId))
530
+ .filter(Boolean);
517
531
  }
518
532
  async allNewTestResults(filter, history) {
519
533
  if (!__classPrivateFieldGet(this, _DefaultAllureStore_history, "f") && !history) {
@@ -631,7 +645,7 @@ export class DefaultAllureStore {
631
645
  }
632
646
  async blockingFailedTestResults() {
633
647
  const failedTestResults = await this.failedTestResults();
634
- return failedTestResults.filter((tr) => !tr.known);
648
+ return failedTestResults.filter((tr) => !isIgnoredFailure(tr));
635
649
  }
636
650
  async unknownFailedTestResults() {
637
651
  return this.blockingFailedTestResults();
@@ -792,9 +806,10 @@ export class DefaultAllureStore {
792
806
  indexAttachmentByTestResult: {},
793
807
  indexTestResultByHistoryId: {},
794
808
  indexTestResultByTestCase: {},
809
+ indexTestResultByResolutionIssue: {},
795
810
  indexAttachmentByFixture: {},
796
811
  indexFixturesByTestResult: {},
797
- knownIssues: {},
812
+ resolutionIssues: mapToObject(__classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f")),
798
813
  qualityGateResults: __classPrivateFieldGet(this, _DefaultAllureStore_qualityGateResults, "f"),
799
814
  testResultIdsIngestOrder: __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").ingestOrderIdsForDump(),
800
815
  };
@@ -810,16 +825,16 @@ export class DefaultAllureStore {
810
825
  this.indexTestResultByTestCase.forEach((trs, tcId) => {
811
826
  storeDump.indexTestResultByTestCase[tcId] = trs.map((tr) => tr.id);
812
827
  });
828
+ __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").forEach((trIds, resolutionIssueId) => {
829
+ storeDump.indexTestResultByResolutionIssue[resolutionIssueId] = [...trIds];
830
+ });
813
831
  this.indexFixturesByTestResult.forEach((fixtures, trId) => {
814
832
  storeDump.indexFixturesByTestResult[trId] = fixtures.map((f) => f.id);
815
833
  });
816
- this.known.forEach((known, historyId) => {
817
- storeDump.knownIssues[historyId] = known;
818
- });
819
834
  return storeDump;
820
835
  }
821
836
  async restoreState(stateDump, attachmentsContents = {}) {
822
- const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, knownIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], } = stateDump;
837
+ const { testResults, attachments, testCases, fixtures, reportVariables, environments, globalAttachmentIds = [], globalErrors = [], checkResults, indexAttachmentByTestResult = {}, indexTestResultByHistoryId = {}, indexTestResultByTestCase = {}, indexTestResultByResolutionIssue = {}, indexAttachmentByFixture = {}, indexFixturesByTestResult = {}, resolutionIssues = {}, qualityGateResults = [], testResultIdsIngestOrder = [], } = stateDump;
823
838
  const storedEnvironmentAliases = environments.flatMap((environmentValue) => {
824
839
  if (typeof environmentValue === "string") {
825
840
  return [{ id: environmentValue, name: environmentValue }];
@@ -869,7 +884,7 @@ export class DefaultAllureStore {
869
884
  envNameToId.set(id, id);
870
885
  }
871
886
  Object.values(testResults).forEach((testResult) => {
872
- testResult.known ?? (testResult.known = false);
887
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
873
888
  __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").set(testResult.id, testResult);
874
889
  const storedEnvKey = typeof testResult.environment === "string" ? testResult.environment : undefined;
875
890
  const envId = (storedEnvKey ? envNameToId.get(storedEnvKey) : undefined) ?? __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
@@ -885,7 +900,18 @@ export class DefaultAllureStore {
885
900
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_attachments, "f"), attachments);
886
901
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_testCases, "f"), testCases);
887
902
  updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_fixtures, "f"), fixtures);
888
- updateMapWithRecord(this.known, knownIssues);
903
+ updateMapWithRecord(__classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f"), resolutionIssues);
904
+ Object.entries(indexTestResultByResolutionIssue).forEach(([resolutionIssueId, testResultIds]) => {
905
+ const resolutionIssue = __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").get(resolutionIssueId);
906
+ if (!resolutionIssue) {
907
+ return;
908
+ }
909
+ testResultIds.forEach((testResultId) => {
910
+ if (__classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").has(testResultId)) {
911
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_associateResolutionIssue).call(this, resolutionIssue, testResultId);
912
+ }
913
+ });
914
+ });
889
915
  Object.entries(attachmentsContents).forEach(([id, content]) => {
890
916
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_restoreAttachmentContent).call(this, id, content);
891
917
  });
@@ -975,8 +1001,13 @@ export class DefaultAllureStore {
975
1001
  existingFixtures.push(...fxs);
976
1002
  });
977
1003
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_rebuildRetrySubstore).call(this);
978
- for (const testResult of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()) {
979
- testResult.known = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyKnownIssue).call(this, testResult, __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult));
1004
+ if (__classPrivateFieldGet(this, _DefaultAllureStore_resolutionsConfig, "f")) {
1005
+ __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").clear();
1006
+ __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").clear();
1007
+ __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssueIdByTestResultId, "f").clear();
1008
+ for (const testResult of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f").values()) {
1009
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_classifyResolution).call(this, testResult);
1010
+ }
980
1011
  }
981
1012
  qualityGateResults.forEach((result, index) => {
982
1013
  __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_assertAllowedStoredEnvironment).call(this, {
@@ -989,9 +1020,7 @@ export class DefaultAllureStore {
989
1020
  });
990
1021
  }
991
1022
  }
992
- _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environmentDisplayNames = new WeakMap(), _DefaultAllureStore_environmentNameToId = new WeakMap(), _DefaultAllureStore_attachments = new WeakMap(), _DefaultAllureStore_attachmentContents = new WeakMap(), _DefaultAllureStore_testCases = new WeakMap(), _DefaultAllureStore_metadata = new WeakMap(), _DefaultAllureStore_history = new WeakMap(), _DefaultAllureStore_knownIssuesConfig = new WeakMap(), _DefaultAllureStore_fixtures = new WeakMap(), _DefaultAllureStore_defaultLabels = new WeakMap(), _DefaultAllureStore_environment = new WeakMap(), _DefaultAllureStore_environmentsConfig = new WeakMap(), _DefaultAllureStore_reportVariables = new WeakMap(), _DefaultAllureStore_realtimeDispatcher = new WeakMap(), _DefaultAllureStore_realtimeSubscriber = new WeakMap(), _DefaultAllureStore_allowedEnvironmentIds = new WeakMap(), _DefaultAllureStore_retrySubstore = new WeakMap(), _DefaultAllureStore_testResultIdsByEnvironmentId = new WeakMap(), _DefaultAllureStore_cachedEnvironmentEntries = new WeakMap(), _DefaultAllureStore_globalAttachmentIds = new WeakMap(), _DefaultAllureStore_globalAttachmentIdsByEnv = new WeakMap(), _DefaultAllureStore_globalErrors = new WeakMap(), _DefaultAllureStore_globalErrorsByEnv = new WeakMap(), _DefaultAllureStore_globalExitCode = new WeakMap(), _DefaultAllureStore_checkResultsById = new WeakMap(), _DefaultAllureStore_qualityGateResults = new WeakMap(), _DefaultAllureStore_historyPoints = new WeakMap(), _DefaultAllureStore_environments = new WeakMap(), _DefaultAllureStore_instances = new WeakSet(), _DefaultAllureStore_historyIdMatches = function _DefaultAllureStore_historyIdMatches(historyIdCandidates, indexByHistoryId) {
993
- return historyIdCandidates.find((historyId) => indexByHistoryId.has(historyId));
994
- }, _DefaultAllureStore_mergeEnvironmentIdentity = function _DefaultAllureStore_mergeEnvironmentIdentity(existingEnvironment, incomingEnvironment) {
1023
+ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environmentDisplayNames = new WeakMap(), _DefaultAllureStore_environmentNameToId = new WeakMap(), _DefaultAllureStore_attachments = new WeakMap(), _DefaultAllureStore_attachmentContents = new WeakMap(), _DefaultAllureStore_testCases = new WeakMap(), _DefaultAllureStore_metadata = new WeakMap(), _DefaultAllureStore_history = new WeakMap(), _DefaultAllureStore_resolutionsConfig = new WeakMap(), _DefaultAllureStore_resolutionIssues = new WeakMap(), _DefaultAllureStore_testResultIdsByResolutionIssueId = new WeakMap(), _DefaultAllureStore_resolutionIssueIdByTestResultId = new WeakMap(), _DefaultAllureStore_fixtures = new WeakMap(), _DefaultAllureStore_defaultLabels = new WeakMap(), _DefaultAllureStore_environment = new WeakMap(), _DefaultAllureStore_environmentsConfig = new WeakMap(), _DefaultAllureStore_reportVariables = new WeakMap(), _DefaultAllureStore_realtimeDispatcher = new WeakMap(), _DefaultAllureStore_realtimeSubscriber = new WeakMap(), _DefaultAllureStore_allowedEnvironmentIds = new WeakMap(), _DefaultAllureStore_retrySubstore = new WeakMap(), _DefaultAllureStore_testResultIdsByEnvironmentId = new WeakMap(), _DefaultAllureStore_cachedEnvironmentEntries = new WeakMap(), _DefaultAllureStore_globalAttachmentIds = new WeakMap(), _DefaultAllureStore_globalAttachmentIdsByEnv = new WeakMap(), _DefaultAllureStore_globalErrors = new WeakMap(), _DefaultAllureStore_globalErrorsByEnv = new WeakMap(), _DefaultAllureStore_globalExitCode = new WeakMap(), _DefaultAllureStore_checkResultsById = new WeakMap(), _DefaultAllureStore_qualityGateResults = new WeakMap(), _DefaultAllureStore_historyPoints = new WeakMap(), _DefaultAllureStore_environments = new WeakMap(), _DefaultAllureStore_instances = new WeakSet(), _DefaultAllureStore_mergeEnvironmentIdentity = function _DefaultAllureStore_mergeEnvironmentIdentity(existingEnvironment, incomingEnvironment) {
995
1024
  const configuredEnvironment = environmentIdentityById(__classPrivateFieldGet(this, _DefaultAllureStore_environmentsConfig, "f"), incomingEnvironment.id);
996
1025
  if (configuredEnvironment) {
997
1026
  return configuredEnvironment;
@@ -1093,23 +1122,35 @@ _DefaultAllureStore_testResults = new WeakMap(), _DefaultAllureStore_environment
1093
1122
  const environmentId = options?.environmentId ?? __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_environmentIdByTestResult).call(this, testResult);
1094
1123
  const parametersHash = options?.parametersHash ?? calculateParametersHash(testResult.parameters);
1095
1124
  testResult.retryHash = calculateRetryHash(testResult.testCase?.id, parametersHash, environmentId);
1096
- }, _DefaultAllureStore_setKnownIssue = function _DefaultAllureStore_setKnownIssue(knownIssue) {
1097
- this.known.set(knownIssue.historyId, knownIssue);
1098
- }, _DefaultAllureStore_classifyKnownIssue = function _DefaultAllureStore_classifyKnownIssue(testResult, environmentId) {
1099
- if (testResult.status !== "failed" && testResult.status !== "broken") {
1100
- return false;
1125
+ }, _DefaultAllureStore_classifyResolution = function _DefaultAllureStore_classifyResolution(testResult) {
1126
+ const rule = getResolutionByRules(testResult, __classPrivateFieldGet(this, _DefaultAllureStore_resolutionsConfig, "f"));
1127
+ testResult.resolution = rule?.resolution;
1128
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResult.id);
1129
+ if (rule?.resolution !== "issue") {
1130
+ return;
1101
1131
  }
1102
- const historyIdCandidates = getHistoryIdCandidates(testResult);
1103
- if (historyIdCandidates.length === 0) {
1104
- return false;
1132
+ const resolutionIssue = { ...rule.issue, comment: rule.comment };
1133
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_associateResolutionIssue).call(this, resolutionIssue, testResult.id);
1134
+ }, _DefaultAllureStore_associateResolutionIssue = function _DefaultAllureStore_associateResolutionIssue(resolutionIssue, testResultId) {
1135
+ __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_removeResolutionIssueAssociation).call(this, testResultId);
1136
+ __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").set(resolutionIssue.id, resolutionIssue);
1137
+ const testResultIds = __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").get(resolutionIssue.id) ?? new Set();
1138
+ testResultIds.add(testResultId);
1139
+ __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").set(resolutionIssue.id, testResultIds);
1140
+ __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssueIdByTestResultId, "f").set(testResultId, resolutionIssue.id);
1141
+ }, _DefaultAllureStore_removeResolutionIssueAssociation = function _DefaultAllureStore_removeResolutionIssueAssociation(testResultId) {
1142
+ const resolutionIssueId = __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssueIdByTestResultId, "f").get(testResultId);
1143
+ if (!resolutionIssueId) {
1144
+ return;
1105
1145
  }
1106
- const ruleKnownIssue = getKnownIssueByRules(testResult, __classPrivateFieldGet(this, _DefaultAllureStore_knownIssuesConfig, "f"), environmentId);
1107
- if (ruleKnownIssue) {
1108
- __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_setKnownIssue).call(this, ruleKnownIssue);
1109
- return true;
1146
+ __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssueIdByTestResultId, "f").delete(testResultId);
1147
+ const testResultIds = __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").get(resolutionIssueId);
1148
+ testResultIds?.delete(testResultId);
1149
+ if (testResultIds?.size) {
1150
+ return;
1110
1151
  }
1111
- const knownHistoryId = __classPrivateFieldGet(this, _DefaultAllureStore_instances, "m", _DefaultAllureStore_historyIdMatches).call(this, historyIdCandidates, this.known);
1112
- return !!knownHistoryId;
1152
+ __classPrivateFieldGet(this, _DefaultAllureStore_testResultIdsByResolutionIssueId, "f").delete(resolutionIssueId);
1153
+ __classPrivateFieldGet(this, _DefaultAllureStore_resolutionIssues, "f").delete(resolutionIssueId);
1113
1154
  }, _DefaultAllureStore_rebuildRetrySubstore = function _DefaultAllureStore_rebuildRetrySubstore() {
1114
1155
  __classPrivateFieldGet(this, _DefaultAllureStore_retrySubstore, "f").reset();
1115
1156
  for (const [, testResult] of __classPrivateFieldGet(this, _DefaultAllureStore_testResults, "f")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/core",
3
- "version": "3.16.0",
3
+ "version": "3.16.1",
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.16.0",
29
- "@allurereport/cli-commons": "3.16.0",
30
- "@allurereport/core-api": "3.16.0",
31
- "@allurereport/plugin-agent": "3.16.0",
32
- "@allurereport/plugin-allure2": "3.16.0",
33
- "@allurereport/plugin-api": "3.16.0",
34
- "@allurereport/plugin-awesome": "3.16.0",
35
- "@allurereport/plugin-classic": "3.16.0",
36
- "@allurereport/plugin-csv": "3.16.0",
37
- "@allurereport/plugin-dashboard": "3.16.0",
38
- "@allurereport/plugin-jira": "3.16.0",
39
- "@allurereport/plugin-log": "3.16.0",
40
- "@allurereport/plugin-progress": "3.16.0",
41
- "@allurereport/plugin-slack": "3.16.0",
42
- "@allurereport/plugin-testops": "3.16.0",
43
- "@allurereport/plugin-testplan": "3.16.0",
44
- "@allurereport/reader": "3.16.0",
45
- "@allurereport/reader-api": "3.16.0",
46
- "@allurereport/service": "3.16.0",
47
- "@allurereport/summary": "3.16.0",
28
+ "@allurereport/ci": "3.16.1",
29
+ "@allurereport/cli-commons": "3.16.1",
30
+ "@allurereport/core-api": "3.16.1",
31
+ "@allurereport/plugin-agent": "3.16.1",
32
+ "@allurereport/plugin-allure2": "3.16.1",
33
+ "@allurereport/plugin-api": "3.16.1",
34
+ "@allurereport/plugin-awesome": "3.16.1",
35
+ "@allurereport/plugin-classic": "3.16.1",
36
+ "@allurereport/plugin-csv": "3.16.1",
37
+ "@allurereport/plugin-dashboard": "3.16.1",
38
+ "@allurereport/plugin-jira": "3.16.1",
39
+ "@allurereport/plugin-log": "3.16.1",
40
+ "@allurereport/plugin-progress": "3.16.1",
41
+ "@allurereport/plugin-slack": "3.16.1",
42
+ "@allurereport/plugin-testops": "3.16.1",
43
+ "@allurereport/plugin-testplan": "3.16.1",
44
+ "@allurereport/reader": "3.16.1",
45
+ "@allurereport/reader-api": "3.16.1",
46
+ "@allurereport/service": "3.16.1",
47
+ "@allurereport/summary": "3.16.1",
48
48
  "glob": "^13.0.6",
49
49
  "handlebars": "^4.7.9",
50
50
  "jiti": "^2.5.1",
package/dist/known.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import type { KnownIssuesConfig, KnownTestFailure, TestResult } from "@allurereport/core-api";
2
- import type { AllureStore } from "@allurereport/plugin-api";
3
- export declare const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
4
- export declare const hasKnownIssueRules: (knownIssues?: KnownIssuesConfig) => boolean;
5
- export declare const getKnownIssueByRules: (testResult: TestResult, knownIssues: KnownIssuesConfig | undefined, environmentId?: string) => KnownTestFailure | undefined;
6
- export declare const resolveExactIssuesFilePath: (pathOrDir: string | undefined, label: string) => Promise<string | undefined>;
7
- export declare const readKnownIssues: (knownIssuePath: string) => Promise<KnownTestFailure[]>;
8
- export declare const writeKnownIssues: (store: AllureStore, knownIssuesPath?: string) => Promise<void>;
package/dist/known.js DELETED
@@ -1,86 +0,0 @@
1
- import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
- import { dirname, extname, resolve } from "node:path";
3
- import { isFileNotFoundError } from "./utils/misc.js";
4
- export const DEFAULT_KNOWN_ISSUES_PATH = "known-issues.json";
5
- export const hasKnownIssueRules = (knownIssues) => (knownIssues?.rules?.length ?? 0) > 0;
6
- const messageMatches = (rule, testResult) => {
7
- if (!rule.messageRegexp) {
8
- return true;
9
- }
10
- const message = testResult.error?.message;
11
- return typeof message === "string" && new RegExp(rule.messageRegexp).test(message);
12
- };
13
- const testCaseIdMatches = (rule, testResult) => {
14
- const { id, allureId, externalId } = testResult.testCase ?? {};
15
- return !rule.testCaseId || [id, allureId, externalId].includes(rule.testCaseId);
16
- };
17
- const retryHashMatches = (rule, testResult) => {
18
- return !rule.retryHash || rule.retryHash === testResult.retryHash;
19
- };
20
- const environmentMatches = (rule, testResult) => {
21
- return !rule.environmentId || rule.environmentId === testResult.environment;
22
- };
23
- export const getKnownIssueByRules = (testResult, knownIssues, environmentId) => {
24
- if (!testResult.historyId || !hasKnownIssueRules(knownIssues)) {
25
- return undefined;
26
- }
27
- const matchedRule = knownIssues.rules.find((rule) => [messageMatches, testCaseIdMatches, environmentMatches, retryHashMatches].every((matcher) => matcher(rule, testResult)));
28
- if (!matchedRule) {
29
- return undefined;
30
- }
31
- return {
32
- historyId: testResult.historyId,
33
- reason: matchedRule.decision.reason,
34
- links: matchedRule.decision.links,
35
- error: testResult.error,
36
- };
37
- };
38
- export const resolveExactIssuesFilePath = async (pathOrDir, label) => {
39
- if (!pathOrDir) {
40
- return undefined;
41
- }
42
- const path = resolve(pathOrDir);
43
- if (extname(path).toLowerCase() !== ".json") {
44
- throw new Error(`Invalid ${label} path ${JSON.stringify(pathOrDir)}: expected exact .json file path`);
45
- }
46
- try {
47
- const pathStats = await stat(path);
48
- if (pathStats.isDirectory()) {
49
- throw new Error(`Invalid ${label} path ${JSON.stringify(pathOrDir)}: expected file, got directory`);
50
- }
51
- }
52
- catch (e) {
53
- if (!isFileNotFoundError(e)) {
54
- throw e;
55
- }
56
- }
57
- return path;
58
- };
59
- export const readKnownIssues = async (knownIssuePath) => {
60
- const path = await resolveExactIssuesFilePath(knownIssuePath, "known issues");
61
- if (!path) {
62
- return [];
63
- }
64
- try {
65
- const content = await readFile(path, { encoding: "utf-8" });
66
- return JSON.parse(content);
67
- }
68
- catch (e) {
69
- if (isFileNotFoundError(e)) {
70
- return [];
71
- }
72
- throw e;
73
- }
74
- };
75
- const writeKnownIssueRecords = async (knownIssues, knownIssuesPath) => {
76
- const path = await resolveExactIssuesFilePath(knownIssuesPath, "known issues");
77
- if (!path) {
78
- return;
79
- }
80
- await mkdir(dirname(path), { recursive: true });
81
- await writeFile(path, `${JSON.stringify(knownIssues)}\n`, "utf-8");
82
- };
83
- export const writeKnownIssues = async (store, knownIssuesPath) => {
84
- const knownIssues = await store.allKnownIssues();
85
- await writeKnownIssueRecords(knownIssues, knownIssuesPath);
86
- };