codeceptjs 3.6.10 → 3.7.0-beta.10

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 (127) hide show
  1. package/README.md +89 -119
  2. package/bin/codecept.js +9 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +66 -102
  5. package/lib/ai.js +130 -121
  6. package/lib/assert/empty.js +3 -5
  7. package/lib/assert/equal.js +4 -7
  8. package/lib/assert/include.js +4 -6
  9. package/lib/assert/throws.js +2 -4
  10. package/lib/assert/truth.js +2 -2
  11. package/lib/codecept.js +87 -83
  12. package/lib/command/check.js +186 -0
  13. package/lib/command/configMigrate.js +2 -4
  14. package/lib/command/definitions.js +8 -26
  15. package/lib/command/generate.js +10 -14
  16. package/lib/command/gherkin/snippets.js +10 -8
  17. package/lib/command/gherkin/steps.js +1 -1
  18. package/lib/command/info.js +1 -3
  19. package/lib/command/init.js +8 -12
  20. package/lib/command/interactive.js +2 -2
  21. package/lib/command/list.js +1 -1
  22. package/lib/command/run-multiple.js +12 -35
  23. package/lib/command/run-workers.js +5 -57
  24. package/lib/command/utils.js +5 -6
  25. package/lib/command/workers/runTests.js +68 -232
  26. package/lib/container.js +354 -237
  27. package/lib/data/context.js +10 -13
  28. package/lib/data/dataScenarioConfig.js +8 -8
  29. package/lib/data/dataTableArgument.js +6 -6
  30. package/lib/data/table.js +5 -11
  31. package/lib/effects.js +218 -0
  32. package/lib/els.js +158 -0
  33. package/lib/event.js +19 -17
  34. package/lib/heal.js +88 -80
  35. package/lib/helper/AI.js +2 -1
  36. package/lib/helper/ApiDataFactory.js +3 -6
  37. package/lib/helper/Appium.js +45 -51
  38. package/lib/helper/FileSystem.js +3 -3
  39. package/lib/helper/GraphQLDataFactory.js +3 -3
  40. package/lib/helper/JSONResponse.js +57 -37
  41. package/lib/helper/Nightmare.js +35 -53
  42. package/lib/helper/Playwright.js +211 -252
  43. package/lib/helper/Protractor.js +54 -77
  44. package/lib/helper/Puppeteer.js +139 -232
  45. package/lib/helper/REST.js +5 -17
  46. package/lib/helper/TestCafe.js +21 -44
  47. package/lib/helper/WebDriver.js +131 -169
  48. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  49. package/lib/listener/emptyRun.js +55 -0
  50. package/lib/listener/exit.js +7 -10
  51. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  52. package/lib/listener/globalTimeout.js +165 -0
  53. package/lib/listener/helpers.js +15 -15
  54. package/lib/listener/mocha.js +1 -1
  55. package/lib/listener/result.js +12 -0
  56. package/lib/listener/steps.js +20 -18
  57. package/lib/listener/store.js +20 -0
  58. package/lib/mocha/asyncWrapper.js +216 -0
  59. package/lib/{interfaces → mocha}/bdd.js +3 -3
  60. package/lib/mocha/cli.js +308 -0
  61. package/lib/mocha/factory.js +104 -0
  62. package/lib/{interfaces → mocha}/featureConfig.js +24 -12
  63. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  64. package/lib/mocha/hooks.js +112 -0
  65. package/lib/mocha/index.js +12 -0
  66. package/lib/mocha/inject.js +29 -0
  67. package/lib/{interfaces → mocha}/scenarioConfig.js +21 -6
  68. package/lib/mocha/suite.js +81 -0
  69. package/lib/mocha/test.js +159 -0
  70. package/lib/mocha/types.d.ts +42 -0
  71. package/lib/mocha/ui.js +219 -0
  72. package/lib/output.js +82 -62
  73. package/lib/pause.js +155 -138
  74. package/lib/plugin/analyze.js +349 -0
  75. package/lib/plugin/autoDelay.js +6 -6
  76. package/lib/plugin/autoLogin.js +6 -7
  77. package/lib/plugin/commentStep.js +6 -1
  78. package/lib/plugin/coverage.js +10 -19
  79. package/lib/plugin/customLocator.js +3 -3
  80. package/lib/plugin/customReporter.js +52 -0
  81. package/lib/plugin/eachElement.js +1 -1
  82. package/lib/plugin/fakerTransform.js +1 -1
  83. package/lib/plugin/heal.js +36 -9
  84. package/lib/plugin/pageInfo.js +140 -0
  85. package/lib/plugin/retryFailedStep.js +4 -4
  86. package/lib/plugin/retryTo.js +18 -118
  87. package/lib/plugin/screenshotOnFail.js +17 -49
  88. package/lib/plugin/selenoid.js +15 -35
  89. package/lib/plugin/standardActingHelpers.js +4 -1
  90. package/lib/plugin/stepByStepReport.js +56 -17
  91. package/lib/plugin/stepTimeout.js +5 -12
  92. package/lib/plugin/subtitles.js +4 -4
  93. package/lib/plugin/tryTo.js +17 -107
  94. package/lib/plugin/wdio.js +8 -10
  95. package/lib/recorder.js +146 -125
  96. package/lib/rerun.js +43 -42
  97. package/lib/result.js +161 -0
  98. package/lib/secret.js +1 -1
  99. package/lib/step/base.js +228 -0
  100. package/lib/step/config.js +50 -0
  101. package/lib/step/func.js +46 -0
  102. package/lib/step/helper.js +50 -0
  103. package/lib/step/meta.js +99 -0
  104. package/lib/step/record.js +74 -0
  105. package/lib/step/retry.js +11 -0
  106. package/lib/step/section.js +55 -0
  107. package/lib/step.js +21 -332
  108. package/lib/steps.js +50 -0
  109. package/lib/store.js +10 -2
  110. package/lib/template/heal.js +2 -11
  111. package/lib/timeout.js +66 -0
  112. package/lib/utils.js +317 -216
  113. package/lib/within.js +73 -55
  114. package/lib/workers.js +259 -275
  115. package/package.json +56 -54
  116. package/typings/index.d.ts +175 -186
  117. package/typings/promiseBasedTypes.d.ts +164 -17
  118. package/typings/types.d.ts +284 -115
  119. package/lib/cli.js +0 -256
  120. package/lib/helper/ExpectHelper.js +0 -391
  121. package/lib/helper/SoftExpectHelper.js +0 -381
  122. package/lib/listener/artifacts.js +0 -19
  123. package/lib/listener/timeout.js +0 -109
  124. package/lib/mochaFactory.js +0 -113
  125. package/lib/plugin/debugErrors.js +0 -67
  126. package/lib/scenario.js +0 -224
  127. package/lib/ui.js +0 -236
