@tiden/reporter-commons 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +201 -0
  3. package/README.md +34 -0
  4. package/dist/client/interface.d.ts +8 -0
  5. package/dist/client/interface.js +2 -0
  6. package/dist/client/reporter-api-models.d.ts +96 -0
  7. package/dist/client/reporter-api-models.js +2 -0
  8. package/dist/client/reporter-api.d.ts +14 -0
  9. package/dist/client/reporter-api.js +25 -0
  10. package/dist/client/services/api-error-handler.d.ts +10 -0
  11. package/dist/client/services/api-error-handler.js +44 -0
  12. package/dist/client/services/attachment-service.d.ts +29 -0
  13. package/dist/client/services/attachment-service.js +268 -0
  14. package/dist/client/services/result-transformer.d.ts +21 -0
  15. package/dist/client/services/result-transformer.js +180 -0
  16. package/dist/client/services/run-service.d.ts +20 -0
  17. package/dist/client/services/run-service.js +68 -0
  18. package/dist/client/tiden-client.d.ts +34 -0
  19. package/dist/client/tiden-client.js +99 -0
  20. package/dist/client/tiden-http.d.ts +11 -0
  21. package/dist/client/tiden-http.js +23 -0
  22. package/dist/config/config-loader-interface.d.ts +4 -0
  23. package/dist/config/config-loader-interface.js +2 -0
  24. package/dist/config/config-loader.d.ts +10 -0
  25. package/dist/config/config-loader.js +60 -0
  26. package/dist/config/config-type.d.ts +2 -0
  27. package/dist/config/config-type.js +2 -0
  28. package/dist/config/config-validation-schema.d.ts +212 -0
  29. package/dist/config/config-validation-schema.js +213 -0
  30. package/dist/config/index.d.ts +4 -0
  31. package/dist/config/index.js +7 -0
  32. package/dist/core/options-resolver.d.ts +19 -0
  33. package/dist/core/options-resolver.js +47 -0
  34. package/dist/core/reporter-factory.d.ts +18 -0
  35. package/dist/core/reporter-factory.js +63 -0
  36. package/dist/core/status-processor.d.ts +17 -0
  37. package/dist/core/status-processor.js +48 -0
  38. package/dist/env/env-enum.d.ts +64 -0
  39. package/dist/env/env-enum.js +75 -0
  40. package/dist/env/env-to-config.d.ts +7 -0
  41. package/dist/env/env-to-config.js +60 -0
  42. package/dist/env/env-type.d.ts +29 -0
  43. package/dist/env/env-type.js +3 -0
  44. package/dist/env/env-validation-schema.d.ts +6 -0
  45. package/dist/env/env-validation-schema.js +113 -0
  46. package/dist/env/index.d.ts +4 -0
  47. package/dist/env/index.js +13 -0
  48. package/dist/formatter/formatter-interface.d.ts +3 -0
  49. package/dist/formatter/formatter-interface.js +2 -0
  50. package/dist/formatter/index.d.ts +4 -0
  51. package/dist/formatter/index.js +9 -0
  52. package/dist/formatter/json-formatter.d.ts +13 -0
  53. package/dist/formatter/json-formatter.js +28 -0
  54. package/dist/formatter/jsonp-formatter.d.ts +13 -0
  55. package/dist/formatter/jsonp-formatter.js +29 -0
  56. package/dist/formatter/report-serializer.d.ts +20 -0
  57. package/dist/formatter/report-serializer.js +92 -0
  58. package/dist/index.d.ts +14 -0
  59. package/dist/index.js +30 -0
  60. package/dist/internal/filter-positive-ids.d.ts +17 -0
  61. package/dist/internal/filter-positive-ids.js +38 -0
  62. package/dist/internal/ids-parser.d.ts +9 -0
  63. package/dist/internal/ids-parser.js +21 -0
  64. package/dist/internal/index.d.ts +8 -0
  65. package/dist/internal/index.js +15 -0
  66. package/dist/internal/step-parser.d.ts +11 -0
  67. package/dist/internal/step-parser.js +28 -0
  68. package/dist/internal/suite-file.d.ts +13 -0
  69. package/dist/internal/suite-file.js +16 -0
  70. package/dist/internal/suite-normalizer.d.ts +6 -0
  71. package/dist/internal/suite-normalizer.js +11 -0
  72. package/dist/internal/title.d.ts +5 -0
  73. package/dist/internal/title.js +15 -0
  74. package/dist/models/attachment.d.ts +8 -0
  75. package/dist/models/attachment.js +2 -0
  76. package/dist/models/config/TidenOptionsType.d.ts +33 -0
  77. package/dist/models/config/TidenOptionsType.js +2 -0
  78. package/dist/models/configuration.d.ts +12 -0
  79. package/dist/models/configuration.js +2 -0
  80. package/dist/models/error.d.ts +7 -0
  81. package/dist/models/error.js +24 -0
  82. package/dist/models/execution-sum.d.ts +6 -0
  83. package/dist/models/execution-sum.js +2 -0
  84. package/dist/models/host-data.d.ts +12 -0
  85. package/dist/models/host-data.js +2 -0
  86. package/dist/models/index.d.ts +11 -0
  87. package/dist/models/index.js +15 -0
  88. package/dist/models/report.d.ts +14 -0
  89. package/dist/models/report.js +2 -0
  90. package/dist/models/short-result.d.ts +7 -0
  91. package/dist/models/short-result.js +2 -0
  92. package/dist/models/stats.d.ts +9 -0
  93. package/dist/models/stats.js +2 -0
  94. package/dist/models/step-data.d.ts +19 -0
  95. package/dist/models/step-data.js +2 -0
  96. package/dist/models/step-execution.d.ts +13 -0
  97. package/dist/models/step-execution.js +23 -0
  98. package/dist/models/test-execution.d.ts +20 -0
  99. package/dist/models/test-execution.js +32 -0
  100. package/dist/models/test-result.d.ts +44 -0
  101. package/dist/models/test-result.js +50 -0
  102. package/dist/models/test-step.d.ts +18 -0
  103. package/dist/models/test-step.js +53 -0
  104. package/dist/options/composeOptions.d.ts +8 -0
  105. package/dist/options/composeOptions.js +12 -0
  106. package/dist/options/index.d.ts +3 -0
  107. package/dist/options/index.js +7 -0
  108. package/dist/options/mode-enum.d.ts +8 -0
  109. package/dist/options/mode-enum.js +12 -0
  110. package/dist/options/options-type.d.ts +38 -0
  111. package/dist/options/options-type.js +3 -0
  112. package/dist/profilers/abstract-profiler.d.ts +5 -0
  113. package/dist/profilers/abstract-profiler.js +6 -0
  114. package/dist/profilers/fetch-interceptor.d.ts +60 -0
  115. package/dist/profilers/fetch-interceptor.js +277 -0
  116. package/dist/profilers/http-interceptor.d.ts +76 -0
  117. package/dist/profilers/http-interceptor.js +252 -0
  118. package/dist/profilers/index.d.ts +5 -0
  119. package/dist/profilers/index.js +11 -0
  120. package/dist/profilers/network-profiler.d.ts +61 -0
  121. package/dist/profilers/network-profiler.js +94 -0
  122. package/dist/reporters/abstract-reporter.d.ts +68 -0
  123. package/dist/reporters/abstract-reporter.js +78 -0
  124. package/dist/reporters/index.d.ts +3 -0
  125. package/dist/reporters/index.js +9 -0
  126. package/dist/reporters/report-reporter.d.ts +50 -0
  127. package/dist/reporters/report-reporter.js +167 -0
  128. package/dist/reporters/run-reporter.d.ts +65 -0
  129. package/dist/reporters/run-reporter.js +155 -0
  130. package/dist/reporters/shared/fallback-coordinator.d.ts +47 -0
  131. package/dist/reporters/shared/fallback-coordinator.js +119 -0
  132. package/dist/reporters/shared/run-constants.d.ts +11 -0
  133. package/dist/reporters/shared/run-constants.js +14 -0
  134. package/dist/state/state.d.ts +16 -0
  135. package/dist/state/state.js +70 -0
  136. package/dist/steps/step.d.ts +16 -0
  137. package/dist/steps/step.js +86 -0
  138. package/dist/tiden.d.ts +48 -0
  139. package/dist/tiden.js +223 -0
  140. package/dist/utils/disabled-exception.d.ts +6 -0
  141. package/dist/utils/disabled-exception.js +10 -0
  142. package/dist/utils/get-package-version.d.ts +5 -0
  143. package/dist/utils/get-package-version.js +25 -0
  144. package/dist/utils/hostData.d.ts +8 -0
  145. package/dist/utils/hostData.js +140 -0
  146. package/dist/utils/is-axios-error.d.ts +6 -0
  147. package/dist/utils/is-axios-error.js +11 -0
  148. package/dist/utils/logger.d.ts +33 -0
  149. package/dist/utils/logger.js +141 -0
  150. package/dist/utils/mimeTypes.d.ts +5 -0
  151. package/dist/utils/mimeTypes.js +50 -0
  152. package/dist/utils/project-mapping-utils.d.ts +17 -0
  153. package/dist/utils/project-mapping-utils.js +29 -0
  154. package/dist/utils/signature.d.ts +11 -0
  155. package/dist/utils/signature.js +34 -0
  156. package/dist/utils/status-mapping-utils.d.ts +14 -0
  157. package/dist/utils/status-mapping-utils.js +45 -0
  158. package/dist/utils/test-status-utils.d.ts +8 -0
  159. package/dist/utils/test-status-utils.js +152 -0
  160. package/dist/utils/tiden-error.d.ts +18 -0
  161. package/dist/utils/tiden-error.js +19 -0
  162. package/dist/utils/token-masker.d.ts +11 -0
  163. package/dist/utils/token-masker.js +26 -0
  164. package/dist/utils/validate-json.d.ts +20 -0
  165. package/dist/utils/validate-json.js +41 -0
  166. package/dist/writer/driver-enum.d.ts +13 -0
  167. package/dist/writer/driver-enum.js +18 -0
  168. package/dist/writer/fs-writer.d.ts +40 -0
  169. package/dist/writer/fs-writer.js +139 -0
  170. package/dist/writer/index.d.ts +3 -0
  171. package/dist/writer/index.js +8 -0
  172. package/dist/writer/writer-interface.d.ts +7 -0
  173. package/dist/writer/writer-interface.js +2 -0
  174. package/package.json +83 -0
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkProfiler = void 0;
4
+ const node_async_hooks_1 = require("node:async_hooks");
5
+ const abstract_profiler_1 = require("./abstract-profiler");
6
+ const http_interceptor_1 = require("./http-interceptor");
7
+ const fetch_interceptor_1 = require("./fetch-interceptor");
8
+ // Module-level ALS store — shared across all NetworkProfiler instances to
9
+ // allow nested run() calls to see the same context.
10
+ const store = new node_async_hooks_1.AsyncLocalStorage();
11
+ class NetworkProfiler extends abstract_profiler_1.AbstractProfiler {
12
+ skipDomains;
13
+ // Stored for Phase 13 use (controls whether to capture network steps on test failure)
14
+ _trackOnFail;
15
+ /**
16
+ * Fallback accumulator for steps captured outside any run() context.
17
+ * Used by frameworks where test bodies cannot be wrapped in run() (Jest, Vitest, WDIO).
18
+ * Public so interceptors can push to it via ProfilerRef.
19
+ */
20
+ fallbackSteps = [];
21
+ httpInterceptor = null;
22
+ fetchInterceptor = null;
23
+ constructor(options = {}) {
24
+ super();
25
+ this.skipDomains = options.skipDomains ?? [];
26
+ this._trackOnFail = options.trackOnFail ?? true;
27
+ }
28
+ get trackOnFail() {
29
+ return this._trackOnFail;
30
+ }
31
+ shouldSkip(url) {
32
+ return this.skipDomains.some((domain) => url.includes(domain));
33
+ }
34
+ /**
35
+ * Install HTTP/HTTPS monkey-patches and subscribe to undici diagnostics_channel
36
+ * so that outgoing requests inside a `run()` context are captured as REQUEST steps.
37
+ */
38
+ enable() {
39
+ this.httpInterceptor = new http_interceptor_1.HttpInterceptor(store, this);
40
+ this.httpInterceptor.install();
41
+ this.fetchInterceptor = new fetch_interceptor_1.FetchInterceptor(store, this);
42
+ this.fetchInterceptor.subscribe();
43
+ }
44
+ /**
45
+ * No-op per v1.3 design decision (flag-based activation model).
46
+ * Satisfies the AbstractProfiler contract.
47
+ */
48
+ disable() {
49
+ // Intentional no-op
50
+ }
51
+ /**
52
+ * Uninstall HTTP/HTTPS monkey-patches and unsubscribe from undici diagnostics_channel —
53
+ * permanently removes interception.
54
+ */
55
+ restore() {
56
+ this.fetchInterceptor?.unsubscribe();
57
+ this.fetchInterceptor = null;
58
+ this.httpInterceptor?.uninstall();
59
+ this.httpInterceptor = null;
60
+ }
61
+ /**
62
+ * Wraps `fn` in an AsyncLocalStorage context so that any HTTP requests
63
+ * made inside `fn` are attributed to the step accumulator for this call.
64
+ *
65
+ * Returns `{ result, steps }` where `result` is the return value of `fn`
66
+ * and `steps` is the array of REQUEST steps captured during execution.
67
+ */
68
+ async run(fn) {
69
+ const accumulator = [];
70
+ const result = await store.run(accumulator, fn);
71
+ return { result, steps: accumulator };
72
+ }
73
+ /**
74
+ * Returns the current ALS store when called inside a `run()` context.
75
+ * Returns an empty array when called outside (no active context).
76
+ */
77
+ getSteps() {
78
+ return store.getStore() ?? [];
79
+ }
80
+ /**
81
+ * Returns a copy of steps accumulated outside any run() context (fallback accumulator).
82
+ * Used by frameworks where test bodies cannot be wrapped in run() (Jest, Vitest, WDIO).
83
+ */
84
+ getAllSteps() {
85
+ return [...this.fallbackSteps];
86
+ }
87
+ /**
88
+ * Clears the fallback step accumulator. Call after collecting steps per test.
89
+ */
90
+ clearSteps() {
91
+ this.fallbackSteps.length = 0;
92
+ }
93
+ }
94
+ exports.NetworkProfiler = NetworkProfiler;
@@ -0,0 +1,68 @@
1
+ import { Attachment, TestResultType } from '../models';
2
+ import { LoggerInterface } from '../utils/logger';
3
+ export interface InternalReporterInterface {
4
+ addTestResult(result: TestResultType): Promise<void>;
5
+ publish(): Promise<void>;
6
+ startTestRun(): Promise<void>;
7
+ getTestResults(): TestResultType[];
8
+ setTestResults(results: TestResultType[]): void;
9
+ sendResults(): Promise<void>;
10
+ complete(): Promise<void>;
11
+ uploadAttachment(attachments: Attachment): Promise<string>;
12
+ }
13
+ /**
14
+ * @abstract
15
+ * @class AbstractReporter
16
+ * @implements InternalReporterInterface
17
+ */
18
+ export declare abstract class AbstractReporter implements InternalReporterInterface {
19
+ /**
20
+ * @type {LoggerInterface}
21
+ * @private
22
+ */
23
+ protected readonly logger: LoggerInterface;
24
+ /**
25
+ * @type {TestResultType[]}
26
+ * @protected
27
+ */
28
+ protected results: TestResultType[];
29
+ /**
30
+ * @returns {Promise<void>}
31
+ */
32
+ abstract publish(): Promise<void>;
33
+ /**
34
+ * @returns {Promise<void>}
35
+ */
36
+ abstract startTestRun(): Promise<void>;
37
+ /**
38
+ * @returns {Promise<void>}
39
+ */
40
+ abstract complete(): Promise<void>;
41
+ /**
42
+ * @returns {Promise<void>}
43
+ */
44
+ abstract sendResults(): Promise<void>;
45
+ /**
46
+ * @param {Attachment} attachment
47
+ * @returns {Promise<string>}
48
+ */
49
+ abstract uploadAttachment(attachment: Attachment): Promise<string>;
50
+ /**
51
+ * @protected
52
+ * @param {LoggerInterface} logger
53
+ */
54
+ protected constructor(logger: LoggerInterface);
55
+ /**
56
+ * @returns {TestResultType[]}
57
+ */
58
+ getTestResults(): TestResultType[];
59
+ /**
60
+ * @param {TestResultType} result
61
+ */
62
+ addTestResult(result: TestResultType): Promise<void>;
63
+ /**
64
+ * @param {TestResultType[]} results
65
+ */
66
+ setTestResults(results: TestResultType[]): void;
67
+ protected removeAnsiEscapeCodes(str: string): string;
68
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractReporter = void 0;
4
+ const uuid_1 = require("uuid");
5
+ /**
6
+ * @abstract
7
+ * @class AbstractReporter
8
+ * @implements InternalReporterInterface
9
+ */
10
+ class AbstractReporter {
11
+ /**
12
+ * @type {LoggerInterface}
13
+ * @private
14
+ */
15
+ logger;
16
+ /**
17
+ * @type {TestResultType[]}
18
+ * @protected
19
+ */
20
+ results = [];
21
+ /**
22
+ * @protected
23
+ * @param {LoggerInterface} logger
24
+ */
25
+ constructor(logger) {
26
+ this.logger = logger;
27
+ }
28
+ /**
29
+ * @returns {TestResultType[]}
30
+ */
31
+ getTestResults() {
32
+ const results = this.results;
33
+ this.results = [];
34
+ return results;
35
+ }
36
+ /**
37
+ * @param {TestResultType} result
38
+ */
39
+ // eslint-disable-next-line @typescript-eslint/require-await
40
+ async addTestResult(result) {
41
+ this.logger.logDebug(`Adding test result: ${JSON.stringify(result)}`);
42
+ if (result.execution.stacktrace) {
43
+ result.execution.stacktrace = this.removeAnsiEscapeCodes(result.execution.stacktrace);
44
+ }
45
+ if (result.message) {
46
+ result.message = this.removeAnsiEscapeCodes(result.message);
47
+ }
48
+ if (result.case_id === null || !Array.isArray(result.case_id)) {
49
+ this.results.push(result);
50
+ return;
51
+ }
52
+ // if we have multiple ids, we need to create multiple test results and set duration to 0 for all but the first one
53
+ let firstCase = true;
54
+ for (const id of result.case_id) {
55
+ const testResultCopy = { ...result, execution: { ...result.execution } };
56
+ testResultCopy.case_id = id;
57
+ testResultCopy.id = (0, uuid_1.v4)();
58
+ if (!firstCase) {
59
+ testResultCopy.execution.duration = 0;
60
+ }
61
+ firstCase = false;
62
+ this.results.push(testResultCopy);
63
+ }
64
+ }
65
+ /**
66
+ * @param {TestResultType[]} results
67
+ */
68
+ setTestResults(results) {
69
+ this.results = results;
70
+ }
71
+ removeAnsiEscapeCodes(str) {
72
+ const ansiEscapeSequences = new RegExp([
73
+ '\x1B[[(?);]{0,2}(;?\\d)*.',
74
+ ].join('|'), 'g');
75
+ return str.replace(ansiEscapeSequences, '');
76
+ }
77
+ }
78
+ exports.AbstractReporter = AbstractReporter;
@@ -0,0 +1,3 @@
1
+ export { AbstractReporter, type InternalReporterInterface, } from './abstract-reporter';
2
+ export { ReportReporter } from './report-reporter';
3
+ export { RunReporter } from './run-reporter';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RunReporter = exports.ReportReporter = exports.AbstractReporter = void 0;
4
+ var abstract_reporter_1 = require("./abstract-reporter");
5
+ Object.defineProperty(exports, "AbstractReporter", { enumerable: true, get: function () { return abstract_reporter_1.AbstractReporter; } });
6
+ var report_reporter_1 = require("./report-reporter");
7
+ Object.defineProperty(exports, "ReportReporter", { enumerable: true, get: function () { return report_reporter_1.ReportReporter; } });
8
+ var run_reporter_1 = require("./run-reporter");
9
+ Object.defineProperty(exports, "RunReporter", { enumerable: true, get: function () { return run_reporter_1.RunReporter; } });
@@ -0,0 +1,50 @@
1
+ import { AbstractReporter } from './abstract-reporter';
2
+ import { Attachment } from '../models';
3
+ import { WriterInterface } from '../writer';
4
+ import { LoggerInterface } from '../utils/logger';
5
+ import { HostData } from '../models/host-data';
6
+ import { ReportSerializerInterface } from '../formatter/report-serializer';
7
+ /**
8
+ * @class ReportReporter
9
+ * @extends AbstractReporter
10
+ */
11
+ export declare class ReportReporter extends AbstractReporter {
12
+ private writer;
13
+ private frameworkName;
14
+ private reporterName;
15
+ private readonly environment;
16
+ private readonly runId;
17
+ private readonly rootSuite;
18
+ private readonly hostData;
19
+ private readonly serializer;
20
+ private startTime;
21
+ /**
22
+ * @param {LoggerInterface} logger
23
+ * @param {WriterInterface} writer
24
+ * @param {string} frameworkName
25
+ * @param {string} reporterName
26
+ * @param {string | undefined} environment
27
+ * @param {string | undefined} rootSuite
28
+ * @param {number | undefined} runId
29
+ * @param {HostData | undefined} hostData
30
+ * @param {ReportSerializerInterface} serializer
31
+ */
32
+ constructor(logger: LoggerInterface, writer: WriterInterface, frameworkName: string, reporterName: string, environment?: string, rootSuite?: string, runId?: number, hostData?: HostData, serializer?: ReportSerializerInterface);
33
+ /**
34
+ * @returns {Promise<void>}
35
+ */
36
+ startTestRun(): Promise<void>;
37
+ /**
38
+ * @returns {Promise<void>}
39
+ *
40
+ */
41
+ publish(): Promise<void>;
42
+ sendResults(): Promise<void>;
43
+ complete(): Promise<void>;
44
+ uploadAttachment(attachment: Attachment): Promise<string>;
45
+ /**
46
+ * @param {TestStepType[]} steps
47
+ * @returns {TestStepType[]}
48
+ */
49
+ private copyStepAttachments;
50
+ }
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReportReporter = void 0;
4
+ const abstract_reporter_1 = require("./abstract-reporter");
5
+ const models_1 = require("../models");
6
+ const hostData_1 = require("../utils/hostData");
7
+ const report_serializer_1 = require("../formatter/report-serializer");
8
+ /**
9
+ * @class ReportReporter
10
+ * @extends AbstractReporter
11
+ */
12
+ class ReportReporter extends abstract_reporter_1.AbstractReporter {
13
+ writer;
14
+ frameworkName;
15
+ reporterName;
16
+ environment;
17
+ runId;
18
+ rootSuite;
19
+ hostData;
20
+ serializer;
21
+ startTime = Date.now();
22
+ /**
23
+ * @param {LoggerInterface} logger
24
+ * @param {WriterInterface} writer
25
+ * @param {string} frameworkName
26
+ * @param {string} reporterName
27
+ * @param {string | undefined} environment
28
+ * @param {string | undefined} rootSuite
29
+ * @param {number | undefined} runId
30
+ * @param {HostData | undefined} hostData
31
+ * @param {ReportSerializerInterface} serializer
32
+ */
33
+ constructor(logger, writer, frameworkName, reporterName, environment, rootSuite, runId, hostData, serializer = new report_serializer_1.ReportSerializer()) {
34
+ super(logger);
35
+ this.writer = writer;
36
+ this.frameworkName = frameworkName;
37
+ this.reporterName = reporterName;
38
+ this.environment = environment;
39
+ this.runId = runId;
40
+ this.rootSuite = rootSuite;
41
+ this.hostData = hostData;
42
+ this.serializer = serializer;
43
+ }
44
+ /**
45
+ * @returns {Promise<void>}
46
+ */
47
+ // eslint-disable-next-line @typescript-eslint/require-await
48
+ async startTestRun() {
49
+ this.startTime = Date.now();
50
+ }
51
+ /**
52
+ * @returns {Promise<void>}
53
+ *
54
+ */
55
+ async publish() {
56
+ await this.sendResults();
57
+ await this.complete();
58
+ }
59
+ async sendResults() {
60
+ this.writer.clearPreviousResults();
61
+ for (const result of this.results) {
62
+ if (result.attachments.length > 0) {
63
+ result.attachments = this.writer.writeAttachment(result.attachments);
64
+ }
65
+ result.steps = this.copyStepAttachments(result.steps);
66
+ result.run_id = this.runId ?? null;
67
+ if (result.relations != null && this.rootSuite != null) {
68
+ const data = {
69
+ title: this.rootSuite,
70
+ public_id: null,
71
+ };
72
+ result.relations.suite?.data.unshift(data);
73
+ }
74
+ else if (this.rootSuite != null) {
75
+ result.relations = {
76
+ suite: {
77
+ data: [
78
+ {
79
+ title: this.rootSuite,
80
+ public_id: null,
81
+ },
82
+ ],
83
+ },
84
+ };
85
+ }
86
+ // Serialize to spec-compliant format before writing
87
+ const serialized = this.serializer.serializeResult(result);
88
+ await this.writer.writeTestResult(serialized);
89
+ }
90
+ }
91
+ async complete() {
92
+ const endTime = Date.now();
93
+ const report = {
94
+ title: 'Test run',
95
+ execution: {
96
+ start_time: this.startTime / 1000,
97
+ end_time: endTime / 1000,
98
+ duration: endTime - this.startTime,
99
+ cumulative_duration: 0,
100
+ },
101
+ stats: {
102
+ total: 0,
103
+ passed: 0,
104
+ failed: 0,
105
+ skipped: 0,
106
+ blocked: 0,
107
+ invalid: 0,
108
+ muted: 0,
109
+ },
110
+ results: [],
111
+ threads: [],
112
+ suites: [],
113
+ environment: this.environment ?? '',
114
+ host_data: this.hostData ?? (0, hostData_1.getHostInfo)(this.frameworkName, this.reporterName),
115
+ };
116
+ for (const result of this.results) {
117
+ report.stats.total++;
118
+ switch (result.execution.status) {
119
+ case models_1.TestStatusEnum.passed:
120
+ report.stats.passed++;
121
+ break;
122
+ case models_1.TestStatusEnum.failed:
123
+ report.stats.failed++;
124
+ break;
125
+ case models_1.TestStatusEnum.skipped:
126
+ report.stats.skipped++;
127
+ break;
128
+ case models_1.TestStatusEnum.invalid:
129
+ report.stats.invalid++;
130
+ break;
131
+ case models_1.TestStatusEnum.blocked:
132
+ report.stats.blocked++;
133
+ break;
134
+ }
135
+ report.execution.cumulative_duration += result.execution.duration ?? 0;
136
+ report.results.push({
137
+ duration: result.execution.duration ?? 0,
138
+ id: result.id,
139
+ status: result.execution.status,
140
+ thread: result.execution.thread,
141
+ title: result.title,
142
+ });
143
+ }
144
+ const path = await this.writer.writeReport(report);
145
+ this.logger.log(`Report saved to ${path}`);
146
+ }
147
+ uploadAttachment(attachment) {
148
+ this.writer.writeAttachment([attachment]);
149
+ return Promise.resolve('');
150
+ }
151
+ /**
152
+ * @param {TestStepType[]} steps
153
+ * @returns {TestStepType[]}
154
+ */
155
+ copyStepAttachments(steps) {
156
+ for (const step of steps) {
157
+ if (step.attachments.length > 0) {
158
+ step.attachments = this.writer.writeAttachment(step.attachments);
159
+ }
160
+ if (step.steps.length > 0) {
161
+ step.steps = this.copyStepAttachments(step.steps);
162
+ }
163
+ }
164
+ return steps;
165
+ }
166
+ }
167
+ exports.ReportReporter = ReportReporter;
@@ -0,0 +1,65 @@
1
+ import { AbstractReporter } from './abstract-reporter';
2
+ import { Attachment, TestResultType } from '../models';
3
+ import { LoggerInterface } from '../utils/logger';
4
+ import { IClient } from '../client/interface';
5
+ /**
6
+ * @class RunReporter
7
+ * @extends AbstractReporter
8
+ */
9
+ export declare class RunReporter extends AbstractReporter {
10
+ private api;
11
+ private withState;
12
+ /** Not read internally (no dashboard link without a workspace id) — kept for callers/telemetry. */
13
+ readonly projectCode: string;
14
+ private readonly batchSize;
15
+ private runId;
16
+ private firstIndex;
17
+ private isTestRunReady;
18
+ private mutex;
19
+ /**
20
+ * @param {LoggerInterface} logger
21
+ * @param {IClient} api
22
+ * @param {boolean} withState
23
+ * @param {string} projectCode
24
+ * @param {number | undefined} batchSize
25
+ */
26
+ constructor(logger: LoggerInterface, api: IClient, withState: boolean,
27
+ /** Not read internally (no dashboard link without a workspace id) — kept for callers/telemetry. */
28
+ projectCode: string, batchSize?: number, runId?: number);
29
+ /**
30
+ * @returns {Promise<void>}
31
+ */
32
+ startTestRun(): Promise<void>;
33
+ /**
34
+ * @param {TestResultType} result
35
+ * @returns {Promise<void>}
36
+ */
37
+ addTestResult(result: TestResultType): Promise<void>;
38
+ /**
39
+ * @returns {Promise<void>}
40
+ */
41
+ private checkOrCreateTestRun;
42
+ /**
43
+ * @returns {Promise<void>}
44
+ * @param testResults
45
+ * @private
46
+ */
47
+ private publishResults;
48
+ /**
49
+ * @returns {Promise<void>}
50
+ */
51
+ publish(): Promise<void>;
52
+ /**
53
+ * @returns {Promise<void>}
54
+ */
55
+ sendResults(): Promise<void>;
56
+ /**
57
+ * @param {Attachment} attachment
58
+ * @returns {Promise<string>}
59
+ */
60
+ uploadAttachment(attachment: Attachment): Promise<string>;
61
+ /**
62
+ * @returns {Promise<void>}
63
+ */
64
+ complete(): Promise<void>;
65
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RunReporter = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const abstract_reporter_1 = require("./abstract-reporter");
9
+ const models_1 = require("../models");
10
+ const state_1 = require("../state/state");
11
+ const async_mutex_1 = require("async-mutex");
12
+ const run_constants_1 = require("./shared/run-constants");
13
+ /**
14
+ * @class RunReporter
15
+ * @extends AbstractReporter
16
+ */
17
+ class RunReporter extends abstract_reporter_1.AbstractReporter {
18
+ api;
19
+ withState;
20
+ projectCode;
21
+ batchSize;
22
+ runId;
23
+ firstIndex = 0;
24
+ isTestRunReady = false;
25
+ mutex = new async_mutex_1.Mutex();
26
+ /**
27
+ * @param {LoggerInterface} logger
28
+ * @param {IClient} api
29
+ * @param {boolean} withState
30
+ * @param {string} projectCode
31
+ * @param {number | undefined} batchSize
32
+ */
33
+ constructor(logger, api, withState,
34
+ /** Not read internally (no dashboard link without a workspace id) — kept for callers/telemetry. */
35
+ projectCode, batchSize, runId) {
36
+ super(logger);
37
+ this.api = api;
38
+ this.withState = withState;
39
+ this.projectCode = projectCode;
40
+ this.batchSize = Math.min(batchSize ?? run_constants_1.DEFAULT_BATCH_SIZE, run_constants_1.MAX_BATCH_SIZE);
41
+ this.runId = runId;
42
+ }
43
+ /**
44
+ * @returns {Promise<void>}
45
+ */
46
+ async startTestRun() {
47
+ this.runId = undefined;
48
+ this.isTestRunReady = false;
49
+ this.firstIndex = 0;
50
+ this.results = [];
51
+ await this.checkOrCreateTestRun();
52
+ }
53
+ /**
54
+ * @param {TestResultType} result
55
+ * @returns {Promise<void>}
56
+ */
57
+ async addTestResult(result) {
58
+ if (result.execution.status === models_1.TestStatusEnum.failed) {
59
+ this.logger.logDebug(`Test run #${this.runId ?? 'unknown'}`);
60
+ }
61
+ const release = await this.mutex.acquire();
62
+ try {
63
+ await super.addTestResult(result);
64
+ if (!this.isTestRunReady) {
65
+ return;
66
+ }
67
+ const countOfResults = this.batchSize + this.firstIndex;
68
+ if (this.results.length >= countOfResults) {
69
+ const firstIndex = this.firstIndex;
70
+ this.firstIndex = countOfResults;
71
+ await this.publishResults(this.results.slice(firstIndex, countOfResults));
72
+ }
73
+ }
74
+ finally {
75
+ release();
76
+ }
77
+ }
78
+ /**
79
+ * @returns {Promise<void>}
80
+ */
81
+ async checkOrCreateTestRun() {
82
+ const runId = await this.api.createRun();
83
+ this.runId = runId;
84
+ process.env['TIDEN_RUN_ID'] = String(runId);
85
+ this.logger.log(`Test run created: #${runId}`);
86
+ if (this.withState) {
87
+ state_1.StateManager.setRunId(runId);
88
+ }
89
+ this.isTestRunReady = true;
90
+ }
91
+ /**
92
+ * @returns {Promise<void>}
93
+ * @param testResults
94
+ * @private
95
+ */
96
+ async publishResults(testResults) {
97
+ if (!this.runId) {
98
+ throw new Error('Run ID is not set');
99
+ }
100
+ await this.api.uploadResults(this.runId, testResults);
101
+ this.logger.logDebug(`Results sent to Tiden: ${testResults.length}`);
102
+ }
103
+ /**
104
+ * @returns {Promise<void>}
105
+ */
106
+ async publish() {
107
+ const release = await this.mutex.acquire();
108
+ try {
109
+ await this.sendResults();
110
+ }
111
+ finally {
112
+ release();
113
+ }
114
+ await this.complete();
115
+ }
116
+ /**
117
+ * @returns {Promise<void>}
118
+ */
119
+ async sendResults() {
120
+ if (this.results.length === 0) {
121
+ this.logger.log((0, chalk_1.default) `{yellow No results to send to Tiden}`);
122
+ return;
123
+ }
124
+ const remainingResults = this.results.slice(this.firstIndex);
125
+ if (this.firstIndex < this.results.length) {
126
+ if (remainingResults.length <= run_constants_1.DEFAULT_BATCH_SIZE) {
127
+ await this.publishResults(remainingResults);
128
+ return;
129
+ }
130
+ for (let i = 0; i < remainingResults.length; i += run_constants_1.DEFAULT_BATCH_SIZE) {
131
+ await this.publishResults(remainingResults.slice(i, i + run_constants_1.DEFAULT_BATCH_SIZE));
132
+ }
133
+ }
134
+ // Clear results because we don't need to send them again then we use Cypress reporter
135
+ this.results.length = 0;
136
+ }
137
+ /**
138
+ * @param {Attachment} attachment
139
+ * @returns {Promise<string>}
140
+ */
141
+ async uploadAttachment(attachment) {
142
+ return await this.api.uploadAttachment(attachment);
143
+ }
144
+ /**
145
+ * @returns {Promise<void>}
146
+ */
147
+ async complete() {
148
+ if (!this.runId) {
149
+ throw new Error('Run ID is not set');
150
+ }
151
+ await this.api.completeRun(this.runId);
152
+ this.logger.log((0, chalk_1.default) `{green Run ${this.runId} completed}`);
153
+ }
154
+ }
155
+ exports.RunReporter = RunReporter;