@testomatio/reporter 2.1.3-beta.3-multi-links → 2.3.0-beta.1-links

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 (40) hide show
  1. package/lib/adapter/codecept.js +5 -3
  2. package/lib/adapter/cucumber/current.js +2 -0
  3. package/lib/adapter/jest.js +2 -0
  4. package/lib/adapter/mocha.js +14 -0
  5. package/lib/adapter/playwright.js +2 -0
  6. package/lib/adapter/webdriver.js +8 -9
  7. package/lib/bin/startTest.js +38 -91
  8. package/lib/client.js +4 -32
  9. package/lib/data-storage.d.ts +4 -4
  10. package/lib/data-storage.js +11 -12
  11. package/lib/pipe/testomatio.js +3 -3
  12. package/lib/reporter-functions.d.ts +26 -6
  13. package/lib/reporter-functions.js +36 -35
  14. package/lib/reporter.d.ts +10 -8
  15. package/lib/reporter.js +9 -7
  16. package/lib/services/index.d.ts +2 -2
  17. package/lib/services/index.js +2 -2
  18. package/lib/services/labels.d.ts +0 -22
  19. package/lib/services/labels.js +0 -62
  20. package/lib/services/links.d.ts +1 -1
  21. package/lib/utils/utils.js +3 -1
  22. package/package.json +1 -1
  23. package/src/adapter/codecept.js +6 -4
  24. package/src/adapter/cucumber/current.js +2 -0
  25. package/src/adapter/jest.js +2 -0
  26. package/src/adapter/mocha.js +15 -0
  27. package/src/adapter/playwright.js +2 -0
  28. package/src/adapter/webdriver.js +8 -9
  29. package/src/bin/startTest.js +43 -114
  30. package/src/client.js +4 -32
  31. package/src/data-storage.js +11 -14
  32. package/src/pipe/testomatio.js +3 -3
  33. package/src/reporter-functions.js +36 -38
  34. package/src/reporter.js +8 -6
  35. package/src/services/index.js +2 -2
  36. package/src/services/labels.js +0 -58
  37. package/src/services/links.js +69 -0
  38. package/src/utils/utils.js +5 -3
  39. package/lib/utils/cli_utils.d.ts +0 -1
  40. package/lib/utils/cli_utils.js +0 -524304
@@ -26,6 +26,8 @@ const HOOK_EXECUTION_ORDER = {
26
26
  PRE_TEST: ['BeforeSuiteHook', 'BeforeHook'],
27
27
  POST_TEST: ['AfterHook', 'AfterSuiteHook']
28
28
  };