package/lib/utils.js CHANGED
@@ -1,104 +1,95 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
- const getFunctionArguments = require('fn-args');
5
- const deepClone = require('lodash.clonedeep');
6
- const { convertColorToRGBA, isColorProperty } = require('./colorUtils');
1
+ const fs = require('fs')
2
+ const os = require('os')
3
+ const path = require('path')
4
+ const chalk = require('chalk')
5
+ const getFunctionArguments = require('fn-args')
6
+ const deepClone = require('lodash.clonedeep')
7
+ const { convertColorToRGBA, isColorProperty } = require('./colorUtils')
8
+ const Fuse = require('fuse.js')
7
9
 
8
10
  function deepMerge(target, source) {
9
- const merge = require('lodash.merge');
10
- return merge(target, source);
11
+ const merge = require('lodash.merge')
12
+ return merge(target, source)
11
13
  }
12
14
 
13
- module.exports.genTestId = (test) => {
14
- return require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64')
15
- .slice(0, -2);
16
- };
15
+ module.exports.genTestId = test => {
16
+ return require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64').slice(0, -2)
17
+ }
17
18
 
18
- module.exports.deepMerge = deepMerge;
19
+ module.exports.deepMerge = deepMerge
19
20
 
20
- module.exports.deepClone = deepClone;
21
+ module.exports.deepClone = deepClone
21
22
 
22
23
  module.exports.isGenerator = function (fn) {
23
- return fn.constructor.name === 'GeneratorFunction';
24
- };
24
+ return fn.constructor.name === 'GeneratorFunction'
25
+ }
25
26
 
26
- const isFunction = module.exports.isFunction = function (fn) {
27
- return typeof fn === 'function';
28
- };
27
+ const isFunction = (module.exports.isFunction = function (fn) {
28
+ return typeof fn === 'function'
29
+ })
29
30
 
30
- const isAsyncFunction = module.exports.isAsyncFunction = function (fn) {
31
- if (!fn) return false;
32
- return fn[Symbol.toStringTag] === 'AsyncFunction';
33
- };
31
+ const isAsyncFunction = (module.exports.isAsyncFunction = function (fn) {
32
+ if (!fn) return false
33
+ return fn[Symbol.toStringTag] === 'AsyncFunction'
34
+ })
34
35
 
35
36
  module.exports.fileExists = function (filePath) {
36
- return fs.existsSync(filePath);
37
- };
37
+ return fs.existsSync(filePath)
38
+ }
38
39
 
39
40
  module.exports.isFile = function (filePath) {
40
- let filestat;
41
+ let filestat
41
42
  try {
42
- filestat = fs.statSync(filePath);
43
+ filestat = fs.statSync(filePath)
43
44
  } catch (err) {
44
- if (err.code === 'ENOENT') return false;
45
+ if (err.code === 'ENOENT') return false
45
46
  }
46
- if (!filestat) return false;
47
- return filestat.isFile();
48
- };
47
+ if (!filestat) return false
48
+ return filestat.isFile()
49
+ }
49
50
 
