testit-adapter-jest 4.0.5 → 4.1.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
@@ -12,6 +12,10 @@ npm install testit-adapter-jest
12
12
 
13
13
  ### Configuration
14
14
 
15
+ #### Log level
16
+
17
+ The adapter includes a custom logger; the default level is `warn`. Set another level at runtime with the `LOG_LEVEL` environment variable: `error`, `warn`, `info`, or `debug`.
18
+
15
19
  | Description | File property | Environment variable |
16
20
  |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|--------------------------------------------|
17
21
  | Location of the TMS instance | url | TMS_URL |
@@ -24,6 +28,7 @@ npm install testit-adapter-jest
24
28
  | It enables/disables certificate validation (**It's optional**). Default value - true | certValidation | TMS_CERT_VALIDATION |
25
29
  | Mode of automatic creation test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will create a test case linked to the created autotest (not to the updated autotest)<br/>false - in this mode, the adapter will not create a test case | automaticCreationTestCases | TMS_AUTOMATIC_CREATION_TEST_CASES |
26
30
  | Mode of automatic updation links to test cases (**It's optional**). Default value - false. The adapter supports following modes:<br/>true - in this mode, the adapter will update links to test cases<br/>false - in this mode, the adapter will not update link to test cases | automaticUpdationLinksToTestCases | TMS_AUTOMATIC_UPDATION_LINKS_TO_TEST_CASES |
31
+ | Logger level, default value is `warn`, available values: [`error`, `warn`, `info`, `debug`] | | LOG_LEVEL |
27
32
 
28
33
  #### File
29
34
 
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const testit_js_commons_1 = require("testit-js-commons");
4
+ const testit_js_commons_2 = require("testit-js-commons");
4
5
  const globalUnhandledRejectionLogger = (reason) => {
5
6
  const normalized = reason?.body ?? reason?.error ?? reason;
6
- console.error("[jest-globalSetup] unhandledRejection:", normalized);
7
+ testit_js_commons_2.logger.error("[jest-globalSetup] unhandledRejection:", normalized);
7
8
  };
8
9
  exports.default = async (globalConfig, projectConfig) => {
9
10
  process.on("unhandledRejection", globalUnhandledRejectionLogger);
@@ -17,6 +18,6 @@ exports.default = async (globalConfig, projectConfig) => {
17
18
  projectConfig.testEnvironmentOptions["adapterMode"] = 1;
18
19
  }
19
20
  catch (err) {
20
- console.error("Failed globalSetup for testit-adapter-jest:", err?.body ?? err?.error ?? err);
21
+ testit_js_commons_2.logger.error("Failed globalSetup for testit-adapter-jest:", err?.body ?? err?.error ?? err);
21
22
  }
22
23
  };
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const testit_js_commons_1 = require("testit-js-commons");
3
4
  exports.default = async (globalConfig, projectConfig) => {
4
5
  try {
5
6
  if (globalThis.strategy) {
@@ -7,6 +8,6 @@ exports.default = async (globalConfig, projectConfig) => {
7
8
  }
8
9
  }
9
10
  catch (err) {
10
- console.error('Failed to complete test run');
11
+ testit_js_commons_1.logger.error('Failed to complete test run');
11
12
  }
12
13
  };
@@ -18,6 +18,10 @@ export default class TestItEnvironment extends NodeEnvironment {
18
18
  private readonly usesGlobalStrategy;
19
19
  private readonly unhandledRejectionHandler;
20
20
  private finalized;
21
+ private readonly importRealtime;
22
+ private currentTestAttachmentsQueue;
23
+ /** Sent in realtime on test_done; finalized with afterAll on run_finish. */
24
+ private realtimeSent;
21
25
  constructor(jestConfig: JestEnvironmentConfig, jestContext: EnvironmentContext);
22
26
  setup(): Promise<void>;
23
27
  teardown(): Promise<void>;
@@ -40,6 +44,12 @@ export default class TestItEnvironment extends NodeEnvironment {
40
44
  saveResult(test: Extract<Event, {
41
45
  name: "test_done";
42
46
  }>["test"]): Promise<void>;
47
+ private snapshotAutotestData;
48
+ private buildSetupSteps;
49
+ private buildTeardownSteps;
50
+ private sendRealtimePayload;
51
+ /** afterAll hooks run after all tests; patch teardown when importRealtime is enabled. */
52
+ private flushRealtimeTeardown;
43
53
  loadResults(): Promise<void>;
44
54
  resetTest(): void;
45
55
  resetStep(): void;
@@ -7,6 +7,7 @@ const jest_environment_node_1 = __importDefault(require("jest-environment-node")
7
7
  const testit_js_commons_1 = require("testit-js-commons");
8
8
  const debug_1 = require("debug");
9
9
  const utils_1 = require("./utils");
10
+ const testit_js_commons_2 = require("testit-js-commons");
10
11
  const log = (0, debug_1.debug)("tms").extend("environment");
11
12
  const emptyAutotestData = () => ({
12
13
  externalId: "",
@@ -35,12 +36,16 @@ class TestItEnvironment extends jest_environment_node_1.default {
35
36
  this.autotests = [];
36
37
  this.attachmentsQueue = [];
37
38
  this.finalized = false;
39
+ this.currentTestAttachmentsQueue = [];
40
+ /** Sent in realtime on test_done; finalized with afterAll on run_finish. */
41
+ this.realtimeSent = [];
38
42
  const config = new testit_js_commons_1.ConfigComposer().compose(jestConfig.projectConfig.testEnvironmentOptions);
43
+ this.importRealtime = Boolean(config.importRealtime);
39
44
  this.additions = new testit_js_commons_1.Additions(config);
40
45
  this.usesGlobalStrategy = Boolean(globalThis.strategy);
41
46
  this.strategy = globalThis.strategy ?? testit_js_commons_1.StrategyFactory.create(config);
42
47
  this.unhandledRejectionHandler = (reason) => {
43
- console.error("Unhandled promise rejection in Jest environment:", this.formatError(reason));
48
+ testit_js_commons_2.logger.error("Unhandled promise rejection in Jest environment:", this.formatError(reason));
44
49
  };
45
50
  this.testPath = (0, utils_1.excludePath)(jestContext.testPath, jestConfig.globalConfig.rootDir);
46
51
  }
@@ -50,9 +55,10 @@ class TestItEnvironment extends jest_environment_node_1.default {
50
55
  await super.setup();
51
56
  const testRunId = await this.strategy.testRunId;
52
57
  const syncRunner = this.strategy.syncStorageRunner;
53
- console.debug("[jest-env] setup start", {
58
+ testit_js_commons_2.logger.debug("[jest-env] setup start", {
54
59
  pid: process.pid,
55
60
  testRunId,
61
+ importRealtime: this.importRealtime,
56
62
  usesGlobalStrategy: this.usesGlobalStrategy,
57
63
  syncRunnerActive: Boolean(syncRunner?.isActive?.()),
58
64
  syncRunnerMaster: Boolean(syncRunner?.isMasterWorker?.()),
@@ -63,7 +69,7 @@ class TestItEnvironment extends jest_environment_node_1.default {
63
69
  try {
64
70
  await this.strategy.setup();
65
71
  const localSyncRunner = this.strategy.syncStorageRunner;
66
- console.debug("[jest-env] local setup done", {
72
+ testit_js_commons_2.logger.debug("[jest-env] local setup done", {
67
73
  pid: process.pid,
68
74
  testRunId,
69
75
  syncRunnerActive: Boolean(localSyncRunner?.isActive?.()),
@@ -71,12 +77,12 @@ class TestItEnvironment extends jest_environment_node_1.default {
71
77
  });
72
78
  }
73
79
  catch (err) {
74
- console.error("Failed local strategy.setup() in Jest environment:", this.formatError(err));
80
+ testit_js_commons_2.logger.error("Failed local strategy.setup() in Jest environment:", this.formatError(err));
75
81
  }
76
82
  }
77
83
  else {
78
84
  const sharedSyncRunner = this.strategy.syncStorageRunner;
79
- console.debug("[jest-env] shared strategy mode", {
85
+ testit_js_commons_2.logger.debug("[jest-env] shared strategy mode", {
80
86
  pid: process.pid,
81
87
  testRunId,
82
88
  syncRunnerActive: Boolean(sharedSyncRunner?.isActive?.()),
@@ -141,11 +147,17 @@ class TestItEnvironment extends jest_environment_node_1.default {
141
147
  break;
142
148
  }
143
149
  case "run_finish": {
150
+ testit_js_commons_2.logger.debug("[jest-env] run_finish", {
151
+ importRealtime: this.importRealtime,
152
+ realtimeSent: this.realtimeSent.length,
153
+ afterAllSteps: this.afterAllSteps.length,
154
+ });
144
155
  try {
156
+ await this.flushRealtimeTeardown();
145
157
  await this.loadResults();
146
158
  }
147
159
  catch (err) {
148
- console.error("Failed to load results in Jest run_finish:", this.formatError(err));
160
+ testit_js_commons_2.logger.error("Failed to load results in Jest run_finish:", this.formatError(err));
149
161
  }
150
162
  finally {
151
163
  // In worker mode we run local setup, so finalization must happen here even if loadResults fails.
@@ -155,7 +167,7 @@ class TestItEnvironment extends jest_environment_node_1.default {
155
167
  await this.strategy.teardown();
156
168
  }
157
169
  catch (err) {
158
- console.error("Failed strategy.teardown() in Jest run_finish:", this.formatError(err));
170
+ testit_js_commons_2.logger.error("Failed strategy.teardown() in Jest run_finish:", this.formatError(err));
159
171
  }
160
172
  }
161
173
  }
@@ -164,7 +176,7 @@ class TestItEnvironment extends jest_environment_node_1.default {
164
176
  }
165
177
  }
166
178
  catch (err) {
167
- console.error("Unhandled async error in Jest environment event handler:", this.formatError(err));
179
+ testit_js_commons_2.logger.error("Unhandled async error in Jest environment event handler:", this.formatError(err));
168
180
  }
169
181
  }
170
182
  formatError(err) {
@@ -219,11 +231,12 @@ class TestItEnvironment extends jest_environment_node_1.default {
219
231
  }
220
232
  async saveResult(test) {
221
233
  log("Saving result for %s", test.name);
222
- await Promise.allSettled(this.attachmentsQueue);
234
+ await Promise.allSettled(this.currentTestAttachmentsQueue);
235
+ const autotestSnapshot = this.snapshotAutotestData();
223
236
  const errorMessage = test.errors.length > 0 ? test.errors.map((err) => err[0]?.message).join("\n") : undefined;
224
237
  const errorTraces = test.errors.length > 0 ? test.errors.map((err) => err[0]?.stack).join("\n") : undefined;
225
238
  const result = {
226
- autoTestExternalId: this.autotestData.externalId,
239
+ autoTestExternalId: autotestSnapshot.externalId,
227
240
  outcome: test.errors.length > 0 ? "Failed" : "Passed",
228
241
  startedOn: test.startedAt ? new Date(test.startedAt) : undefined,
229
242
  duration: test.duration ?? undefined,
@@ -232,11 +245,120 @@ class TestItEnvironment extends jest_environment_node_1.default {
232
245
  message: this.additions.messages.join("\n").concat(errorMessage ?? ""),
233
246
  links: this.additions.links,
234
247
  };
235
- this.autotests.push(this.autotestData);
236
- this.autotestResults.push(result);
248
+ if (this.importRealtime) {
249
+ this.realtimeSent.push({ autotest: autotestSnapshot, result });
250
+ testit_js_commons_2.logger.debug("[jest-env] realtime test_done", {
251
+ externalId: autotestSnapshot.externalId,
252
+ outcome: result.outcome,
253
+ afterEachSteps: autotestSnapshot.afterEach.length,
254
+ afterAllStepsKnown: this.afterAllSteps.length,
255
+ });
256
+ try {
257
+ await this.sendRealtimePayload(autotestSnapshot, result, false);
258
+ }
259
+ catch (err) {
260
+ testit_js_commons_2.logger.error("Failed realtime send in Jest environment:", this.formatError(err));
261
+ }
262
+ }
263
+ else {
264
+ this.autotests.push(autotestSnapshot);
265
+ this.autotestResults.push(result);
266
+ }
267
+ this.currentTestAttachmentsQueue = [];
237
268
  this.resetTest();
238
269
  }
270
+ snapshotAutotestData() {
271
+ return {
272
+ ...this.autotestData,
273
+ beforeEach: [...this.autotestData.beforeEach],
274
+ afterEach: [...this.autotestData.afterEach],
275
+ testSteps: [...this.autotestData.testSteps],
276
+ attachments: [...this.autotestData.attachments],
277
+ links: this.autotestData.links ? [...this.autotestData.links] : [],
278
+ labels: this.autotestData.labels ? [...this.autotestData.labels] : [],
279
+ tags: this.autotestData.tags ? [...this.autotestData.tags] : [],
280
+ };
281
+ }
282
+ buildSetupSteps(autotest) {
283
+ return this.beforeAllSteps.concat(autotest.beforeEach);
284
+ }
285
+ buildTeardownSteps(autotest, includeAfterAll) {
286
+ const afterEach = autotest.afterEach;
287
+ return includeAfterAll ? afterEach.concat(this.afterAllSteps) : afterEach;
288
+ }
289
+ async sendRealtimePayload(autotest, result, includeAfterAll) {
290
+ const setupSteps = this.buildSetupSteps(autotest);
291
+ const teardownSteps = this.buildTeardownSteps(autotest, includeAfterAll);
292
+ testit_js_commons_2.logger.debug("[jest-env] sendRealtimePayload", {
293
+ externalId: autotest.externalId,
294
+ outcome: result.outcome,
295
+ includeAfterAll,
296
+ setupSteps: setupSteps.length,
297
+ teardownSteps: teardownSteps.length,
298
+ testSteps: autotest.testSteps.length,
299
+ });
300
+ const autotestPost = {
301
+ externalId: autotest.externalId,
302
+ title: autotest.title,
303
+ name: autotest.name,
304
+ description: autotest.description,
305
+ links: autotest.links,
306
+ labels: autotest.labels,
307
+ tags: autotest.tags,
308
+ namespace: autotest.namespace ?? testit_js_commons_1.Utils.getDir(this.testPath),
309
+ classname: autotest.classname ?? testit_js_commons_1.Utils.getFileName(this.testPath),
310
+ setup: setupSteps,
311
+ steps: autotest.testSteps,
312
+ teardown: teardownSteps,
313
+ externalKey: autotest.externalKey,
314
+ };
315
+ await this.strategy.loadAutotest(autotestPost, result.outcome);
316
+ await this.strategy.loadTestRun([
317
+ {
318
+ autoTestExternalId: autotestPost.externalId,
319
+ outcome: result.outcome,
320
+ startedOn: result.startedOn,
321
+ duration: result.duration,
322
+ attachments: result.attachments,
323
+ message: result.message,
324
+ links: result.links,
325
+ stepResults: autotest.testSteps,
326
+ traces: result.traces,
327
+ setupResults: setupSteps,
328
+ teardownResults: teardownSteps,
329
+ parameters: autotest.parameters !== undefined ? (0, utils_1.mapParams)(autotest.parameters) : undefined,
330
+ },
331
+ ]);
332
+ }
333
+ /** afterAll hooks run after all tests; patch teardown when importRealtime is enabled. */
334
+ async flushRealtimeTeardown() {
335
+ if (!this.importRealtime || this.realtimeSent.length === 0 || this.afterAllSteps.length === 0) {
336
+ testit_js_commons_2.logger.debug("[jest-env] flushRealtimeTeardown skip", {
337
+ importRealtime: this.importRealtime,
338
+ realtimeSent: this.realtimeSent.length,
339
+ afterAllSteps: this.afterAllSteps.length,
340
+ });
341
+ return;
342
+ }
343
+ testit_js_commons_2.logger.debug("[jest-env] flushRealtimeTeardown", {
344
+ tests: this.realtimeSent.length,
345
+ afterAllSteps: this.afterAllSteps.length,
346
+ });
347
+ for (const { autotest, result } of this.realtimeSent) {
348
+ try {
349
+ await this.sendRealtimePayload(autotest, result, true);
350
+ }
351
+ catch (err) {
352
+ testit_js_commons_2.logger.error("Failed realtime teardown flush in Jest environment:", this.formatError(err));
353
+ }
354
+ }
355
+ }
239
356
  async loadResults() {
357
+ if (this.importRealtime) {
358
+ testit_js_commons_2.logger.debug("[jest-env] loadResults skip (importRealtime)");
359
+ return;
360
+ }
361
+ testit_js_commons_2.logger.debug("[jest-env] loadResults batch", { tests: this.autotests.length });
240
362
  log("Waiting for attachments to be uploaded");
241
363
  await Promise.allSettled(this.attachmentsQueue);
242
364
  const results = [];
@@ -265,7 +387,7 @@ class TestItEnvironment extends jest_environment_node_1.default {
265
387
  await this.strategy.loadAutotest(autotestPost, result.outcome);
266
388
  }
267
389
  catch (err) {
268
- console.error("Failed to load autotest in Jest environment:", this.formatError(err));
390
+ testit_js_commons_2.logger.error("Failed to load autotest in Jest environment:", this.formatError(err));
269
391
  }
270
392
  results.push({
271
393
  autoTestExternalId: autotestPost.externalId,
@@ -287,7 +409,7 @@ class TestItEnvironment extends jest_environment_node_1.default {
287
409
  await this.strategy.loadTestRun(results);
288
410
  }
289
411
  catch (err) {
290
- console.error("Failed to load test run in Jest environment:", this.formatError(err));
412
+ testit_js_commons_2.logger.error("Failed to load test run in Jest environment:", this.formatError(err));
291
413
  }
292
414
  }
293
415
  resetTest() {
@@ -337,10 +459,11 @@ class TestItEnvironment extends jest_environment_node_1.default {
337
459
  return ids;
338
460
  })
339
461
  .catch((err) => {
340
- console.log("Error uploading attachment (Jest). \n", err?.body ?? err?.error ?? err);
462
+ testit_js_commons_2.logger.log("Error uploading attachment (Jest). \n", err?.body ?? err?.error ?? err);
341
463
  return [];
342
464
  });
343
465
  this.attachmentsQueue.push(promise);
466
+ this.currentTestAttachmentsQueue.push(promise);
344
467
  return promise;
345
468
  }
346
469
  setLinks(links) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testit-adapter-jest",
3
- "version": "4.0.5",
3
+ "version": "4.1.0",
4
4
  "description": "Jest adapter for Test IT",
5
5
  "main": "dist/testitEnvironment.js",
6
6
  "scripts": {
@@ -16,7 +16,7 @@
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
18
  "debug": "^4.3.4",
19
- "testit-js-commons": "4.0.5"
19
+ "testit-js-commons": "4.1.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/debug": "^4.1.7",