@testomatio/reporter 2.8.1 → 2.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/bin/cli.js CHANGED
@@ -19,14 +19,14 @@ const filesize_1 = require("filesize");
19
19
  const dotenv_1 = __importDefault(require("dotenv"));
20
20
  const replay_js_1 = __importDefault(require("../replay.js"));
21
21
  const log_js_1 = require("../utils/log.js");
22
+ const pipe_utils_js_1 = require("../utils/pipe_utils.js");
22
23
  const debug = (0, debug_1.default)('@testomatio/reporter:cli');
23
24
  const version = (0, utils_js_1.getPackageVersion)();
24
- console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
25
25
  const program = new commander_1.Command();
26
26
  program
27
27
  .version(version)
28
28
  .option('--env-file <envfile>', 'Load environment variables from env file')
29
- .hook('preAction', thisCommand => {
29
+ .hook('preAction', (thisCommand, actionCommand) => {
30
30
  const opts = thisCommand.opts();
31
31
  if (opts.envFile) {
32
32
  dotenv_1.default.config({ path: opts.envFile });
@@ -34,6 +34,15 @@ program
34
34
  else {
35
35
  dotenv_1.default.config();
36
36
  }
37
+ // --filter-list produces a machine-readable test list on stdout, so route
38
+ // logs to stderr and skip the banner to keep stdout clean for piping.
39
+ const subOpts = actionCommand.opts();
40
+ if (subOpts.filterList || subOpts.format) {
41
+ process.env.TESTOMATIO_LOG_STDERR = '1';
42
+ }
43
+ else {
44
+ console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
45
+ }
37
46
  });
38
47
  program
39
48
  .command('start')
@@ -77,6 +86,7 @@ program
77
86
  .argument('[command]', 'Test runner command')
78
87
  .option('--filter <filter>', 'Additional execution filter')
79
88
  .option('--filter-list <filter>', 'Get a list of all tests by filter before running')
89
+ .option('--format <format>', 'Machine-readable output format for --filter-list (grep, json, newline, ids)')
80
90
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
81
91
  .action(async (command, opts) => {
82
92
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
@@ -86,7 +96,7 @@ program
86
96
  log_js_1.log.info('Filtering tests...');
87
97
  // Example of use: npx @testomatio/reporter run "npx jest" --filter "testomatio:tag-name=frontend"
88
98
  // Example of use: npx @testomatio/reporter run "npx jest" --filter "coverage:file=coverage.yml"
89
- // Example of use: npx @testomatio/reporter run "npx jest" --filter-list "coverage:file=coverage.yml"
99
+ // Example of use: npx @testomatio/reporter run --filter-list "coverage:file=coverage.yml" --format grep
90
100
  const [pipe, ...optsArray] = opts?.filter ? opts?.filter.split(':') : opts?.filterList.split(':');
91
101
  const pipeOptions = optsArray.join(':');
92
102
  const prepareRunParams = { pipe, pipeOptions };
@@ -99,19 +109,19 @@ program
99
109
  log_js_1.log.info(picocolors_1.default.yellow('No tests found.'));
100
110
  return;
101
111
  }
102
- const pattern = `(${tests.join('|')})`;
103
- const filteredCommand = (0, utils_js_1.applyFilter)(command, tests);
104
- debug(`Execution pattern: "${pattern}"`);
105
112
  if (opts.filterList) {
106
- if (command)
107
- log_js_1.log.info(picocolors_1.default.green(`Full Running Command: ${filteredCommand}`));
108
- log_js_1.log.info();
109
- log_js_1.log.info(`Grep string:`);
110
- log_js_1.log.info(`${tests.join(', ')}`);
113
+ const out = (0, pipe_utils_js_1.formatFilterListIds)(tests, opts.format || 'ids');
114
+ if (out)
115
+ console.log(out);
116
+ // Show the runnable-command hint only in interactive mode (no explicit --format).
117
+ // When --format is set the user is scripting and doesn't need stderr noise.
118
+ if (command && !opts.format) {
119
+ log_js_1.log.info(picocolors_1.default.green(`Full Running Command: ${(0, utils_js_1.applyFilter)(command, tests)}`));
120
+ }
111
121
  return;
112
122
  }
113
123
  if (command && command.split) {
114
- command = filteredCommand;
124
+ command = (0, utils_js_1.applyFilter)(command, tests);
115
125
  }
116
126
  }
117
127
  catch (err) {
@@ -54,7 +54,7 @@ class CoveragePipe {
54
54
  this.branch = options?.diff || process.env.COVERAGE_BRANCH || this.#GIT.default_branch;
55
55
  this.isBranchDefault = !options.diff && !process.env.COVERAGE_BRANCH;
56
56
  if (this.isBranchDefault) {
57
- console.log(constants_js_1.APP_PREFIX, `🟡 No "diff" branch provided. That's why we use default one = "${this.branch}".\n` +
57
+ log_js_1.log.info(`🟡 No "diff" branch provided. That's why we use default one = "${this.branch}".\n` +
58
58
  '👉 You can set it via --filter "coverage:file=coverage.yml,diff=your-branch"');
59
59
  }
60
60
  // Client config section
@@ -136,7 +136,7 @@ class CoveragePipe {
136
136
  const tests = await this.#getTestomatioTestsByParam(tagType, tag);
137
137
  if (!tests)
138
138
  return [];
139
- console.log(constants_js_1.APP_PREFIX, `✅ We found ${tests.length === 1 ? 'one entry' : `${tests.length} (test/suite) entries`}` +
139
+ log_js_1.log.info(`✅ We found ${tests.length === 1 ? 'one entry' : `${tests.length} (test/suite) entries`}` +
140
140
  ' in Testomat.io service side.');
141
141
  tests.forEach(testId => this.tests.add(testId));
142
142
  }
@@ -276,7 +276,7 @@ class CoveragePipe {
276
276
  log_js_1.log.error(err.message);
277
277
  return undefined;
278
278
  }
279
- log_js_1.log.error(`ℹ️ We will use '${cmd}' Git command.`);
279
+ log_js_1.log.warn(`We will use '${cmd}' Git command.`);
280
280
  try {
281
281
  // For clear unit testing process -> Like test_defaultGitChangedFile = todomvc-tests/edit-todos_test.js
282
282
  if (this.isDefaultGitChanges) {
@@ -285,7 +285,7 @@ class CoveragePipe {
285
285
  else {
286
286
  this.changedFiles = this.#getChangedFilesFromGit(cmd);
287
287
  if (this.changedFiles.length === 0) {
288
- console.log(constants_js_1.APP_PREFIX, 'ℹ️ No files changed in the latest Git commit. Skipping coverage processing.');
288
+ log_js_1.log.info('ℹ️ No files changed in the latest Git commit. Skipping coverage processing.');
289
289
  return undefined;
290
290
  }
291
291
  }
package/lib/utils/log.js CHANGED
@@ -46,7 +46,8 @@ function shouldLog(messageLevel) {
46
46
  */
47
47
  function info(...args) {
48
48
  if (shouldLog(exports.LOG_LEVELS.INFO)) {
49
- console.log(constants_js_1.APP_PREFIX, ...args);
49
+ const fn = process.env.TESTOMATIO_LOG_STDERR === '1' ? console.error : console.log;
50
+ fn(constants_js_1.APP_PREFIX, ...args);
50
51
  }
51
52
  }
52
53
  /**
@@ -54,3 +54,13 @@ export function fullName(t: object): string;
54
54
  * => Returns: { foo: 'bar', baz: 'qux' }
55
55
  */
56
56
  export function parsePipeOptions(optionsStr?: string): any;
57
+ /**
58
+ * Format a list of test IDs for `--filter-list` machine-readable output.
59
+ * Used when the CLI `--format` option is passed,
60
+ * e.g. `--filter-list "coverage:file=..." --format grep`.
61
+ *
62
+ * @param {string[]} ids
63
+ * @param {'grep'|'json'|'newline'|'ids'} format
64
+ * @returns {string} Empty string if no ids; otherwise the formatted output.
65
+ */
66
+ export function formatFilterListIds(ids: string[], format: "grep" | "json" | "newline" | "ids"): string;
@@ -7,6 +7,7 @@ exports.setS3Credentials = setS3Credentials;
7
7
  exports.statusEmoji = statusEmoji;
8
8
  exports.fullName = fullName;
9
9
  exports.parsePipeOptions = parsePipeOptions;
10
+ exports.formatFilterListIds = formatFilterListIds;
10
11
  const log_js_1 = require("./log.js");
11
12
  /**
12
13
  * Set S3 credentials from the provided artifacts object.
@@ -161,6 +162,26 @@ function parsePipeOptions(optionsStr) {
161
162
  }
162
163
  return options;
163
164
  }
165
+ /**
166
+ * Format a list of test IDs for `--filter-list` machine-readable output.
167
+ * Used when the CLI `--format` option is passed,
168
+ * e.g. `--filter-list "coverage:file=..." --format grep`.
169
+ *
170
+ * @param {string[]} ids
171
+ * @param {'grep'|'json'|'newline'|'ids'} format
172
+ * @returns {string} Empty string if no ids; otherwise the formatted output.
173
+ */
174
+ function formatFilterListIds(ids, format) {
175
+ if (!ids || ids.length === 0)
176
+ return '';
177
+ switch (format) {
178
+ case 'grep': return `(${ids.join('|')})`;
179
+ case 'json': return JSON.stringify(ids);
180
+ case 'newline': return ids.join('\n');
181
+ case 'ids':
182
+ default: return ids.join(',');
183
+ }
184
+ }
164
185
 
165
186
  module.exports.updateFilterType = updateFilterType;
166
187
 
@@ -175,3 +196,5 @@ module.exports.statusEmoji = statusEmoji;
175
196
  module.exports.fullName = fullName;
176
197
 
177
198
  module.exports.parsePipeOptions = parsePipeOptions;
199
+
200
+ module.exports.formatFilterListIds = formatFilterListIds;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.8.1",
3
+ "version": "2.8.3",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
package/src/bin/cli.js CHANGED
@@ -15,22 +15,31 @@ import { filesize as prettyBytes } from 'filesize';
15
15
  import dotenv from 'dotenv';
16
16
  import Replay from '../replay.js';
17
17
  import { log } from '../utils/log.js';
18
+ import { formatFilterListIds } from '../utils/pipe_utils.js';
18
19
 
19
20
  const debug = createDebugMessages('@testomatio/reporter:cli');
20
21
  const version = getPackageVersion();
21
- console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
22
22
  const program = new Command();
23
23
 
24
24
  program
25
25
  .version(version)
26
26
  .option('--env-file <envfile>', 'Load environment variables from env file')
27
- .hook('preAction', thisCommand => {
27
+ .hook('preAction', (thisCommand, actionCommand) => {
28
28
  const opts = thisCommand.opts();
29
29
  if (opts.envFile) {
30
30
  dotenv.config({ path: opts.envFile });
31
31
  } else {
32
32
  dotenv.config();
33
33
  }
34
+
35
+ // --filter-list produces a machine-readable test list on stdout, so route
36
+ // logs to stderr and skip the banner to keep stdout clean for piping.
37
+ const subOpts = actionCommand.opts();
38
+ if (subOpts.filterList || subOpts.format) {
39
+ process.env.TESTOMATIO_LOG_STDERR = '1';
40
+ } else {
41
+ console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
42
+ }
34
43
  });
35
44
 
36
45
  program
@@ -83,6 +92,7 @@ program
83
92
  .argument('[command]', 'Test runner command')
84
93
  .option('--filter <filter>', 'Additional execution filter')
85
94
  .option('--filter-list <filter>', 'Get a list of all tests by filter before running')
95
+ .option('--format <format>', 'Machine-readable output format for --filter-list (grep, json, newline, ids)')
86
96
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
87
97
  .action(async (command, opts) => {
88
98
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
@@ -93,7 +103,7 @@ program
93
103
  log.info('Filtering tests...');
94
104
  // Example of use: npx @testomatio/reporter run "npx jest" --filter "testomatio:tag-name=frontend"
95
105
  // Example of use: npx @testomatio/reporter run "npx jest" --filter "coverage:file=coverage.yml"
96
- // Example of use: npx @testomatio/reporter run "npx jest" --filter-list "coverage:file=coverage.yml"
106
+ // Example of use: npx @testomatio/reporter run --filter-list "coverage:file=coverage.yml" --format grep
97
107
  const [pipe, ...optsArray] = opts?.filter ? opts?.filter.split(':') : opts?.filterList.split(':');
98
108
  const pipeOptions = optsArray.join(':');
99
109
 
@@ -110,21 +120,19 @@ program
110
120
  return;
111
121
  }
112
122
 
113
- const pattern = `(${tests.join('|')})`;
114
- const filteredCommand = applyFilter(command, tests);
115
-
116
- debug(`Execution pattern: "${pattern}"`);
117
-
118
- if(opts.filterList) {
119
- if (command) log.info(pc.green(`Full Running Command: ${filteredCommand}`));
120
- log.info();
121
- log.info(`Grep string:`);
122
- log.info(`${tests.join(', ')}`);
123
+ if (opts.filterList) {
124
+ const out = formatFilterListIds(tests, opts.format || 'ids');
125
+ if (out) console.log(out);
126
+ // Show the runnable-command hint only in interactive mode (no explicit --format).
127
+ // When --format is set the user is scripting and doesn't need stderr noise.
128
+ if (command && !opts.format) {
129
+ log.info(pc.green(`Full Running Command: ${applyFilter(command, tests)}`));
130
+ }
123
131
  return;
124
132
  }
125
133
 
126
134
  if (command && command.split) {
127
- command = filteredCommand;
135
+ command = applyFilter(command, tests);
128
136
  }
129
137
  }
130
138
  catch (err) {
@@ -57,8 +57,7 @@ class CoveragePipe { // or Changes for the future???
57
57
  this.isBranchDefault = !options.diff && !process.env.COVERAGE_BRANCH;
58
58
 
59
59
  if (this.isBranchDefault) {
60
- console.log(
61
- APP_PREFIX,
60
+ log.info(
62
61
  `🟡 No "diff" branch provided. That's why we use default one = "${this.branch}".\n` +
63
62
  '👉 You can set it via --filter "coverage:file=coverage.yml,diff=your-branch"'
64
63
  );
@@ -154,8 +153,7 @@ class CoveragePipe { // or Changes for the future???
154
153
 
155
154
  if (!tests) return [];
156
155
 
157
- console.log(
158
- APP_PREFIX,
156
+ log.info(
159
157
  `✅ We found ${tests.length === 1 ? 'one entry' : `${tests.length} (test/suite) entries`}` +
160
158
  ' in Testomat.io service side.'
161
159
  );
@@ -324,7 +322,7 @@ class CoveragePipe { // or Changes for the future???
324
322
  return undefined;
325
323
  }
326
324
 
327
- log.error( `ℹ️ We will use '${cmd}' Git command.`);
325
+ log.warn( `We will use '${cmd}' Git command.`);
328
326
 
329
327
  try {
330
328
  // For clear unit testing process -> Like test_defaultGitChangedFile = todomvc-tests/edit-todos_test.js
@@ -335,10 +333,7 @@ class CoveragePipe { // or Changes for the future???
335
333
  this.changedFiles = this.#getChangedFilesFromGit(cmd);
336
334
 
337
335
  if (this.changedFiles.length === 0) {
338
- console.log(
339
- APP_PREFIX,
340
- 'ℹ️ No files changed in the latest Git commit. Skipping coverage processing.'
341
- );
336
+ log.info('ℹ️ No files changed in the latest Git commit. Skipping coverage processing.');
342
337
 
343
338
  return undefined;
344
339
  }
package/src/utils/log.js CHANGED
@@ -42,7 +42,8 @@ export function shouldLog(messageLevel) {
42
42
  */
43
43
  export function info(...args) {
44
44
  if (shouldLog(LOG_LEVELS.INFO)) {
45
- console.log(APP_PREFIX, ...args);
45
+ const fn = process.env.TESTOMATIO_LOG_STDERR === '1' ? console.error : console.log;
46
+ fn(APP_PREFIX, ...args);
46
47
  }
47
48
  }
48
49
 
@@ -161,12 +161,33 @@ function parsePipeOptions(optionsStr) {
161
161
  return options;
162
162
  }
163
163
 
164
- export {
165
- updateFilterType,
166
- parseFilterParams,
167
- generateFilterRequestParams,
168
- setS3Credentials,
169
- statusEmoji,
164
+ /**
165
+ * Format a list of test IDs for `--filter-list` machine-readable output.
166
+ * Used when the CLI `--format` option is passed,
167
+ * e.g. `--filter-list "coverage:file=..." --format grep`.
168
+ *
169
+ * @param {string[]} ids
170
+ * @param {'grep'|'json'|'newline'|'ids'} format
171
+ * @returns {string} Empty string if no ids; otherwise the formatted output.
172
+ */
173
+ function formatFilterListIds(ids, format) {
174
+ if (!ids || ids.length === 0) return '';
175
+ switch (format) {
176
+ case 'grep': return `(${ids.join('|')})`;
177
+ case 'json': return JSON.stringify(ids);
178
+ case 'newline': return ids.join('\n');
179
+ case 'ids':
180
+ default: return ids.join(',');
181
+ }
182
+ }
183
+
184
+ export {
185
+ updateFilterType,
186
+ parseFilterParams,
187
+ generateFilterRequestParams,
188
+ setS3Credentials,
189
+ statusEmoji,
170
190
  fullName,
171
- parsePipeOptions
191
+ parsePipeOptions,
192
+ formatFilterListIds,
172
193
  };