codeceptjs 4.0.0-beta.2 → 4.0.0-beta.21

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 (209) hide show
  1. package/README.md +133 -120
  2. package/bin/codecept.js +107 -96
  3. package/bin/test-server.js +64 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/docs/webapi/click.mustache +5 -1
  6. package/lib/actor.js +73 -103
  7. package/lib/ai.js +159 -188
  8. package/lib/assert/empty.js +22 -24
  9. package/lib/assert/equal.js +30 -37
  10. package/lib/assert/error.js +14 -14
  11. package/lib/assert/include.js +43 -48
  12. package/lib/assert/throws.js +11 -11
  13. package/lib/assert/truth.js +22 -22
  14. package/lib/assert.js +20 -18
  15. package/lib/codecept.js +262 -162
  16. package/lib/colorUtils.js +50 -52
  17. package/lib/command/check.js +206 -0
  18. package/lib/command/configMigrate.js +56 -51
  19. package/lib/command/definitions.js +96 -109
  20. package/lib/command/dryRun.js +77 -79
  21. package/lib/command/generate.js +234 -194
  22. package/lib/command/gherkin/init.js +42 -33
  23. package/lib/command/gherkin/snippets.js +76 -74
  24. package/lib/command/gherkin/steps.js +20 -17
  25. package/lib/command/info.js +74 -38
  26. package/lib/command/init.js +301 -290
  27. package/lib/command/interactive.js +41 -32
  28. package/lib/command/list.js +28 -27
  29. package/lib/command/run-multiple/chunk.js +51 -48
  30. package/lib/command/run-multiple/collection.js +5 -5
  31. package/lib/command/run-multiple/run.js +5 -1
  32. package/lib/command/run-multiple.js +97 -97
  33. package/lib/command/run-rerun.js +19 -25
  34. package/lib/command/run-workers.js +68 -92
  35. package/lib/command/run.js +39 -27
  36. package/lib/command/utils.js +80 -64
  37. package/lib/command/workers/runTests.js +388 -226
  38. package/lib/config.js +109 -50
  39. package/lib/container.js +765 -261
  40. package/lib/data/context.js +60 -61
  41. package/lib/data/dataScenarioConfig.js +47 -47
  42. package/lib/data/dataTableArgument.js +32 -32
  43. package/lib/data/table.js +22 -22
  44. package/lib/effects.js +307 -0
  45. package/lib/element/WebElement.js +327 -0
  46. package/lib/els.js +160 -0
  47. package/lib/event.js +173 -163
  48. package/lib/globals.js +141 -0
  49. package/lib/heal.js +89 -85
  50. package/lib/helper/AI.js +131 -41
  51. package/lib/helper/ApiDataFactory.js +107 -75
  52. package/lib/helper/Appium.js +542 -404
  53. package/lib/helper/FileSystem.js +100 -79
  54. package/lib/helper/GraphQL.js +44 -43
  55. package/lib/helper/GraphQLDataFactory.js +52 -52
  56. package/lib/helper/JSONResponse.js +126 -88
  57. package/lib/helper/Mochawesome.js +54 -29
  58. package/lib/helper/Playwright.js +2547 -1316
  59. package/lib/helper/Puppeteer.js +1578 -1181
  60. package/lib/helper/REST.js +209 -68
  61. package/lib/helper/WebDriver.js +1482 -1342
  62. package/lib/helper/errors/ConnectionRefused.js +6 -6
  63. package/lib/helper/errors/ElementAssertion.js +11 -16
  64. package/lib/helper/errors/ElementNotFound.js +5 -9
  65. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  66. package/lib/helper/extras/Console.js +11 -11
  67. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  68. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
  71. package/lib/helper/extras/Popup.js +22 -22
  72. package/lib/helper/extras/React.js +27 -28
  73. package/lib/helper/network/actions.js +36 -42
  74. package/lib/helper/network/utils.js +78 -84
  75. package/lib/helper/scripts/blurElement.js +5 -5
  76. package/lib/helper/scripts/focusElement.js +5 -5
  77. package/lib/helper/scripts/highlightElement.js +8 -8
  78. package/lib/helper/scripts/isElementClickable.js +34 -34
  79. package/lib/helper.js +2 -3
  80. package/lib/history.js +23 -19
  81. package/lib/hooks.js +8 -8
  82. package/lib/html.js +94 -104
  83. package/lib/index.js +38 -27
  84. package/lib/listener/config.js +30 -23
  85. package/lib/listener/emptyRun.js +54 -0
  86. package/lib/listener/enhancedGlobalRetry.js +110 -0
  87. package/lib/listener/exit.js +16 -18
  88. package/lib/listener/globalRetry.js +70 -0
  89. package/lib/listener/globalTimeout.js +181 -0
  90. package/lib/listener/helpers.js +76 -51
  91. package/lib/listener/mocha.js +10 -11
  92. package/lib/listener/result.js +11 -0
  93. package/lib/listener/retryEnhancer.js +85 -0
  94. package/lib/listener/steps.js +71 -59
  95. package/lib/listener/store.js +20 -0
  96. package/lib/locator.js +214 -197
  97. package/lib/mocha/asyncWrapper.js +274 -0
  98. package/lib/mocha/bdd.js +167 -0
  99. package/lib/mocha/cli.js +341 -0
  100. package/lib/mocha/factory.js +163 -0
  101. package/lib/mocha/featureConfig.js +89 -0
  102. package/lib/mocha/gherkin.js +231 -0
  103. package/lib/mocha/hooks.js +121 -0
  104. package/lib/mocha/index.js +21 -0
  105. package/lib/mocha/inject.js +46 -0
  106. package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
  107. package/lib/mocha/suite.js +89 -0
  108. package/lib/mocha/test.js +184 -0
  109. package/lib/mocha/types.d.ts +42 -0
  110. package/lib/mocha/ui.js +242 -0
  111. package/lib/output.js +141 -71
  112. package/lib/parser.js +54 -44
  113. package/lib/pause.js +173 -145
  114. package/lib/plugin/analyze.js +403 -0
  115. package/lib/plugin/{autoLogin.js → auth.js} +178 -79
  116. package/lib/plugin/autoDelay.js +36 -40
  117. package/lib/plugin/coverage.js +131 -78
  118. package/lib/plugin/customLocator.js +22 -21
  119. package/lib/plugin/customReporter.js +53 -0
  120. package/lib/plugin/enhancedRetryFailedStep.js +99 -0
  121. package/lib/plugin/heal.js +101 -110
  122. package/lib/plugin/htmlReporter.js +3648 -0
  123. package/lib/plugin/pageInfo.js +140 -0
  124. package/lib/plugin/pauseOnFail.js +12 -11
  125. package/lib/plugin/retryFailedStep.js +82 -47
  126. package/lib/plugin/screenshotOnFail.js +111 -92
  127. package/lib/plugin/stepByStepReport.js +159 -101
  128. package/lib/plugin/stepTimeout.js +20 -25
  129. package/lib/plugin/subtitles.js +38 -38
  130. package/lib/recorder.js +193 -130
  131. package/lib/rerun.js +94 -49
  132. package/lib/result.js +238 -0
  133. package/lib/retryCoordinator.js +207 -0
  134. package/lib/secret.js +20 -18
  135. package/lib/session.js +95 -89
  136. package/lib/step/base.js +239 -0
  137. package/lib/step/comment.js +10 -0
  138. package/lib/step/config.js +50 -0
  139. package/lib/step/func.js +46 -0
  140. package/lib/step/helper.js +50 -0
  141. package/lib/step/meta.js +99 -0
  142. package/lib/step/record.js +74 -0
  143. package/lib/step/retry.js +11 -0
  144. package/lib/step/section.js +55 -0
  145. package/lib/step.js +18 -329
  146. package/lib/steps.js +54 -0
  147. package/lib/store.js +38 -7
  148. package/lib/template/heal.js +3 -12
  149. package/lib/template/prompts/generatePageObject.js +31 -0
  150. package/lib/template/prompts/healStep.js +13 -0
  151. package/lib/template/prompts/writeStep.js +9 -0
  152. package/lib/test-server.js +334 -0
  153. package/lib/timeout.js +60 -0
  154. package/lib/transform.js +8 -8
  155. package/lib/translation.js +34 -21
  156. package/lib/utils/loaderCheck.js +124 -0
  157. package/lib/utils/mask_data.js +47 -0
  158. package/lib/utils/typescript.js +237 -0
  159. package/lib/utils.js +411 -228
  160. package/lib/workerStorage.js +37 -34
  161. package/lib/workers.js +532 -296
  162. package/package.json +124 -95
  163. package/translations/de-DE.js +5 -3
  164. package/translations/fr-FR.js +5 -4
  165. package/translations/index.js +22 -12
  166. package/translations/it-IT.js +4 -3
  167. package/translations/ja-JP.js +4 -3
  168. package/translations/nl-NL.js +76 -0
  169. package/translations/pl-PL.js +4 -3
  170. package/translations/pt-BR.js +4 -3
  171. package/translations/ru-RU.js +4 -3
  172. package/translations/utils.js +10 -0
  173. package/translations/zh-CN.js +4 -3
  174. package/translations/zh-TW.js +4 -3
  175. package/typings/index.d.ts +546 -185
  176. package/typings/promiseBasedTypes.d.ts +150 -875
  177. package/typings/types.d.ts +547 -992
  178. package/lib/cli.js +0 -249
  179. package/lib/dirname.js +0 -5
  180. package/lib/helper/Expect.js +0 -425
  181. package/lib/helper/ExpectHelper.js +0 -399
  182. package/lib/helper/MockServer.js +0 -223
  183. package/lib/helper/Nightmare.js +0 -1411
  184. package/lib/helper/Protractor.js +0 -1835
  185. package/lib/helper/SoftExpectHelper.js +0 -381
  186. package/lib/helper/TestCafe.js +0 -1410
  187. package/lib/helper/clientscripts/nightmare.js +0 -213
  188. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  189. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  190. package/lib/interfaces/bdd.js +0 -98
  191. package/lib/interfaces/featureConfig.js +0 -69
  192. package/lib/interfaces/gherkin.js +0 -195
  193. package/lib/listener/artifacts.js +0 -19
  194. package/lib/listener/retry.js +0 -68
  195. package/lib/listener/timeout.js +0 -109
  196. package/lib/mochaFactory.js +0 -110
  197. package/lib/plugin/allure.js +0 -15
  198. package/lib/plugin/commentStep.js +0 -136
  199. package/lib/plugin/debugErrors.js +0 -67
  200. package/lib/plugin/eachElement.js +0 -127
  201. package/lib/plugin/fakerTransform.js +0 -49
  202. package/lib/plugin/retryTo.js +0 -121
  203. package/lib/plugin/selenoid.js +0 -371
  204. package/lib/plugin/standardActingHelpers.js +0 -9
  205. package/lib/plugin/tryTo.js +0 -105
  206. package/lib/plugin/wdio.js +0 -246
  207. package/lib/scenario.js +0 -222
  208. package/lib/ui.js +0 -238
  209. package/lib/within.js +0 -70
