codeceptjs 4.0.0-beta.4 → 4.0.0-beta.6.esm-aria

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