@testomatio/reporter 2.0.1-beta-2-ignore-xml → 2.0.1-beta.2

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 (66) hide show
  1. package/lib/adapter/codecept.js +0 -2
  2. package/lib/adapter/cypress-plugin/index.js +0 -2
  3. package/lib/adapter/mocha.js +0 -1
  4. package/lib/adapter/nightwatch.d.ts +4 -0
  5. package/lib/adapter/nightwatch.js +80 -0
  6. package/lib/adapter/webdriver.d.ts +1 -1
  7. package/lib/adapter/webdriver.js +18 -8
  8. package/lib/bin/cli.js +73 -8
  9. package/lib/bin/reportXml.js +4 -2
  10. package/lib/bin/startTest.js +3 -2
  11. package/lib/bin/uploadArtifacts.js +5 -4
  12. package/lib/client.js +18 -9
  13. package/lib/config.js +2 -2
  14. package/lib/data-storage.d.ts +1 -1
  15. package/lib/data-storage.js +17 -7
  16. package/lib/junit-adapter/csharp.d.ts +1 -0
  17. package/lib/junit-adapter/csharp.js +11 -1
  18. package/lib/pipe/bitbucket.d.ts +2 -0
  19. package/lib/pipe/bitbucket.js +38 -26
  20. package/lib/pipe/debug.js +17 -3
  21. package/lib/pipe/github.d.ts +2 -2
  22. package/lib/pipe/github.js +35 -3
  23. package/lib/pipe/gitlab.d.ts +2 -0
  24. package/lib/pipe/gitlab.js +27 -9
  25. package/lib/pipe/html.d.ts +1 -0
  26. package/lib/pipe/html.js +1 -3
  27. package/lib/pipe/index.js +17 -7
  28. package/lib/pipe/testomatio.d.ts +3 -2
  29. package/lib/pipe/testomatio.js +85 -75
  30. package/lib/replay.d.ts +31 -0
  31. package/lib/replay.js +237 -0
  32. package/lib/reporter.d.ts +12 -12
  33. package/lib/services/artifacts.d.ts +1 -1
  34. package/lib/services/key-values.d.ts +1 -1
  35. package/lib/services/logger.d.ts +1 -1
  36. package/lib/services/logger.js +1 -2
  37. package/lib/template/testomatio.hbs +443 -68
  38. package/lib/uploader.js +10 -6
  39. package/lib/utils/utils.d.ts +3 -1
  40. package/lib/utils/utils.js +54 -21
  41. package/lib/xmlReader.js +54 -19
  42. package/package.json +8 -9
  43. package/src/adapter/codecept.js +0 -2
  44. package/src/adapter/cypress-plugin/index.js +0 -2
  45. package/src/adapter/mocha.js +0 -1
  46. package/src/adapter/nightwatch.js +88 -0
  47. package/src/adapter/webdriver.js +2 -2
  48. package/src/bin/cli.js +70 -2
  49. package/src/bin/reportXml.js +4 -1
  50. package/src/bin/startTest.js +2 -1
  51. package/src/bin/uploadArtifacts.js +2 -1
  52. package/src/client.js +1 -2
  53. package/src/config.js +2 -2
  54. package/src/junit-adapter/csharp.js +13 -1
  55. package/src/pipe/bitbucket.js +22 -24
  56. package/src/pipe/debug.js +18 -3
  57. package/src/pipe/github.js +1 -2
  58. package/src/pipe/gitlab.js +27 -9
  59. package/src/pipe/html.js +3 -4
  60. package/src/pipe/testomatio.js +106 -105
  61. package/src/replay.js +245 -0
  62. package/src/services/logger.js +1 -2
  63. package/src/template/testomatio.hbs +443 -68
  64. package/src/uploader.js +11 -6
  65. package/src/utils/utils.js +31 -12
  66. package/src/xmlReader.js +69 -17