50
51
  module.exports.getParamNames = function (fn) {
51
- if (fn.isSinonProxy) return [];
52
- return getFunctionArguments(fn);
53
- };
52
+ if (fn.isSinonProxy) return []
53
+ return getFunctionArguments(fn)
54
+ }
54
55
 
55
56
  module.exports.installedLocally = function () {
56
- return path.resolve(`${__dirname}/../`).indexOf(process.cwd()) === 0;
57
- };
57
+ return path.resolve(`${__dirname}/../`).indexOf(process.cwd()) === 0
58
+ }
58
59
 
59
60
  module.exports.methodsOfObject = function (obj, className) {
60
- const methods = [];
61
-
62
- const standard = [
63
- 'constructor',
64
- 'toString',
65
- 'toLocaleString',
66
- 'valueOf',
67
- 'hasOwnProperty',
68
- 'bind',
69
- 'apply',
70
- 'call',
71
- 'isPrototypeOf',
72
- 'propertyIsEnumerable',
73
- ];
61
+ const methods = []
62
+
63
+ const standard = ['constructor', 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'bind', 'apply', 'call', 'isPrototypeOf', 'propertyIsEnumerable']
74
64
 
75
65
  function pushToMethods(prop) {
76
66
  try {
77
- if (!isFunction(obj[prop]) && !isAsyncFunction(obj[prop])) return;
78
- } catch (err) { // can't access property
79
- return;
67
+ if (!isFunction(obj[prop]) && !isAsyncFunction(obj[prop])) return
68
+ } catch (err) {
69
+ // can't access property
70
+ return
80
71
  }
81
- if (standard.indexOf(prop) >= 0) return;
82
- if (prop.indexOf('_') === 0) return;
83
- methods.push(prop);
72
+ if (standard.indexOf(prop) >= 0) return
73
+ if (prop.indexOf('_') === 0) return
74
+ methods.push(prop)
84
75
  }
85
76
 
86
77
  while (obj.constructor.name !== className) {
87
- Object.getOwnPropertyNames(obj).forEach(pushToMethods);
88
- obj = Object.getPrototypeOf(obj);
78
+ Object.getOwnPropertyNames(obj).forEach(pushToMethods)
79
+ obj = Object.getPrototypeOf(obj)
89
80
 
90
- if (!obj || !obj.constructor) break;
81
+ if (!obj || !obj.constructor) break
91
82
  }
92
- return methods;
93
- };
83
+ return methods
84
+ }
94
85
 
95
86
  module.exports.template = function (template, data) {
96
87
  return template.replace(/{{([^{}]*)}}/g, (a, b) => {
97
- const r = data[b];
98
- if (r === undefined) return '';
99
- return r.toString();
100
- });
101
- };
88
+ const r = data[b]
89
+ if (r === undefined) return ''
90
+ return r.toString()
91
+ })
92
+ }
102
93
 
103
94
  /**
104
95
  * Make first char uppercase.
@@ -106,8 +97,8 @@ module.exports.template = function (template, data) {
106
97
  * @returns {string}
107
98
  */
108
99
  module.exports.ucfirst = function (str) {
109
- return str.charAt(0).toUpperCase() + str.substr(1);
110
- };
100
+ return str.charAt(0).toUpperCase() + str.substr(1)
101
+ }
111
102
 
112
103
  /**
113
104
  * Make first char lowercase.
@@ -115,25 +106,25 @@ module.exports.ucfirst = function (str) {
115
106
  * @returns {string}
116
107
  */
117
108
  module.exports.lcfirst = function (str) {
118
- return str.charAt(0).toLowerCase() + str.substr(1);
119
- };
109
+ return str.charAt(0).toLowerCase() + str.substr(1)
110
+ }
120
111
 
121
112
  module.exports.chunkArray = function (arr, chunk) {
122
- let i;
123
- let j;
124
- const tmp = [];
113
+ let i
114
+ let j
115
+ const tmp = []
125
116
  for (i = 0, j = arr.length; i < j; i += chunk) {
126
- tmp.push(arr.slice(i, i + chunk));
117
+ tmp.push(arr.slice(i, i + chunk))
127
118
  }
128
- return tmp;
129
- };
119
+ return tmp
120
+ }
130
121
 
131
122
  module.exports.clearString = function (str) {
132
- if (!str) return '';
123
+ if (!str) return ''
133
124
  /* Replace forbidden symbols in string
134
125
  */
135
126
  if (str.endsWith('.')) {
136
- str = str.slice(0, -1);
127
+ str = str.slice(0, -1)
137
128
  }
138
129
  return str
139
130
  .replace(/ /g, '_')
@@ -146,26 +137,29 @@ module.exports.clearString = function (str) {
146
137
  .replace(/\|/g, '_')
147
138
  .replace(/\?/g, '.')
148
139
  .replace(/\*/g, '^')
149
- .replace(/'/g, '');
150
- };
140
+ .replace(/'/g, '')
141
+ }
151
142
 
152
143
  module.exports.decodeUrl = function (url) {
153
144
  /* Replace forbidden symbols in string
154
145
  */
155
- return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)));
156
- };
146
+ return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)))
147
+ }
157
148
 