package/lib/recorder.js CHANGED
@@ -1,28 +1,29 @@
1
- import debug from 'debug';
2
- import promiseRetry from 'promise-retry';
3
- import chalk from 'chalk';
4
- import { printObjectProperties } from './utils.js';
5
- import * as output from './output.js';
6
-
7
- debug('codeceptjs:recorder');
8
- const MAX_TASKS = 100;
9
-
10
- let promise;
11
- let running = false;
12
- let errFn;
13
- let queueId = 0;
14
- let sessionId = null;
15
- let asyncErr = null;
16
- let ignoredErrs = [];
17
-
18
- let tasks = [];
19
- let oldPromises = [];
1
+ import debugModule from 'debug'
2
+ const debug = debugModule('codeceptjs:recorder')
3
+ import promiseRetry from 'promise-retry'
4
+ import chalk from 'chalk'
5
+ import { printObjectProperties } from './utils.js'
6
+ import output from './output.js'
7
+ import { TimeoutError } from './timeout.js'
8
+ const MAX_TASKS = 100
9
+
10
+ let promise
11
+ let running = false
12
+ let errFn
13
+ let queueId = 0
14
+ let sessionId = null
15
+ let sessionStack = [] // Stack to support nested sessions
16
+ let asyncErr = null
17
+ let ignoredErrs = []
18
+
19
+ let tasks = []
20
+ let oldPromises = []
20
21
 