package/lib/replay.js ADDED
@@ -0,0 +1,237 @@
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.Replay = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const client_js_1 = __importDefault(require("./client.js"));
11
+ const constants_js_1 = require("./constants.js");
12
+ const config_js_1 = require("./config.js");
13
+ class Replay {
14
+ constructor(options = {}) {
15
+ this.apiKey = options.apiKey || config_js_1.config.TESTOMATIO || undefined;
16
+ this.dryRun = options.dryRun || false;
17
+ this.onProgress = options.onProgress || (() => { });
18
+ this.onLog = options.onLog || console.log;
19
+ this.onError = options.onError || console.error;
20
+ }
21
+ /**
22
+ * Get the default debug file path
23
+ * @returns {string} Path to the latest debug file
24
+ */
25
+ getDefaultDebugFile() {
26
+ return path_1.default.join(os_1.default.tmpdir(), 'testomatio.debug.latest.json');
27
+ }
28
+ /**
29
+ * Parse a debug file and extract test data
30
+ * @param {string} debugFile - Path to the debug file
31
+ * @returns {Object} Parsed debug data
32
+ */
33
+ parseDebugFile(debugFile) {
34
+ if (!fs_1.default.existsSync(debugFile)) {
35
+ throw new Error(`Debug file not found: ${debugFile}`);
36
+ }
37
+ const fileContent = fs_1.default.readFileSync(debugFile, 'utf-8');
38
+ const lines = fileContent.trim().split('\n').filter(line => line.trim() !== '');
39
+ if (lines.length === 0) {
40
+ throw new Error('Debug file is empty');
41
+ }
42
+ let runParams = {};
43
+ let finishParams = {};
44
+ let parseErrors = 0;
45
+ const testsMap = new Map(); // Use Map to deduplicate by rid
46
+ const testsWithoutRid = []; // For tests without rid (backward compatibility)
47
+ const envVars = {};
48
+ // Parse debug file line by line
49
+ for (const [lineIndex, line] of lines.entries()) {
50
+ try {
51
+ const logEntry = JSON.parse(line);
52
+ if (logEntry.data === 'variables' && logEntry.testomatioEnvVars) {
53
+ Object.assign(envVars, logEntry.testomatioEnvVars);
54
+ }
55
+ else if (logEntry.action === 'createRun') {
56
+ runParams = logEntry.params || {};
57
+ }
58
+ else if (logEntry.action === 'addTestsBatch' && logEntry.tests) {
59
+ // Process each test in the batch
60
+ for (const test of logEntry.tests) {
61
+ if (test.rid) {
62
+ // Handle tests with rid (deduplicate)
63
+ const existingTest = testsMap.get(test.rid);
64
+ if (existingTest) {
65
+ // Merge test data - prioritize non-null/non-empty values
66
+ const mergedTest = { ...existingTest };
67
+ Object.keys(test).forEach(key => {
68
+ if (test[key] !== null && test[key] !== undefined) {
69
+ if (key === 'files' && Array.isArray(test[key]) && test[key].length > 0) {
70
+ // Merge files arrays
71
+ mergedTest.files = [...(existingTest.files || []), ...test[key]];
72
+ }
73
+ else if (key === 'artifacts' && Array.isArray(test[key]) && test[key].length > 0) {
74
+ // Merge artifacts arrays
75
+ mergedTest.artifacts = [...(existingTest.artifacts || []), ...test[key]];
76
+ }
77
+ else if (existingTest[key] === null || existingTest[key] === undefined ||
78
+ (Array.isArray(existingTest[key]) && existingTest[key].length === 0)) {
79
+ // Use new value if existing is null/undefined/empty array
80
+ mergedTest[key] = test[key];
81
+ }
82
+ }
83
+ });
84
+ testsMap.set(test.rid, mergedTest);
85
+ }
86
+ else {
87
+ testsMap.set(test.rid, { ...test });
88
+ }
89
+ }
90
+ else {
91
+ // Handle tests without rid (no deduplication)
92
+ testsWithoutRid.push({ ...test });
93
+ }
94
+ }
95
+ }
96
+ else if (logEntry.action === 'addTest' && logEntry.testId) {
97
+ const test = logEntry.testId;
98
+ if (test.rid) {
99
+ // Handle tests with rid (deduplicate)
100
+ const existingTest = testsMap.get(test.rid);
101
+ if (existingTest) {
102
+ // Merge with existing test
103
+ const mergedTest = { ...existingTest, ...test };
104
+ testsMap.set(test.rid, mergedTest);
105
+ }
106
+ else {
107
+ testsMap.set(test.rid, { ...test });
108
+ }
109
+ }
110
+ else {
111
+ // Handle tests without rid (no deduplication)
112
+ testsWithoutRid.push({ ...test });
113
+ }
114
+ }
115
+ else if (logEntry.actions === 'finishRun') {
116
+ finishParams = logEntry.params || {};
117
+ }
118
+ }
119
+ catch (err) {
120
+ parseErrors++;
121
+ if (parseErrors <= 3) {
122
+ // Only show first 3 parse errors
123
+ this.onError(`Failed to parse line ${lineIndex + 1}: ${line.substring(0, 100)}...`);
124
+ }
125
+ }
126
+ }
127
+ if (parseErrors > 3) {
128
+ this.onError(`${parseErrors - 3} more parse errors occurred`);
129
+ }
130
+ // Combine tests with rid and tests without rid
131
+ const allTests = [...Array.from(testsMap.values()), ...testsWithoutRid];
132
+ return {
133
+ runParams,
134
+ finishParams,
135
+ tests: allTests,
136
+ envVars,
137
+ parseErrors,
138
+ totalLines: lines.length
139
+ };
140
+ }
141
+ /**
142
+ * Restore environment variables from debug data
143
+ * @param {Object} envVars - Environment variables to restore
144
+ */
145
+ restoreEnvironmentVariables(envVars) {
146
+ // Only restore env vars that aren't already set (don't override current values)
147
+ Object.keys(envVars).forEach(key => {
148
+ if (process.env[key] === undefined || process.env[key] === '') {
149
+ process.env[key] = envVars[key];
150
+ }
151
+ });
152
+ }
153
+ /**
154
+ * Replay test data to Testomat.io
155
+ * @param {string} debugFile - Path to debug file (optional, uses default if not provided)
156
+ * @returns {Promise<Object>} Replay results
157
+ */
158
+ async replay(debugFile) {
159
+ if (!debugFile) {
160
+ debugFile = this.getDefaultDebugFile();
161
+ }
162
+ if (!this.apiKey) {
163
+ throw new Error('TESTOMATIO API key not found. Set TESTOMATIO environment variable.');
164
+ }
165
+ this.onLog(`Replaying data from debug file: ${debugFile}`);
166
+ // Parse the debug file
167
+ const debugData = this.parseDebugFile(debugFile);
168
+ const { runParams, finishParams, tests, envVars } = debugData;
169
+ this.onLog(`Found ${tests.length} tests to replay`);
170
+ if (tests.length === 0) {
171
+ throw new Error('No test data found in debug file');
172
+ }
173
+ // Restore environment variables
174
+ this.restoreEnvironmentVariables(envVars);
175
+ if (this.dryRun) {
176
+ return {
177
+ success: true,
178
+ testsCount: tests.length,
179
+ runParams,
180
+ finishParams,
181
+ envVars,
182
+ dryRun: true
183
+ };
184
+ }
185
+ // Create client and restore the run
186
+ const client = new client_js_1.default({
187
+ apiKey: this.apiKey,
188
+ isBatchEnabled: true,
189
+ ...runParams,
190
+ });
191
+ this.onLog('Publishing to run...');
192
+ await client.createRun(runParams);
193
+ // Send each test result
194
+ let successCount = 0;
195
+ let failureCount = 0;
196
+ for (const [index, test] of tests.entries()) {
197
+ try {
198
+ await client.addTestRun(test.status, test);
199
+ successCount++;
200
+ this.onProgress({
201
+ current: index + 1,
202
+ total: tests.length,
203
+ test,
204
+ success: true
205
+ });
206
+ }
207
+ catch (err) {
208
+ failureCount++;
209
+ this.onError(`Failed to send test ${index + 1}: ${err.message}`);
210
+ this.onProgress({
211
+ current: index + 1,
212
+ total: tests.length,
213
+ test,
214
+ success: false,
215
+ error: err.message
216
+ });
217
+ }
218
+ }
219
+ await client.updateRunStatus(finishParams.status || constants_js_1.STATUS.FINISHED, finishParams.parallel || false);
220
+ const result = {
221
+ success: true,
222
+ testsCount: tests.length,
223
+ successCount,
224
+ failureCount,
225
+ runParams,
226
+ finishParams,
227
+ envVars,
228
+ runId: client.runId
229
+ };
230
+ this.onLog(`Successfully replayed ${successCount}/${tests.length} tests from debug file`);
231
+ return result;
232
+ }
233
+ }
234
+ exports.Replay = Replay;
235
+ module.exports = Replay;
236
+
237
+ module.exports.Replay = Replay;
package/lib/reporter.d.ts CHANGED
@@ -8,7 +8,7 @@ export type log = typeof import("./reporter-functions.js");
8
8
  export const log: (...args: any[]) => void;