29
+ // codeceptjs workers are self-contained
30
+ data_storage_js_1.dataStorage.isFileStorage = false;
29
31
  const DATA_REGEXP = /[|\s]+?(\{".*\}|\[.*\])/;
30
32
  if (MAJOR_VERSION < 3) {
31
33
  console.log('🔴 This reporter works with CodeceptJS 3+, please update your tests');
@@ -48,7 +50,6 @@ function CodeceptReporter(config) {
48
50
  step: output.step,
49
51
  say: output.say,
50
52
  };
51
- output.stepShift = 0;
52
53
  output.debug = function (msg) {
53
54
  originalOutput.debug(msg);
54
55
  data_storage_js_1.dataStorage.putData('log', repeat(this?.stepShift || 0) + picocolors_1.default.cyan(msg.toString()));
@@ -62,6 +63,7 @@ function CodeceptReporter(config) {
62
63
  originalOutput.log(msg);
63
64
  data_storage_js_1.dataStorage.putData('log', repeat(this?.stepShift || 0) + picocolors_1.default.gray(msg));
64
65
  };
66
+ output.stepShift = 0;
65
67
  recorder.startUnlessRunning();
66
68
  const hookSteps = new Map();
67
69
  let currentHook = null;
@@ -140,7 +142,7 @@ function CodeceptReporter(config) {
140
142
  const manuallyAttachedArtifacts = index_js_1.services.artifacts.get(test.fullTitle());
141
143
  const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
142
144
  const stepHierarchy = buildUnifiedStepHierarchy(test.steps, hookSteps);
143
- const labels = index_js_1.services.labels.get(test.fullTitle());
145
+ const links = index_js_1.services.links.get(test.fullTitle());
144
146
  index_js_1.services.setContext(null);
145
147
  client.addTestRun(test.state, {
146
148
  ...stripExampleFromTitle(title),
@@ -153,7 +155,7 @@ function CodeceptReporter(config) {
153
155
  files,
154
156
  steps: stepHierarchy, // Array of step objects per API schema
155
157
  logs,
156
- labels,
158
+ links,
157
159
  manuallyAttachedArtifacts,
158
160
  meta: { ...keyValues, ...test.meta },
159
161
  });
@@ -105,6 +105,7 @@ class CucumberReporter extends cucumber_1.Formatter {
105
105
  const logs = index_js_1.services.logger.getLogs(testTitle).join('\n');
106
106
  const artifacts = index_js_1.services.artifacts.get(testTitle);
107
107
  const keyValues = index_js_1.services.keyValues.get(testTitle);
108
+ const links = index_js_1.services.links.get(testTitle);
108
109
  this.client.addTestRun(status, {
109
110
  // error: testCaseAttempt.worstTestStepResult.message,
110
111
  message,
@@ -114,6 +115,7 @@ class CucumberReporter extends cucumber_1.Formatter {
114
115
  .trim(),
115
116
  example: { ...example },
116
117
  logs,
118
+ links,
117
119
  manuallyAttachedArtifacts: artifacts,
118
120
  meta: keyValues,
119
121
  title: scenario,
@@ -57,6 +57,7 @@ class JestReporter {
57
57
  const logs = getTestLogs(result);
58
58
  const artifacts = index_js_1.services.artifacts.get(result.fullName);
59
59
  const keyValues = index_js_1.services.keyValues.get(result.fullName);
60
+ const links = index_js_1.services.links.get(result.fullName);
60
61
  const deducedStatus = status === 'pending' ? 'skipped' : status;
61
62
  // In jest if test is not matched with test name pattern it is considered as skipped.
62
63
  // So adding a check if it is skipped for real or because of test pattern
@@ -69,6 +70,7 @@ class JestReporter {
69
70
  title,
70
71
  time: duration,
71
72
  logs,
73
+ links,
72
74
  manuallyAttachedArtifacts: artifacts,
73
75
  meta: keyValues,
74
76
  });
@@ -43,6 +43,7 @@ function MochaReporter(runner, opts) {
43
43
  const logs = getTestLogs(test);
44
44
  const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
45
45
  const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
46
+ const links = index_js_1.services.links.get(test.fullTitle());
46
47
  client.addTestRun(constants_js_1.STATUS.PASSED, {
47
48
  test_id: testId,
48
49
  suite_title: getSuiteTitle(test),
@@ -53,12 +54,16 @@ function MochaReporter(runner, opts) {
53
54
  logs,
54
55
  manuallyAttachedArtifacts: artifacts,
55
56
  meta: keyValues,
57
+ links,
56
58
  });
57
59
  });
58
60
  runner.on(EVENT_TEST_PENDING, test => {
59
61
  skipped += 1;
60
62
  console.log('skip: %s', test.fullTitle());
61
63
  const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
64
+ const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
65
+ const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
66
+ const links = index_js_1.services.links.get(test.fullTitle());
62
67
  client.addTestRun(constants_js_1.STATUS.SKIPPED, {
63
68
  title: getTestName(test),
64
69
  suite_title: getSuiteTitle(test),
@@ -66,6 +71,9 @@ function MochaReporter(runner, opts) {
66
71
  file: getFile(test),
67
72
  test_id: testId,
68
73
  time: test.duration,
74
+ manuallyAttachedArtifacts: artifacts,
75
+ meta: keyValues,
76
+ links,
69
77
  });
70
78
  });
71
79
  runner.on(EVENT_TEST_FAIL, async (test, err) => {
@@ -73,6 +81,9 @@ function MochaReporter(runner, opts) {
73
81
  console.log(picocolors_1.default.bold(picocolors_1.default.red('✖')), test.fullTitle(), picocolors_1.default.gray(err.message));
74
82
  const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
75
83
  const logs = getTestLogs(test);
84
+ const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
85
+ const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
86
+ const links = index_js_1.services.links.get(test.fullTitle());
76
87
  client.addTestRun(constants_js_1.STATUS.FAILED, {
77
88
  error: err,
78
89
  suite_title: getSuiteTitle(test),
@@ -82,6 +93,9 @@ function MochaReporter(runner, opts) {
82
93
  code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
83
94
  time: test.duration,
84
95
  logs,
96
+ manuallyAttachedArtifacts: artifacts,
97
+ meta: keyValues,
98
+ links,
85
99
  });
86
100
  });
87
101
  runner.on(EVENT_RUN_END, () => {
@@ -55,6 +55,7 @@ class PlaywrightReporter {
55
55
  }
56
56
  const manuallyAttachedArtifacts = index_js_1.services.artifacts.get(fullTestTitle);
57
57
  const testMeta = index_js_1.services.keyValues.get(fullTestTitle);
58
+ const links = index_js_1.services.links.get(fullTestTitle);
58
59
  const rid = test.id || test.testId || (0, uuid_1.v4)();
59
60
  /**
60
61
  * @type {{
@@ -91,6 +92,7 @@ class PlaywrightReporter {
91
92
  steps: steps.length ? steps : undefined,
92
93
  time: duration,
93
94
  logs,
95
+ links,
94
96
  manuallyAttachedArtifacts,
95
97
  meta: {
96
98
  browser: project.browser,
@@ -77,12 +77,10 @@ class WebdriverReporter extends reporter_1.default {
77
77
  onTestEnd(test) {
78
78
  test.suite = test.parent;
79
79
  const logs = getTestLogs(test.fullTitle);
80
- // TODO: FIX: artifacts for some reason leads to empty report on Testomat.io
81
- // const artifacts = services.artifacts.get(test.fullTitle);
82
- // const keyValues = services.keyValues.get(test.fullTitle);
80
+ test.artifacts = index_js_1.services.artifacts.get(test.fullTitle);
81
+ test.meta = index_js_1.services.keyValues.get(test.fullTitle);
82
+ test.links = index_js_1.services.links.get(test.fullTitle);
83
83
  test.logs = logs;
84
- // test.artifacts = artifacts;
85
- // test.meta = keyValues;
86
84
  this._addTestPromises.push(this.addTest(test));
87
85
  }
88
86
  // wdio-cucumber does not trigger onTestEnd hook, thus, using this one
@@ -94,7 +92,7 @@ class WebdriverReporter extends reporter_1.default {
94
92
  async addTest(test) {
95
93
  if (!this.client)
96
94
  return;
97
- const { title, _duration: duration, state, error, output } = test;
95
+ const { title, _duration: duration, state, error, output, links, artifacts, meta, logs } = test;
98
96
  const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(title);
99
97
  const screenshotEndpoint = '/session/:sessionId/screenshot';
100
98
  const screenshotsBuffers = output
@@ -102,10 +100,11 @@ class WebdriverReporter extends reporter_1.default {
102
100
  .map(el => Buffer.from(el.result.value, 'base64'));
103
101
  await this.client.addTestRun(state, {
104
102
  rid: test.uid || '',
105
- manuallyAttachedArtifacts: test.artifacts,
103
+ manuallyAttachedArtifacts: artifacts,
106
104
  error,
107
- logs: test.logs,
108
- meta: test.meta,
105
+ logs,
106
+ meta,
107
+ links,
109
108
  title,
110
109
  test_id: testId,
111
110
  time: duration,
@@ -4,103 +4,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- const cross_spawn_1 = require("cross-spawn");
8
- const commander_1 = require("commander");
9
- const picocolors_1 = __importDefault(require("picocolors"));
10
- const client_js_1 = __importDefault(require("../client.js"));
11
- const constants_js_1 = require("../constants.js");
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_path_1 = require("node:path");
12
9
  const utils_js_1 = require("../utils/utils.js");
13
- const config_js_1 = require("../config.js");
14
- const dotenv_1 = __importDefault(require("dotenv"));
10
+ const picocolors_1 = __importDefault(require("picocolors"));
11
+ // Define __dirname - this will be replaced by build script with actual __dirname for CommonJS
12
+ const cliPath = (0, node_path_1.join)(__dirname, 'cli.js');
15
13
  const version = (0, utils_js_1.getPackageVersion)();
16
14
  console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
17
- const program = new commander_1.Command();
18
- program
19
- .option('-c, --command <cmd>', 'Test runner command')
20
- .option('--launch', 'Start a new run and return its ID')
21
- .option('--finish', 'Finish Run by its ID')
22
- .option('--env-file <envfile>', 'Load environment variables from env file')
23
- .option('--filter <filter>', 'Additional execution filter')
24
- .action(async (opts) => {
25
- const { launch, finish, filter } = opts;
26
- let { command } = opts;
27
- if (opts.envFile)
28
- dotenv_1.default.config({ path: opts.envFile });
29
- const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
30
- const title = process.env.TESTOMATIO_TITLE;
31
- if (launch) {
32
- console.log('Starting a new Run on Testomat.io...');
33
- const client = new client_js_1.default({ apiKey });
34
- client.createRun().then(() => {
35
- console.log(process.env.runId);
36
- process.exit(0);
37
- });
38
- return;
39
- }
40
- if (finish) {
41
- // TODO: add error in case of TESTOMATIO environment variable is not set
42
- // because command is fine in console, but actually (on testomat.io) run is not finished
43
- if (!process.env.TESTOMATIO_RUN) {
44
- console.log('TESTOMATIO_RUN environment variable must be set.');
45
- return process.exit(1);
15
+ // Parse command line arguments to map start-test-run options to @testomatio/reporter run format
16
+ const args = process.argv.slice(2);
17
+ const newArgs = ['run'];
18
+ let i = 0;
19
+ while (i < args.length) {
20
+ const arg = args[i];
21
+ if (arg === '-c' || arg === '--command') {
22
+ // Map -c/--command to positional argument for run command
23
+ i++;
24
+ if (i < args.length) {
25
+ newArgs.push(args[i]);
46
26
  }
47
- console.log('Finishing Run on Testomat.io...');
48
- const client = new client_js_1.default({ apiKey });
49
- // @ts-ignore
50
- client.updateRunStatus(constants_js_1.STATUS.FINISHED).then(() => {
51
- console.log(picocolors_1.default.yellow(`Run ${process.env.TESTOMATIO_RUN} was finished`));
52
- process.exit(0);
53
- });
54
- return;
55
27
  }
56
- let exitCode = 0;
57
- if (!command.split) {
58
- process.exitCode = 255;
59
- console.log(constants_js_1.APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
60
- return;
28
+ else if (arg.startsWith('--command=')) {
29
+ // Handle --command=value format
30
+ const command = arg.split('=', 2)[1];
31
+ newArgs.push(command);
61
32
  }
62
- const client = new client_js_1.default({ apiKey, title, parallel: true });
63
- if (filter) {
64
- const [pipe, ...optsArray] = filter.split(':');
65
- const pipeOptions = optsArray.join(':');
66
- try {
67
- const tests = await client.prepareRun({ pipe, pipeOptions });
68
- if (!tests || tests.length === 0) {
69
- return;
70
- }
71
- const grep = ` --grep (${tests.join('|')})`;
72
- command += grep;
73
- }
74
- catch (err) {
75
- console.log(constants_js_1.APP_PREFIX, err);
76
- }
33
+ else if (arg === '--launch') {
34
+ // Map --launch to start command
35
+ newArgs[0] = 'start';
77
36
  }
78
- const testCmds = command.split(' ');
79
- console.log(constants_js_1.APP_PREFIX, `🚀 Running`, picocolors_1.default.green(command));
80
- if (!apiKey) {
81
- const cmd = (0, cross_spawn_1.spawn)(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
82
- cmd.on('close', code => {
83
- console.log(constants_js_1.APP_PREFIX, '⚠️ ', `Runner exited with ${picocolors_1.default.bold(code)}, report is ignored`);
84
- if (code > exitCode)
85
- exitCode = code;
86
- process.exitCode = exitCode;
87
- });
88
- return;
37
+ else if (arg === '--finish') {
38
+ // Map --finish to finish command
39
+ newArgs[0] = 'finish';
89
40
  }
90
- client.createRun().then(() => {
91
- const cmd = (0, cross_spawn_1.spawn)(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
92
- cmd.on('close', code => {
93
- const emoji = code === 0 ? '🟢' : '🔴';
94
- console.log(constants_js_1.APP_PREFIX, emoji, `Runner exited with ${picocolors_1.default.bold(code)}`);
95
- const status = code === 0 ? 'passed' : 'failed';
96
- client.updateRunStatus(status, true);
97
- if (code > exitCode)
98
- exitCode = code;
99
- process.exitCode = exitCode;
100
- });
101
- });
102
- });
103
- if (process.argv.length <= 2) {
104
- program.outputHelp();
41
+ else {
42
+ // Pass through other arguments
43
+ newArgs.push(arg);
44
+ }
45
+ i++;
105
46
  }
106
- program.parse(process.argv);
47
+ // Execute the main CLI with mapped arguments
48
+ const child = (0, node_child_process_1.spawn)(process.execPath, [cliPath, ...newArgs], {
49
+ stdio: 'inherit'
50
+ });
51
+ child.on('exit', (code) => {
52
+ process.exit(code);
53
+ });
package/lib/client.js CHANGED
@@ -181,46 +181,18 @@ class Client {
181
181
  /**
182
182
  * @type {TestData}
183
183
  */
184
- const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, manuallyAttachedArtifacts, labels, overwrite, } = testData;
184
+ const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, } = testData;
185
185
  let { message = '', meta = {} } = testData;
186
186
  // stringify meta values and limit keys and values length to 255
187
187
  meta = Object.entries(meta)
188
188
  .filter(([, value]) => value !== null && value !== undefined)
189
- .map(([key, value]) => {
190
- try {
191
- if (typeof value === 'object') {
192
- value = JSON.stringify(value);
193
- }
194
- else if (typeof value !== 'string') {
195
- try {
196
- value = value.toString();
197
- }
198
- catch (err) {
199
- console.warn(constants_js_1.APP_PREFIX, `Can't convert meta value to string`, err);
200
- }
201
- }
202
- if (value?.length > 255) {
203
- value = value.substring(0, 255);
204
- debug(constants_js_1.APP_PREFIX, `Meta info value "${value}" is too long, trimmed to 255 characters`);
205
- }
206
- if (key?.length > 255) {
207
- const newKey = key.substring(0, 255);
208
- debug(constants_js_1.APP_PREFIX, `Meta info key "${key}" is too long, trimmed to 255 characters`);
209
- return [newKey, value];
210
- }
211
- return [key, value];
212
- }
213
- catch (err) {
214
- debug(constants_js_1.APP_PREFIX, `Error while processing meta info key ${key}`, err);
215
- return [null, null];
216
- }
217
- })
218
189
  .reduce((acc, [key, value]) => {
219
190
  if (key)
220
191
  acc[key] = value;
221
192
  return acc;
222
193
  }, {});
223
- // Labels are simple array of strings, no processing needed
194
+ // Get links from storage using the test context
195
+ const testContext = suite_title ? `${suite_title} ${title}` : title;
224
196
  let errorFormatted = '';
225
197
  if (error) {
226
198
  errorFormatted += this.formatError(error) || '';
@@ -268,7 +240,7 @@ class Client {
268
240
  timestamp,
269
241
  artifacts,
270
242
  meta,
271
- labels,
243
+ links,
272
244
  overwrite,
273
245
  ...(rootSuiteId && { root_suite_id: rootSuiteId }),
274
246
  };
@@ -12,22 +12,22 @@ declare class DataStorage {
12
12
  /**
13
13
  * Puts any data to storage (file or global variable).
14
14
  * If file: stores data as text, if global variable – stores as array of data.
15
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
15
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
16
16
  * @param {*} data anything you want to store (string, object, array, etc)
17
17
  * @param {*} context could be testId or any context (test name, suite name, including their IDs etc)
18
18
  * suite name + test name is used by default
19
19
  * @returns
20
20
  */
21
- putData(dataType: "log" | "artifact" | "keyvalue" | "labels", data: any, context?: any): void;
21
+ putData(dataType: "log" | "artifact" | "keyvalue" | "links", data: any, context?: any): void;
22
22
  /**
23
23
  * Returns data, stored for specific test/context (or data which was stored without test id specified).
24
24
  * This method will get data from global variable and/or from from file (previosly saved with put method).
25
25
  *
26
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
26
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
27
27
  * @param {string} context
28
28
  * @returns {any []} array of data (any type), null (if no data found for context) or string (if data type is log)
29
29
  */
30
- getData(dataType: "log" | "artifact" | "keyvalue" | "labels", context: string): any[];
30
+ getData(dataType: "log" | "artifact" | "keyvalue" | "links", context: string): any[];
31
31
  #private;
32
32
  }
33
33
  export function stringToMD5Hash(str: any): string;
@@ -45,7 +45,6 @@ const os_1 = __importDefault(require("os"));
45
45
  const constants_js_1 = require("./constants.js");
46
46
  const utils_js_1 = require("./utils/utils.js");
47
47
  const crypto_1 = __importDefault(require("crypto"));
48
- const startTime = Date.now();
49
48
  const debug = (0, debug_1.default)('@testomatio/reporter:storage');
50
49
  class DataStorage {
51
50
  static #instance;
@@ -76,7 +75,7 @@ class DataStorage {
76
75
  /**
77
76
  * Puts any data to storage (file or global variable).
78
77
  * If file: stores data as text, if global variable – stores as array of data.
79
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
78
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
80
79
  * @param {*} data anything you want to store (string, object, array, etc)
81
80
  * @param {*} context could be testId or any context (test name, suite name, including their IDs etc)
82
81
  * suite name + test name is used by default
@@ -104,7 +103,7 @@ class DataStorage {
104
103
  * Returns data, stored for specific test/context (or data which was stored without test id specified).
105
104
  * This method will get data from global variable and/or from from file (previosly saved with put method).
106
105
  *
107
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
106
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
108
107
  * @param {string} context
109
108
  * @returns {any []} array of data (any type), null (if no data found for context) or string (if data type is log)
110
109
  */
@@ -135,7 +134,7 @@ class DataStorage {
135
134
  return null;
136
135
  }
137
136
  /**
138
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
137
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
139
138
  * @param {string} context
140
139
  * @returns aray of data (any type)
141
140
  */
@@ -144,7 +143,7 @@ class DataStorage {
144
143
  if (global?.testomatioDataStore[dataType]) {
145
144
  const testData = global.testomatioDataStore[dataType][context];
146
145
  if (testData)
147
- debug(`"${dataType}" data for constext "${context}":`, testData.join(', '));
146
+ debug('<=', dataType, 'global', context, testData);
148
147
  return testData || [];
149
148
  }
150
149
  // debug(`No ${this.dataType} data for context ${context} in <global> storage`);
@@ -155,7 +154,7 @@ class DataStorage {
155
154
  }
156
155
  }
157
156
  /**
158
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
157
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
159
158
  * @param {*} context
160
159
  * @returns array of data (any type)
161
160
  */
@@ -166,7 +165,7 @@ class DataStorage {
166
165
  if (fs_1.default.existsSync(filepath)) {
167
166
  const testDataAsText = fs_1.default.readFileSync(filepath, 'utf-8');
168
167
  if (testDataAsText)
169
- debug(`"${dataType}" data for context "${context}":`, testDataAsText);
168
+ debug('<=', dataType, 'file', context, testDataAsText);
170
169
  const testDataArr = testDataAsText?.split(os_1.default.EOL) || [];
171
170
  return testDataArr;
172
171
  }
@@ -180,12 +179,12 @@ class DataStorage {
180
179
  }
181
180
  /**
182
181
  * Puts data to global variable. Unlike the file storage, stores data in array (file storage just append as string).
183
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
182
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
184
183
  * @param {*} data
185
184
  * @param {*} context
186
185
  */
187
186
  #putDataToGlobalVar(dataType, data, context) {
188
- debug('Saving data to global variable for ', context, ':', data);
187
+ debug('=>', dataType, 'global', context, data);
189
188
  if (!global.testomatioDataStore)
190
189
  global.testomatioDataStore = {};
191
190
  if (!global.testomatioDataStore?.[dataType])
@@ -196,7 +195,7 @@ class DataStorage {
196
195
  }
197
196
  /**
198
197
  * Puts data to file. Unlike the global variable storage, stores data as string
199
- * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
198
+ * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
200
199
  * @param {*} data
201
200
  * @param {string} context
202
201
  * @returns
@@ -209,7 +208,7 @@ class DataStorage {
209
208
  const filepath = (0, path_1.join)(dataDirPath, filename);
210
209
  if (!fs_1.default.existsSync(dataDirPath))
211
210
  utils_js_1.fileSystem.createDir(dataDirPath);
212
- debug(`Saving data to file for context "${context}" to ${filepath}. Data: ${JSON.stringify(data)}`);
211
+ debug('=>', dataType, 'file', context, data);
213
212
  // append new line if file already exists (in this case its definitely includes some data)
214
213
  if (fs_1.default.existsSync(filepath)) {
215
214
  fs_1.default.appendFileSync(filepath, os_1.default.EOL + data, 'utf-8');
@@ -223,7 +222,7 @@ function stringToMD5Hash(str) {
223
222
  const md5 = crypto_1.default.createHash('md5');
224
223
  md5.update(str);
225
224
  const hash = md5.digest('hex');
226
- return `${startTime}_${hash}`;
225
+ return `${process.env.runId || 'run'}_${hash}`;
227
226
  }
228
227
  exports.dataStorage = DataStorage.getInstance();
229
228
  // TODO: consider using fs promises instead of writeSync/appendFileSync to
@@ -113,8 +113,7 @@ class TestomatioPipe {
113
113
  const resp = await this.client.request({
114
114
  method: 'GET',
115
115
  url: '/api/test_grep',
116
- params: q.params,
117
- responseType: q.responseType
116
+ ...q,
118
117
  });
119
118
  if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
120
119
  (0, utils_js_1.foundedTestLog)(constants_js_1.APP_PREFIX, resp.data.tests);
@@ -173,7 +172,8 @@ class TestomatioPipe {
173
172
  const resp = await this.client.request({
174
173
  method: 'PUT',
175
174
  url: `/api/reporter/${this.runId}`,
176
- data: runParams
175
+ data: runParams,
176
+ responseType: 'json'
177
177
  });
178
178
  if (resp.data.artifacts)
179
179
  (0, pipe_utils_js_1.setS3Credentials)(resp.data.artifacts);
@@ -4,11 +4,15 @@ declare namespace _default {
4
4
  export { addStep as step };
5
5
  export { setKeyValue as keyValue };
6
6
  export { setLabel as label };
7
+ export { linkTest };
8
+ export { linkJira };
7
9
  }
8
10
  export default _default;
9
11
  /**
10
12
  * Stores path to file as artifact and uploads it to the S3 storage
11
13
  * @param {string | {path: string, type: string, name: string}} data - path to file or object with path, type and name
14
+ * @param {any} [context=null] - optional context parameter
15
+ * @returns {void}
12
16
  */
13
17
  declare function saveArtifact(data: string | {
14
18
  path: string;
@@ -17,18 +21,21 @@ declare function saveArtifact(data: string | {
17
21
  }, context?: any): void;
18
22
  /**
19
23
  * Attach log message(s) to the test report
20
- * @param string
24
+ * @param {...any} args - log messages to attach
25
+ * @returns {void}
21
26
  */
22
27
  declare function logMessage(...args: any[]): void;
23
28
  /**
24
29
  * Similar to "log" function but marks message in report as a step
25
- * @param {string} message
30
+ * @param {string} message - step message
31
+ * @returns {void}
26
32
  */
27
33
  declare function addStep(message: string): void;
28
34
  /**
29
35
  * Add key-value pair(s) to the test report
30
- * @param {{[key: string]: string} | string} keyValue object { key: value } (multiple props allowed) or key (string)
31
- * @param {string?} value
36
+ * @param {{[key: string]: string} | string} keyValue - object { key: value } (multiple props allowed) or key (string)
37
+ * @param {string|null} [value=null] - optional value when keyValue is a string
38
+ * @returns {void}
32
39
  */
33
40
  declare function setKeyValue(keyValue: {
34
41
  [key: string]: string;
@@ -36,6 +43,19 @@ declare function setKeyValue(keyValue: {
36
43
  /**
37
44
  * Add a single label to the test report
38
45
  * @param {string} key - label key (e.g. 'severity', 'feature', or just 'smoke' for labels without values)
39
- * @param {string} [value] - optional label value (e.g. 'high', 'login')
46
+ * @param {string|null} [value=null] - optional label value (e.g. 'high', 'login')
47
+ * @returns {void}
40
48
  */
41
- declare function setLabel(key: string, value?: string): void;
49
+ declare function setLabel(key: string, value?: string | null): void;
50
+ /**
51
+ * Add link(s) to the test report
52
+ * @param {...string} testIds - test IDs to link
53
+ * @returns {void}
54
+ */
55
+ declare function linkTest(...testIds: string[]): void;
56
+ /**
57
+ * Add JIRA issue link(s) to the test report
58
+ * @param {...string} jiraIds - JIRA issue IDs to link
59
+ * @returns {void}
60
+ */
61
+ declare function linkJira(...jiraIds: string[]): void;