21
22
  const defaultRetryOptions = {
22
23
  retries: 0,
23
24
  minTimeout: 150,
24
25
  maxTimeout: 10000,
25
- };
26
+ }
26
27
 
27
28
  /**
28
29
  * Singleton object to record all test steps as promises and run them in chain.
@@ -30,7 +31,6 @@ const defaultRetryOptions = {
30
31
  * @interface
31
32
  */
32
33
  export default {
33
-
34
34
  /**
35
35
  * @type {Array<Object<string, *>>}
36
36
  * @inner
@@ -44,12 +44,11 @@ export default {
44
44
  * @inner
45
45
  */
46
46
  start() {
47
- debug('Starting recording promises');
48
- running = true;
49
- asyncErr = null;
50
- errFn = null;
51
- // @ts-ignore
52
- this.reset();
47
+ debug('Starting recording promises')
48
+ running = true
49
+ asyncErr = null
50
+ errFn = null
51
+ this.reset()
53
52
  },
54
53
 
55
54
  /**
@@ -57,7 +56,7 @@ export default {
57
56
  * @inner
58
57
  */
59
58
  isRunning() {
60
- return running;
59
+ return running
61
60
  },
62
61
 
63
62
  /**
@@ -66,7 +65,7 @@ export default {
66
65
  */
67
66
  startUnlessRunning() {
68
67
  if (!this.isRunning()) {
69
- this.start();
68
+ this.start()
70
69
  }
71
70
  },
72
71
 
@@ -78,7 +77,7 @@ export default {
78
77
  * @inner
79
78
  */
80
79
  errHandler(fn) {
81
- errFn = fn;
80
+ errFn = fn
82
81
  },
83
82
 
84
83
  /**
@@ -89,16 +88,17 @@ export default {
89
88
  * @inner
90
89
  */
91
90
  reset() {
92
- if (promise && running) this.catch();
93
- queueId++;
94
- sessionId = null;
95
- asyncErr = null;
96
- output.output.log(`${currentQueue()} Starting recording promises`);
97
- promise = Promise.resolve();
98
- oldPromises = [];
99
- tasks = [];
100
- ignoredErrs = [];
101
- this.session.running = false;
91
+ if (promise && running) this.catch()
92
+ queueId++
93
+ sessionId = null
94
+ sessionStack = [] // Clear the session stack
95
+ asyncErr = null
96
+ output.log(`${currentQueue()} Starting recording promises`)
97
+ promise = Promise.resolve()
98
+ oldPromises = []
99
+ tasks = []
100
+ ignoredErrs = []
101
+ this.session.running = false
102
102
  // reset this retries makes the retryFailedStep plugin won't work if there is Before/BeforeSuit block due to retries is undefined on Scenario
103
103
  // this.retries = [];
104
104
  },
@@ -125,12 +125,21 @@ export default {
125
125
  * @inner
126
126
  */
127
127
  start(name) {
128
- debug(`${currentQueue()}Starting <${name}> session`);
129
- tasks.push('--->');
130
- oldPromises.push(promise);
131
- this.running = true;
132
- sessionId = name;
133
- promise = Promise.resolve();
128
+ if (sessionId) {
129
+ debug(`${currentQueue()}Session already started as ${sessionId}, nesting session ${name}`)
130
+ // Push current session to stack instead of restoring it
131
+ sessionStack.push({
132
+ id: sessionId,
133
+ promise: promise,
134
+ running: this.running,
135
+ })
136
+ }
137
+ debug(`${currentQueue()}Starting <${name}> session`)
138
+ tasks.push('--->')
139
+ oldPromises.push(promise)
140
+ this.running = true
141
+ sessionId = name
142
+ promise = Promise.resolve()
134
143
  },
135
144
 
136
145
  /**
@@ -138,12 +147,21 @@ export default {
138
147
  * @inner
139
148
  */
140
149
  restore(name) {
141
- tasks.push('<---');
142
- debug(`${currentQueue()}Finalize <${name}> session`);
143
- this.running = false;
144
- sessionId = null;
145
- this.catch(errFn);
146
- promise = promise.then(() => oldPromises.pop());
150
+ tasks.push('<---')
151
+ debug(`${currentQueue()}Finalize <${name}> session`)
152
+ this.running = false
153
+ this.catch(errFn)
154
+ promise = promise.then(() => oldPromises.pop())
155
+
156
+ // Restore parent session from stack if available
157
+ if (sessionStack.length > 0) {
158
+ const parentSession = sessionStack.pop()
159
+ sessionId = parentSession.id
160
+ this.running = parentSession.running
161
+ debug(`${currentQueue()}Restored parent session <${sessionId}>`)
162
+ } else {
163
+ sessionId = null
164
+ }
147
165
  },
148
166
 
149
167
  /**
@@ -151,9 +169,8 @@ export default {
151
169
  * @inner
152
170
  */
153
171
  catch(fn) {
154
- promise = promise.catch(fn);
172
+ promise = promise.catch(fn)
155
173
  },
156
-
157
174
  },
158
175
 
159
176
  /**
@@ -165,50 +182,56 @@ export default {
165
182
  * @param {boolean} [force=false]
166
183
  * @param {boolean} [retry]
167
184
  * undefined: `add(fn)` -> `false` and `add('step',fn)` -> `true`
168
- * true: it will retry if `retryOpts` set.
185
+ * true: it will retries if `retryOpts` set.
169
186
  * false: ignore `retryOpts` and won't retry.
170
187
  * @param {number} [timeout]
171
- * @return {Promise<*>|undefined}
188
+ * @return {Promise<*>}
172
189
  * @inner
173
190
  */
174
191
  add(taskName, fn = undefined, force = false, retry = undefined, timeout = undefined) {
175
192
  if (typeof taskName === 'function') {
176
- fn = taskName;
177
- taskName = fn.toString();
178
- if (retry === undefined) retry = false;
193
+ fn = taskName
194
+ taskName = fn.toString()
195
+ if (retry === undefined) retry = false
179
196
  }
180
- if (retry === undefined) retry = true;
197
+ if (retry === undefined) retry = true
181
198
  if (!running && !force) {
182
- return;
199
+ return Promise.resolve()
183
200
  }
184
- tasks.push(taskName);
185
- debug(chalk.gray(`${currentQueue()} Queued | ${taskName}`));
201
+ tasks.push(taskName)
202
+ debug(chalk.gray(`${currentQueue()} Queued | ${taskName}`))
186
203
 
187
- return promise = Promise.resolve(promise).then((res) => {
204
+ return (promise = Promise.resolve(promise).then(res => {
188
205
  // prefer options for non-conditional retries
189
- const retryOpts = this.retries.sort((r1, r2) => r1.when && !r2.when).slice(-1).pop();
206
+ const retryOpts = this.retries
207
+ .sort((r1, r2) => r1.when && !r2.when)
208
+ .slice(-1)
209
+ .pop()
190
210
  // no retries or unnamed tasks
211
+ debug(`${currentQueue()} Running | ${taskName} | Timeout: ${timeout || 'None'}`)
212
+ if (retryOpts) debug(`${currentQueue()} Retry opts`, JSON.stringify(retryOpts))
213
+
191
214
  if (!retryOpts || !taskName || !retry) {
192
- const [promise, timer] = getTimeoutPromise(timeout, taskName);
193
- return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer));
215
+ const [promise, timer] = getTimeoutPromise(timeout, taskName)
216
+ return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer))
194
217
  }
195
218
 
196
- debug(`${currentQueue()} Running | ${taskName}`);
197
-
198
- const retryRules = this.retries.slice().reverse();
219
+ const retryRules = this.retries.slice().reverse()
199
220
  return promiseRetry(Object.assign(defaultRetryOptions, retryOpts), (retry, number) => {
200
- if (number > 1) output.output.log(`${currentQueue()}Retrying... Attempt #${number}`);
201
- const [promise, timer] = getTimeoutPromise(timeout, taskName);
202
- return Promise.race([promise, Promise.resolve(res).then(fn)]).finally(() => clearTimeout(timer)).catch((err) => {
203
- if (ignoredErrs.includes(err)) return;
204
- for (const retryObj of retryRules) {
205
- if (!retryObj.when) return retry(err);
206
- if (retryObj.when && retryObj.when(err)) return retry(err);
207
- }
208
- throw err;
209
- });
210
- });
211
- });
221
+ if (number > 1) output.log(`${currentQueue()}Retrying... Attempt #${number}`)
222
+ const [promise, timer] = getTimeoutPromise(timeout, taskName)
223
+ return Promise.race([promise, Promise.resolve(res).then(fn)])
224
+ .finally(() => clearTimeout(timer))
225
+ .catch(err => {
226
+ if (ignoredErrs.includes(err)) return
227
+ for (const retryObj of retryRules) {
228
+ if (!retryObj.when) return retry(err)
229
+ if (retryObj.when && retryObj.when(err)) return retry(err)
230
+ }
231
+ throw err
232
+ })
233
+ })
234
+ }))
212
235
  },
