qase-javascript-commons 2.4.0-beta.1 → 2.4.0

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/README.md CHANGED
@@ -58,6 +58,8 @@ All configuration options are listed in the table below:
58
58
  | Size of batch for sending test results | `testops.batch.size` | `QASE_TESTOPS_BATCH_SIZE` | `200` | No | Any integer |
59
59
  | Enable defects for failed test cases | `testops.defect` | `QASE_TESTOPS_DEFECT` | `False` | No | `True`, `False` |
60
60
  | Enable/disable attachment uploads | `testops.uploadAttachments` | `QASE_TESTOPS_UPLOAD_ATTACHMENTS` | `true` | No | `True`, `False` |
61
+ | Configuration values to create/find in groups (format: `group1=value1,group2=value2`) | `testops.configurations.values` | `QASE_TESTOPS_CONFIGURATIONS_VALUES` | undefined | No | Comma-separated key=value pairs |
62
+ | Create configuration groups if they don't exist | `testops.configurations.createIfNotExists` | `QASE_TESTOPS_CONFIGURATIONS_CREATE_IF_NOT_EXISTS` | `false` | No | `True`, `False` |
61
63
 
62
64
  ### Example `qase.config.json` config:
63
65
 
@@ -92,6 +94,19 @@ All configuration options are listed in the table below:
92
94
  "project": "<project_code>",
93
95
  "batch": {
94
96
  "size": 100
97
+ },
98
+ "configurations": {
99
+ "values": [
100
+ {
101
+ "name": "group1",
102
+ "value": "value1"
103
+ },
104
+ {
105
+ "name": "group2",
106
+ "value": "value2"
107
+ }
108
+ ],
109
+ "createIfNotExists": true
95
110
  }
96
111
  }
97
112
  }
package/changelog.md CHANGED
@@ -1,3 +1,9 @@
1
+ # qase-javascript-commons@2.3.6
2
+
3
+ ## What's new
4
+
5
+ Added support for configurations in the test run.
6
+
1
7
  # qase-javascript-commons@2.3.5
2
8
 
3
9
  ## What's new