158
149
  module.exports.xpathLocator = {
159
150
  /**
160
151
  * @param {string} string
161
152
  * @returns {string}
162
153
  */
163
- literal: (string) => {
154
+ literal: string => {
164
155
  if (string.indexOf("'") > -1) {
165
- string = string.split("'", -1).map(substr => `'${substr}'`).join(',"\'",');
166
- return `concat(${string})`;
156
+ string = string
157
+ .split("'", -1)
158
+ .map(substr => `'${substr}'`)
159
+ .join(',"\'",')
160
+ return `concat(${string})`
167
161
  }
168
- return `'${string}'`;
162
+ return `'${string}'`
169
163
  },
170
164
 
171
165
  /**
@@ -174,55 +168,53 @@ module.exports.xpathLocator = {
174
168
  * @returns {string}
175
169
  */
176
170
  combine: locators => locators.join(' | '),
177
- };
171
+ }
178
172
 
179
173
  module.exports.test = {
180
-
181
174
  grepLines(array, startString, endString) {
182
- let startIndex = 0;
183
- let endIndex;
175
+ let startIndex = 0
176
+ let endIndex
184
177
  array.every((elem, index) => {
185
178
  if (elem === startString) {
186
- startIndex = index;
187
- return true;
179
+ startIndex = index
180
+ return true
188
181
  }
189
182
  if (elem === endString) {
190
- endIndex = index;
191
- return false;
183
+ endIndex = index
184
+ return false
192
185
  }
193
- return true;
194
- });
195
- return array.slice(startIndex + 1, endIndex);
186
+ return true
187
+ })
188
+ return array.slice(startIndex + 1, endIndex)
196
189
  },
197
190
 
198
191
  submittedData(dataFile) {
199
192
  return function (key) {
200
193
  if (!fs.existsSync(dataFile)) {
201
- const waitTill = new Date(new Date().getTime() + 1 * 1000); // wait for one sec for file to be created
202
- while (waitTill > new Date()) {} // eslint-disable-line no-empty
194
+ const waitTill = new Date(new Date().getTime() + 1 * 1000) // wait for one sec for file to be created
195
+ while (waitTill > new Date()) {}
203
196
  }
204
197
  if (!fs.existsSync(dataFile)) {
205
- throw new Error('Data file was not created in time');
198
+ throw new Error('Data file was not created in time')
206
199
  }
207
- const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'));
200
+ const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'))
208
201
  if (key) {
209
- return data.form[key];
202
+ return data.form[key]
210
203
  }
211
- return data;
212
- };
204
+ return data
205
+ }
213
206
  },
214
-
215
- };
207
+ }
216
208
 
217
209
  function toCamelCase(name) {
218
210
  if (typeof name !== 'string') {
219
- return name;
211
+ return name
220
212
  }
221
213
  return name.replace(/-(\w)/gi, (_word, letter) => {
222
- return letter.toUpperCase();
223
- });
214
+ return letter.toUpperCase()
215
+ })
224
216
  }
225
- module.exports.toCamelCase = toCamelCase;
217
+ module.exports.toCamelCase = toCamelCase
226
218
 
227
219
  function convertFontWeightToNumber(name) {
228
220
  const fontWeightPatterns = [
@@ -235,106 +227,110 @@ function convertFontWeightToNumber(name) {
235
227
  { num: 700, pattern: /^Bold$/i },
236
228
  { num: 800, pattern: /^(Extra|Ultra)-?bold$/i },
237
229
  { num: 900, pattern: /^(Black|Heavy)$/i },
238
- ];
230
+ ]
239
231
 
240
232
  if (/^[1-9]00$/.test(name)) {
241
- return Number(name);
233
+ return Number(name)
242
234
  }
243
235
 
244
- const matches = fontWeightPatterns.filter(fontWeight => fontWeight.pattern.test(name));
236
+ const matches = fontWeightPatterns.filter(fontWeight => fontWeight.pattern.test(name))
245
237
 
246
238
  if (matches.length) {
247
- return String(matches[0].num);
239
+ return String(matches[0].num)
248
240
  }
249
- return name;
241
+ return name
250
242
  }
251
243
 
252
244
  function isFontWeightProperty(prop) {
253
- return prop === 'fontWeight';
245
+ return prop === 'fontWeight'
254
246
  }
255
247
 