213
236
 
214
237
  /**
@@ -217,15 +240,17 @@ export default {
217
240
  * @inner
218
241
  */
219
242
  retry(opts) {
220
- if (!promise) return;
243
+ if (!promise) return
221
244
 
222
245
  if (opts === null) {
223
- opts = {};
246
+ opts = {}
224
247
  }
225
248
  if (Number.isInteger(opts)) {
226
- opts = { retries: opts };
249
+ opts = { retries: opts }
227
250
  }
228
- return this.add(() => this.retries.push(opts));
251
+ // Push retry options immediately so first step can see them
252
+ this.retries.push(opts)
253
+ return Promise.resolve()
229
254
  },
230
255
 
231
256
  /**
@@ -234,20 +259,26 @@ export default {
234
259
  * @inner
235
260
  */
236
261
  catch(customErrFn) {
237
- const fnDescription = customErrFn?.toString()?.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ')?.slice(0, 50);
238
- debug(chalk.gray(`${currentQueue()} Queued | catch with error handler ${fnDescription || ''}`));
239
- return promise = promise.catch((err) => {
240
- output.output.log(`${currentQueue()}Error | ${err} ${fnDescription}...`);
241
- if (!(err instanceof Error)) { // strange things may happen
242
- err = new Error(`[Wrapped Error] ${printObjectProperties(err)}`); // we should be prepared for them
262
+ const fnDescription = customErrFn
263
+ ?.toString()
264
+ ?.replace(/\s{2,}/g, ' ')
265
+ .replace(/\n/g, ' ')
266
+ ?.slice(0, 50)
267
+ debug(chalk.gray(`${currentQueue()} Queued | catch with error handler ${fnDescription || ''}`))
268
+ if (!promise) promise = Promise.resolve()
269
+ return (promise = promise.catch(err => {
270
+ output.log(`${currentQueue()}Error | ${err} ${fnDescription}...`)
271
+ if (!(err instanceof Error)) {
272
+ // strange things may happen
273
+ err = new Error(`[Wrapped Error] ${printObjectProperties(err)}`) // we should be prepared for them
243
274
  }
244
275
  if (customErrFn) {
245
- customErrFn(err);
276
+ customErrFn(err)
246
277
  } else if (errFn) {
247
- errFn(err);
278
+ errFn(err)
248
279
  }
249
- this.stop();
250
- });
280
+ this.stop()
281
+ }))
251
282
  },
252
283
 
253
284
  /**
@@ -256,17 +287,37 @@ export default {
256
287
  * @inner
257
288
  */
258
289
  catchWithoutStop(customErrFn) {
259
- const fnDescription = customErrFn?.toString()?.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ')?.slice(0, 50);
260
- return promise = promise.catch((err) => {
261
- if (ignoredErrs.includes(err)) return; // already caught
262
- output.output.log(`${currentQueue()}Error (Non-Terminated) | ${err} | ${fnDescription || ''}...`);
263
- if (!(err instanceof Error)) { // strange things may happen
264
- err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`); // we should be prepared for them
290
+ const fnDescription = customErrFn
291
+ ?.toString()
292
+ ?.replace(/\s{2,}/g, ' ')
293
+ .replace(/\n/g, ' ')
294
+ ?.slice(0, 50)
295
+ if (!promise) promise = Promise.resolve()
296
+ return (promise = promise.catch(err => {
297
+ if (ignoredErrs.includes(err)) return // already caught
298
+
299
+ // Handle terminal errors - don't continue, re-throw immediately
300
+ if (err && (err.isTerminal || (err.message && (err.message.includes('ERR_ABORTED') || err.message.includes('frame was detached') || err.message.includes('Target page, context or browser has been closed'))))) {
301
+ output.log(`${currentQueue()} Terminal Error | ${err} | ${fnDescription || ''}...`)
302
+ throw err // Re-throw terminal errors immediately
303
+ }
304
+
305
+ output.log(`${currentQueue()} Error (Non-Terminated) | ${err} | ${fnDescription || ''}...`)
306
+ if (!(err instanceof Error)) {
307
+ // strange things may happen
308
+ err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`) // we should be prepared for them
265
309
  }
266
310
  if (customErrFn) {
267
- return customErrFn(err);
311
+ try {
312
+ const result = customErrFn(err)
313
+ // If customErrFn returns a value (not throwing), treat it as handled
314
+ return result
315
+ } catch (thrownErr) {
316
+ // If customErrFn throws an error, propagate it up
317
+ throw thrownErr
318
+ }
268
319
  }
269
- });
320
+ }))
270
321
  },
