@testomatio/reporter 2.1.3-beta.1-xml-import → 2.1.3-beta.1-multi-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.
@@ -321,7 +321,7 @@ const fileSystem = {
321
321
  exports.fileSystem = fileSystem;
322
322
  const foundedTestLog = (app, tests) => {
323
323
  const n = tests.length;
324
- return console.log(app, `✅ We found ${n === 1 ? 'one test' : `${n} tests`} in Testomat.io!`);
324
+ return n === 1 ? console.log(app, `✅ We found one test!`) : console.log(app, `✅ We found ${n} tests!`);
325
325
  };
326
326
  exports.foundedTestLog = foundedTestLog;
327
327
  const humanize = text => {
@@ -399,8 +399,6 @@ function storeRunId(runId) {
399
399
  function readLatestRunId() {
400
400
  try {
401
401
  const filePath = path_1.default.join(os_1.default.tmpdir(), `testomatio.latest.run`);
402
- if (!fs_1.default.existsSync(filePath))
403
- return null;
404
402
  const stats = fs_1.default.statSync(filePath);
405
403
  const diff = +new Date() - +stats.mtime;
406
404
  const diffHours = diff / 1000 / 60 / 60;
@@ -77,13 +77,6 @@ declare class XmlReader {
77
77
  skipped_count: number;
78
78
  tests: any[];
79
79
  };
80
- deduplicateTestsByFQN(tests: any): any[];
81
- generateFQN(test: any): string;
82
- generateNormalizedFQN(test: any): string;
83
- extractAssemblyName(test: any): any;
84
- extractNamespace(test: any): any;
85
- extractClassName(test: any): any;
86
- extractCsFileFromPath(test: any): any;
87
80
  calculateStats(): {};
88
81
  fetchSourceCode(): void;
89
82
  formatTests(): void;
package/lib/xmlReader.js CHANGED
@@ -131,9 +131,7 @@ class XmlReader {
131
131
  const { result, total, passed, failed, inconclusive, skipped } = jsonSuite;
132
132
  reduceOptions.preferClassname = this.stats.language === 'python';
133
133
  const resultTests = processTestSuite(jsonSuite['test-suite']);
134
- // Deduplicate tests based on FQN (Assembly + Namespace + Class + Method)
135
- const deduplicatedTests = this.deduplicateTestsByFQN(resultTests);
136
- this.tests = this.tests.concat(deduplicatedTests);
134
+ this.tests = this.tests.concat(resultTests);
137
135
  return {
138
136
  status: result?.toLowerCase(),
139
137
  create_tests: true,
@@ -141,7 +139,7 @@ class XmlReader {
141
139
  passed_count: parseInt(passed, 10),
142
140
  failed_count: parseInt(failed, 10),
143
141
  skipped_count: parseInt(inconclusive + skipped, 10),
144
- tests: deduplicatedTests,
142
+ tests: resultTests,
145
143
  };
146
144
  }
147
145
  processTRX(jsonSuite) {
@@ -277,125 +275,6 @@ class XmlReader {
277
275
  tests,
278
276
  };
279
277
  }
280
- deduplicateTestsByFQN(tests) {
281
- const fqnMap = new Map();
282
- tests.forEach(test => {
283
- const fqn = this.generateNormalizedFQN(test);
284
- if (fqnMap.has(fqn)) {
285
- const existingTest = fqnMap.get(fqn);
286
- // Merge test properties, prioritizing Test Explorer structure but updating with IDs
287
- if (test.test_id && !existingTest.test_id) {
288
- existingTest.test_id = test.test_id;
289
- }
290
- // Keep the most complete test data
291
- if (test.stack && !existingTest.stack) {
292
- existingTest.stack = test.stack;
293
- }
294
- if (test.message && !existingTest.message) {
295
- existingTest.message = test.message;
296
- }
297
- // Prefer Test Explorer structure (longer, more complete suite_title)
298
- if (test.suite_title && test.suite_title.length > existingTest.suite_title.length) {
299
- existingTest.suite_title = test.suite_title;
300
- existingTest.file = this.extractCsFileFromPath(test);
301
- }
302
- }
303
- else {
304
- // Fix file path to use proper .cs file names from source paths
305
- test.file = this.extractCsFileFromPath(test);
306
- fqnMap.set(fqn, test);
307
- }
308
- });
309
- return Array.from(fqnMap.values());
310
- }
311
- generateFQN(test) {
312
- // Generate Fully Qualified Name: Namespace + Class + Method (standard .NET FQN)
313
- // Don't include assembly as it can vary between different test structures
314
- const namespace = this.extractNamespace(test);
315
- const className = this.extractClassName(test);
316
- const methodName = test.title;
317
- // Use the most complete namespace.class structure available
318
- if (test.suite_title && test.suite_title.includes('.')) {
319
- return `${test.suite_title}.${methodName}`;
320
- }
321
- return `${namespace}.${className}.${methodName}`;
322
- }
323
- generateNormalizedFQN(test) {
324
- // Generate normalized FQN for deduplication by extracting the core namespace.class.method
325
- // This normalizes different representations of the same test
326
- const fullClassName = test.suite_title || '';
327
- const methodName = test.title;
328
- // Extract the most specific namespace.class pattern
329
- if (fullClassName.includes('.')) {
330
- const parts = fullClassName.split('.');
331
- if (parts.length >= 2) {
332
- const className = parts[parts.length - 1];
333
- // Look for common .NET namespace patterns and normalize them:
334
- // TestProject.Tests.MyClass -> Tests.MyClass
335
- // Tests.MyClass -> Tests.MyClass
336
- // MyProject.SubNamespace.Tests.MyClass -> Tests.MyClass
337
- let normalizedNamespace = '';
338
- for (let i = parts.length - 2; i >= 0; i--) {
339
- const part = parts[i];
340
- // Build namespace from right to left, excluding project names
341
- if (part === 'Tests' || part.endsWith('Tests') || part.includes('Test')) {
342
- // Found a test namespace, use it as the normalized namespace
343
- normalizedNamespace = part;
344
- break;
345
- }
346
- else if (i === parts.length - 2) {
347
- // If no test namespace found, use the immediate parent as namespace
348
- normalizedNamespace = part;
349
- }
350
- }
351
- return `${normalizedNamespace}.${className}.${methodName}`;
352
- }
353
- }
354
- // Fallback for simple class names
355
- return `${fullClassName}.${methodName}`;
356
- }
357
- extractAssemblyName(test) {
358
- // Extract assembly name from file path or use default
359
- if (test.file) {
360
- const parts = test.file.split(/[/\\]/);
361
- return parts[0] || 'DefaultAssembly';
362
- }
363
- return 'DefaultAssembly';
364
- }
365
- extractNamespace(test) {
366
- // Extract namespace from suite_title or classname
367
- if (test.suite_title && test.suite_title.includes('.')) {
368
- const parts = test.suite_title.split('.');
369
- return parts.slice(0, -1).join('.');
370
- }
371
- return test.suite_title || 'DefaultNamespace';
372
- }
373
- extractClassName(test) {
374
- // Extract class name from suite_title
375
- if (test.suite_title && test.suite_title.includes('.')) {
376
- const parts = test.suite_title.split('.');
377
- return parts[parts.length - 1];
378
- }
379
- return test.suite_title || 'DefaultClass';
380
- }
381
- extractCsFileFromPath(test) {
382
- // Extract .cs file name from source file path, not namespace
383
- if (test.file) {
384
- // Look for actual .cs file path patterns
385
- const csFileMatch = test.file.match(/([^/\\]+\.cs)$/);
386
- if (csFileMatch) {
387
- return test.file;
388
- }
389
- // If no .cs extension, assume it's a namespace path and convert to likely file name
390
- const className = this.extractClassName(test);
391
- const pathParts = test.file.split(/[/\\]/);
392
- pathParts[pathParts.length - 1] = `${className}.cs`;
393
- return pathParts.join('/');
394
- }
395
- // Fallback to class name
396
- const className = this.extractClassName(test);
397
- return `${className}.cs`;
398
- }
399
278
  calculateStats() {
400
279
  this.stats = {
401
280
  ...this.stats,
@@ -551,8 +430,7 @@ function reduceTestCases(prev, item) {
551
430
  testCases
552
431
  .filter(t => !!t)
553
432
  .forEach(testCaseItem => {
554
- // Use consistent Test Explorer structure: prioritize fullname for file path
555
- const file = extractSourceFilePath(testCaseItem, item);
433
+ const file = testCaseItem.file || item.filepath || item.fullname || item.package || '';
556
434
  let stack = '';
557
435
  let message = '';
558
436
  if (testCaseItem.error)
@@ -572,11 +450,11 @@ function reduceTestCases(prev, item) {
572
450
  if (!message)
573
451
  message = stack.trim().split('\n')[0];
574
452
  const isParametrized = item.type === 'ParameterizedMethod';
453
+ const preferClassname = reduceOptions.preferClassname || isParametrized;
575
454
  // SpecFlow config
576
455
  let { title, tags, testId } = fetchProperties(isParametrized ? item : testCaseItem);
577
456
  let example = null;
578
- // Use consistent Test Explorer structure for suite title
579
- const suiteTitle = extractTestExplorerSuiteTitle(testCaseItem, item);
457
+ const suiteTitle = preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
580
458
  title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
581
459
  tags ||= [];
582
460
  const exampleMatches = testCaseItem.name?.match(/\S\((.*?)\)/);
@@ -638,51 +516,6 @@ function reduceTestCases(prev, item) {
638
516
  });
639
517
  return prev;
640
518
  }
641
- function extractSourceFilePath(testCaseItem, item) {
642
- // Priority order for file path extraction to match Test Explorer structure:
643
- // 1. fullname (contains full project path)
644
- // 2. filepath (direct file path)
645
- // 3. file attribute from test case
646
- // 4. package (fallback)
647
- if (item.fullname) {
648
- // Extract actual file path from fullname if it contains path separators
649
- const fullnameParts = item.fullname.split('.');
650
- if (fullnameParts.length > 2) {
651
- // Reconstruct path from project.namespace.class structure
652
- const projectName = fullnameParts[0];
653
- const namespaceParts = fullnameParts.slice(1, -1);
654
- const className = fullnameParts[fullnameParts.length - 1];
655
- return `${projectName}/${namespaceParts.join('/')}/${className}.cs`;
656
- }
657
- }
658
- if (item.filepath)
659
- return item.filepath;
660
- if (testCaseItem.file)
661
- return testCaseItem.file;
662
- if (item.package)
663
- return item.package;
664
- // Fallback: construct from classname
665
- if (testCaseItem.classname) {
666
- const parts = testCaseItem.classname.split('.');
667
- const className = parts[parts.length - 1];
668
- const namespacePath = parts.slice(0, -1).join('/');
669
- return `${namespacePath}/${className}.cs`;
670
- }
671
- return '';
672
- }
673
- function extractTestExplorerSuiteTitle(testCaseItem, item) {
674
- // Extract suite title to match Test Explorer structure (Project/Namespace hierarchy)
675
- // Priority: fullname > classname > name
676
- if (item.fullname) {
677
- // Use fullname to maintain Test Explorer structure
678
- return item.fullname;
679
- }
680
- if (testCaseItem.classname) {
681
- return testCaseItem.classname;
682
- }
683
- // Fallback to item name but prefer classname structure
684
- return item.name || testCaseItem.classname || 'UnknownClass';
685
- }
686
519
  function processTestSuite(testsuite) {
687
520
  if (!testsuite)
688
521
  return [];
@@ -694,14 +527,8 @@ function processTestSuite(testsuite) {
694
527
  if (!Array.isArray(testsuite)) {
695
528
  suites = [testsuite];
696
529
  }
697
- // Only process suites that have test cases OR child suites, but avoid double processing
698
- const subSuites = suites.filter(s => s['test-suite'] && !s['test-case']);
699
- const leafSuites = suites.filter(s => s['test-case'] || s.testcase);
700
- // Process child suites recursively
701
- const childResults = subSuites.map(s => processTestSuite(s['test-suite'])).flat();
702
- // Process leaf suites with actual test cases
703
- const leafResults = leafSuites.reduce(reduceTestCases, []);
704
- return [...childResults, ...leafResults];
530
+ const subSuites = suites.filter(s => s['test-suite'] && !testsuite['test-case']);
531
+ return [...subSuites.map(s => processTestSuite(s['test-suite'])), ...suites.reduce(reduceTestCases, [])].flat();
705
532
  }
706
533
  function fetchProperties(item) {
707
534
  const tags = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.1.3-beta.1-xml-import",
3
+ "version": "2.1.3-beta.1-multi-links",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -56,20 +56,19 @@ function CodeceptReporter(config) {
56
56
 
57
57
  output.debug = function(msg) {
58
58
  originalOutput.debug(msg);
59
- dataStorage.putData('log', repeat(this?.stepShift || 0) + pc.cyan(msg.toString()));
59
+ dataStorage.putData('log', repeat(this.stepShift) + pc.cyan(msg.toString()));
60
60
  };
61
61
 
62
62
  output.say = function(message, color = 'cyan') {
63
63
  originalOutput.say(message, color);
64
- const sayMsg = repeat(this?.stepShift || 0) + ` ${pc.bold(pc[color](message))}`;
64
+ const sayMsg = repeat(this.stepShift) + ` ${pc.bold(pc[color](message))}`;
65
65
  dataStorage.putData('log', sayMsg);
66
66
  };
67
67
 
68
68
  output.log = function(msg) {
69
69
  originalOutput.log(msg);
70
- dataStorage.putData('log', repeat(this?.stepShift || 0) + pc.gray(msg));
70
+ dataStorage.putData('log', repeat(this.stepShift) + pc.gray(msg));
71
71
  };
72
- output.stepShift = 0;
73
72
 
74
73
  recorder.startUnlessRunning();
75
74
 
@@ -163,7 +162,7 @@ function CodeceptReporter(config) {
163
162
  const manuallyAttachedArtifacts = services.artifacts.get(test.fullTitle());
164
163
  const keyValues = services.keyValues.get(test.fullTitle());
165
164
  const stepHierarchy = buildUnifiedStepHierarchy(test.steps, hookSteps);
166
- const links = services.links.get(test.fullTitle());
165
+ const labels = services.labels.get(test.fullTitle());
167
166
 
168
167
  services.setContext(null);
169
168
 
@@ -178,7 +177,7 @@ function CodeceptReporter(config) {
178
177
  files,
179
178
  steps: stepHierarchy, // Array of step objects per API schema
180
179
  logs,
181
- links,
180
+ labels,
182
181
  manuallyAttachedArtifacts,
183
182
  meta: { ...keyValues, ...test.meta },
184
183
  });
@@ -61,7 +61,6 @@ function MochaReporter(runner, opts) {
61
61
  const logs = getTestLogs(test);
62
62
  const artifacts = services.artifacts.get(test.fullTitle());
63
63
  const keyValues = services.keyValues.get(test.fullTitle());
64
- const links = services.links.get(test.fullTitle());
65
64
 
66
65
  client.addTestRun(STATUS.PASSED, {
67
66
  test_id: testId,
@@ -73,7 +72,6 @@ function MochaReporter(runner, opts) {
73
72
  logs,
74
73
  manuallyAttachedArtifacts: artifacts,
75
74
  meta: keyValues,
76
- links,
77
75
  });
78
76
  });
79
77
 
@@ -81,10 +79,6 @@ function MochaReporter(runner, opts) {
81
79
  skipped += 1;
82
80
  console.log('skip: %s', test.fullTitle());
83
81
  const testId = getTestomatIdFromTestTitle(test.title);
84
- const artifacts = services.artifacts.get(test.fullTitle());
85
- const keyValues = services.keyValues.get(test.fullTitle());
86
- const links = services.links.get(test.fullTitle());
87
-
88
82
  client.addTestRun(STATUS.SKIPPED, {
89
83
  title: getTestName(test),
90
84
  suite_title: getSuiteTitle(test),
@@ -92,9 +86,6 @@ function MochaReporter(runner, opts) {
92
86
  file: getFile(test),
93
87
  test_id: testId,
94
88
  time: test.duration,
95
- manuallyAttachedArtifacts: artifacts,
96
- meta: keyValues,
97
- links,
98
89
  });
99
90
  });
100
91
 
@@ -104,9 +95,6 @@ function MochaReporter(runner, opts) {
104
95
  const testId = getTestomatIdFromTestTitle(test.title);
105
96
 
106
97
  const logs = getTestLogs(test);
107
- const artifacts = services.artifacts.get(test.fullTitle());
108
- const keyValues = services.keyValues.get(test.fullTitle());
109
- const links = services.links.get(test.fullTitle());
110
98
 
111
99
  client.addTestRun(STATUS.FAILED, {
112
100
  error: err,
@@ -117,9 +105,6 @@ function MochaReporter(runner, opts) {
117
105
  code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
118
106
  time: test.duration,
119
107
  logs,
120
- manuallyAttachedArtifacts: artifacts,
121
- meta: keyValues,
122
- links,
123
108
  });
124
109
  });
125
110
 
@@ -53,13 +53,11 @@ class WebdriverReporter extends WDIOReporter {
53
53
  test.suite = test.parent;
54
54
  const logs = getTestLogs(test.fullTitle);
55
55
  // TODO: FIX: artifacts for some reason leads to empty report on Testomat.io
56
- // ^ not reproduced anymore (Jul 2025)
57
- // but still be under investigation
58
- const artifacts = services.artifacts.get(test.fullTitle);
59
- const keyValues = services.keyValues.get(test.fullTitle);
56
+ // const artifacts = services.artifacts.get(test.fullTitle);
57
+ // const keyValues = services.keyValues.get(test.fullTitle);
60
58
  test.logs = logs;
61
- test.artifacts = artifacts;
62
- test.meta = keyValues;
59
+ // test.artifacts = artifacts;
60
+ // test.meta = keyValues;
63
61
 
64
62
  this._addTestPromises.push(this.addTest(test));
65
63
  }
@@ -1,53 +1,124 @@
1
1
  #!/usr/bin/env node
2
- import { spawn } from 'node:child_process';
3
- import { join, dirname } from 'node:path';
4
- import { getPackageVersion } from '../utils/utils.js';
2
+ import { spawn } from 'cross-spawn';
3
+ import { Command } from 'commander';
5
4
  import pc from 'picocolors';
6
-
7
- // Define __dirname - this will be replaced by build script with actual __dirname for CommonJS
8
- const __dirname = typeof globalThis.__dirname !== 'undefined' ? globalThis.__dirname : '.';
9
- const cliPath = join(__dirname, 'cli.js');
5
+ import TestomatClient from '../client.js';
6
+ import { APP_PREFIX, STATUS } from '../constants.js';
7
+ import { getPackageVersion } from '../utils/utils.js';
8
+ import { config } from '../config.js';
9
+ import dotenv from 'dotenv';
10
10
 
11
11
  const version = getPackageVersion();
12
12
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
13
+ const program = new Command();
14
+
15
+ program
16
+ .option('-c, --command <cmd>', 'Test runner command')
17
+ .option('--launch', 'Start a new run and return its ID')
18
+ .option('--finish', 'Finish Run by its ID')
19
+ .option('--env-file <envfile>', 'Load environment variables from env file')
20
+ .option('--filter <filter>', 'Additional execution filter')
21
+ .action(async opts => {
22
+ const { launch, finish, filter } = opts;
23
+ let { command } = opts;
13
24
 
14
- // Parse command line arguments to map start-test-run options to @testomatio/reporter run format
15
- const args = process.argv.slice(2);
16
- const newArgs = ['run'];
17
-
18
- let i = 0;
19
- while (i < args.length) {
20
- const arg = args[i];
21
-
22
- if (arg === '-c' || arg === '--command') {
23
- // Map -c/--command to positional argument for run command
24
- i++;
25
- if (i < args.length) {
26
- newArgs.push(args[i]);
25
+ if (opts.envFile) dotenv.config({ path: opts.envFile });
26
+
27
+ const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
28
+ const title = process.env.TESTOMATIO_TITLE;
29
+
30
+ if (launch) {
31
+ console.log('Starting a new Run on Testomat.io...');
32
+ const client = new TestomatClient({ apiKey });
33
+
34
+ client.createRun().then(() => {
35
+ console.log(process.env.runId);
36
+ process.exit(0);
37
+ });
38
+ return;
39
+ }
40
+
41
+ if (finish) {
42
+ // TODO: add error in case of TESTOMATIO environment variable is not set
43
+ // because command is fine in console, but actually (on testomat.io) run is not finished
44
+ if (!process.env.TESTOMATIO_RUN) {
45
+ console.log('TESTOMATIO_RUN environment variable must be set.');
46
+ return process.exit(1);
47
+ }
48
+
49
+ console.log('Finishing Run on Testomat.io...');
50
+
51
+ const client = new TestomatClient({ apiKey });
52
+
53
+ // @ts-ignore
54
+ client.updateRunStatus(STATUS.FINISHED).then(() => {
55
+ console.log(pc.yellow(`Run ${process.env.TESTOMATIO_RUN} was finished`));
56
+ process.exit(0);
57
+ });
58
+ return;
59
+ }
60
+
61
+ let exitCode = 0;
62
+
63
+ if (!command.split) {
64
+ process.exitCode = 255;
65
+ console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
66
+ return;
27
67
  }
28
- } else if (arg.startsWith('--command=')) {
29
- // Handle --command=value format
30
- const command = arg.split('=', 2)[1];
31
- newArgs.push(command);
32
- } else if (arg === '--launch') {
33
- // Map --launch to start command
34
- newArgs[0] = 'start';
35
- } else if (arg === '--finish') {
36
- // Map --finish to finish command
37
- newArgs[0] = 'finish';
38
- } else {
39
- // Pass through other arguments
40
- newArgs.push(arg);
41
- }
42
- i++;
43
- }
44
68
 
45
- // Execute the main CLI with mapped arguments
69
+ const client = new TestomatClient({ apiKey, title, parallel: true });
46
70
 
47
- const child = spawn(process.execPath, [cliPath, ...newArgs], {
48
- stdio: 'inherit'
49
- });
71
+ if (filter) {
72
+ const [pipe, ...optsArray] = filter.split(':');
73
+ const pipeOptions = optsArray.join(':');
74
+
75
+ try {
76
+ const tests = await client.prepareRun({ pipe, pipeOptions });
77
+
78
+ if (!tests || tests.length === 0) {
79
+ return;
80
+ }
81
+
82
+ const grep = ` --grep (${tests.join('|')})`;
83
+ command += grep;
84
+ } catch (err) {
85
+ console.log(APP_PREFIX, err);
86
+ }
87
+ }
88
+
89
+ const testCmds = command.split(' ');
90
+ console.log(APP_PREFIX, `🚀 Running`, pc.green(command));
91
+
92
+ if (!apiKey) {
93
+ const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
94
+
95
+ cmd.on('close', code => {
96
+ console.log(APP_PREFIX, '⚠️ ', `Runner exited with ${pc.bold(code)}, report is ignored`);
97
+
98
+ if (code > exitCode) exitCode = code;
99
+ process.exitCode = exitCode;
100
+ });
101
+
102
+ return;
103
+ }
104
+
105
+ client.createRun().then(() => {
106
+ const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
107
+
108
+ cmd.on('close', code => {
109
+ const emoji = code === 0 ? '🟢' : '🔴';
110
+ console.log(APP_PREFIX, emoji, `Runner exited with ${pc.bold(code)}`);
111
+ const status = code === 0 ? 'passed' : 'failed';
112
+ client.updateRunStatus(status, true);
113
+
114
+ if (code > exitCode) exitCode = code;
115
+ process.exitCode = exitCode;
116
+ });
117
+ });
118
+ });
119
+
120
+ if (process.argv.length <= 2) {
121
+ program.outputHelp();
122
+ }
50
123
 
51
- child.on('exit', (code) => {
52
- process.exit(code);
53
- });
124
+ program.parse(process.argv);
package/src/client.js CHANGED
@@ -11,7 +11,6 @@ import path, { sep } from 'path';
11
11
  import { fileURLToPath } from 'node:url';
12
12
  import { S3Uploader } from './uploader.js';
13
13
  import { formatStep, readLatestRunId, storeRunId, validateSuiteId } from './utils/utils.js';
14
- import { linkStorage } from './services/links.js';
15
14
  import { filesize as prettyBytes } from 'filesize';
16
15
 
17
16
  const debug = createDebugMessages('@testomatio/reporter:client');
@@ -183,6 +182,7 @@ class Client {
183
182
  test_id,
184
183
  timestamp,
185
184
  manuallyAttachedArtifacts,
185
+ labels,
186
186
  overwrite,
187
187
  } = testData;
188
188
  let { message = '', meta = {} } = testData;
@@ -224,9 +224,7 @@ class Client {
224
224
  return acc;
225
225
  }, {});
226
226
 
227
- // Get links from storage using the test context
228
- const testContext = suite_title ? `${suite_title} ${title}` : title;
229
- const links = linkStorage.get(testContext) || [];
227
+ // Labels are simple array of strings, no processing needed
230
228
 
231
229
  let errorFormatted = '';
232
230
  if (error) {
@@ -282,7 +280,7 @@ class Client {
282
280
  timestamp,
283
281
  artifacts,
284
282
  meta,
285
- links,
283
+ labels,
286
284
  overwrite,
287
285
  ...(rootSuiteId && { root_suite_id: rootSuiteId }),
288
286
  };
@@ -41,7 +41,7 @@ class DataStorage {
41
41
  /**
42
42
  * Puts any data to storage (file or global variable).
43
43
  * If file: stores data as text, if global variable – stores as array of data.
44
- * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
44
+ * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
45
45
  * @param {*} data anything you want to store (string, object, array, etc)
46
46
  * @param {*} context could be testId or any context (test name, suite name, including their IDs etc)
47
47
  * suite name + test name is used by default
@@ -70,7 +70,7 @@ class DataStorage {
70
70
  * Returns data, stored for specific test/context (or data which was stored without test id specified).
71
71
  * This method will get data from global variable and/or from from file (previosly saved with put method).
72
72
  *
73
- * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
73
+ * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
74
74
  * @param {string} context
75
75
  * @returns {any []} array of data (any type), null (if no data found for context) or string (if data type is log)
76
76
  */
@@ -108,7 +108,7 @@ class DataStorage {
108
108
  }
109
109
 
110
110
  /**
111
- * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
111
+ * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
112
112
  * @param {string} context
113
113
  * @returns aray of data (any type)
114
114
  */
@@ -127,7 +127,7 @@ class DataStorage {
127
127
  }
128
128
 
129
129
  /**
130
- * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
130
+ * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
131
131
  * @param {*} context
132
132
  * @returns array of data (any type)
133
133
  */
@@ -151,7 +151,7 @@ class DataStorage {
151
151
 
152
152
  /**
153
153
  * Puts data to global variable. Unlike the file storage, stores data in array (file storage just append as string).
154
- * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
154
+ * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
155
155
  * @param {*} data
156
156
  * @param {*} context
157
157
  */
@@ -166,7 +166,7 @@ class DataStorage {
166
166
 
167
167
  /**
168
168
  * Puts data to file. Unlike the global variable storage, stores data as string
169
- * @param {'log' | 'artifact' | 'keyvalue' | 'links'} dataType
169
+ * @param {'log' | 'artifact' | 'keyvalue' | 'labels'} dataType
170
170
  * @param {*} data
171
171
  * @param {string} context
172
172
  * @returns
@@ -119,7 +119,8 @@ class TestomatioPipe {
119
119
  const resp = await this.client.request({
120
120
  method: 'GET',
121
121
  url: '/api/test_grep',
122
- ...q,
122
+ params: q.params,
123
+ responseType: q.responseType
123
124
  });
124
125
 
125
126
  if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {