@dev-blinq/cucumber-js 1.0.101-dev → 1.0.101-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 (39) hide show
  1. package/bin/download-install.js +22 -2
  2. package/lib/api/console_logger.js.map +1 -1
  3. package/lib/cli/validate_node_engine_version.js +3 -1
  4. package/lib/cli/validate_node_engine_version.js.map +1 -1
  5. package/lib/configuration/axios_client.js +1 -1
  6. package/lib/configuration/axios_client.js.map +1 -1
  7. package/lib/formatter/api.js +16 -5
  8. package/lib/formatter/api.js.map +1 -1
  9. package/lib/formatter/builder.js +1 -3
  10. package/lib/formatter/builder.js.map +1 -1
  11. package/lib/formatter/bvt_analysis_formatter.d.ts +13 -1
  12. package/lib/formatter/bvt_analysis_formatter.js +202 -78
  13. package/lib/formatter/bvt_analysis_formatter.js.map +1 -1
  14. package/lib/formatter/feature_data_format.js +12 -3
  15. package/lib/formatter/feature_data_format.js.map +1 -1
  16. package/lib/formatter/helpers/constants.d.ts +50 -0
  17. package/lib/formatter/helpers/constants.js +60 -0
  18. package/lib/formatter/helpers/constants.js.map +1 -0
  19. package/lib/formatter/helpers/report_generator.d.ts +32 -2
  20. package/lib/formatter/helpers/report_generator.js +348 -15
  21. package/lib/formatter/helpers/report_generator.js.map +1 -1
  22. package/lib/formatter/helpers/test_case_attempt_formatter.js +1 -1
  23. package/lib/formatter/helpers/test_case_attempt_formatter.js.map +1 -1
  24. package/lib/formatter/helpers/test_case_attempt_parser.js.map +1 -1
  25. package/lib/formatter/helpers/upload_serivce.d.ts +16 -0
  26. package/lib/formatter/helpers/upload_serivce.js +166 -0
  27. package/lib/formatter/helpers/upload_serivce.js.map +1 -1
  28. package/lib/formatter/helpers/uploader.d.ts +2 -1
  29. package/lib/formatter/helpers/uploader.js +27 -3
  30. package/lib/formatter/helpers/uploader.js.map +1 -1
  31. package/lib/formatter/summary_formatter.js +4 -0
  32. package/lib/formatter/summary_formatter.js.map +1 -1
  33. package/lib/runtime/test_case_runner.d.ts +1 -0
  34. package/lib/runtime/test_case_runner.js +10 -1
  35. package/lib/runtime/test_case_runner.js.map +1 -1
  36. package/lib/version.d.ts +1 -1
  37. package/lib/version.js +1 -1
  38. package/lib/version.js.map +1 -1
  39. package/package.json +5 -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,21 @@ 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
+ this.uploadService = new upload_serivce_1.RunUploadService(REPORT_SERVICE_URL, REPORT_SERVICE_TOKEN);
83
+ this.retryCount = 3;
31
84
  }
32
- handleMessage(envelope) {
85
+ async handleMessage(envelope, reRunId) {
86
+ if (envelope.meta && 'runName' in envelope.meta) {
87
+ this.runName = envelope.meta.runName;
88
+ }
33
89
  const type = Object.keys(envelope)[0];
34
90
  switch (type) {
35
91
  // case "meta": { break}
@@ -54,10 +110,30 @@ class ReportGenerator {
54
110
  case 'testRunStarted': {
55
111
  const testRunStarted = envelope[type];
56
112
  this.onTestRunStarted(testRunStarted);
113
+ await this.uploadService.createStatus('running');
57
114
  break;
58
115
  }
59
116
  case 'testCase': {
60
117
  const testCase = envelope[type];
118
+ // Initialize the log storage
119
+ this.testCaseLog = [];
120
+ if (!this.loggingOverridden) {
121
+ this.loggingOverridden = true;
122
+ // Store the original process.stdout.write, and process.stderr.write
123
+ const originalStdoutWrite = process.stdout.write;
124
+ const originalStderrWrite = process.stderr.write;
125
+ // Override process.stdout.write
126
+ process.stdout.write = (chunk, ...args) => {
127
+ this.testCaseLog.push(chunk.toString());
128
+ return originalStdoutWrite.call(process.stdout, chunk, ...args);
129
+ };
130
+ // Override process.stderr.write
131
+ process.stderr.write = (chunk, ...args) => {
132
+ this.testCaseLog.push(chunk.toString());
133
+ return originalStderrWrite.call(process.stderr, chunk, ...args);
134
+ };
135
+ }
136
+ // Call the onTestCase method
61
137
  this.onTestCase(testCase);
62
138
  break;
63
139
  }
@@ -83,13 +159,14 @@ class ReportGenerator {
83
159
  }
84
160
  case 'testCaseFinished': {
85
161
  const testCaseFinished = envelope[type];
86
- this.onTestCaseFinished(testCaseFinished);
87
- break;
162
+ // Call the onTestCaseFinished method
163
+ const result = await this.onTestCaseFinished(testCaseFinished, reRunId);
164
+ return result;
88
165
  }
89
166
  // case "hook": { break} // After Hook
90
167
  case 'testRunFinished': {
91
168
  const testRunFinished = envelope[type];
92
- this.onTestRunFinished(testRunFinished);
169
+ await this.onTestRunFinished(testRunFinished);
93
170
  break;
94
171
  }
95
172
  // case "parameterType" : { break}
@@ -220,6 +297,10 @@ class ReportGenerator {
220
297
  result: {
221
298
  status: 'UNKNOWN',
222
299
  },
300
+ networkData: [],
301
+ webLog: [],
302
+ data: {},
303
+ ariaSnapshot: this.ariaSnapshot,
223
304
  });
224
305
  return this.stepReportMap.get(pickleStep.id);
225
306
  });
@@ -236,6 +317,10 @@ class ReportGenerator {
236
317
  },
237
318
  webLog: [],
238
319
  networkLog: [],
320
+ env: {
321
+ name: this.report.env.name,
322
+ baseUrl: this.report.env.baseUrl,
323
+ },
239
324
  });
240
325
  this.report.testCases.push(this.testCaseReportMap.get(id));
241
326
  }
@@ -258,19 +343,34 @@ class ReportGenerator {
258
343
  this.reportFolder = body.replaceAll('\\', '/');
259
344
  return;
260
345
  }
346
+ if (mediaType === 'application/json+snapshot-before') {
347
+ this.initialAriaSnapshot = body;
348
+ return;
349
+ }
350
+ if (mediaType === 'application/json+snapshot-after') {
351
+ this.ariaSnapshot = body;
352
+ return;
353
+ }
261
354
  if (mediaType === 'application/json+env') {
262
355
  const data = JSON.parse(body);
263
356
  this.report.env = data;
357
+ this.report.testCases.map((testCase) => {
358
+ testCase.env = data;
359
+ return testCase;
360
+ });
264
361
  }
265
362
  if (mediaType === 'application/json+log') {
266
363
  const log = JSON.parse(body);
267
- if (this.logs.length < 1000)
364
+ if (this.logs.length < 1000) {
268
365
  this.logs.push(log);
366
+ this.stepLogs.push(log);
367
+ }
269
368
  }
270
369
  if (mediaType === 'application/json+network') {
271
370
  const networkLog = JSON.parse(body);
272
371
  if (this.networkLog.length < 1000)
273
372
  this.networkLog.push(networkLog);
373
+ this.stepNetworkLogs.push(networkLog);
274
374
  }
275
375
  const testStep = this.testStepMap.get(testStepId);
276
376
  if (testStep.pickleStepId === undefined)
@@ -280,6 +380,46 @@ class ReportGenerator {
280
380
  const command = JSON.parse(body);
281
381
  stepProgess.commands.push(command);
282
382
  }
383
+ else if (mediaType === 'application/json+trace') {
384
+ const data = JSON.parse(body);
385
+ stepProgess.traceFilePath = data.traceFilePath;
386
+ }
387
+ if (mediaType === 'application/json+bruno') {
388
+ try {
389
+ const data = JSON.parse(body);
390
+ stepProgess.brunoData = data;
391
+ }
392
+ catch (error) {
393
+ console.error('Error parsing bruno data:', error);
394
+ }
395
+ }
396
+ if (mediaType === 'application/json+intercept-results') {
397
+ try {
398
+ const data = JSON.parse(body);
399
+ stepProgess.interceptResults = data;
400
+ }
401
+ catch (error) {
402
+ console.error('Error parsing intercept results:', error);
403
+ }
404
+ }
405
+ }
406
+ getFailedTestStepResult({ commands, startTime, endTime, result, }) {
407
+ for (const command of commands) {
408
+ if (command.result.status === 'FAILED') {
409
+ return {
410
+ status: 'FAILED',
411
+ message: command.result.message,
412
+ startTime,
413
+ endTime,
414
+ };
415
+ }
416
+ }
417
+ return {
418
+ status: 'FAILED',
419
+ startTime,
420
+ endTime,
421
+ message: result.message,
422
+ };
283
423
  }