271
322
 
272
323
  /**
@@ -278,15 +329,15 @@ export default {
278
329
  */
279
330
 
280
331
  throw(err) {
281
- if (ignoredErrs.includes(err)) return promise; // already caught
332
+ if (ignoredErrs.includes(err)) return promise // already caught
282
333
  return this.add(`throw error: ${err.message}`, () => {
283
- if (ignoredErrs.includes(err)) return; // already caught
284
- throw err;
285
- });
334
+ if (ignoredErrs.includes(err)) return // already caught
335
+ throw err
336
+ })
286
337
  },
287
338
 
288
339
  ignoreErr(err) {
289
- ignoredErrs.push(err);
340
+ ignoredErrs.push(err)
290
341
  },
291
342
 
292
343
  /**
@@ -295,7 +346,7 @@ export default {
295
346
  */
296
347
  saveFirstAsyncError(err) {
297
348
  if (asyncErr === null) {
298
- asyncErr = err;
349
+ asyncErr = err
299
350
  }
300
351
  },
301
352
 
@@ -304,7 +355,7 @@ export default {
304
355
  * @inner
305
356
  */
306
357
  getAsyncErr() {
307
- return asyncErr;
358
+ return asyncErr
308
359
  },
309
360
 
310
361
  /**
@@ -312,7 +363,7 @@ export default {
312
363
  * @inner
313
364
  */
314
365
  cleanAsyncErr() {
315
- asyncErr = null;
366
+ asyncErr = null
316
367
  },
317
368
 
318
369
  /**
@@ -321,11 +372,9 @@ export default {
321
372
  * @inner
322
373
  */
323
374
  stop() {
324
- if (process.env.DEBUG) debug(this.toString());
325
- output.output.log(`${currentQueue()}Stopping recording promises`);
326
- debug(this.toString());
327
-
328
- running = false;
375
+ debug(this.toString())
376
+ output.log(`${currentQueue()} Stopping recording promises`)
377
+ running = false
329
378
  },
330
379
 
331
380
  /**
@@ -336,7 +385,7 @@ export default {
336
385
  * @inner
337
386
  */
338
387
  promise() {
339
- return promise;
388
+ return promise
340
389
  },
341
390
 
342
391
  /**
@@ -345,7 +394,7 @@ export default {
345
394
  * @inner
346
395
  */
347
396
  scheduled() {
348
- return tasks.slice(-MAX_TASKS).join('\n');
397
+ return tasks.slice(-MAX_TASKS).join('\n')
349
398
  },
350
399
 
351
400
  /**
@@ -354,7 +403,7 @@ export default {
354
403
  * @inner
355
404
  */
356
405
  getQueueId() {
357
- return queueId;
406
+ return queueId
358
407
  },
359
408
 
360
409
  /**
@@ -363,20 +412,34 @@ export default {
363
412
  * @inner
364
413
  */
365
414
  toString() {
366
- return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`;
415
+ return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`
416
+ },
417
+
418
+ /**
419
+ * Get current session ID
420
+ * @return {string|null}
421
+ * @inner
422
+ */
423
+ getCurrentSessionId() {
424
+ return sessionId
367
425
  },
368
- };
426
+ }
369
427
 
370
428
  function getTimeoutPromise(timeoutMs, taskName) {
371
- let timer;
372
- if (timeoutMs) debug(`Timing out in ${timeoutMs}ms`);
373
- return [new Promise((done, reject) => {
374
- timer = setTimeout(() => { reject(new Error(`Action ${taskName} was interrupted on step timeout ${timeoutMs}ms`)); }, timeoutMs || 2e9);
375
- }), timer];
429
+ let timer
430
+ if (timeoutMs) debug(`Timing out in ${timeoutMs}ms`)
431
+ return [
432
+ new Promise((done, reject) => {
433
+ timer = setTimeout(() => {
434
+ reject(new TimeoutError(`Action ${taskName} was interrupted on timeout ${timeoutMs}ms`))
435
+ }, timeoutMs || 2e9)
436
+ }),
437
+ timer,
438
+ ]
376
439
  }
377
440
 
378
441
  function currentQueue() {
379
- let session = '';
380
- if (sessionId) session = `<${sessionId}> `;
381
- return `[${queueId}] ${session}`;
442
+ let session = ''
443
+ if (sessionId) session = `<${sessionId}> `
444
+ return `[${queueId}] ${session}`
382
445
  }