@dev-blinq/cucumber-js 1.0.88-dev → 1.0.88-stage

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.
Files changed (34) hide show
  1. package/bin/download-install.js +40 -13
  2. package/lib/configuration/axios_client.js +1 -1
  3. package/lib/configuration/axios_client.js.map +1 -1
  4. package/lib/formatter/api.d.ts +2 -0
  5. package/lib/formatter/api.js +59 -0
  6. package/lib/formatter/api.js.map +1 -0
  7. package/lib/formatter/bvt_analysis_formatter.d.ts +13 -1
  8. package/lib/formatter/bvt_analysis_formatter.js +235 -88
  9. package/lib/formatter/bvt_analysis_formatter.js.map +1 -1
  10. package/lib/formatter/feature_data_format.js +14 -2
  11. package/lib/formatter/feature_data_format.js.map +1 -1
  12. package/lib/formatter/helpers/constants.d.ts +50 -0
  13. package/lib/formatter/helpers/constants.js +60 -0
  14. package/lib/formatter/helpers/constants.js.map +1 -0
  15. package/lib/formatter/helpers/report_generator.d.ts +33 -2
  16. package/lib/formatter/helpers/report_generator.js +340 -20
  17. package/lib/formatter/helpers/report_generator.js.map +1 -1
  18. package/lib/formatter/helpers/test_case_attempt_formatter.js +1 -1
  19. package/lib/formatter/helpers/test_case_attempt_formatter.js.map +1 -1
  20. package/lib/formatter/helpers/upload_serivce.d.ts +15 -0
  21. package/lib/formatter/helpers/upload_serivce.js +173 -13
  22. package/lib/formatter/helpers/upload_serivce.js.map +1 -1
  23. package/lib/formatter/helpers/uploader.d.ts +2 -1
  24. package/lib/formatter/helpers/uploader.js +30 -3
  25. package/lib/formatter/helpers/uploader.js.map +1 -1
  26. package/lib/formatter/summary_formatter.js +4 -0
  27. package/lib/formatter/summary_formatter.js.map +1 -1
  28. package/lib/runtime/test_case_runner.d.ts +1 -0
  29. package/lib/runtime/test_case_runner.js +10 -1
  30. package/lib/runtime/test_case_runner.js.map +1 -1
  31. package/lib/version.d.ts +1 -1
  32. package/lib/version.js +1 -1
  33. package/lib/version.js.map +1 -1
  34. package/package.json +4 -2
@@ -1,10 +1,54 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
28
+ var _a, _b;
5
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
+ const messages = __importStar(require("@cucumber/messages"));
6
31
  const fs_1 = __importDefault(require("fs"));
7
32
  const path_1 = __importDefault(require("path"));