284
424
  onTestStepFinished(testStepFinished) {
285
425
  const { testStepId, testStepResult, timestamp } = testStepFinished;
@@ -290,6 +430,25 @@ class ReportGenerator {
290
430
  }
291
431
  return;
292
432
  }
433
+ if (testStepResult.status === 'UNDEFINED') {
434
+ const step = this.stepReportMap.get(testStep.pickleStepId);
435
+ const stepName = step ? step.keyword + ' ' + step.text : 'Undefined step';
436
+ const undefinedCommand = {
437
+ testStepId: testStepId,
438
+ body: JSON.stringify({
439
+ type: 'error',
440
+ text: 'Undefined step: ' + stepName,
441
+ result: {
442
+ status: 'FAILED',
443
+ startTime: this.getTimeStamp(timestamp),
444
+ endTime: this.getTimeStamp(timestamp),
445
+ },
446
+ }),
447
+ mediaType: 'application/json',
448
+ contentEncoding: messages.AttachmentContentEncoding.IDENTITY,
449
+ };
450
+ this.onAttachment(undefinedCommand);
451
+ }
293
452
  const stepProgess = this.stepReportMap.get(testStep.pickleStepId);
294
453
  const prevStepResult = stepProgess.result;
295
454
  let data = {};
@@ -305,16 +464,73 @@ class ReportGenerator {
305
464
  catch (error) {
306
465
  console.log('Error reading data.json');
307
466
  }
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
- };
467
+ if (testStepResult.status === 'FAILED') {
468
+ stepProgess.result = this.getFailedTestStepResult({
469
+ commands: stepProgess.commands,
470
+ startTime: prevStepResult.startTime,
471
+ endTime: this.getTimeStamp(timestamp),
472
+ result: testStepResult,
473
+ });
474
+ }
475
+ else {
476
+ stepProgess.result = {
477
+ status: testStepResult.status,
478
+ startTime: prevStepResult.startTime,
479
+ endTime: this.getTimeStamp(timestamp),
480
+ };
481
+ }
482
+ stepProgess.webLog = this.stepLogs;
483
+ stepProgess.networkData = this.stepNetworkLogs;
484
+ stepProgess.ariaSnapshot = this.ariaSnapshot;
485
+ this.ariaSnapshot = '';
486
+ this.stepNetworkLogs = [];
487
+ this.stepLogs = [];
315
488
  if (Object.keys(data).length > 0) {
316
489
  stepProgess.data = data;
490
+ const id = testStepFinished.testCaseStartedId;
491
+ const parameters = this.testCaseReportMap.get(id).parameters;
492
+ const _parameters = {};
493
+ Object.keys(parameters).map((key) => {
494
+ var _a;
495
+ if (parameters[key].startsWith('{{') &&
496
+ parameters[key].endsWith('}}')) {
497
+ const path = parameters[key].slice(2, -2).split('.');
498
+ let value = String((_a = object_path_1.default.get(data, path)) !== null && _a !== void 0 ? _a : parameters[key]);
499
+ if (value) {
500
+ if (value.startsWith('secret:')) {
501
+ value = 'secret:****';
502
+ }
503
+ else if (value.startsWith('totp:')) {
504
+ value = 'totp:****';
505
+ }
506
+ else if (value.startsWith('mask:')) {
507
+ value = 'mask:****';
508
+ }
509
+ _parameters[key] = value;
510
+ }
511
+ }
512
+ else {
513
+ _parameters[key] = parameters[key];
514
+ }
515
+ });
516
+ this.report.testCases.find((testCase) => {
517
+ return testCase.id === id;
518
+ }).parameters = _parameters;
317
519
  }