256
248
  module.exports.convertCssPropertiesToCamelCase = function (props) {
257
- const output = {};
258
- Object.keys(props).forEach((key) => {
259
- const keyCamel = toCamelCase(key);
249
+ const output = {}
250
+ Object.keys(props).forEach(key => {
251
+ const keyCamel = toCamelCase(key)
260
252
 
261
253
  if (isFontWeightProperty(keyCamel)) {
262
- output[keyCamel] = convertFontWeightToNumber(props[key]);
254
+ output[keyCamel] = convertFontWeightToNumber(props[key])
263
255
  } else if (isColorProperty(keyCamel)) {
264
- output[keyCamel] = convertColorToRGBA(props[key]);
256
+ output[keyCamel] = convertColorToRGBA(props[key])
265
257
  } else {
266
- output[keyCamel] = props[key];
258
+ output[keyCamel] = props[key]
267
259
  }
268
- });
269
- return output;
270
- };
260
+ })
261
+ return output
262
+ }
271
263
 
272
264
  module.exports.deleteDir = function (dir_path) {
273
265
  if (fs.existsSync(dir_path)) {
274
266
  fs.readdirSync(dir_path).forEach(function (entry) {
275
- const entry_path = path.join(dir_path, entry);
267
+ const entry_path = path.join(dir_path, entry)
276
268
  if (fs.lstatSync(entry_path).isDirectory()) {
277
- this.deleteDir(entry_path);
269
+ this.deleteDir(entry_path)
278
270
  } else {
279
- fs.unlinkSync(entry_path);
271
+ fs.unlinkSync(entry_path)
280
272
  }
281
- });
282
- fs.rmdirSync(dir_path);
273
+ })
274
+ fs.rmdirSync(dir_path)
283
275
  }
284
- };
276
+ }
285
277
 
286
278
  /**
287
279
  * Returns absolute filename to save screenshot.
288
280
  * @param fileName {string} - filename.
289
281
  */
290
282
  module.exports.screenshotOutputFolder = function (fileName) {
291
- const fileSep = path.sep;
283
+ const fileSep = path.sep
292
284
 
293
285
  if (!fileName.includes(fileSep) || fileName.includes('record_')) {
294
- return path.resolve(global.output_dir, fileName);
286
+ return path.resolve(global.output_dir, fileName)
295
287
  }
296
- return path.resolve(global.codecept_dir, fileName);
297
- };
288
+ return path.resolve(global.codecept_dir, fileName)
289
+ }
290
+
291
+ module.exports.relativeDir = function (fileName) {
292
+ return fileName.replace(global.codecept_dir, '').replace(/^\//, '')
293
+ }
298
294
 
299
295
  module.exports.beautify = function (code) {
300
- const format = require('js-beautify').js;
301
- return format(code, { indent_size: 2, space_in_empty_paren: true });
302
- };
296
+ const format = require('js-beautify').js
297
+ return format(code, { indent_size: 2, space_in_empty_paren: true })
298
+ }
303
299
 
304
300
  function shouldAppendBaseUrl(url) {
305
- return !/^\w+\:\/\//.test(url);
301
+ return !/^\w+\:\/\//.test(url)
306
302
  }
307
303
 
308
304
  function trimUrl(url) {
309
- const firstChar = url.substr(1);
305
+ const firstChar = url.substr(1)
310
306
  if (firstChar === '/') {
311
- url = url.slice(1);
307
+ url = url.slice(1)
312
308
  }
313
- return url;
309
+ return url
314
310
  }
315
311
 
316
312
  function joinUrl(baseUrl, url) {
317
- return shouldAppendBaseUrl(url) ? `${baseUrl}/${trimUrl(url)}` : url;
313
+ return shouldAppendBaseUrl(url) ? `${baseUrl}/${trimUrl(url)}` : url
318
314
  }
319
315
 
320
316
  module.exports.appendBaseUrl = function (baseUrl = '', oneOrMoreUrls) {
321
317
  if (typeof baseUrl !== 'string') {
322
- throw new Error(`Invalid value for baseUrl: ${baseUrl}`);
318
+ throw new Error(`Invalid value for baseUrl: ${baseUrl}`)
323
319
  }
324
320
  if (!(typeof oneOrMoreUrls === 'string' || Array.isArray(oneOrMoreUrls))) {
325
- throw new Error(`Expected type of Urls is 'string' or 'array', Found '${typeof oneOrMoreUrls}'.`);
321
+ throw new Error(`Expected type of Urls is 'string' or 'array', Found '${typeof oneOrMoreUrls}'.`)
326
322
  }
327
323
  // Remove '/' if it's at the end of baseUrl
328
- const lastChar = baseUrl.substr(-1);
324
+ const lastChar = baseUrl.substr(-1)
329
325
  if (lastChar === '/') {
330
- baseUrl = baseUrl.slice(0, -1);
326
+ baseUrl = baseUrl.slice(0, -1)
331
327
  }
332
328
 
333
329
  if (!Array.isArray(oneOrMoreUrls)) {
334
- return joinUrl(baseUrl, oneOrMoreUrls);
330
+ return joinUrl(baseUrl, oneOrMoreUrls)
335
331
  }
336
- return oneOrMoreUrls.map(url => joinUrl(baseUrl, url));
337
- };
332
+ return oneOrMoreUrls.map(url => joinUrl(baseUrl, url))
333
+ }
338
334
 
339
335
  /**
340
336
  * Recursively search key in object and replace it's value.
@@ -344,56 +340,53 @@ module.exports.appendBaseUrl = function (baseUrl = '', oneOrMoreUrls) {
344
340
  * @param {*} value value to set for key
345
341
  */
346
342
  module.exports.replaceValueDeep = function replaceValueDeep(obj, key, value) {
347
- if (!obj) return;
343
+ if (!obj) return
348
344
 
349
345
  if (obj instanceof Array) {
350
346
  for (const i in obj) {
351
- replaceValueDeep(obj[i], key, value);
347
+ replaceValueDeep(obj[i], key, value)
352
348
  }
353
349
  }
354
350
 
355
351
  if (Object.prototype.hasOwnProperty.call(obj, key)) {
356
- obj[key] = value;
352
+ obj[key] = value
357
353
  }
358
354
 
359
355
  if (typeof obj === 'object' && obj !== null) {
360
- const children = Object.values(obj);
356
+ const children = Object.values(obj)
361
357
  for (const child of children) {
362
- replaceValueDeep(child, key, value);
358
+ replaceValueDeep(child, key, value)
363
359
  }
364
360
  }
365
- return obj;
366
- };
361
+ return obj
362
+ }
367
363
 
368
364
  module.exports.ansiRegExp = function ({ onlyFirst = false } = {}) {
369
- const pattern = [
370
- '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
371
- '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
372
- ].join('|');
365
+ const pattern = ['[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'].join('|')
373
366
 
374
- return new RegExp(pattern, onlyFirst ? undefined : 'g');
375
- };
367
+ return new RegExp(pattern, onlyFirst ? undefined : 'g')
368
+ }
376
369
 
377
370
  module.exports.tryOrDefault = function (fn, defaultValue) {
378
371
  try {
379
- return fn();
372
+ return fn()
380
373
  } catch (_) {
381
- return defaultValue;
374
+ return defaultValue
382
375
  }
383
- };
376
+ }
384
377
 
385
378
  function normalizeKeyReplacer(match, prefix, key, suffix, offset, string) {
386
379
  if (typeof key !== 'string') {
387
- return string;
380
+ return string
388
381
  }
389
- const normalizedKey = key.charAt(0).toUpperCase() + key.substr(1).toLowerCase();
390
- let position = '';
382
+ const normalizedKey = key.charAt(0).toUpperCase() + key.substr(1).toLowerCase()
383
+ let position = ''
391
384
  if (typeof prefix === 'string') {
392
- position = prefix;
385
+ position = prefix
393
386
  } else if (typeof suffix === 'string') {
394
- position = suffix;
387
+ position = suffix
395
388
  }
396
- return normalizedKey + position.charAt(0).toUpperCase() + position.substr(1).toLowerCase();
389
+ return normalizedKey + position.charAt(0).toUpperCase() + position.substr(1).toLowerCase()
397
390
  }
398
391
 
399
392
  /**
@@ -403,76 +396,184 @@ function normalizeKeyReplacer(match, prefix, key, suffix, offset, string) {
403
396
  */