33
+ const upload_serivce_1 = require("./upload_serivce");
34
+ const fs_extra_1 = require("fs-extra");
35
+ // type JsonException = messages.Exception
36
+ const object_path_1 = __importDefault(require("object-path"));
37
+ const URL = process.env.NODE_ENV_BLINQ === 'dev'
38
+ ? 'https://dev.api.blinq.io/api/runs'
39
+ : process.env.NODE_ENV_BLINQ === 'local'
40
+ ? 'http://localhost:5001/api/runs'
41
+ : process.env.NODE_ENV_BLINQ === 'stage'
42
+ ? 'https://stage.api.blinq.io/api/runs'
43
+ : process.env.NODE_ENV_BLINQ === 'prod'
44
+ ? 'https://api.blinq.io/api/runs'
45
+ : !process.env.NODE_ENV_BLINQ
46
+ ? 'https://api.blinq.io/api/runs'
47
+ : `${process.env.NODE_ENV_BLINQ}/api/runs`;
48
+ const REPORT_SERVICE_URL = (_a = process.env.REPORT_SERVICE_URL) !== null && _a !== void 0 ? _a : URL;
49
+ const BATCH_SIZE = 10;
50
+ const MAX_RETRIES = 3;
51
+ const REPORT_SERVICE_TOKEN = (_b = process.env.TOKEN) !== null && _b !== void 0 ? _b : process.env.REPORT_SERVICE_TOKEN;
8
52
  class ReportGenerator {
9
53
  constructor() {
10
54
  this.report = {
@@ -27,9 +71,24 @@ class ReportGenerator {
27
71
  this.scenarioIterationCountMap = new Map();
28
72
  this.logs = [];
29
73
  this.networkLog = [];
74
+ this.stepLogs = [];
75
+ this.stepNetworkLogs = [];
76
+ this.runName = '';
77
+ this.ariaSnapshot = '';
78
+ this.initialAriaSnapshot = '';
79
+ this.testCaseLog = [];
80
+ this.loggingOverridden = false; // Flag to track if logging is overridden
30
81
  this.reportFolder = null;
82
+ // Add this property to track if we've encountered an undefined step
83
+ this.hasUndefinedStep = false;
84
+ this.currentTestCaseId = null;
85
+ this.uploadService = new upload_serivce_1.RunUploadService(REPORT_SERVICE_URL, REPORT_SERVICE_TOKEN);
86
+ this.retryCount = 3;
31
87
  }
32
- handleMessage(envelope) {
88
+ async handleMessage(envelope, reRunId) {
89
+ if (envelope.meta && 'runName' in envelope.meta) {
90
+ this.runName = envelope.meta.runName;
91
+ }
33
92
  const type = Object.keys(envelope)[0];
34
93
  switch (type) {
35
94
  // case "meta": { break}
@@ -54,10 +113,30 @@ class ReportGenerator {
54
113
  case 'testRunStarted': {
55
114
  const testRunStarted = envelope[type];
56
115
  this.onTestRunStarted(testRunStarted);
116
+ await this.uploadService.createStatus('running');
57
117
  break;
58
118
  }
59
119
  case 'testCase': {
60
120
  const testCase = envelope[type];
121
+ // Initialize the log storage
122
+ this.testCaseLog = [];
123
+ if (!this.loggingOverridden) {
124
+ this.loggingOverridden = true;
125
+ // Store the original process.stdout.write, and process.stderr.write
126
+ const originalStdoutWrite = process.stdout.write;
127
+ const originalStderrWrite = process.stderr.write;
128
+ // Override process.stdout.write
129
+ process.stdout.write = (chunk, ...args) => {
130
+ this.testCaseLog.push(chunk.toString());
131
+ return originalStdoutWrite.call(process.stdout, chunk, ...args);
132
+ };
133
+ // Override process.stderr.write
134
+ process.stderr.write = (chunk, ...args) => {
135
+ this.testCaseLog.push(chunk.toString());
136
+ return originalStderrWrite.call(process.stderr, chunk, ...args);
137
+ };
138
+ }
139
+ // Call the onTestCase method
61
140
  this.onTestCase(testCase);
62
141
  break;
63
142
  }
@@ -83,13 +162,14 @@ class ReportGenerator {
83
162
  }
84
163
  case 'testCaseFinished': {
85
164
  const testCaseFinished = envelope[type];
86
- this.onTestCaseFinished(testCaseFinished);
87
- break;
165
+ // Call the onTestCaseFinished method
166
+ const result = await this.onTestCaseFinished(testCaseFinished, reRunId);
167
+ return result;
88
168
  }
89
169
  // case "hook": { break} // After Hook
90
170
  case 'testRunFinished': {
91
171
  const testRunFinished = envelope[type];
92
- this.onTestRunFinished(testRunFinished);
172
+ await this.onTestRunFinished(testRunFinished);
93
173
  break;
94
174
  }
95
175
  // case "parameterType" : { break}
@@ -188,6 +268,9 @@ class ReportGenerator {
188
268
  return parameters;
189
269
  }
190
270
  onTestCaseStarted(testCaseStarted) {
271
+ // Reset the undefined step flag for each new test case
272
+ this.hasUndefinedStep = false;
273
+ this.currentTestCaseId = testCaseStarted.id;
191
274
  const { testCaseId, id, timestamp } = testCaseStarted;
192
275
  const testCase = this.testCaseMap.get(testCaseId);
193
276
  if (testCase === undefined)
@@ -220,6 +303,10 @@ class ReportGenerator {
220
303
  result: {
221
304
  status: 'UNKNOWN',
222
305
  },
306
+ networkData: [],
307
+ webLog: [],
308
+ data: {},
309
+ ariaSnapshot: this.ariaSnapshot,
223
310
  });
224
311
  return this.stepReportMap.get(pickleStep.id);
225
312
  });
@@ -236,6 +323,10 @@ class ReportGenerator {
236
323
  },
237
324
  webLog: [],
238
325
  networkLog: [],
326
+ env: {
327
+ name: this.report.env.name,
328
+ baseUrl: this.report.env.baseUrl,
329
+ },
239
330
  });
240
331
  this.report.testCases.push(this.testCaseReportMap.get(id));
241
332
  }
@@ -246,8 +337,16 @@ class ReportGenerator {
246
337
  throw new Error(`testStep with id ${testStepId} not found`);
247
338
  if (testStep.pickleStepId === undefined)
248
339
  return;
249
- const stepProgess = this.stepReportMap.get(testStep.pickleStepId);
250
- stepProgess.result = {
340
+ // If we already have an undefined step in this test case, skip remaining steps
341
+ if (this.hasUndefinedStep) {
342
+ const stepProgress = this.stepReportMap.get(testStep.pickleStepId);
343
+ stepProgress.result = {
344
+ status: 'SKIPPED'
345
+ };
346
+ return;
347
+ }
348
+ const stepProgress = this.stepReportMap.get(testStep.pickleStepId);
349
+ stepProgress.result = {
251
350
  status: 'STARTED',
252
351
  startTime: this.getTimeStamp(timestamp),
253
352
  };
@@ -258,19 +357,34 @@ class ReportGenerator {
258
357
  this.reportFolder = body.replaceAll('\\', '/');
259
358
  return;
260
359
  }
360
+ if (mediaType === 'application/json+snapshot-before') {
361
+ this.initialAriaSnapshot = body;
362
+ return;
363
+ }
364
+ if (mediaType === 'application/json+snapshot-after') {
365
+ this.ariaSnapshot = body;
366
+ return;
367
+ }
261
368
  if (mediaType === 'application/json+env') {
262
369
  const data = JSON.parse(body);
263
370
  this.report.env = data;
371
+ this.report.testCases.map((testCase) => {
372
+ testCase.env = data;
373
+ return testCase;
374
+ });
264
375
  }
265
376
  if (mediaType === 'application/json+log') {
266
377
  const log = JSON.parse(body);
267
- if (this.logs.length < 1000)
378
+ if (this.logs.length < 1000) {
268
379
  this.logs.push(log);
380
+ this.stepLogs.push(log);
381
+ }
269
382
  }
270
383
  if (mediaType === 'application/json+network') {
271
384
  const networkLog = JSON.parse(body);
272
385
  if (this.networkLog.length < 1000)
273
386
  this.networkLog.push(networkLog);
387
+ this.stepNetworkLogs.push(networkLog);
274
388
  }
275
389
  const testStep = this.testStepMap.get(testStepId);
276
390
  if (testStep.pickleStepId === undefined)
@@ -280,6 +394,37 @@ class ReportGenerator {
280
394
  const command = JSON.parse(body);
281
395
  stepProgess.commands.push(command);
282
396
  }
397
+ else if (mediaType === 'application/json+trace') {
398
+ const data = JSON.parse(body);
399
+ stepProgess.traceFilePath = data.traceFilePath;
400
+ }
401
+ if (mediaType === 'application/json+bruno') {
402
+ try {
403
+ const data = JSON.parse(body);
404
+ stepProgess.brunoData = data;
405
+ }
406
+ catch (error) {
407
+ console.error('Error parsing bruno data:', error);
408
+ }
409
+ }
410
+ }
411
+ getFailedTestStepResult({ commands, startTime, endTime, result, }) {
412
+ for (const command of commands) {
413
+ if (command.result.status === 'FAILED') {
414
+ return {
415
+ status: 'FAILED',
416
+ message: command.result.message,
417
+ startTime,
418
+ endTime,
419
+ };
420
+ }
421
+ }
422
+ return {
423
+ status: 'FAILED',
424
+ startTime,
425
+ endTime,
426
+ message: result.message,
427
+ };
283
428
  }
284
429
  onTestStepFinished(testStepFinished) {
285
430
  const { testStepId, testStepResult, timestamp } = testStepFinished;
@@ -290,8 +435,33 @@ class ReportGenerator {
290
435
  }
291
436
  return;
292
437
  }
293
- const stepProgess = this.stepReportMap.get(testStep.pickleStepId);
294
- const prevStepResult = stepProgess.result;
438
+ const stepProgress = this.stepReportMap.get(testStep.pickleStepId);
439
+ // If this step was skipped due to previous undefined step, don't process it
440
+ if (stepProgress.result.status === 'SKIPPED') {
441
+ return;
442
+ }
443
+ if (testStepResult.status === 'UNDEFINED') {
444
+ // Mark that we've encountered an undefined step
445
+ this.hasUndefinedStep = true;
446
+ const step = this.stepReportMap.get(testStep.pickleStepId);
447
+ const stepName = step ? step.keyword + ' ' + step.text : 'Undefined step';
448
+ const undefinedCommand = {
449
+ testStepId: testStepId,
450
+ body: JSON.stringify({
451
+ type: 'error',
452
+ text: 'Undefined step: ' + stepName,
453
+ result: {
454
+ status: 'FAILED',
455
+ startTime: this.getTimeStamp(timestamp),
456
+ endTime: this.getTimeStamp(timestamp),
457
+ },
458
+ }),
459
+ mediaType: 'application/json',
460
+ contentEncoding: messages.AttachmentContentEncoding.IDENTITY,
461
+ };
462
+ this.onAttachment(undefinedCommand);
463
+ }
464
+ const prevStepResult = stepProgress.result;
295
465
  let data = {};
296
466
  try {
297
467
  const reportFolder = this.reportFolder;
@@ -305,15 +475,57 @@ class ReportGenerator {
305
475
  catch (error) {
306
476
  console.log('Error reading data.json');
307
477
  }
308
- stepProgess.result = {
309
- status: testStepResult.status,
310
- startTime: prevStepResult.startTime,
311
- endTime: this.getTimeStamp(timestamp),
312
- message: testStepResult.message,
313
- // exception: testStepResult.exception,
314
- };
478
+ if (testStepResult.status === 'FAILED') {
479
+ stepProgress.result = this.getFailedTestStepResult({
480
+ commands: stepProgress.commands,
481
+ startTime: prevStepResult.startTime,
482
+ endTime: this.getTimeStamp(timestamp),
483
+ result: testStepResult,
484
+ });
485
+ }
486
+ else {
487
+ stepProgress.result = {
488
+ status: testStepResult.status,
489
+ startTime: prevStepResult.startTime,
490
+ endTime: this.getTimeStamp(timestamp),
491
+ };
492
+ }
493
+ stepProgress.webLog = this.stepLogs;
494
+ stepProgress.networkData = this.stepNetworkLogs;
495
+ stepProgress.ariaSnapshot = this.ariaSnapshot;
496
+ this.ariaSnapshot = '';
497
+ this.stepNetworkLogs = [];
498
+ this.stepLogs = [];
315
499
  if (Object.keys(data).length > 0) {
316
- stepProgess.data = data;
500
+ stepProgress.data = data;
501
+ const id = testStepFinished.testCaseStartedId;
502
+ const parameters = this.testCaseReportMap.get(id).parameters;
503
+ const _parameters = {};
504
+ Object.keys(parameters).map((key) => {
505
+ if (parameters[key].startsWith('{{') &&
506
+ parameters[key].endsWith('}}')) {
507
+ const path = parameters[key].slice(2, -2).split('.');
508
+ let value = String(object_path_1.default.get(data, path));
509
+ if (value) {
510
+ if (value.startsWith('secret:')) {
511
+ value = 'secret:****';
512
+ }
513
+ else if (value.startsWith('totp:')) {
514
+ value = 'totp:****';
515
+ }
516
+ else if (value.startsWith('mask:')) {
517
+ value = 'mask:****';
518
+ }
519
+ _parameters[key] = value;
520
+ }
521
+ }
522
+ else {
523
+ _parameters[key] = parameters[key];
524
+ }
525
+ });
526
+ this.report.testCases.find((testCase) => {
527
+ return testCase.id === id;
528
+ }).parameters = _parameters;
317
529
  }
318
530
  }
319
531
  getLogFileContent() {
@@ -362,23 +574,130 @@ class ReportGenerator {
362
574
  status: 'PASSED',
363
575
  };
364
576
  }
365
- onTestCaseFinished(testCaseFinished) {
577
+ async onTestCaseFinished(testCaseFinished, reRunId) {
366
578
  const { testCaseStartedId, timestamp } = testCaseFinished;
367
579
  const testProgress = this.testCaseReportMap.get(testCaseStartedId);
368
580
  const prevResult = testProgress.result;
369
581
  const steps = Object.values(testProgress.steps);
370
582
  const result = this.getTestCaseResult(steps);
583
+ const endTime = this.getTimeStamp(timestamp);
371
584
  testProgress.result = {
372
585
  ...result,
373
586
  startTime: prevResult.startTime,
374
- endTime: this.getTimeStamp(timestamp),
587
+ endTime,
375
588
  };
376
589
  testProgress.webLog = this.logs;
377
590
  testProgress.networkLog = this.networkLog;
591
+ testProgress.initialAriaSnapshot = this.initialAriaSnapshot;
592
+ this.initialAriaSnapshot = '';
378
593
  this.networkLog = [];
379
594
  this.logs = [];
595
+ if (this.testCaseLog && this.testCaseLog.length > 0) {
596
+ // Create the logs directory
597
+ const logsDir = path_1.default.join(this.reportFolder, 'editorLogs');
598
+ const fileName = `testCaseLog_${testCaseStartedId}.log`;
599
+ const filePath = path_1.default.join(logsDir, fileName);
600
+ // Ensure the logs directory exists
601
+ fs_1.default.mkdirSync(logsDir, { recursive: true });
602
+ // Write the logs to the file
603
+ fs_1.default.writeFileSync(filePath, this.testCaseLog.join('\n'), 'utf8');
604
+ // Store this ID in the testProgress object so it can be accessed later
605
+ testProgress.logFileId = testCaseStartedId;
606
+ }
607
+ this.testCaseLog = [];
608
+ if (process.env.TESTCASE_REPORT_FOLDER_PATH) {
609
+ this.reportFolder = process.env.TESTCASE_REPORT_FOLDER_PATH;
610
+ if (!fs_1.default.existsSync(this.reportFolder)) {
611
+ fs_1.default.mkdirSync(this.reportFolder);
612
+ }
613
+ const reportFilePath = path_1.default.join(this.reportFolder, `${endTime}_${testProgress.scenarioName}.json`);
614
+ (0, fs_extra_1.writeFileSync)(reportFilePath, JSON.stringify(testProgress, null, 2));
615
+ return undefined;
616
+ }
617
+ else {
618
+ return await this.uploadTestCase(testProgress, reRunId);
619
+ }
620
+ }
621
+ async uploadTestCase(testCase, rerunId) {
622
+ let data = null;
623
+ for (let attempt = 1; attempt <= this.retryCount; attempt++) {
624
+ try {
625
+ data = await this.tryUpload(testCase, rerunId);
626
+ break;
627
+ }
628
+ catch (e) {
629
+ console.error(`Attempt ${attempt} to upload testcase failed:`, e);
630
+ if (attempt === this.retryCount) {
631
+ console.error('All retry attempts failed, failed to upload testcase.');
632
+ }
633
+ else {
634
+ const waitTime = 1000 * 2 ** (attempt - 1); //? exponential backoff: 1s, 2s, 4s...
635
+ await new Promise((r) => setTimeout(r, waitTime));
636
+ }
637
+ }
638
+ }
639
+ return data;
640
+ }
641
+ async tryUpload(testCase, rerunId) {
642
+ let runId = '';
643
+ let projectId = '';
644
+ if (!process.env.UPLOADING_TEST_CASE) {
645
+ process.env.UPLOADING_TEST_CASE = '[]';
646
+ }
647
+ const anyRemArr = JSON.parse(process.env.UPLOADING_TEST_CASE);
648
+ const randomID = Math.random().toString(36).substring(7);
649
+ anyRemArr.push(randomID);
650
+ process.env.UPLOADING_TEST_CASE = JSON.stringify(anyRemArr);
651
+ try {
652
+ if (process.env.RUN_ID &&
653
+ process.env.PROJECT_ID &&
654
+ !process.env.IGNORE_ENV_VARIABLES) {
655
+ runId = process.env.RUN_ID;
656
+ projectId = process.env.PROJECT_ID;
657
+ }
658
+ else {
659
+ const runDoc = await this.uploadService.createRunDocument(this.runName);
660
+ runId = runDoc._id;
661
+ projectId = runDoc.project_id;
662
+ if (!process.env.IGNORE_ENV_VARIABLES) {
663
+ process.env.RUN_ID = runId;
664
+ process.env.PROJECT_ID = projectId;
665
+ }
666
+ }
667
+ const data = await this.uploadService.uploadTestCase(testCase, runId, projectId, this.reportFolder, rerunId);
668
+ this.writeTestCaseReportToDisk(testCase);
669
+ return data;
670
+ }
671
+ finally {
672
+ const arrRem = JSON.parse(process.env.UPLOADING_TEST_CASE);
673
+ arrRem.splice(arrRem.indexOf(randomID), 1);
674
+ process.env.UPLOADING_TEST_CASE = JSON.stringify(arrRem);
675
+ }
676
+ }
677
+ writeTestCaseReportToDisk(testCase) {
678
+ var _a;
679
+ const reportFolder = (_a = this.reportFolder) !== null && _a !== void 0 ? _a : process.env.TESTCASE_REPORT_FOLDER_PATH;
680
+ if (!reportFolder) {
681
+ console.error('Report folder is not defined');
682
+ return;
683
+ }
684
+ try {
685
+ let i = 0;
686
+ while (fs_1.default.existsSync(path_1.default.join(reportFolder, `${i}`))) {
687
+ i++;
688
+ }
689
+ fs_1.default.mkdirSync(path_1.default.join(reportFolder, `${i}`));
690
+ //exclude network log from the saved report
691
+ const networkLog = testCase.networkLog;
692
+ delete testCase.networkLog;
693
+ fs_1.default.writeFileSync(path_1.default.join(reportFolder, `${i}`, `report.json`), JSON.stringify(testCase, null, 2));
694
+ fs_1.default.writeFileSync(path_1.default.join(reportFolder, `${i}`, `network.json`), JSON.stringify(networkLog, null, 2));
695
+ }
696
+ catch (error) {
697
+ console.error('Error writing test case report to disk:', error);
698
+ }
380
699
  }
381
- onTestRunFinished(testRunFinished) {
700
+ async onTestRunFinished(testRunFinished) {
382
701
  const { timestamp, success, message } = testRunFinished;
383
702
  const prevResult = this.report.result;
384
703
  this.report.result = {
@@ -388,6 +707,7 @@ class ReportGenerator {
388
707
  message,
389
708
  // exception,
390
709
  };
710
+ await this.uploadService.createStatus(success ? 'passed' : 'failed');
391
711
  }
392
712
  }
393
713
  exports.default = ReportGenerator;