520
+ // if (process.env.TESTCASE_REPORT_FOLDER_PATH) {
521
+ // this.reportFolder = process.env.TESTCASE_REPORT_FOLDER_PATH
522
+ // if (!fs.existsSync(this.reportFolder)) {
523
+ // fs.mkdirSync(this.reportFolder)
524
+ // }
525
+ // const reportFilePath = path.join(
526
+ // this.reportFolder,
527
+ // `report.json`
528
+ // )
529
+ // writeFileSync(reportFilePath, JSON.stringify(this.report, null, 2))
530
+ // return undefined
531
+ // // } else {
532
+ // // return await this.uploadTestCase(testProgress, reRunId)
533
+ // }
318
534
  }
319
535
  getLogFileContent() {
320
536
  let projectPath = process.cwd();
@@ -341,6 +557,12 @@ class ReportGenerator {
341
557
  }
342
558
  }
343
559
  getTestCaseResult(steps) {
560
+ if (steps[0] && steps[0].result.status === 'SKIPPED') {
561
+ return {
562
+ status: 'FAILED',
563
+ message: 'Test skipped due to failure in before hooks',
564
+ };
565
+ }
344
566
  for (const step of steps) {
345
567
  switch (step.result.status) {
346
568
  case 'FAILED':
@@ -362,23 +584,133 @@ class ReportGenerator {
362
584
  status: 'PASSED',
363
585
  };
364
586
  }
365
- onTestCaseFinished(testCaseFinished) {
587
+ async onTestCaseFinished(testCaseFinished, reRunId) {
366
588
  const { testCaseStartedId, timestamp } = testCaseFinished;
367
589
  const testProgress = this.testCaseReportMap.get(testCaseStartedId);
368
590
  const prevResult = testProgress.result;
369
591
  const steps = Object.values(testProgress.steps);
370
592
  const result = this.getTestCaseResult(steps);
593
+ if (result.status === 'PASSED' && reRunId) {
594
+ this.uploadService.updateProjectAnalytics(process.env.PROJECT_ID);
595
+ }
596
+ const endTime = this.getTimeStamp(timestamp);
371
597
  testProgress.result = {
372
598
  ...result,
373
599
  startTime: prevResult.startTime,
374
- endTime: this.getTimeStamp(timestamp),
600
+ endTime,
375
601
  };
376
602
  testProgress.webLog = this.logs;
377
603
  testProgress.networkLog = this.networkLog;
604
+ testProgress.initialAriaSnapshot = this.initialAriaSnapshot;
605
+ this.initialAriaSnapshot = '';
378
606
  this.networkLog = [];
379
607
  this.logs = [];
608
+ if (this.testCaseLog && this.testCaseLog.length > 0) {
609
+ // Create the logs directory
610
+ const logsDir = path_1.default.join(this.reportFolder, 'editorLogs');
611
+ const fileName = `testCaseLog_${testCaseStartedId}.log`;
612
+ const filePath = path_1.default.join(logsDir, fileName);
613
+ // Ensure the logs directory exists
614
+ fs_1.default.mkdirSync(logsDir, { recursive: true });
615
+ // Write the logs to the file
616
+ fs_1.default.writeFileSync(filePath, this.testCaseLog.join('\n'), 'utf8');
617
+ // Store this ID in the testProgress object so it can be accessed later
618
+ testProgress.logFileId = testCaseStartedId;
619
+ }
620
+ this.testCaseLog = [];
621
+ if (process.env.TESTCASE_REPORT_FOLDER_PATH) {
622
+ this.reportFolder = process.env.TESTCASE_REPORT_FOLDER_PATH;
623
+ if (!fs_1.default.existsSync(this.reportFolder)) {
624
+ fs_1.default.mkdirSync(this.reportFolder);
625
+ }
626
+ const reportFilePath = path_1.default.join(this.reportFolder, `${endTime}_${testProgress.scenarioName}.json`);
627
+ (0, fs_extra_1.writeFileSync)(reportFilePath, JSON.stringify(testProgress, null, 2));
628
+ return undefined;
629
+ }
630
+ else {
631
+ return await this.uploadTestCase(testProgress, reRunId);
632
+ }
633
+ }
634
+ async uploadTestCase(testCase, rerunId) {
635
+ let data = null;
636
+ for (let attempt = 1; attempt <= this.retryCount; attempt++) {
637
+ try {
638
+ data = await this.tryUpload(testCase, rerunId);
639
+ break;
640
+ }
641
+ catch (e) {
642
+ console.error(`Attempt ${attempt} to upload testcase failed:`, e);
643
+ if (attempt === this.retryCount) {
644
+ console.error('All retry attempts failed, failed to upload testcase.');
645
+ }
646
+ else {
647
+ const waitTime = 1000 * 2 ** (attempt - 1); //? exponential backoff: 1s, 2s, 4s...
648
+ await new Promise((r) => setTimeout(r, waitTime));
649
+ }
650
+ }
651
+ }
652
+ return data;
653
+ }
654
+ async tryUpload(testCase, rerunId) {
655
+ let runId = '';
656
+ let projectId = '';
657
+ if (!process.env.UPLOADING_TEST_CASE) {
658
+ process.env.UPLOADING_TEST_CASE = '[]';
659
+ }
660
+ const anyRemArr = JSON.parse(process.env.UPLOADING_TEST_CASE);
661
+ const randomID = Math.random().toString(36).substring(7);
662
+ anyRemArr.push(randomID);
663
+ process.env.UPLOADING_TEST_CASE = JSON.stringify(anyRemArr);
664
+ try {
665
+ if (process.env.RUN_ID &&
666
+ process.env.PROJECT_ID &&
667
+ !process.env.IGNORE_ENV_VARIABLES) {
668
+ runId = process.env.RUN_ID;
669
+ projectId = process.env.PROJECT_ID;
670
+ }
671
+ else {
672
+ const runDoc = await this.uploadService.createRunDocument(this.runName);
673
+ runId = runDoc._id;
674
+ projectId = runDoc.project_id;
675
+ if (!process.env.IGNORE_ENV_VARIABLES) {
676
+ process.env.RUN_ID = runId;
677
+ process.env.PROJECT_ID = projectId;
678
+ }
679
+ }
680
+ const data = await this.uploadService.uploadTestCase(testCase, runId, projectId, this.reportFolder, rerunId);
681
+ this.writeTestCaseReportToDisk(testCase);
682
+ return data;
683
+ }
684
+ finally {
685
+ const arrRem = JSON.parse(process.env.UPLOADING_TEST_CASE);
686
+ arrRem.splice(arrRem.indexOf(randomID), 1);
687
+ process.env.UPLOADING_TEST_CASE = JSON.stringify(arrRem);
688
+ }
689
+ }
690
+ writeTestCaseReportToDisk(testCase) {
691
+ var _a;
692
+ const reportFolder = (_a = this.reportFolder) !== null && _a !== void 0 ? _a : process.env.TESTCASE_REPORT_FOLDER_PATH;
693
+ if (!reportFolder) {
694
+ console.error('Report folder is not defined');
695
+ return;
696
+ }
697
+ try {
698
+ let i = 0;
699
+ while (fs_1.default.existsSync(path_1.default.join(reportFolder, `${i}`))) {
700
+ i++;
701
+ }
702
+ fs_1.default.mkdirSync(path_1.default.join(reportFolder, `${i}`));
703
+ //exclude network log from the saved report
704
+ const networkLog = testCase.networkLog;
705
+ delete testCase.networkLog;
706
+ fs_1.default.writeFileSync(path_1.default.join(reportFolder, `${i}`, `report.json`), JSON.stringify(testCase, null, 2));
707
+ fs_1.default.writeFileSync(path_1.default.join(reportFolder, `${i}`, `network.json`), JSON.stringify(networkLog, null, 2));
708
+ }
709
+ catch (error) {
710
+ console.error('Error writing test case report to disk:', error);
711
+ }
380
712
  }
381
- onTestRunFinished(testRunFinished) {
713
+ async onTestRunFinished(testRunFinished) {
382
714
  const { timestamp, success, message } = testRunFinished;
383
715
  const prevResult = this.report.result;
384
716
  this.report.result = {
@@ -388,6 +720,7 @@ class ReportGenerator {
388
720
  message,
389
721
  // exception,
390
722
  };
723
+ await this.uploadService.createStatus(success ? 'passed' : 'failed');
391
724
  }
392
725
  }
393
726
  exports.default = ReportGenerator;