9
9
  export type logger = typeof import("./services/index.js");
10
10
  export const logger: {
11
- "__#12@#originalUserLogger": {
11
+ "__#13@#originalUserLogger": {
12
12
  assert(condition?: boolean, ...data: any[]): void;
13
13
  assert(value: any, message?: string, ...optionalParams: any[]): void;
14
14
  clear(): void;
@@ -53,13 +53,13 @@ export const logger: {
53
53
  profile(label?: string): void;
54
54
  profileEnd(label?: string): void;
55
55
  };
56
- "__#12@#userLoggerWithOverridenMethods": any;
56
+ "__#13@#userLoggerWithOverridenMethods": any;
57
57
  logLevel: string;
58
58
  step(strings: any, ...values: any[]): void;
59
59
  getLogs(context: string): string[];
60
- "__#12@#stringifyLogs"(...args: any[]): string;
60
+ "__#13@#stringifyLogs"(...args: any[]): string;
61
61
  _templateLiteralLog(strings: any, ...args: any[]): void;
62
- "__#12@#logWrapper"(argsArray: any, level: any): void;
62
+ "__#13@#logWrapper"(argsArray: any, level: any): void;
63
63
  assert(...args: any[]): void;
64
64
  debug(...args: any[]): void;
65
65
  error(...args: any[]): void;
@@ -83,7 +83,7 @@ export type step = typeof import("./reporter-functions.js");
83
83
  export const step: (message: string) => void;
84
84
  declare namespace _default {
85
85
  let testomatioLogger: {
86
- "__#12@#originalUserLogger": {
86
+ "__#13@#originalUserLogger": {
87
87
  assert(condition?: boolean, ...data: any[]): void;
88
88
  assert(value: any, message?: string, ...optionalParams: any[]): void;
89
89
  clear(): void;
@@ -128,13 +128,13 @@ declare namespace _default {
128
128
  profile(label?: string): void;
129
129
  profileEnd(label?: string): void;
130
130
  };
131
- "__#12@#userLoggerWithOverridenMethods": any;
131
+ "__#13@#userLoggerWithOverridenMethods": any;
132
132
  logLevel: string;
133
133
  step(strings: any, ...values: any[]): void;
134
134
  getLogs(context: string): string[];
135
- "__#12@#stringifyLogs"(...args: any[]): string;
135
+ "__#13@#stringifyLogs"(...args: any[]): string;
136
136
  _templateLiteralLog(strings: any, ...args: any[]): void;
137
- "__#12@#logWrapper"(argsArray: any, level: any): void;
137
+ "__#13@#logWrapper"(argsArray: any, level: any): void;
138
138
  assert(...args: any[]): void;
139
139
  debug(...args: any[]): void;
140
140
  error(...args: any[]): void;
@@ -157,7 +157,7 @@ declare namespace _default {
157
157
  }, context?: any) => void;
158
158
  let log: (...args: any[]) => void;
159
159
  let logger: {
160
- "__#12@#originalUserLogger": {
160
+ "__#13@#originalUserLogger": {
161
161
  assert(condition?: boolean, ...data: any[]): void;
162
162
  assert(value: any, message?: string, ...optionalParams: any[]): void;
163
163
  clear(): void;
@@ -202,13 +202,13 @@ declare namespace _default {
202
202
  profile(label?: string): void;
203
203
  profileEnd(label?: string): void;
204
204
  };
205
- "__#12@#userLoggerWithOverridenMethods": any;
205
+ "__#13@#userLoggerWithOverridenMethods": any;
206
206
  logLevel: string;
207
207
  step(strings: any, ...values: any[]): void;
208
208
  getLogs(context: string): string[];
209
- "__#12@#stringifyLogs"(...args: any[]): string;
209
+ "__#13@#stringifyLogs"(...args: any[]): string;
210
210
  _templateLiteralLog(strings: any, ...args: any[]): void;
211
- "__#12@#logWrapper"(argsArray: any, level: any): void;
211
+ "__#13@#logWrapper"(argsArray: any, level: any): void;
212
212
  assert(...args: any[]): void;
213
213
  debug(...args: any[]): void;
214
214
  error(...args: any[]): void;
@@ -3,7 +3,7 @@ export const artifactStorage: ArtifactStorage;
3
3
  * Artifact storage is supposed to store file paths
4
4
  */
5
5
  declare class ArtifactStorage {
6
- static "__#13@#instance": any;
6
+ static "__#14@#instance": any;
7
7
  /**
8
8
  * Singleton
9
9
  * @returns {ArtifactStorage}
@@ -1,6 +1,6 @@
1
1
  export const keyValueStorage: KeyValueStorage;
2
2
  declare class KeyValueStorage {
3
- static "__#14@#instance": any;
3
+ static "__#15@#instance": any;
4
4
  /**
5
5
  *
6
6
  * @returns {KeyValueStorage}
@@ -5,7 +5,7 @@ export const logger: Logger;
5
5
  * Supports different syntaxes to satisfy any user preferences.
6
6
  */
7
7
  declare class Logger {
8
- static "__#12@#instance": any;
8
+ static "__#13@#instance": any;
9
9
  /**
10
10
  *
11
11
  * @returns {Logger}
@@ -90,7 +90,6 @@ class Logger {
90
90
  }
91
91
  else {
92
92
  try {
93
- // eslint-disable-next-line no-unused-expressions
94
93
  this.prettyObjects ? logs.push(JSON.stringify(arg, null, 2)) : logs.push(JSON.stringify(arg));
95
94
  }
96
95
  catch (e) {
@@ -119,7 +118,7 @@ class Logger {
119
118
  current +
120
119
  // strings are splitted by args when use tagged template, thus we add arg after each string
121
120
  // it looks like: `string1 arg1 string2 arg2 string3`
122
- (args[index] !== undefined // eslint-disable-line no-nested-ternary
121
+ (args[index] !== undefined
123
122
  ? typeof args[index] === 'string'
124
123
  ? args[index] // add arg as it is
125
124
  : this.#stringifyLogs(args[index]) // stringify arg