404
397
  module.exports.getNormalizedKeyAttributeValue = function (key) {
405
398
  // Use operation modifier key based on operating system
406
- key = key.replace(/(Ctrl|Control|Cmd|Command)[ _]?Or[ _]?(Ctrl|Control|Cmd|Command)/i, os.platform() === 'darwin' ? 'Meta' : 'Control');
399
+ key = key.replace(/(Ctrl|Control|Cmd|Command)[ _]?Or[ _]?(Ctrl|Control|Cmd|Command)/i, os.platform() === 'darwin' ? 'Meta' : 'Control')
407
400
  // Selection of keys (https://www.w3.org/TR/uievents-key/#named-key-attribute-values)
408
401
  // which can be written in various ways and should be normalized.
409
402
  // For example 'LEFT ALT', 'ALT_Left', 'alt left' or 'LeftAlt' will be normalized as 'AltLeft'.
410
- key = key.replace(/^\s*(?:(Down|Left|Right|Up)[ _]?)?(Arrow|Alt|Ctrl|Control|Cmd|Command|Meta|Option|OS|Page|Shift|Super)(?:[ _]?(Down|Left|Right|Up|Gr(?:aph)?))?\s*$/i, normalizeKeyReplacer);
403
+ key = key.replace(/^\s*(?:(Down|Left|Right|Up)[ _]?)?(Arrow|Alt|Ctrl|Control|Cmd|Command|Meta|Option|OS|Page|Shift|Super)(?:[ _]?(Down|Left|Right|Up|Gr(?:aph)?))?\s*$/i, normalizeKeyReplacer)
411
404
  // Map alias to corresponding key value
412
- key = key.replace(/^(Add|Divide|Decimal|Multiply|Subtract)$/, 'Numpad$1');
413
- key = key.replace(/^AltGr$/, 'AltGraph');
414
- key = key.replace(/^(Cmd|Command|Os|Super)/, 'Meta');
415
- key = key.replace('Ctrl', 'Control');
416
- key = key.replace('Option', 'Alt');
417
- key = key.replace(/^(NumpadComma|Separator)$/, 'Comma');
418
- return key;
419
- };
420
-
421
- const modifierKeys = [
422
- 'Alt', 'AltGraph', 'AltLeft', 'AltRight',
423
- 'Control', 'ControlLeft', 'ControlRight',
424
- 'Meta', 'MetaLeft', 'MetaRight',
425
- 'Shift', 'ShiftLeft', 'ShiftRight',
426
- ];
427
-
428
- module.exports.modifierKeys = modifierKeys;
405
+ key = key.replace(/^(Add|Divide|Decimal|Multiply|Subtract)$/, 'Numpad$1')
406
+ key = key.replace(/^AltGr$/, 'AltGraph')
407
+ key = key.replace(/^(Cmd|Command|Os|Super)/, 'Meta')
408
+ key = key.replace('Ctrl', 'Control')
409
+ key = key.replace('Option', 'Alt')
410
+ key = key.replace(/^(NumpadComma|Separator)$/, 'Comma')
411
+ return key
412
+ }
413
+
414
+ const modifierKeys = ['Alt', 'AltGraph', 'AltLeft', 'AltRight', 'Control', 'ControlLeft', 'ControlRight', 'Meta', 'MetaLeft', 'MetaRight', 'Shift', 'ShiftLeft', 'ShiftRight']
415
+
416
+ module.exports.modifierKeys = modifierKeys
429
417
  module.exports.isModifierKey = function (key) {
430
- return modifierKeys.includes(key);
431
- };
418
+ return modifierKeys.includes(key)
419
+ }
432
420
 
433
421
  module.exports.requireWithFallback = function (...packages) {
434
422
  const exists = function (pkg) {
435
423
  try {
436
- require.resolve(pkg);
424
+ require.resolve(pkg)
437
425
  } catch (e) {
438
- return false;
426
+ return false
439
427
  }
440
428
 
441
- return true;
442
- };
429
+ return true
430
+ }
443
431
 
444
432
  for (const pkg of packages) {
445
433
  if (exists(pkg)) {
446
- return require(pkg);
434
+ return require(pkg)
447
435
  }
448
436
  }
449
437
 
450
- throw new Error(`Cannot find modules ${packages.join(',')}`);
451
- };
438
+ throw new Error(`Cannot find modules ${packages.join(',')}`)
439
+ }
452
440
 
453
441
  module.exports.isNotSet = function (obj) {
454
- if (obj === null) return true;
455
- if (obj === undefined) return true;
456
- return false;
457
- };
442
+ if (obj === null) return true
443
+ if (obj === undefined) return true
444
+ return false
445
+ }
458
446
 
459
- module.exports.emptyFolder = async (directoryPath) => {
460
- require('child_process').execSync(`rm -rf ${directoryPath}/*`);
461
- };
447
+ module.exports.emptyFolder = async directoryPath => {
448
+ require('child_process').execSync(`rm -rf ${directoryPath}/*`)
449
+ }
462
450
 
463
- module.exports.printObjectProperties = (obj) => {
451
+ module.exports.printObjectProperties = obj => {
464
452
  if (typeof obj !== 'object' || obj === null) {
465
- return obj;
453
+ return obj
466
454
  }
467
455
 
468
- let result = '';
456
+ let result = ''
469
457
  for (const [key, value] of Object.entries(obj)) {
470
- result += `${key}: "${value}"; `;
458
+ result += `${key}: "${value}"; `
459
+ }
460
+
461
+ return `{${result}}`
462
+ }
463
+
464
+ module.exports.normalizeSpacesInString = string => {
465
+ return string.replace(/\s+/g, ' ')
466
+ }
467
+
468
+ module.exports.humanizeFunction = function (fn) {
469
+ const fnStr = fn.toString().trim()
470
+ // Remove arrow function syntax, async, and parentheses
471
+ let simplified = fnStr
472
+ .replace(/^async\s*/, '')
473
+ .replace(/^\([^)]*\)\s*=>/, '')
474
+ .replace(/^function\s*\([^)]*\)/, '')
475
+ // Remove curly braces and any whitespace around them
476
+ .replace(/{\s*(.*)\s*}/, '$1')
477
+ // Remove return statement
478
+ .replace(/return\s+/, '')
479
+ // Remove trailing semicolon
480
+ .replace(/;$/, '')
481
+ .trim()
482
+
483
+ if (simplified.length > 100) {
484
+ simplified = simplified.slice(0, 97) + '...'
471
485
  }
