@testomatio/reporter 1.0.8 → 1.0.9-beta.1

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.
@@ -13,9 +13,11 @@ program
13
13
  .option('--launch', 'Start a new run and return its ID')
14
14
  .option('--finish', 'Finish Run by its ID')
15
15
  .option("--env-file <envfile>", "Load environment variables from env file")
16
- .action(opts => {
16
+ .option("--filter <filter>", "Additional execution filter")
17
+ .action(async (opts) => {
18
+ const { launch, finish, filter } = opts;
19
+ let { command } = opts;
17
20
 
18
- const { command, launch, finish } = opts;
19
21
  if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
20
22
 
21
23
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || process.env.TESTOMATIO;
@@ -51,6 +53,26 @@ program
51
53
 
52
54
  let exitCode = 0;
53
55
 
56
+ const client = new TestomatClient({ apiKey, title, parallel: true });
57
+
58
+ if(filter) {
59
+ const [pipe, opts] = filter.split(":");
60
+
61
+ try {
62
+ const tests = await client.prepareRun({pipe, opts});
63
+
64
+ if(!tests || tests.length === 0) {
65
+ return;
66
+ }
67
+
68
+ const grep = ` --grep (${tests.join('|')})`;
69
+ command += grep;
70
+ }
71
+ catch(err) {
72
+ console.log(APP_PREFIX, err);
73
+ }
74
+ }
75
+
54
76
  if (!command.split) {
55
77
  process.exitCode = 255;
56
78
  console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
@@ -73,8 +95,6 @@ program
73
95
  return;
74
96
  }
75
97
 
76
- const client = new TestomatClient({ apiKey, title, parallel: true });
77
-
78
98
  client.createRun().then(() => {
79
99
  const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
80
100
 
package/lib/client.js CHANGED
@@ -27,9 +27,60 @@ class Client {
27
27
  this.queue = Promise.resolve();
28
28
  this.totalUploaded = 0;
29
29
  this.version = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()).version;
30
+ this.executionList = Promise.resolve();
31
+
30
32
  console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
31
33
  }
32
34
 
35
+ /**
36
+ * Asynchronously prepares the execution list for running tests through various pipes.
37
+ * Each pipe in the client is checked for enablement,
38
+ * and if all pipes are disabled, the function returns a resolved Promise.
39
+ * Otherwise, it executes the `prepareRun` method for each enabled pipe and collects the results.
40
+ * The results are then filtered to remove any undefined values.
41
+ * If no valid results are found, the function returns undefined.
42
+ * Otherwise, it returns the first non-empty array from the filtered results.
43
+ *
44
+ * @param {Object} params - The options for preparing the test execution list.
45
+ * @param {string} params.pipe - Name of the executed pipe.
46
+ * @param {string} params.opts - Filter option.
47
+ * @returns {Promise<any>} - A Promise that resolves to an
48
+ * array containing the prepared execution list,
49
+ * or resolves to undefined if no valid results are found or if all pipes are disabled.
50
+ */
51
+ async prepareRun(params) {
52
+ const { pipe, opts } = params;
53
+ // all pipes disabled, skipping
54
+ if (!this.pipes.some(p => p.isEnabled)) {
55
+ return Promise.resolve();
56
+ }
57
+
58
+ try {
59
+ if (pipe.toLowerCase() !== "testomatio") {
60
+ //TODO: for the future for the another pipes
61
+ console.warn(APP_PREFIX, `At the moment processing is available only for the "testomatio" key. Example: "testomatio:tag-name=xxx"`)
62
+ return;
63
+ }
64
+
65
+ const results = await Promise.all(this.pipes.map(async p => {
66
+ return { pipe: p.toString(), result: await p.prepareRun(opts) };
67
+ }));
68
+
69
+ const result = results.filter(p => p.pipe.includes('Testomatio'))[0]?.result;
70
+
71
+ if (!result || result.length === 0) {
72
+ return;
73
+ }
74
+
75
+ debug('Execution tests list', result);
76
+
77
+ return result;
78
+ } catch (err) {
79
+ console.error(APP_PREFIX, err);
80
+ return;
81
+ }
82
+ }
83
+
33
84
  /**
34
85
  * Used to create a new Test run
35
86
  *
package/lib/pipe/csv.js CHANGED
@@ -40,6 +40,8 @@ class CsvPipe {
40
40
  }
41
41
  }
42
42
 
43
+ async prepareRun(opts) {}
44
+
43
45
  async createRun() {
44
46
  // empty
45
47
  }
@@ -6,6 +6,7 @@ const merge = require('lodash.merge');
6
6
  const { Octokit } = require('@octokit/rest');
7
7
  const { APP_PREFIX } = require('../constants');
8
8
  const { ansiRegExp, isSameTest } = require('../util');
9
+ const { statusEmoji, fullName } = require('./misc');
9
10
 
10
11
  /**
11
12
  * @typedef {import('../../types').Pipe} Pipe
@@ -37,6 +38,8 @@ class GitHubPipe {
37
38
  debug('GitHub Pipe: Enabled');
38
39
  }
39
40
 
41
+ async prepareRun(opts) {}
42
+
40
43
  async createRun() {}
41
44
 
42
45
  addTest(test) {
@@ -182,21 +185,6 @@ class GitHubPipe {
182
185
  }
183
186
  }
184
187
 
185
- function statusEmoji(status) {
186
- if (status === 'passed') return '🟢';
187
- if (status === 'failed') return '🔴';
188
- if (status === 'skipped') return '🟡';
189
- return '';
190
- }
191
-
192
- function fullName(t) {
193
- let line = '';
194
- if (t.suite_title) line = `${t.suite_title}: `;
195
- line += `**${t.title}**`;
196
- if (t.example) line += ` \`[${Object.values(t.example)}]\``;
197
- return line;
198
- }
199
-
200
188
  async function deletePreviousReport(octokit, owner, repo, issue, hiddenCommentData) {
201
189
  if (process.env.GH_KEEP_OUTDATED_REPORTS) return;
202
190
 
@@ -6,6 +6,7 @@ const merge = require('lodash.merge');
6
6
  const path = require('path');
7
7
  const { APP_PREFIX } = require('../constants');
8
8
  const { ansiRegExp, isSameTest } = require('../util');
9
+ const { statusEmoji, fullName } = require('./misc');
9
10
 
10
11
  //! GITLAB_PAT environment variable is required for this functionality to work
11
12
  //! and your pipeline trigger should be merge_request
@@ -46,6 +47,8 @@ class GitLabPipe {
46
47
  debug('GitLab Pipe: Enabled');
47
48
  }
48
49
 
50
+ async prepareRun(opts) {}
51
+
49
52
  async createRun() {}
50
53
 
51
54
  addTest(test) {
@@ -179,21 +182,6 @@ class GitLabPipe {
179
182
  updateRun() {}
180
183
  }
181
184
 
182
- function statusEmoji(status) {
183
- if (status === 'passed') return '🟢';
184
- if (status === 'failed') return '🔴';
185
- if (status === 'skipped') return '🟡';
186
- return '';
187
- }
188
-
189
- function fullName(t) {
190
- let line = '';
191
- if (t.suite_title) line = `${t.suite_title}: `;
192
- line += `**${t.title}**`;
193
- if (t.example) line += ` \`[${Object.values(t.example)}]\``;
194
- return line;
195
- }
196
-
197
185
  async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
198
186
  if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
199
187
 
@@ -0,0 +1,134 @@
1
+ const { resetConfig } = require('../fileUploader');
2
+ const { APP_PREFIX } = require('../constants');
3
+
4
+ /**
5
+ * Set S3 credentials from the provided artifacts object.
6
+ * @param {Object} artifacts - The artifacts object containing S3 credentials.
7
+ */
8
+ function setS3Credentials(artifacts) {
9
+ if (!Object.keys(artifacts).length) return;
10
+
11
+ console.log(APP_PREFIX, 'S3 were credentials obtained from Testomat.io...');
12
+
13
+ if (artifacts.ACCESS_KEY_ID) process.env.S3_ACCESS_KEY_ID = artifacts.ACCESS_KEY_ID;
14
+ if (artifacts.SECRET_ACCESS_KEY) process.env.S3_SECRET_ACCESS_KEY = artifacts.SECRET_ACCESS_KEY;
15
+ if (artifacts.REGION) process.env.S3_REGION = artifacts.REGION;
16
+ if (artifacts.BUCKET) process.env.S3_BUCKET = artifacts.BUCKET;
17
+ if (artifacts.ENDPOINT) process.env.S3_ENDPOINT = artifacts.ENDPOINT;
18
+ if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
19
+ resetConfig();
20
+ }
21
+ /**
22
+ * Generates mode request parameters based on the input params.
23
+ * @param {Object} params - The input parameters for the request.
24
+ * @param {string} params.type - The type of the request (e.g., "tag").
25
+ * @param {string} params.id - The ID associated with the request.
26
+ * @param {string} params.apiKey - The API key for authentication.
27
+ * @returns {Object|null} - An object containing the generated request parameters, or null if the type is invalid.
28
+ */
29
+ function generateFilterRequestParams(params) {
30
+ const { type, id, apiKey } = params;
31
+
32
+ if (!type) {
33
+ return;
34
+ }
35
+
36
+ if (!id) {
37
+ console.error(APP_PREFIX, `Please make sure your settings "${type.toUpperCase()}"= "${id}" is correct!`);
38
+ return;
39
+ }
40
+
41
+ return {
42
+ params: {
43
+ type,
44
+ id,
45
+ api_key: apiKey
46
+ },
47
+ responseType: "json"
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Parse filter parameters from a string in the format "type=id".
53
+ * @param {string} opts - The input string containing the filter parameters.
54
+ * @returns {Object} An object containing the parsed filter parameters.
55
+ * The object has properties "type" and "id".
56
+ */
57
+ function parseFilterParams(opts) {
58
+ const [type, id] = opts.split("=");
59
+ const validType = updateFilterType(type);
60
+
61
+ return {
62
+ type: validType,
63
+ id
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Update and validate the filter type.
69
+ * @param {string} type - The original filter type.
70
+ * @returns {string|undefined} The updated and validated filter type.
71
+ * Returns undefined if the type is not valid.
72
+ */
73
+ function updateFilterType(type) {
74
+ const typeLowerCase = type.toLowerCase();
75
+
76
+ const filterTypes = [
77
+ "tag-name",
78
+ "plan-id",
79
+ "label-name"
80
+ ];
81
+
82
+ const filterApi = [
83
+ "tag",
84
+ "plan",
85
+ "label",
86
+ // "issue", //TODO: WIP
87
+ // "jira" //TODO: WIP
88
+ ];
89
+
90
+ if (!filterTypes.includes(typeLowerCase)) {
91
+ console.log(APP_PREFIX, `❗❗❗ Invalid "filter=${type}" start settings! Available option list: ${filterTypes}`);
92
+ return;
93
+ }
94
+
95
+ const index = filterTypes.indexOf(typeLowerCase);
96
+
97
+ return (index !== -1)
98
+ ? filterApi[index]
99
+ : undefined
100
+ }
101
+
102
+ /**
103
+ * Return an emoji based on the provided status.
104
+ * @param {string} status - The status value ('passed', 'failed', or 'skipped').
105
+ * @returns {string} - An emoji corresponding to the provided status.
106
+ */
107
+ function statusEmoji(status) {
108
+ if (status === 'passed') return '🟢';
109
+ if (status === 'failed') return '🔴';
110
+ if (status === 'skipped') return '🟡';
111
+ return '';
112
+ }
113
+
114
+ /**
115
+ * Generate a full name string based on the provided test object.
116
+ * @param {object} t - The test object.
117
+ * @returns {string} - A formatted full name string for the test object.
118
+ */
119
+ function fullName(t) {
120
+ let line = '';
121
+ if (t.suite_title) line = `${t.suite_title}: `;
122
+ line += `**${t.title}**`;
123
+ if (t.example) line += ` \`[${Object.values(t.example)}]\``;
124
+ return line;
125
+ }
126
+
127
+ module.exports = {
128
+ updateFilterType,
129
+ parseFilterParams,
130
+ generateFilterRequestParams,
131
+ setS3Credentials,
132
+ statusEmoji,
133
+ fullName
134
+ };
@@ -3,8 +3,8 @@ const chalk = require('chalk');
3
3
  const axios = require('axios');
4
4
  const JsonCycle = require('json-cycle');
5
5
  const { APP_PREFIX, STATUS } = require('../constants');
6
- const { isValidUrl } = require('../util');
7
- const { resetConfig } = require('../fileUploader');
6
+ const { isValidUrl, foundedTestLog } = require('../util');
7
+ const { parseFilterParams, generateFilterRequestParams, setS3Credentials, } = require('./misc');
8
8
 
9
9
  const { TESTOMATIO_RUN } = process.env;
10
10
  if (TESTOMATIO_RUN) {
@@ -33,7 +33,12 @@ class TestomatioPipe {
33
33
  this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
34
34
  this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
35
35
  this.env = process.env.TESTOMATIO_ENV;
36
- this.axios = axios.create();
36
+
37
+ this.axios = axios.create({
38
+ baseURL: `${this.url.trim()}`,
39
+ timeout: 20000
40
+ });
41
+
37
42
  this.isEnabled = true;
38
43
  // do not finish this run (for parallel testing)
39
44
  this.proceed = process.env.TESTOMATIO_PROCEED;
@@ -47,6 +52,50 @@ class TestomatioPipe {
47
52
  }
48
53
  }
49
54
 
55
+ /**
56
+ * Asynchronously prepares and retrieves the Testomat.io test grepList based on the provided options.
57
+ * @param {Object} opts - The options for preparing the test grepList.
58
+ * @returns {Promise<string[]>} - An array containing the retrieved
59
+ * test grepList, or an empty array if no tests are found or the request is disabled.
60
+ * @throws {Error} - Throws an error if there was a problem while making the request.
61
+ */
62
+ async prepareRun(opts) {
63
+ if (!this.isEnabled) return [];
64
+
65
+ const { type, id } = parseFilterParams(opts);
66
+
67
+ try {
68
+ const q = generateFilterRequestParams({
69
+ type,
70
+ id,
71
+ apiKey: this.apiKey.trim()
72
+ });
73
+
74
+ if (!q) {
75
+ return;
76
+ }
77
+
78
+ const resp = await this.axios.get('/api/test_grep', q);
79
+ const { data } = resp;
80
+
81
+ if (Array.isArray(data?.tests) && data?.tests?.length > 0) {
82
+ foundedTestLog(APP_PREFIX, data.tests);
83
+ return data.tests;
84
+ }
85
+ else {
86
+ console.log(APP_PREFIX, `⛔ No tests found for your --filter --> ${type}=${id}`);
87
+ return;
88
+ }
89
+ }
90
+ catch (err) {
91
+ console.error(
92
+ APP_PREFIX,
93
+ `🚩 Error getting Testomat.io test grepList: ${err}`,
94
+ );
95
+ return;
96
+ }
97
+ }
98
+
50
99
  /**
51
100
  * @returns Promise<void>
52
101
  */
@@ -84,13 +133,13 @@ class TestomatioPipe {
84
133
  );
85
134
 
86
135
  if (this.runId) {
87
- const resp = await this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
136
+ const resp = await this.axios.put(`/api/reporter/${this.runId}`, runParams);
88
137
  if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
89
138
  return;
90
139
  }
91
140
 
92
141
  try {
93
- const resp = await this.axios.post(`${this.url.trim()}/api/reporter`, runParams, {
142
+ const resp = await this.axios.post(`/api/reporter`, runParams, {
94
143
  maxContentLength: Infinity,
95
144
  maxBodyLength: Infinity,
96
145
  });
@@ -121,7 +170,7 @@ class TestomatioPipe {
121
170
  data.create = this.createNewTests;
122
171
  const json = JsonCycle.stringify(data);
123
172
 
124
- return this.axios.post(`${this.url}/api/reporter/${this.runId}/testrun`, json, {
173
+ return this.axios.post(`/api/reporter/${this.runId}/testrun`, json, {
125
174
  maxContentLength: Infinity,
126
175
  maxBodyLength: Infinity,
127
176
  headers: {
@@ -169,7 +218,7 @@ class TestomatioPipe {
169
218
 
170
219
  try {
171
220
  if (this.runId && !this.proceed) {
172
- await this.axios.put(`${this.url}/api/reporter/${this.runId}`, {
221
+ await this.axios.put(`/api/reporter/${this.runId}`, {
173
222
  api_key: this.apiKey,
174
223
  status_event,
175
224
  tests: params.tests,
@@ -209,18 +258,3 @@ class TestomatioPipe {
209
258
  }
210
259
 
211
260
  module.exports = TestomatioPipe;
212
-
213
-
214
- function setS3Credentials(artifacts) {
215
- if (!Object.keys(artifacts).length) return;
216
-
217
- console.log(APP_PREFIX, 'S3 were credentials obtained from Testomat.io...');
218
-
219
- if (artifacts.ACCESS_KEY_ID) process.env.S3_ACCESS_KEY_ID = artifacts.ACCESS_KEY_ID;
220
- if (artifacts.SECRET_ACCESS_KEY) process.env.S3_SECRET_ACCESS_KEY = artifacts.SECRET_ACCESS_KEY;
221
- if (artifacts.REGION) process.env.S3_REGION = artifacts.REGION;
222
- if (artifacts.BUCKET) process.env.S3_BUCKET = artifacts.BUCKET;
223
- if (artifacts.ENDPOINT) process.env.S3_ENDPOINT = artifacts.ENDPOINT;
224
- if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
225
- resetConfig();
226
- }
package/lib/util.js CHANGED
@@ -192,6 +192,15 @@ const fileSystem = {
192
192
  }
193
193
  };
194
194
 
195
+
196
+ const foundedTestLog = (app, tests) => {
197
+ const n = tests.length;
198
+
199
+ return (n === 1)
200
+ ? console.log(app, `✅ We found only one test!`)
201
+ : console.log(app, `✅ We found ${n} tests!`);
202
+ }
203
+
195
204
  module.exports = {
196
205
  isSameTest,
197
206
  fetchSourceCode,
@@ -204,4 +213,5 @@ module.exports = {
204
213
  ansiRegExp,
205
214
  parseTest,
206
215
  parseSuite,
207
- };
216
+ foundedTestLog
217
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.0.8",
3
+ "version": "1.0.9-beta.1",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",