@@ -11,6 +11,7 @@ export declare class ClientV1 implements IClient {
11
11
  private readonly runClient;
12
12
  private readonly environmentClient;
13
13
  private readonly attachmentClient;
14
+ private readonly configurationClient;
14
15
  constructor(logger: LoggerInterface, config: TestOpsOptionsType, environment: string | undefined);
15
16
  private createApiConfig;
16
17
  uploadResults(_runId: number, _results: TestResultType[]): Promise<void>;
@@ -21,6 +22,33 @@ export declare class ClientV1 implements IClient {
21
22
  private prepareAttachmentData;
22
23
  private getEnvironmentId;
23
24
  private prepareRunObject;
25
+ /**
26
+ * Get all configuration groups with their configurations
27
+ * @returns Promise<ConfigurationGroup[]> Array of configuration groups
28
+ * @private
29
+ */
30
+ private getConfigurations;
31
+ /**
32
+ * Create a configuration group
33
+ * @param title Group title
34
+ * @returns Promise<number | undefined> Created group ID
35
+ * @private
36
+ */
37
+ private createConfigurationGroup;
38
+ /**
39
+ * Create a configuration in a group
40
+ * @param title Configuration title
41
+ * @param groupId Group ID
42
+ * @returns Promise<number | undefined> Created configuration ID
43
+ * @private
44
+ */
45
+ private createConfiguration;
46
+ /**
47
+ * Handle configuration creation based on config settings
48
+ * @returns Promise<number[]> Array of configuration IDs
49
+ * @private
50
+ */
51
+ private handleConfigurations;
24
52
  /**
25
53
  * Process error and throw QaseError
26
54
  * @param {Error | AxiosError} error
@@ -31,6 +31,7 @@ class ClientV1 {
31
31
  runClient;
32
32
  environmentClient;
33
33
  attachmentClient;
34
+ configurationClient;
34
35
  constructor(logger, config, environment) {
35
36
  this.logger = logger;
36
37
  this.config = config;
@@ -40,6 +41,7 @@ class ClientV1 {
40
41
  this.runClient = new qase_api_client_1.RunsApi(apiConfig);
41
42
  this.environmentClient = new qase_api_client_1.EnvironmentsApi(apiConfig);
42
43
  this.attachmentClient = new qase_api_client_1.AttachmentsApi(apiConfig);
44
+ this.configurationClient = new qase_api_client_1.ConfigurationsApi(apiConfig);
43
45
  }
44
46
  createApiConfig() {
45
47
  const apiConfig = new qase_api_client_1.Configuration({ apiKey: this.config.api.token, formDataCtor: form_data_1.default });
@@ -59,8 +61,13 @@ class ClientV1 {
59
61
  return this.config.run.id;
60
62
  }
61
63
  try {
64
+ // Handle configurations if provided
65
+ let configurationIds = [];
66
+ if (this.config.configurations) {
67
+ configurationIds = await this.handleConfigurations();
68
+ }
62
69
  const environmentId = await this.getEnvironmentId();
63
- const runObject = this.prepareRunObject(environmentId);
70
+ const runObject = this.prepareRunObject(environmentId, configurationIds);
64
71
  this.logger.logDebug(`Creating test run: ${JSON.stringify(runObject)}`);
65
72
  const { data } = await this.runClient.createRun(this.config.project, runObject);
66
73
  if (!data.result?.id) {
@@ -139,7 +146,7 @@ class ClientV1 {
139
146
  const { data } = await this.environmentClient.getEnvironments(this.config.project, undefined, this.environment, 100);
140
147
  return data.result?.entities?.find((env) => env.slug === this.environment)?.id;
141
148
  }
142
- prepareRunObject(environmentId) {
149
+ prepareRunObject(environmentId, configurationIds) {
143
150
  const runObject = {
144
151
  title: this.config.run.title ?? `Automated run ${new Date().toISOString()}`,
145
152
  description: this.config.run.description ?? '',
@@ -154,8 +161,137 @@ class ClientV1 {
154
161
  if (this.config.plan.id) {
155
162
  runObject.plan_id = this.config.plan.id;
156
163
  }
164
+ if (configurationIds && configurationIds.length > 0) {
165
+ runObject.configurations = configurationIds;
166
+ }
157
167
  return runObject;
158
168
  }
169
+ /**
170
+ * Get all configuration groups with their configurations
171
+ * @returns Promise<ConfigurationGroup[]> Array of configuration groups
172
+ * @private
173
+ */
174
+ async getConfigurations() {
175
+ try {
176
+ const { data } = await this.configurationClient.getConfigurations(this.config.project);
177
+ const entities = data.result?.entities ?? [];
178
+ // Convert API response to domain model
179
+ return entities.map(group => ({
180
+ id: group.id ?? 0,
181
+ title: group.title ?? '',
182
+ configurations: group.configurations?.map(config => ({
183
+ id: config.id ?? 0,
184
+ title: config.title ?? ''
185
+ })) ?? []
186
+ }));
187
+ }
188
+ catch (error) {
189
+ throw this.processError(error, 'Error getting configurations');
190
+ }
191
+ }
192
+ /**
193
+ * Create a configuration group
194
+ * @param title Group title
195
+ * @returns Promise<number | undefined> Created group ID
196
+ * @private
197
+ */
198
+ async createConfigurationGroup(title) {
199
+ try {
200
+ const group = { title };
201
+ const { data } = await this.configurationClient.createConfigurationGroup(this.config.project, group);
202
+ return data.result?.id;
203
+ }
204
+ catch (error) {
205
+ throw this.processError(error, 'Error creating configuration group');
206
+ }
207
+ }
208
+ /**
209
+ * Create a configuration in a group
210
+ * @param title Configuration title
211
+ * @param groupId Group ID
212
+ * @returns Promise<number | undefined> Created configuration ID
213
+ * @private
214
+ */
215
+ async createConfiguration(title, groupId) {
216
+ try {
217
+ const config = { title, group_id: groupId };
218
+ const { data } = await this.configurationClient.createConfiguration(this.config.project, config);
219
+ return data.result?.id;
220
+ }
221
+ catch (error) {
222
+ throw this.processError(error, 'Error creating configuration');
223
+ }
224
+ }
225
+ /**
226
+ * Handle configuration creation based on config settings
227
+ * @returns Promise<number[]> Array of configuration IDs
228
+ * @private
229
+ */
230
+ async handleConfigurations() {
231
+ if (!this.config.configurations?.values.length) {
232
+ return [];
233
+ }
234
+ const configurationIds = [];
235
+ try {
236
+ // Get existing configuration groups
237
+ const existingGroups = await this.getConfigurations();
238
+ for (const configValue of this.config.configurations.values) {
239
+ const { name: groupName, value: configName } = configValue;
240
+ // Find existing group or create new one
241
+ const group = existingGroups.find(g => g.title === groupName);
242
+ let groupId;
243
+ if (group) {
244
+ groupId = group.id;
245
+ this.logger.logDebug(`Found existing configuration group: ${groupName}`);
246
+ }
247
+ else {
248
+ if (this.config.configurations.createIfNotExists) {
249
+ const newGroupId = await this.createConfigurationGroup(groupName);
250
+ if (newGroupId) {
251
+ groupId = newGroupId;
252
+ this.logger.logDebug(`Created new configuration group: ${groupName} with ID: ${groupId}`);
253
+ }
254
+ else {
255
+ this.logger.logDebug(`Failed to create configuration group: ${groupName}, skipping`);
256
+ continue;
257
+ }
258
+ }
259
+ else {
260
+ this.logger.logDebug(`Configuration group not found: ${groupName}, skipping`);
261
+ continue;
262
+ }
263
+ }
264
+ if (groupId) {
265
+ // Check if configuration already exists in the group
266
+ const existingConfig = group?.configurations.find(c => c.title === configName);
267
+ if (!existingConfig) {
268
+ // Check if we should create configuration if it doesn't exist
269
+ if (this.config.configurations.createIfNotExists) {
270
+ const configId = await this.createConfiguration(configName, groupId);
271
+ if (configId) {
272
+ configurationIds.push(configId);
273
+ }
274
+ this.logger.logDebug(`Created configuration: ${configName} in group: ${groupName}`);
275
+ }
276
+ else {
277
+ this.logger.logDebug(`Configuration not found: ${configName} in group: ${groupName}, skipping`);
278
+ }
279
+ }
280
+ else {
281
+ if (existingConfig.id) {
282
+ configurationIds.push(existingConfig.id);
283
+ }
284
+ this.logger.logDebug(`Configuration already exists: ${configName} in group: ${groupName}`);
285
+ }
286
+ }
287
+ }
288
+ }
289
+ catch (error) {
290
+ this.logger.logError('Error handling configurations:', error);
291
+ // Don't throw error to avoid blocking test run creation
292
+ }
293
+ return configurationIds;
294
+ }
159
295
  /**
160
296
  * Process error and throw QaseError
161
297
  * @param {Error | AxiosError} error
@@ -1,6 +1,177 @@
1
- import { JSONSchemaType } from 'ajv';
2
- import { ConfigType } from './config-type';
1
+ import { ModeEnum } from '../options';
2
+ import { DriverEnum, FormatEnum } from '../writer';
3
3
  /**
4
4
  * @type {JSONSchemaType<ConfigType>}
5
5
  */
6
- export declare const configValidationSchema: JSONSchemaType<ConfigType>;
6
+ export declare const configValidationSchema: {
7
+ type: string;
8
+ properties: {
9
+ mode: {
10
+ type: string;
11
+ enum: ModeEnum[];
12
+ nullable: boolean;
13
+ };
14
+ fallback: {
15
+ type: string;
16
+ enum: ModeEnum[];
17
+ nullable: boolean;
18
+ };
19
+ debug: {
20
+ type: string;
21
+ nullable: boolean;
22
+ };
23
+ environment: {
24
+ type: string;
25
+ nullable: boolean;
26
+ };
27
+ captureLogs: {
28
+ type: string;
29
+ nullable: boolean;
30
+ };
31
+ rootSuite: {
32
+ type: string;
33
+ nullable: boolean;
34
+ };
35
+ testops: {
36
+ type: string;
37
+ nullable: boolean;
38
+ properties: {
39
+ api: {
40
+ type: string;
41
+ nullable: boolean;
42
+ properties: {
43
+ token: {
44
+ type: string;
45
+ nullable: boolean;
46
+ };
47
+ host: {
48
+ type: string;
49
+ nullable: boolean;
50
+ };
51
+ };
52
+ };
53
+ project: {
54
+ type: string;
55
+ nullable: boolean;
56
+ };
57
+ uploadAttachments: {
58
+ type: string;
59
+ nullable: boolean;
60
+ };
61
+ run: {
62
+ type: string;
63
+ nullable: boolean;
64
+ properties: {
65
+ id: {
66
+ type: string;
67
+ nullable: boolean;
68
+ };
69
+ title: {
70
+ type: string;
71
+ nullable: boolean;
72
+ };
73
+ description: {
74
+ type: string;
75
+ nullable: boolean;
76
+ };
77
+ complete: {
78
+ type: string;
79
+ nullable: boolean;
80
+ };
81
+ tags: {
82
+ type: string;
83
+ items: {
84
+ type: string;
85
+ };
86
+ nullable: boolean;
87
+ };
88
+ };
89
+ };
90
+ plan: {
91
+ type: string;
92
+ nullable: boolean;
93
+ properties: {
94
+ id: {
95
+ type: string;
96
+ nullable: boolean;
97
+ };
98
+ };
99
+ };
100
+ batch: {
101
+ type: string;
102
+ nullable: boolean;
103
+ properties: {
104
+ size: {
105
+ type: string;
106
+ nullable: boolean;
107
+ };
108
+ };
109
+ };
110
+ defect: {
111
+ type: string;
112
+ nullable: boolean;
113
+ };
114
+ configurations: {
115
+ type: string;
116
+ nullable: boolean;
117
+ properties: {
118
+ values: {
119
+ type: string;
120
+ items: {
121
+ type: string;
122
+ properties: {
123
+ name: {
124
+ type: string;
125
+ nullable: boolean;
126
+ };
127
+ value: {
128
+ type: string;
129
+ nullable: boolean;
130
+ };
131
+ };
132
+ required: string[];
133
+ };
134
+ };
135
+ createIfNotExists: {
136
+ type: string;
137
+ nullable: boolean;
138
+ };
139
+ };
140
+ required: string[];
141
+ };
142
+ };
143
+ };
144
+ report: {
145
+ type: string;
146
+ nullable: boolean;
147
+ properties: {
148
+ driver: {
149
+ type: string;
150
+ enum: DriverEnum[];
151
+ nullable: boolean;
152
+ };
153
+ connections: {
154
+ type: string;
155
+ nullable: boolean;
156
+ properties: {
157
+ local: {
158
+ type: string;
159
+ nullable: boolean;
160
+ properties: {
161
+ path: {
162
+ type: string;
163
+ nullable: boolean;
164
+ };
165
+ format: {
166
+ type: string;
167
+ enum: FormatEnum[];
168
+ nullable: boolean;
169
+ };
170
+ };
171
+ };
172
+ };
173
+ };
174
+ };
175
+ };
176
+ };
177
+ };
@@ -114,6 +114,34 @@ exports.configValidationSchema = {
114
114
  type: 'boolean',
115
115
  nullable: true,
116
116
  },
117
+ configurations: {
118
+ type: 'object',
119
+ nullable: true,
120
+ properties: {
121
+ values: {
122
+ type: 'array',
123
+ items: {
124
+ type: 'object',
125
+ properties: {
126
+ name: {
127
+ type: 'string',
128
+ nullable: true,
129
+ },
130
+ value: {
131
+ type: 'string',
132
+ nullable: true,
133
+ },
134
+ },
135
+ required: ['name', 'value'],
136
+ },
137
+ },
138
+ createIfNotExists: {
139
+ type: 'boolean',
140
+ nullable: true,
141
+ },
142
+ },
143
+ required: ['values'],
144
+ },
117
145
  },
118
146
  },