472
486
 
473
- return `{${result}}`;
474
- };
487
+ return simplified
488
+ }
475
489
 
476
- module.exports.normalizeSpacesInString = (string) => {
477
- return string.replace(/\s+/g, ' ');
478
- };
490
+ /**
491
+ * Searches through a given data source using the Fuse.js library for fuzzy searching.
492
+ *
493
+ * @function searchWithFusejs
494
+ * @param {Array|Object} source - The data source to search through. This can be an array of objects or strings.
495
+ * @param {string} searchString - The search query string to match against the source.
496
+ * @param {Object} [opts] - Optional configuration object for Fuse.js.
497
+ * @param {boolean} [opts.includeScore=true] - Whether to include the score of the match in the results.
498
+ * @param {number} [opts.threshold=0.6] - Determines the match threshold; lower values mean stricter matching.
499
+ * @param {boolean} [opts.caseSensitive=false] - Whether the search should be case-sensitive.
500
+ * @param {number} [opts.distance=100] - Determines how far apart the search term is allowed to be from the target.
501
+ * @param {number} [opts.maxPatternLength=32] - The maximum length of the search pattern. Patterns longer than this are ignored.
502
+ * @param {boolean} [opts.ignoreLocation=false] - Whether the location of the match is ignored when scoring.
503
+ * @param {boolean} [opts.ignoreFieldNorm=false] - When true, the field's length is not considered when scoring.
504
+ * @param {Array<string>} [opts.keys=[]] - List of keys to search in the objects of the source array.
505
+ * @param {boolean} [opts.shouldSort=true] - Whether the results should be sorted by score.
506
+ * @param {string} [opts.sortFn] - A custom sorting function for sorting results.
507
+ * @param {number} [opts.minMatchCharLength=1] - The minimum number of characters that must match.
508
+ * @param {boolean} [opts.useExtendedSearch=false] - Enables extended search capabilities.
509
+ *
510
+ * @returns {Array<Object>} - An array of search results. Each result contains an item and, if `includeScore` is true, a score.
511
+ *
512
+ * @example
513
+ * const data = [
514
+ * { title: "Old Man's War", author: "John Scalzi" },
515
+ * { title: "The Lock Artist", author: "Steve Hamilton" },
516
+ * ];
517
+ *
518
+ * const options = {
519
+ * keys: ['title', 'author'],
520
+ * includeScore: true,
521
+ * threshold: 0.4,
522
+ * caseSensitive: false,
523
+ * distance: 50,
524
+ * ignoreLocation: true,
525
+ * };
526
+ *
527
+ * const results = searchWithFusejs(data, 'lock', options);
528
+ * console.log(results);
529
+ */
530
+ module.exports.searchWithFusejs = function (source, searchString, opts) {
531
+ const fuse = new Fuse(source, opts)
532
+
533
+ return fuse.search(searchString)
534
+ }
535
+
536
+ module.exports.humanizeString = function (string) {
537
+ // split strings by words, then make them all lowercase
538
+ const _result = string
539
+ .replace(/([a-z](?=[A-Z]))/g, '$1 ')
540
+ .split(' ')
541
+ .map(word => word.toLowerCase())
542
+
543
+ _result[0] = _result[0] === 'i' ? this.ucfirst(_result[0]) : _result[0]
544
+ return _result.join(' ').trim()
545
+ }
546
+
547
+ module.exports.serializeError = function (error) {
548
+ if (error) {
549
+ const { stack, uncaught, message, actual, expected } = error
550
+ return { stack, uncaught, message, actual, expected }
551
+ }
552
+ return null
553
+ }
554
+
555
+ module.exports.base64EncodeFile = function (filePath) {
556
+ return Buffer.from(fs.readFileSync(filePath)).toString('base64')
557
+ }
558
+
559
+ module.exports.markdownToAnsi = function (markdown) {
560
+ return (
561
+ markdown
562
+ // Headers (# Text) - make blue and bold
563
+ .replace(/^(#{1,6})\s+(.+)$/gm, (_, hashes, text) => {
564
+ return chalk.bold.blue(`${hashes} ${text}`)
565
+ })
566
+ // Bullet points - replace with yellow bullet character
567
+ .replace(/^[-*]\s+(.+)$/gm, (_, text) => {
568
+ return `${chalk.yellow('•')} ${text}`
569
+ })
570
+ // Bold (**text**) - make bold
571
+ .replace(/\*\*(.+?)\*\*/g, (_, text) => {
572
+ return chalk.bold(text)
573
+ })
574
+ // Italic (*text*) - make italic (dim in terminals)
575
+ .replace(/\*(.+?)\*/g, (_, text) => {
576
+ return chalk.italic(text)
577
+ })
578
+ )
579
+ }