@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,3 +1,6 @@
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ /* eslint-disable no-console */
3
+ /* eslint-disable no-undef */
1
4
  const { argv } = require('node:process')
2
5
  const fs = require('fs')
3
6
  const path = require('path')
@@ -14,8 +17,16 @@ const getSSoUrl = () => {
14
17
  return 'http://localhost:5000/api/auth'
15
18
  case 'dev':
16
19
  return 'https://dev.api.blinq.io/api/auth'
17
- default:
20
+ case 'stage':
21
+ return 'https://stage.api.blinq.io/api/auth'
22
+ case 'prod':
23
+ return 'https://api.blinq.io/api/auth'
24
+ case null:
25
+ return 'https://api.blinq.io/api/auth'
26
+ case undefined:
18
27
  return 'https://api.blinq.io/api/auth'
28
+ default:
29
+ return `${process.env.NODE_ENV_BLINQ}/api/auth`
19
30
  }
20
31
  }
21
32
 
@@ -46,8 +57,14 @@ const getWorkSpaceUrl = () => {
46
57
  return 'https://dev.api.blinq.io/api/workspace'
47
58
  case "stage":
48
59
  return 'https://stage.api.blinq.io/api/workspace'
49
- default:
60
+ case 'prod':
50
61
  return 'https://api.blinq.io/api/workspace'
62
+ case null:
63
+ return 'https://api.blinq.io/api/workspace'
64
+ case undefined:
65
+ return 'https://api.blinq.io/api/workspace'
66
+ default:
67
+ return `${process.env.NODE_ENV_BLINQ}/api/workspace`
51
68
  }
52
69
  }
53
70
 
@@ -100,26 +117,31 @@ const dirExists = (path) => {
100
117
  }
101
118
  const ssoUrl = getSSoUrl()
102
119
 
120
+ const getProjectByAccessKey = async (access_key) => {
121
+ const accessKeyUrl = `${ssoUrl}/getProjectByAccessKey`
122
+ const response = await axios.post(accessKeyUrl, {
123
+ access_key,
124
+ httpAgent: getProxy(),
125
+ proxy: false,
126
+ })
127
+ if (response.status !== 200) {
128
+ console.error('Error: Invalid access key')
129
+ process.exit(1)
130
+ }
131
+ return response.data
132
+ };
133
+
103
134
  const downloadAndInstall = async (extractPath, token) => {
104
135
  if (!dirExists(extractPath)) {
105
136
  fs.mkdirSync(extractPath, { recursive: true })
106
137
  }
107
138
  try {
108
- const accessKeyUrl = `${ssoUrl}/getProjectByAccessKey`
109
- const response = await axios.post(accessKeyUrl, {
110
- access_key: token,
111
- httpAgent: getProxy(),
112
- proxy: false,
113
- })
114
- if (response.status !== 200) {
115
- console.error('Error: Invalid access key')
116
- process.exit(1)
117
- }
139
+ const data = await getProjectByAccessKey(token)
118
140
 
119
141
  const workspaceUrl = getWorkSpaceUrl() + '/pull-workspace'
120
142
  const res = await axios.get(workspaceUrl, {
121
143
  params: {
122
- projectId: response.data.project._id,
144
+ projectId: data.project._id,
123
145
  },
124
146
  httpAgent: getProxy(),
125
147
  proxy: false,
@@ -130,6 +152,11 @@ const downloadAndInstall = async (extractPath, token) => {
130
152
  },
131
153
  })
132
154
 
155
+ if (res.status !== 200) {
156
+ console.error('Error: Unable to fetch workspace')
157
+ process.exit(1)
158
+ }
159
+
133
160
  const zip = await JSZip.loadAsync(res.data)
134
161
  for (const filename of Object.keys(zip.files)) {
135
162
  const fileData = zip.files[filename]
@@ -27,7 +27,7 @@ const createAxiosClient = () => {
27
27
  try {
28
28
  const agent = getProxy();
29
29
  return axios_1.default.create({
30
- httpAgent: agent,
30
+ httpsAgent: agent,
31
31
  proxy: false,
32
32
  });
33
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"axios_client.js","sourceRoot":"","sources":["../../src/configuration/axios_client.ts"],"names":[],"mappings":";;;;;;AAAA,+BAA+B;AAC/B,kDAAyB;AACzB,oDAA6C;AAG7C,MAAM,QAAQ,GAAG,GAAiB,EAAE;IAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;QACtB,OAAO,IAAI,CAAA;KACZ;IAED,MAAM,KAAK,GAAkB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IAC1B,MAAM,WAAW,GAAiB;QAChC,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;KACvB,CAAA;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IAElC,IAAI,QAAQ,IAAI,QAAQ,EAAE;QACxB,WAAW,CAAC,SAAS,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAA;KAClD;IACD,OAAO,gBAAM,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;AACrD,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAG,GAAG,EAAE;IAC7B,IAAI;QACF,MAAM,KAAK,GAAmB,QAAQ,EAAE,CAAA;QACxC,OAAO,eAAK,CAAC,MAAM,CAAC;YAClB,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,KAAK;SACb,CAAC,CAAA;KACH;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC1B,MAAM,IAAI,KAAK,CACb,+BACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAC1D,EAAE,CACH,CAAA;KACF;AACH,CAAC,CAAA;AAEY,QAAA,WAAW,GAAG,iBAAiB,EAAE,CAAA","sourcesContent":["/* eslint-disable no-console */\nimport axios from 'axios'\nimport tunnel, { ProxyOptions } from 'tunnel'\nimport { Agent } from 'http'\n\nconst getProxy = (): Agent | null => {\n if (!process.env.PROXY) {\n return null\n }\n\n const proxy: string | null = process.env.PROXY\n const url = new URL(proxy)\n const proxyObject: ProxyOptions = {\n host: url.hostname,\n port: Number(url.port),\n }\n\n const { username, password } = url\n\n if (username && password) {\n proxyObject.proxyAuth = `${username}:${password}`\n }\n return tunnel.httpsOverHttp({ proxy: proxyObject })\n}\n\nconst createAxiosClient = () => {\n try {\n const agent: string | Agent = getProxy()\n return axios.create({\n httpAgent: agent,\n proxy: false,\n })\n } catch (error) {\n console.log(error.message)\n throw new Error(\n `Error creating axios client ${\n error instanceof Error ? error.message : error.response.data\n }`\n )\n }\n}\n\nexport const axiosClient = createAxiosClient()\n"]}
1
+ {"version":3,"file":"axios_client.js","sourceRoot":"","sources":["../../src/configuration/axios_client.ts"],"names":[],"mappings":";;;;;;AAAA,+BAA+B;AAC/B,kDAAyB;AACzB,oDAA6C;AAG7C,MAAM,QAAQ,GAAG,GAAiB,EAAE;IAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;QACtB,OAAO,IAAI,CAAA;KACZ;IAED,MAAM,KAAK,GAAkB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAA;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IAC1B,MAAM,WAAW,GAAiB;QAChC,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;KACvB,CAAA;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IAElC,IAAI,QAAQ,IAAI,QAAQ,EAAE;QACxB,WAAW,CAAC,SAAS,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAA;KAClD;IACD,OAAO,gBAAM,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;AACrD,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAG,GAAG,EAAE;IAC7B,IAAI;QACF,MAAM,KAAK,GAAmB,QAAQ,EAAE,CAAA;QACxC,OAAO,eAAK,CAAC,MAAM,CAAC;YAClB,UAAU,EAAE,KAAK;YACjB,KAAK,EAAE,KAAK;SACb,CAAC,CAAA;KACH;IAAC,OAAO,KAAK,EAAE;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC1B,MAAM,IAAI,KAAK,CACb,+BACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAC1D,EAAE,CACH,CAAA;KACF;AACH,CAAC,CAAA;AAEY,QAAA,WAAW,GAAG,iBAAiB,EAAE,CAAA","sourcesContent":["/* eslint-disable no-console */\nimport axios from 'axios'\nimport tunnel, { ProxyOptions } from 'tunnel'\nimport { Agent } from 'http'\n\nconst getProxy = (): Agent | null => {\n if (!process.env.PROXY) {\n return null\n }\n\n const proxy: string | null = process.env.PROXY\n const url = new URL(proxy)\n const proxyObject: ProxyOptions = {\n host: url.hostname,\n port: Number(url.port),\n }\n\n const { username, password } = url\n\n if (username && password) {\n proxyObject.proxyAuth = `${username}:${password}`\n }\n return tunnel.httpsOverHttp({ proxy: proxyObject })\n}\n\nconst createAxiosClient = () => {\n try {\n const agent: string | Agent = getProxy()\n return axios.create({\n httpsAgent: agent,\n proxy: false,\n })\n } catch (error) {\n console.log(error.message)\n throw new Error(\n `Error creating axios client ${\n error instanceof Error ? error.message : error.response.data\n }`\n )\n }\n}\n\nexport const axiosClient = createAxiosClient()\n"]}
@@ -0,0 +1,2 @@
1
+ declare const getProjectByAccessKey: (access_key: string) => Promise<any>;
2
+ export { getProjectByAccessKey };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getProjectByAccessKey = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const tunnel_1 = __importDefault(require("tunnel"));
9
+ const getSSoUrl = () => {
10
+ switch (process.env.NODE_ENV_BLINQ) {
11
+ case 'local':
12
+ return 'http://localhost:5000/api/auth';
13
+ case 'dev':
14
+ return 'https://dev.api.blinq.io/api/auth';
15
+ case 'stage':
16
+ return 'https://stage.api.blinq.io/api/auth';
17
+ case 'prod':
18
+ return 'https://api.blinq.io/api/auth';
19
+ case null:
20
+ return 'https://api.blinq.io/api/auth';
21
+ case undefined:
22
+ return 'https://api.blinq.io/api/auth';
23
+ default:
24
+ return `${process.env.NODE_ENV_BLINQ}/api/auth`;
25
+ }
26
+ };
27
+ const getProxy = () => {
28
+ if (!process.env.PROXY) {
29
+ return null;
30
+ }
31
+ const proxy = process.env.PROXY;
32
+ const url = new URL(proxy);
33
+ const proxyObject = {
34
+ host: url.hostname,
35
+ port: Number(url.port),
36
+ };
37
+ const { username, password } = url;
38
+ if (username && password) {
39
+ //@ts-ignore
40
+ proxyObject.proxyAuth = `${username}:${password}`;
41
+ }
42
+ return tunnel_1.default.httpsOverHttp({ proxy: proxyObject });
43
+ };
44
+ const getProjectByAccessKey = async (access_key) => {
45
+ const ssoUrl = getSSoUrl();
46
+ const accessKeyUrl = `${ssoUrl}/getProjectByAccessKey`;
47
+ const response = await axios_1.default.post(accessKeyUrl, {
48
+ access_key,
49
+ httpAgent: getProxy(),
50
+ proxy: false,
51
+ });
52
+ if (response.status !== 200) {
53
+ console.error('Error: Invalid access key');
54
+ process.exit(1);
55
+ }
56
+ return response.data;
57
+ };
58
+ exports.getProjectByAccessKey = getProjectByAccessKey;
59
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/formatter/api.ts"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AACzB,oDAA2B;AAE3B,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,QAAQ,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE;QAClC,KAAK,OAAO;YACV,OAAO,gCAAgC,CAAA;QACzC,KAAK,KAAK;YACR,OAAO,mCAAmC,CAAA;QAC5C,KAAK,OAAO;YACV,OAAO,qCAAqC,CAAA;QAC9C,KAAK,MAAM;YACT,OAAO,+BAA+B,CAAA;QACxC,KAAK,IAAI;YACP,OAAO,+BAA+B,CAAA;QACxC,KAAK,SAAS;YACZ,OAAO,+BAA+B,CAAA;QACxC;YACE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,WAAW,CAAA;KAClD;AACH,CAAC,CAAA;AACD,MAAM,QAAQ,GAAG,GAAG,EAAE;IACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;QACtB,OAAO,IAAI,CAAA;KACZ;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAA;IAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IAC1B,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;KACvB,CAAA;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IAElC,IAAI,QAAQ,IAAI,QAAQ,EAAE;QACxB,YAAY;QACZ,WAAW,CAAC,SAAS,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAA;KAClD;IACD,OAAO,gBAAM,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;AACrD,CAAC,CAAA;AAED,MAAM,qBAAqB,GAAG,KAAK,EAAE,UAAkB,EAAE,EAAE;IACzD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,YAAY,GAAG,GAAG,MAAM,wBAAwB,CAAA;IACtD,MAAM,QAAQ,GAAG,MAAM,eAAK,CAAC,IAAI,CAAC,YAAY,EAAE;QAC9C,UAAU;QACV,SAAS,EAAE,QAAQ,EAAE;QACrB,KAAK,EAAE,KAAK;KACb,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;QAC3B,OAAO,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAA;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KAChB;IACD,OAAO,QAAQ,CAAC,IAAI,CAAA;AACtB,CAAC,CAAA;AAEQ,sDAAqB","sourcesContent":["import axios from 'axios'\nimport tunnel from 'tunnel'\n\nconst getSSoUrl = () => {\n switch (process.env.NODE_ENV_BLINQ) {\n case 'local':\n return 'http://localhost:5000/api/auth'\n case 'dev':\n return 'https://dev.api.blinq.io/api/auth'\n case 'stage':\n return 'https://stage.api.blinq.io/api/auth'\n case 'prod':\n return 'https://api.blinq.io/api/auth'\n case null:\n return 'https://api.blinq.io/api/auth'\n case undefined:\n return 'https://api.blinq.io/api/auth'\n default:\n return `${process.env.NODE_ENV_BLINQ}/api/auth`\n }\n}\nconst getProxy = () => {\n if (!process.env.PROXY) {\n return null\n }\n\n const proxy = process.env.PROXY\n const url = new URL(proxy)\n const proxyObject = {\n host: url.hostname,\n port: Number(url.port),\n }\n\n const { username, password } = url\n\n if (username && password) {\n //@ts-ignore\n proxyObject.proxyAuth = `${username}:${password}`\n }\n return tunnel.httpsOverHttp({ proxy: proxyObject })\n}\n\nconst getProjectByAccessKey = async (access_key: string) => {\n const ssoUrl = getSSoUrl()\n const accessKeyUrl = `${ssoUrl}/getProjectByAccessKey`\n const response = await axios.post(accessKeyUrl, {\n access_key,\n httpAgent: getProxy(),\n proxy: false,\n })\n if (response.status !== 200) {\n console.error('Error: Invalid access key')\n process.exit(1)\n }\n return response.data\n}\n\nexport { getProjectByAccessKey }\n"]}
@@ -1,11 +1,22 @@
1
1
  import Formatter, { IFormatterOptions } from '.';
2
+ import ReportGenerator, { JsonTestResult } from './helpers/report_generator';
3
+ export declare let globalReportLink: string;
2
4
  export default class BVTAnalysisFormatter extends Formatter {
5
+ static reportGenerator: ReportGenerator;
6
+ static reRunFailedStepsIndex: {
7
+ testCaseId: string;
8
+ failedStepIndex: number;
9
+ }[] | null;
3
10
  private reportGenerator;
4
11
  private uploader;
5
12
  private exit;
6
13
  private START;
7
14
  private runName;
15
+ private failedStepsIndex;
16
+ private summaryFormatter;
17
+ private rootCauseArray;
8
18
  constructor(options: IFormatterOptions);
19
+ private sendEvent;
9
20
  private uploadReport;
10
21
  finished(): Promise<any>;
11
22
  private analyzeReport;
@@ -13,7 +24,8 @@ export default class BVTAnalysisFormatter extends Formatter {
13
24
  private processTestCase;
14
25
  private uploadFinalReport;
15
26
  private retrain;
27
+ private rerun;
16
28
  private call_cucumber_client;
17
- private logReportLink;
18
29
  private getAppDataDir;
19
30
  }
31
+ export declare function logReportLink(runId: string, projectId: string, status: JsonTestResult): string;
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logReportLink = exports.globalReportLink = void 0;
6
7
  const child_process_1 = require("child_process");
7
8
  const fs_1 = require("fs");
8
9
  const promises_1 = require("fs/promises");
@@ -13,36 +14,77 @@ const value_checker_1 = require("../value_checker");
13
14
  const report_generator_1 = __importDefault(require("./helpers/report_generator"));
14
15
  const uploader_1 = __importDefault(require("./helpers/uploader"));
15
16
  const os_1 = __importDefault(require("os"));
17
+ const api_1 = require("./api");
18
+ const summary_formatter_1 = __importDefault(require("./summary_formatter"));
19
+ const constants_1 = require("./helpers/constants");
20
+ const axios_client_1 = require("../configuration/axios_client");
21
+ const util_1 = require("util");
22
+ const child_process_2 = require("child_process");
16
23
  //User token
17
24
  const TOKEN = process.env.TOKEN;
25
+ exports.globalReportLink = '';
18
26
  class BVTAnalysisFormatter extends _1.default {
19
27
  constructor(options) {
20
28
  super(options);
21
29
  this.reportGenerator = new report_generator_1.default();
22
30
  this.uploader = new uploader_1.default(this.reportGenerator);
23
31
  this.exit = false;
32
+ this.rootCauseArray = [];
33
+ this.summaryFormatter = new summary_formatter_1.default(options);
34
+ BVTAnalysisFormatter.reportGenerator = this.reportGenerator;
35
+ this.rootCauseArray = [];
36
+ this.failedStepsIndex = [];
37
+ BVTAnalysisFormatter.reRunFailedStepsIndex = process.env.RERUN
38
+ ? JSON.parse(process.env.RERUN)
39
+ : null;
24
40
  if (!TOKEN && process.env.BVT_FORMATTER === 'ANALYSIS') {
25
41
  throw new Error('TOKEN must be set');
26
42
  }
27
- options.eventBroadcaster.on('envelope', async (envelope) => {
28
- this.reportGenerator.handleMessage(envelope);
43
+ this.sendEvent(constants_1.ActionEvents.cli_run_tests);
44
+ options.eventBroadcaster.on('envelope', async (envelope, data) => {
45
+ if ((0, value_checker_1.doesHaveValue)(envelope.testCaseFinished) && data) {
46
+ const { rootCause, report } = data;
47
+ if (!rootCause.status) {
48
+ console.error(`Root cause: ${rootCause.failClass}\n, ${rootCause.analysis}\nfailing step: ${rootCause.failedStep}`);
49
+ this.rootCauseArray.push({ rootCause, report });
50
+ this.failedStepsIndex.push({
51
+ testCaseId: report.id,
52
+ failedStepIndex: rootCause.failedStep,
53
+ });
54
+ }
55
+ return;
56
+ }
57
+ await this.reportGenerator.handleMessage(envelope);
29
58
  if ((0, value_checker_1.doesHaveValue)(envelope.meta) &&
30
59
  (0, value_checker_1.doesHaveValue)(envelope.meta.runName)) {
31
60
  this.runName = envelope.meta.runName;
32
61
  }
33
62
  if ((0, value_checker_1.doesHaveValue)(envelope.testRunFinished)) {
34
- const report = this.reportGenerator.getReport();
35
63
  this.START = Date.now();
36
64
  if (process.env.BVT_FORMATTER === 'ANALYSIS') {
37
- await this.analyzeReport(report);
65
+ await this.analyzeReport();
38
66
  }
39
67
  else {
40
- await this.uploadReport(report);
68
+ // await this.uploadReport(report)
41
69
  }
42
70
  this.exit = true;
43
71
  }
44
72
  });
45
73
  }
74
+ sendEvent(event) {
75
+ axios_client_1.axiosClient
76
+ .post(`${constants_1.SERVICES_URI.STORAGE}/event`, {
77
+ event,
78
+ }, {
79
+ headers: {
80
+ Authorization: `Bearer ${TOKEN}`,
81
+ 'x-source': 'cucumber_js',
82
+ },
83
+ })
84
+ .catch((err) => {
85
+ // Error with events, ignoring
86
+ });
87
+ }
46
88
  async uploadReport(report) {
47
89
  const uploadSuccessful = await this.uploadFinalReport(report);
48
90
  if (uploadSuccessful && report.result.status !== 'FAILED') {
@@ -53,77 +95,80 @@ class BVTAnalysisFormatter extends _1.default {
53
95
  async finished() {
54
96
  await new Promise((resolve) => {
55
97
  const checkInterval = setInterval(() => {
56
- if (this.exit) {
57
- clearInterval(checkInterval);
58
- resolve(null);
98
+ let anyRem;
99
+ if (process.env.UPLOADING_TEST_CASE) {
100
+ anyRem = JSON.parse(process.env.UPLOADING_TEST_CASE);
101
+ }
102
+ else {
103
+ anyRem = undefined;
104
+ }
105
+ if (this.exit && (!anyRem || anyRem.length === 0)) {
106
+ // clearInterval(checkInterval)
107
+ // resolve(null)
108
+ if (this.reportGenerator.getReport().result.status === 'FAILED') {
109
+ process.exit(1);
110
+ }
111
+ else {
112
+ process.exit(0);
113
+ }
59
114
  }
60
115
  }, 100); // check every 100ms
61
116
  });
62
117
  }
63
- async analyzeReport(report) {
64
- if (report.result.status === 'PASSED') {
65
- this.log('No test failed. No need to retrain\n');
66
- const uploadSuccessful = await this.uploadFinalReport(report);
67
- if (uploadSuccessful) {
68
- process.exit(0);
118
+ async analyzeReport() {
119
+ if (this.rootCauseArray.length === 0 ||
120
+ process.env.NO_RETRAIN === 'false') {
121
+ if (this.rootCauseArray.length === 0) {
122
+ this.log('No test failed. No need to retrain\n');
69
123
  }
70
- process.exit(1);
124
+ if (process.env.NO_RETRAIN === 'false') {
125
+ this.log('Retraining is skipped since the failed step contains an API request\n');
126
+ }
127
+ // const uploadSuccessful = await this.uploadFinalReport(report)
128
+ // process.exit(0)
129
+ this.exit = true;
130
+ return;
71
131
  }
72
132
  //checking if the type of report.result is JsonResultFailed or not
73
133
  this.log('Some tests failed, starting the retraining...\n');
74
- if (!('startTime' in report.result) || !('endTime' in report.result)) {
75
- this.log('Unknown error occured,not retraining\n');
76
- await this.uploadFinalReport(report);
77
- return;
78
- }
79
- const finalReport = await this.processTestCases(report);
80
- const uploadSuccessful = await this.uploadFinalReport(finalReport);
81
- if (finalReport.result.status !== 'FAILED' && uploadSuccessful) {
82
- process.exit(0);
83
- }
84
- else {
134
+ await this.processTestCases();
135
+ if (this.reportGenerator.getReport().result.status === 'FAILED') {
85
136
  process.exit(1);
86
137
  }
138
+ process.exit(0);
87
139
  }
88
- async processTestCases(report) {
89
- const finalTestCases = [];
90
- for (const testCase of report.testCases) {
91
- const modifiedTestCase = await this.processTestCase(testCase, report);
92
- finalTestCases.push(modifiedTestCase);
140
+ async processTestCases() {
141
+ for (const { rootCause, report } of this.rootCauseArray) {
142
+ await this.processTestCase(rootCause, report);
93
143
  }
94
- const finalResult = finalTestCases.some((tc) => tc.result.status !== 'PASSED')
95
- ? report.result
96
- : {
97
- ...report.result,
98
- status: 'PASSED',
99
- };
100
- return {
101
- result: finalResult,
102
- testCases: finalTestCases,
103
- env: report.env,
104
- };
105
- }
106
- async processTestCase(testCase, report) {
107
- if (testCase.result.status === 'PASSED') {
108
- return testCase;
144
+ }
145
+ async processTestCase(rootCause, report) {
146
+ const failedTestSteps = rootCause.failedStep;
147
+ if (BVTAnalysisFormatter.reRunFailedStepsIndex &&
148
+ BVTAnalysisFormatter.reRunFailedStepsIndex.length > 0) {
149
+ const previousRun = BVTAnalysisFormatter.reRunFailedStepsIndex[0];
150
+ if (previousRun.failedStepIndex === failedTestSteps) {
151
+ console.log('Same step has failed again, skipping retraining');
152
+ BVTAnalysisFormatter.reRunFailedStepsIndex.shift();
153
+ return;
154
+ }
155
+ BVTAnalysisFormatter.reRunFailedStepsIndex.shift();
109
156
  }
110
- const failedTestSteps = testCase.steps
111
- .map((step, i) => (step.result.status === 'FAILED' ? i : null))
112
- .filter((i) => i !== null);
113
- const retrainStats = await this.retrain(failedTestSteps, testCase);
157
+ const retrainStats = await this.retrain(failedTestSteps, report);
114
158
  if (!retrainStats) {
115
- return testCase;
159
+ return;
116
160
  }
117
- return {
118
- ...testCase,
161
+ await this.uploader.modifyTestCase({
162
+ ...report,
119
163
  retrainStats,
120
- };
164
+ });
165
+ await this.rerun(report);
121
166
  }
122
167
  async uploadFinalReport(finalReport) {
123
168
  let success = true;
124
169
  try {
125
170
  const { projectId, runId } = await this.uploader.uploadRun(finalReport, this.runName);
126
- this.logReportLink(runId, projectId);
171
+ logReportLink(runId, projectId, finalReport.result);
127
172
  }
128
173
  catch (err) {
129
174
  this.log('Error uploading report\n');
@@ -132,76 +177,141 @@ class BVTAnalysisFormatter extends _1.default {
132
177
  }
133
178
  success = false;
134
179
  }
180
+ finally {
181
+ try {
182
+ (0, fs_1.writeFileSync)(path_1.default.join(this.reportGenerator.reportFolder, 'report.json'), JSON.stringify(finalReport, null, 2), 'utf-8');
183
+ }
184
+ catch (e) {
185
+ console.error('failed to write report.json to local disk');
186
+ }
187
+ }
135
188
  //this.log(JSON.stringify(finalReport, null, 2))
136
189
  return success;
137
190
  }
138
191
  async retrain(failedTestCases, testCase) {
192
+ const data = await (0, api_1.getProjectByAccessKey)(TOKEN);
193
+ const currentTimestampInSeconds = Math.floor(Date.now() / 1000);
194
+ if (data.project.expriration_date < currentTimestampInSeconds) {
195
+ console.log('Warning: Your project has expired, retraining is restricted. Please contact sales.');
196
+ process.exit(1);
197
+ }
139
198
  return await this.call_cucumber_client(failedTestCases, testCase);
140
199
  }
200
+ async rerun(report) {
201
+ await new Promise((resolve) => {
202
+ // Default to system Node.js
203
+ let node_path = process.argv.shift();
204
+ // Use bundled Node if running from recorder app on macOS or Windows
205
+ const isFromRecorderApp = process.env.FROM_RECORDER_APP === "true";
206
+ const isSupportedPlatform = process.platform === "darwin" || process.platform === "win32";
207
+ if (isFromRecorderApp && isSupportedPlatform) {
208
+ node_path = process.execPath;
209
+ }
210
+ const args = [
211
+ path_1.default.join(process.cwd(), "node_modules", "@dev-blinq", "cucumber-js", "bin", "cucumber.js"),
212
+ "--name",
213
+ `^${report.scenarioName}$`,
214
+ "--exit",
215
+ "--format",
216
+ "bvt",
217
+ "--run-name",
218
+ `${report.scenarioName}@debug`,
219
+ path_1.default.join(process.cwd(), "features", `${report.featureName}.feature`),
220
+ ];
221
+ const envVars = {
222
+ ...process.env,
223
+ RERUN: JSON.stringify(this.failedStepsIndex),
224
+ };
225
+ // Inject Electron node env only if using bundled node
226
+ if (node_path === process.execPath) {
227
+ envVars.ELECTRON_RUN_AS_NODE = "1";
228
+ }
229
+ const cucumberClient = (0, child_process_1.spawn)(node_path, args, {
230
+ env: envVars,
231
+ });
232
+ cucumberClient.stdout.on("data", (data) => {
233
+ console.log(data.toString());
234
+ });
235
+ cucumberClient.stderr.on("data", (data) => {
236
+ console.error(data.toString());
237
+ });
238
+ cucumberClient.on("close", () => {
239
+ resolve();
240
+ });
241
+ });
242
+ }
141
243
  async call_cucumber_client(stepsToRetrain, testCase) {
142
244
  return new Promise((resolve, reject) => {
143
- const cucumber_client_path = path_1.default.resolve(process.cwd(), 'node_modules', '@dev-blinq', 'cucumber_client', 'bin', 'client', 'cucumber.js');
245
+ const cucumber_client_path = path_1.default.resolve(process.cwd(), "node_modules", "@dev-blinq", "cucumber_client", "bin", "client", "cucumber.js");
144
246
  const args = [
145
247
  process.cwd(),
146
248
  path_1.default.join(process.cwd(), testCase.uri),
147
249
  `${testCase.scenarioName}`,
148
- 'undefined',
149
- `${stepsToRetrain.join(',')}`,
250
+ "undefined",
251
+ `${stepsToRetrain},`,
150
252
  ];
151
253
  if (process.env.BLINQ_ENV) {
152
254
  args.push(`--env=${process.env.BLINQ_ENV}`);
153
255
  }
154
- if (!(0, fs_1.existsSync)(path_1.default.join(this.getAppDataDir(), 'blinq.io', '.temp'))) {
155
- (0, promises_1.mkdir)(path_1.default.join(this.getAppDataDir(), 'blinq.io', '.temp'), {
256
+ if (!(0, fs_1.existsSync)(path_1.default.join(this.getAppDataDir(), "blinq.io", ".temp"))) {
257
+ (0, promises_1.mkdir)(path_1.default.join(this.getAppDataDir(), "blinq.io", ".temp"), {
156
258
  recursive: true,
157
259
  });
158
260
  }
159
261
  (0, tmp_1.tmpName)(async (err, name) => {
160
- const tempFile = path_1.default.join(this.getAppDataDir(), 'blinq.io', '.temp', path_1.default.basename(name));
161
- console.log('File path: ', tempFile);
162
- await (0, promises_1.writeFile)(tempFile, '', 'utf-8');
262
+ const tempFile = path_1.default.join(this.getAppDataDir(), "blinq.io", ".temp", path_1.default.basename(name));
263
+ console.log("File path: ", tempFile);
264
+ if (!(0, fs_1.existsSync)(path_1.default.dirname(tempFile))) {
265
+ await (0, promises_1.mkdir)(path_1.default.dirname(tempFile), { recursive: true });
266
+ }
267
+ await (0, promises_1.writeFile)(tempFile, "", "utf-8");
163
268
  args.push(`--temp-file=${tempFile}`);
164
- const cucumberClient = (0, child_process_1.spawn)('node', [cucumber_client_path, ...args], {
165
- env: {
166
- ...process.env,
167
- TEMP_FILE_PATH: tempFile,
168
- },
269
+ // Determine node path
270
+ const isFromRecorderApp = process.env.FROM_RECORDER_APP === "true";
271
+ const isSupportedPlatform = process.platform === "darwin" || process.platform === "win32";
272
+ const node_path = isFromRecorderApp && isSupportedPlatform
273
+ ? process.execPath
274
+ : "node";
275
+ const envVars = {
276
+ ...process.env,
277
+ TEMP_FILE_PATH: tempFile,
278
+ };
279
+ if (node_path === process.execPath) {
280
+ envVars.ELECTRON_RUN_AS_NODE = "1";
281
+ }
282
+ const cucumberClient = (0, child_process_1.spawn)(node_path, [cucumber_client_path, ...args], {
283
+ env: envVars,
169
284
  });
170
- cucumberClient.stdout.on('data', (data) => {
285
+ cucumberClient.stdout.on("data", (data) => {
171
286
  console.log(data.toString());
172
287
  });
173
- cucumberClient.stderr.on('data', (data) => {
288
+ cucumberClient.stderr.on("data", (data) => {
174
289
  console.error(data.toString());
175
290
  });
176
- cucumberClient.on('close', async (code) => {
291
+ cucumberClient.on("close", async (code) => {
177
292
  if (code === 0) {
178
- const reportData = (0, fs_1.readFileSync)(tempFile, 'utf-8');
293
+ const reportData = (0, fs_1.readFileSync)(tempFile, "utf-8");
179
294
  const retrainStats = JSON.parse(reportData);
180
295
  await (0, promises_1.unlink)(tempFile);
181
296
  resolve(retrainStats);
182
297
  }
183
298
  else {
184
- this.log('Error retraining\n');
185
- resolve(null);
299
+ this.log("Error retraining\n");
300
+ try {
301
+ const reportData = (0, fs_1.readFileSync)(tempFile, "utf-8");
302
+ const retrainStats = JSON.parse(reportData);
303
+ await (0, promises_1.unlink)(tempFile);
304
+ resolve(retrainStats);
305
+ }
306
+ catch (e) {
307
+ this.log("Error reading scenario report\n " + e);
308
+ resolve(null);
309
+ }
186
310
  }
187
311
  });
188
312
  });
189
313
  });
190
314
  }
191
- logReportLink(runId, projectId) {
192
- let reportLinkBaseUrl = 'https://app.blinq.io';
193
- if (process.env.NODE_ENV_BLINQ === 'local') {
194
- reportLinkBaseUrl = 'http://localhost:3000';
195
- }
196
- else if (process.env.NODE_ENV_BLINQ === 'dev') {
197
- reportLinkBaseUrl = 'https://dev.app.blinq.io';
198
- }
199
- else if (process.env.NODE_ENV_BLINQ === 'stage') {
200
- reportLinkBaseUrl = 'https://stage.app.blinq.io';
201
- }
202
- const reportLink = `${reportLinkBaseUrl}/${projectId}/run-report/${runId}`;
203
- this.log(`Report link: ${reportLink}\n`);
204
- }
205
315
  getAppDataDir() {
206
316
  if (process.env.BLINQ_APPDATA_DIR) {
207
317
  return process.env.BLINQ_APPDATA_DIR;
@@ -222,4 +332,41 @@ class BVTAnalysisFormatter extends _1.default {
222
332
  }
223
333
  }
224
334
  exports.default = BVTAnalysisFormatter;
335
+ function logReportLink(runId, projectId, status) {
336
+ let reportLinkBaseUrl = 'https://app.blinq.io';
337
+ if (process.env.NODE_ENV_BLINQ === 'local') {
338
+ reportLinkBaseUrl = 'http://localhost:3000';
339
+ }
340
+ else if (process.env.NODE_ENV_BLINQ === 'dev') {
341
+ reportLinkBaseUrl = 'https://dev.app.blinq.io';
342
+ }
343
+ else if (process.env.NODE_ENV_BLINQ === 'stage') {
344
+ reportLinkBaseUrl = 'https://stage.app.blinq.io';
345
+ }
346
+ else if (process.env.NODE_ENV_BLINQ === 'prod') {
347
+ reportLinkBaseUrl = 'https://app.blinq.io';
348
+ }
349
+ else if (!process.env.NODE_ENV_BLINQ) {
350
+ reportLinkBaseUrl = 'https://app.blinq.io';
351
+ }
352
+ else {
353
+ reportLinkBaseUrl = process.env.NODE_ENV_BLINQ.replace('api', 'app');
354
+ }
355
+ const reportLink = `${reportLinkBaseUrl}/${projectId}/run-report/${runId}`;
356
+ exports.globalReportLink = reportLink;
357
+ try {
358
+ publishReportLinkToGuacServer(reportLink, status.status === "PASSED");
359
+ }
360
+ catch (err) {
361
+ // Error with events, ignoring
362
+ }
363
+ return reportLink;
364
+ }
365
+ exports.logReportLink = logReportLink;
366
+ function publishReportLinkToGuacServer(reportLink, result) {
367
+ if ((0, fs_1.existsSync)('/tmp/report_publish.sh')) {
368
+ const execAsync = (0, util_1.promisify)(child_process_2.exec);
369
+ execAsync('sh /tmp/report_publish.sh ' + reportLink + ' ' + result);
370
+ }
371
+ }
225
372
  //# sourceMappingURL=bvt_analysis_formatter.js.map