119
147
  report: {
@@ -46,6 +46,13 @@ export declare enum EnvPlanEnum {
46
46
  export declare enum EnvBatchEnum {
47
47
  size = "QASE_TESTOPS_BATCH_SIZE"
48
48
  }
49
+ /**
50
+ * @enum {string}
51
+ */
52
+ export declare enum EnvConfigurationsEnum {
53
+ values = "QASE_TESTOPS_CONFIGURATIONS_VALUES",
54
+ createIfNotExists = "QASE_TESTOPS_CONFIGURATIONS_CREATE_IF_NOT_EXISTS"
55
+ }
49
56
  /**
50
57
  * @enum {string}
51
58
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EnvLocalEnum = exports.EnvBatchEnum = exports.EnvPlanEnum = exports.EnvRunEnum = exports.EnvApiEnum = exports.EnvTestOpsEnum = exports.EnvEnum = void 0;
3
+ exports.EnvLocalEnum = exports.EnvConfigurationsEnum = exports.EnvBatchEnum = exports.EnvPlanEnum = exports.EnvRunEnum = exports.EnvApiEnum = exports.EnvTestOpsEnum = exports.EnvEnum = void 0;
4
4
  /**
5
5
  * @enum {string}
6
6
  */
@@ -55,6 +55,14 @@ var EnvBatchEnum;
55
55
  (function (EnvBatchEnum) {
56
56
  EnvBatchEnum["size"] = "QASE_TESTOPS_BATCH_SIZE";
57
57
  })(EnvBatchEnum || (exports.EnvBatchEnum = EnvBatchEnum = {}));
58
+ /**
59
+ * @enum {string}
60
+ */
61
+ var EnvConfigurationsEnum;
62
+ (function (EnvConfigurationsEnum) {
63
+ EnvConfigurationsEnum["values"] = "QASE_TESTOPS_CONFIGURATIONS_VALUES";
64
+ EnvConfigurationsEnum["createIfNotExists"] = "QASE_TESTOPS_CONFIGURATIONS_CREATE_IF_NOT_EXISTS";
65
+ })(EnvConfigurationsEnum || (exports.EnvConfigurationsEnum = EnvConfigurationsEnum = {}));
58
66
  /**
59
67
  * @enum {string}
60
68
  */
@@ -34,6 +34,13 @@ const envToConfig = (env) => ({
34
34
  size: env[env_enum_1.EnvBatchEnum.size],
35
35
  },
36
36
  defect: env[env_enum_1.EnvTestOpsEnum.defect],
37
+ configurations: env[env_enum_1.EnvConfigurationsEnum.values] ? {
38
+ values: env[env_enum_1.EnvConfigurationsEnum.values].split(',').map(item => {
39
+ const [name, value] = item.split('=');
40
+ return { name: (name ?? '').trim(), value: value ? value.trim() : '' };
41
+ }),
42
+ createIfNotExists: env[env_enum_1.EnvConfigurationsEnum.createIfNotExists],
43
+ } : undefined,
37
44
  },
38
45
  report: {
39
46
  connections: {
@@ -1,4 +1,4 @@
1
- import { EnvEnum, EnvTestOpsEnum, EnvApiEnum, EnvRunEnum, EnvLocalEnum, EnvPlanEnum, EnvBatchEnum } from './env-enum';
1
+ import { EnvEnum, EnvTestOpsEnum, EnvApiEnum, EnvRunEnum, EnvLocalEnum, EnvPlanEnum, EnvBatchEnum, EnvConfigurationsEnum } from './env-enum';
2
2
  import { ModeEnum } from '../options';
3
3
  import { FormatEnum } from '../writer';
4
4
  export interface EnvType {
@@ -20,6 +20,8 @@ export interface EnvType {
20
20
  [EnvRunEnum.tags]?: string;
21
21
  [EnvPlanEnum.id]?: number;
22
22
  [EnvBatchEnum.size]?: number;
23
+ [EnvConfigurationsEnum.values]?: string;
24
+ [EnvConfigurationsEnum.createIfNotExists]?: boolean;
23
25
  [EnvLocalEnum.path]?: string;
24
26
  [EnvLocalEnum.format]?: `${FormatEnum}`;
25
27
  }
@@ -84,6 +84,14 @@ exports.envValidationSchema = {
84
84
  type: 'number',
85
85
  nullable: true,
86
86
  },
87
+ [env_enum_1.EnvConfigurationsEnum.values]: {
88
+ type: 'string',
89
+ nullable: true,
90
+ },
91
+ [env_enum_1.EnvConfigurationsEnum.createIfNotExists]: {
92
+ type: 'boolean',
93
+ nullable: true,
94
+ },
87
95
  [env_enum_1.EnvLocalEnum.path]: {
88
96
  type: 'string',
89
97
  nullable: true,
@@ -6,6 +6,15 @@ export interface TestOpsOptionsType {
6
6
  plan: TestOpsPlanType;
7
7
  batch?: TestOpsBatchType;
8
8
  defect?: boolean | undefined;
9
+ configurations?: TestOpsConfigurationType | undefined;
10
+ }
11
+ export interface TestOpsConfigurationType {
12
+ values: TestOpsConfigurationValueType[];
13
+ createIfNotExists?: boolean | undefined;
14
+ }
15
+ export interface TestOpsConfigurationValueType {
16
+ name: string;
17
+ value: string;
9
18
  }
10
19
  export interface TestOpsRunType {
11
20
  id?: number | undefined;
@@ -0,0 +1,12 @@
1
+ export interface ConfigurationGroup {
2
+ id: number;
3
+ title: string;
4
+ configurations: ConfigurationItem[];
5
+ }
6
+ export interface ConfigurationItem {
7
+ id: number;
8
+ title: string;
9
+ }
10
+ export interface ConfigurationGroupResponse {
11
+ groups: ConfigurationGroup[];
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -5,3 +5,4 @@ export { StepStatusEnum } from './step-execution';
5
5
  export { Attachment } from './attachment';
6
6
  export { Report } from './report';
7
7
  export { CompoundError } from './error';
8
+ export { ConfigurationGroup, ConfigurationItem, ConfigurationGroupResponse } from './configuration';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qase-javascript-commons",
3
- "version": "2.4.0-beta.1",
3
+ "version": "2.4.0",
4
4
  "description": "Qase JS Reporters",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -28,7 +28,7 @@
28
28
  "async-mutex": "~0.5.0",
29
29
  "chalk": "^4.1.2",
30
30
  "env-schema": "^5.2.0",
31
- "form-data": "^4.0.0",
31
+ "form-data": "^4.0.4",
32
32
  "lodash.get": "^4.4.2",
33
33
  "lodash.merge": "^4.6.2",
34
34
  "lodash.mergewith": "^4.6.2",