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
@@ -1,291 +1,181 @@
1
- const tty = require('tty');
1
+ import tty from 'tty'
2
2
 
3
3
  if (!tty.getWindowSize) {
4
4
  // this is really old method, long removed from Node, but Mocha
5
5
  // reporters fall back on it if they cannot use `process.stdout.getWindowSize`
6
6
  // we need to polyfill it.
7
- tty.getWindowSize = () => [40, 80];
7
+ tty.getWindowSize = () => [40, 80]
8
8
  }
9
9
 
10
- const { parentPort, workerData } = require('worker_threads');
11
- const event = require('../../event');
12
- const container = require('../../container');
13
- const { getConfig } = require('../utils');
14
- const { tryOrDefault, deepMerge } = require('../../utils');
10
+ import { parentPort, workerData } from 'worker_threads'
11
+ import event from '../../event.js'
12
+ import container from '../../container.js'
13
+ import { getConfig } from '../utils.js'
14
+ import { tryOrDefault, deepMerge } from '../../utils.js'
15
15
 
16
- let stdout = '';
16
+ let stdout = ''
17
17
 
18
- const stderr = '';
18
+ const stderr = ''
19
19
 
20
- // Requiring of Codecept need to be after tty.getWindowSize is available.
21
- const Codecept = require(process.env.CODECEPT_CLASS_PATH || '../../codecept');
20
+ // Importing of Codecept need to be after tty.getWindowSize is available.
21
+ import Codecept from '../../codecept.js'
22
22
 
23
- const {
24
- options, tests, testRoot, workerIndex,
25
- } = workerData;
23
+ const { options, tests, testRoot, workerIndex } = workerData
26
24
 
27
25
  // hide worker output
28
- if (!options.debug && !options.verbose) process.stdout.write = (string) => { stdout += string; return true; };
26
+ if (!options.debug && !options.verbose)
27
+ process.stdout.write = string => {
28
+ stdout += string
29
+ return true
30
+ }
29
31
 
30
- const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {});
32
+ const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
31
33
 
32
34
  // important deep merge so dynamic things e.g. functions on config are not overridden
33
- const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs);
35
+ const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs)
34
36
 
35
37
  // Load test and run
36
- const codecept = new Codecept(config, options);
37
- codecept.init(testRoot);
38
- codecept.loadTests();
39
- const mocha = container.mocha();
40
- filterTests();
41
-
42
- (async function () {
43
- if (mocha.suite.total()) {
44
- await runTests();
45
- }
46
- }());
47
-
48
- async function runTests() {
49
- try {
50
- await codecept.bootstrap();
51
- } catch (err) {
52
- throw new Error(`Error while running bootstrap file :${err}`);
53
- }
54
- listenToParentThread();
55
- initializeListeners();
56
- disablePause();
57
- try {
58
- await codecept.run();
59
- } finally {
60
- await codecept.teardown();
61
- }
62
- }
63
-
64
- function filterTests() {
65
- const files = codecept.testFiles;
66
- mocha.files = files;
67
- mocha.loadFiles();
68
-
69
- for (const suite of mocha.suite.suites) {
70
- suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0);
71
- }
72
- }
73
-
74
- function initializeListeners() {
75
- function simplifyError(error) {
76
- if (error) {
77
- const {
78
- stack,
79
- uncaught,
80
- message,
81
- actual,
82
- expected,
83
- } = error;
84
-
85
- return {
86
- stack,
87
- uncaught,
88
- message,
89
- actual,
90
- expected,
91
- };
38
+ ;(async function () {
39
+ const codecept = new Codecept(config, options)
40
+ await codecept.init(testRoot)
41
+ codecept.loadTests()
42
+ const mocha = container.mocha()
43
+
44
+ function filterTests() {
45
+ const files = codecept.testFiles
46
+ mocha.files = files
47
+ mocha.loadFiles()
48
+
49
+ for (const suite of mocha.suite.suites) {
50
+ suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0)
92
51
  }
93
-
94
- return null;
95
52
  }
96
- function simplifyTest(test, err = null) {
97
- test = { ...test };
98
53
 
99
- if (test.start && !test.duration) {
100
- const end = new Date();
101
- test.duration = end - test.start;
102
- }
103
-
104
- if (test.err) {
105
- err = simplifyError(test.err);
106
- test.status = 'failed';
107
- } else if (err) {
108
- err = simplifyError(err);
109
- test.status = 'failed';
110
- }
111
- const parent = {};
112
- if (test.parent) {
113
- parent.title = test.parent.title;
54
+ async function runTests() {
55
+ try {
56
+ await codecept.bootstrap()
57
+ } catch (err) {
58
+ throw new Error(`Error while running bootstrap file :${err}`)
114
59
  }
115
-
116
- if (test.opts) {
117
- Object.keys(test.opts).forEach(k => {
118
- if (typeof test.opts[k] === 'object') delete test.opts[k];
119
- if (typeof test.opts[k] === 'function') delete test.opts[k];
120
- });
121
- }
122
-
123
- return {
124
- opts: test.opts || {},
125
- tags: test.tags || [],
126
- uid: test.uid,
127
- workerIndex,
128
- retries: test._retries,
129
- title: test.title,
130
- status: test.status,
131
- duration: test.duration || 0,
132
- err,
133
- parent,
134
- steps: test.steps && test.steps.length > 0 ? simplifyStepsInTestObject(test.steps, err) : [],
135
- };
136
- }
137
-
138
- function simplifyStepsInTestObject(steps, err) {
139
- steps = [...steps];
140
- const _steps = [];
141
-
142
- for (step of steps) {
143
- const _args = [];
144
-
145
- if (step.args) {
146
- for (const arg of step.args) {
147
- // check if arg is a JOI object
148
- if (arg && arg.$_root) {
149
- _args.push(JSON.stringify(arg).slice(0, 300));
150
- // check if arg is a function
151
- } else if (arg && typeof arg === 'function') {
152
- _args.push(arg.name);
153
- } else {
154
- _args.push(arg);
155
- }
156
- }
157
- }
158
-
159
- _steps.push({
160
- actor: step.actor,
161
- name: step.name,
162
- status: step.status,
163
- args: JSON.stringify(_args),
164
- startedAt: step.startedAt,
165
- startTime: step.startTime,
166
- endTime: step.endTime,
167
- finishedAt: step.finishedAt,
168
- duration: step.duration,
169
- err,
170
- });
60
+ listenToParentThread()
61
+ initializeListeners()
62
+ disablePause()
63
+ try {
64
+ await codecept.run()
65
+ } finally {
66
+ await codecept.teardown()
171
67
  }
172
-
173
- return _steps;
174
68
  }
175
69
 
176
- function simplifyStep(step, err = null) {
177
- step = { ...step };
178
-
179
- if (step.startTime && !step.duration) {
180
- const end = new Date();
181
- step.duration = end - step.startTime;
182
- }
183
-
184
- if (step.err) {
185
- err = simplifyError(step.err);
186
- step.status = 'failed';
187
- } else if (err) {
188
- err = simplifyError(err);
189
- step.status = 'failed';
190
- }
191
-
192
- const parent = {};
193
- if (step.metaStep) {
194
- parent.title = step.metaStep.actor;
195
- }
70
+ filterTests()
196
71
 
197
- if (step.opts) {
198
- Object.keys(step.opts).forEach(k => {
199
- if (typeof step.opts[k] === 'object') delete step.opts[k];
200
- if (typeof step.opts[k] === 'function') delete step.opts[k];
201
- });
202
- }
203
-
204
- return {
205
- opts: step.opts || {},
206
- workerIndex,
207
- title: step.name,
208
- status: step.status,
209
- duration: step.duration || 0,
210
- err,
211
- parent,
212
- test: simplifyTest(step.test),
213
- };
72
+ // run tests
73
+ if (mocha.suite.total()) {
74
+ await runTests()
214
75
  }
76
+ })()
215
77
 
216
- collectStats();
78
+ function initializeListeners() {
217
79
  // suite
218
- event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: simplifyTest(suite) }));
219
- event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: simplifyTest(suite) }));
80
+ event.dispatcher.on(event.suite.before, suite => safelySendToParent({ event: event.suite.before, workerIndex, data: suite.simplify() }))
81
+ event.dispatcher.on(event.suite.after, suite => safelySendToParent({ event: event.suite.after, workerIndex, data: suite.simplify() }))
220
82
 
221
83
  // calculate duration
222
- event.dispatcher.on(event.test.started, test => test.start = new Date());
84
+ event.dispatcher.on(event.test.started, test => (test.start = new Date()))
223
85
 
224
86
  // tests
225
- event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: simplifyTest(test) }));
226
- event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: simplifyTest(test) }));
87
+ event.dispatcher.on(event.test.before, test => safelySendToParent({ event: event.test.before, workerIndex, data: test.simplify() }))
88
+ event.dispatcher.on(event.test.after, test => safelySendToParent({ event: event.test.after, workerIndex, data: test.simplify() }))
227
89
  // we should force-send correct errors to prevent race condition
228
- event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test, err) }));
229
- event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test, err) }));
230
- event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test, err) }));
231
- event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: simplifyTest(test) }));
232
- event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: simplifyTest(test) }));
90
+ event.dispatcher.on(event.test.finished, (test, err) => {
91
+ const simplifiedData = test.simplify()
92
+ const serializableErr = serializeError(err)
93
+ safelySendToParent({ event: event.test.finished, workerIndex, data: { ...simplifiedData, err: serializableErr } })
94
+ })
95
+ event.dispatcher.on(event.test.failed, (test, err) => {
96
+ const simplifiedData = test.simplify()
97
+ const serializableErr = serializeError(err)
98
+ safelySendToParent({ event: event.test.failed, workerIndex, data: { ...simplifiedData, err: serializableErr } })
99
+ })
100
+ event.dispatcher.on(event.test.passed, (test, err) => safelySendToParent({ event: event.test.passed, workerIndex, data: { ...test.simplify(), err } }))
101
+ event.dispatcher.on(event.test.started, test => safelySendToParent({ event: event.test.started, workerIndex, data: test.simplify() }))
102
+ event.dispatcher.on(event.test.skipped, test => safelySendToParent({ event: event.test.skipped, workerIndex, data: test.simplify() }))
233
103
 
234
104
  // steps
235
- event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: simplifyStep(step) }));
236
- event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: simplifyStep(step) }));
237
- event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: simplifyStep(step) }));
238
- event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: simplifyStep(step) }));
239
-
240
- event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: simplifyTest(test, err) }));
241
- event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: simplifyTest(test, err) }));
242
- event.dispatcher.on(event.all.failures, (data) => sendToParentThread({ event: event.all.failures, workerIndex, data }));
243
-
105
+ event.dispatcher.on(event.step.finished, step => safelySendToParent({ event: event.step.finished, workerIndex, data: step.simplify() }))
106
+ event.dispatcher.on(event.step.started, step => safelySendToParent({ event: event.step.started, workerIndex, data: step.simplify() }))
107
+ event.dispatcher.on(event.step.passed, step => safelySendToParent({ event: event.step.passed, workerIndex, data: step.simplify() }))
108
+ event.dispatcher.on(event.step.failed, step => safelySendToParent({ event: event.step.failed, workerIndex, data: step.simplify() }))
109
+
110
+ event.dispatcher.on(event.hook.failed, (hook, err) => {
111
+ const serializableErr = serializeError(err)
112
+ safelySendToParent({ event: event.hook.failed, workerIndex, data: { ...hook.simplify(), err: serializableErr } })
113
+ })
114
+ event.dispatcher.on(event.hook.passed, hook => safelySendToParent({ event: event.hook.passed, workerIndex, data: hook.simplify() }))
115
+ event.dispatcher.on(event.hook.finished, hook => safelySendToParent({ event: event.hook.finished, workerIndex, data: hook.simplify() }))
116
+
117
+ event.dispatcher.once(event.all.after, () => safelySendToParent({ event: event.all.after, workerIndex, data: container.result().simplify() }))
244
118
  // all
245
- event.dispatcher.once(event.all.result, () => parentPort.close());
119
+ event.dispatcher.once(event.all.result, () => {
120
+ safelySendToParent({ event: event.all.result, workerIndex, data: container.result().simplify() })
121
+ parentPort?.close()
122
+ })
246
123
  }
247
124
 
248
125
  function disablePause() {
249
- global.pause = () => {};
126
+ global.pause = () => {}
250
127
  }
251
128
 
252
- function collectStats() {
253
- const stats = {
254
- passes: 0,
255
- failures: 0,
256
- skipped: 0,
257
- tests: 0,
258
- pending: 0,
259
- };
260
- event.dispatcher.on(event.test.skipped, () => {
261
- stats.skipped++;
262
- });
263
- event.dispatcher.on(event.test.passed, () => {
264
- stats.passes++;
265
- });
266
- event.dispatcher.on(event.test.failed, (test) => {
267
- if (test.ctx._runnable.title.includes('hook: AfterSuite')) {
268
- stats.failedHooks += 1;
129
+ function serializeError(err) {
130
+ if (!err) return null
131
+ try {
132
+ return {
133
+ message: err.message,
134
+ stack: err.stack,
135
+ name: err.name,
136
+ actual: err.actual,
137
+ expected: err.expected,
269
138
  }
270
- stats.failures++;
271
- });
272
- event.dispatcher.on(event.test.skipped, () => {
273
- stats.pending++;
274
- });
275
- event.dispatcher.on(event.test.finished, () => {
276
- stats.tests++;
277
- });
278
- event.dispatcher.once(event.all.after, () => {
279
- sendToParentThread({ event: event.all.after, data: stats });
280
- });
139
+ } catch {
140
+ return { message: 'Error could not be serialized', name: 'Error' }
141
+ }
142
+ }
143
+
144
+ function safelySendToParent(data) {
145
+ try {
146
+ parentPort?.postMessage(data)
147
+ } catch (cloneError) {
148
+ // Fallback for non-serializable data
149
+ const fallbackData = { ...data }
150
+
151
+ // Try to serialize error objects if present
152
+ if (fallbackData.data && fallbackData.data.err) {
153
+ fallbackData.data.err = serializeError(fallbackData.data.err)
154
+ }
155
+
156
+ // If still fails, send minimal data
157
+ try {
158
+ parentPort?.postMessage(fallbackData)
159
+ } catch (finalError) {
160
+ parentPort?.postMessage({
161
+ event: data.event,
162
+ workerIndex,
163
+ data: {
164
+ title: fallbackData.data?.title || 'Unknown',
165
+ state: fallbackData.data?.state || 'error',
166
+ err: { message: 'Data could not be serialized' },
167
+ },
168
+ })
169
+ }
170
+ }
281
171
  }
282
172
 
283
173
  function sendToParentThread(data) {
284
- parentPort.postMessage(data);
174
+ parentPort?.postMessage(data)
285
175
  }
286
176
 
287
177
  function listenToParentThread() {
288
- parentPort.on('message', (eventData) => {
289
- container.append({ support: eventData.data });
290
- });
178
+ parentPort?.on('message', eventData => {
179
+ container.append({ support: eventData.data })
180
+ })
291
181
  }
package/lib/config.js CHANGED
@@ -1,11 +1,7 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const {
4
- fileExists,
5
- isFile,
6
- deepMerge,
7
- deepClone,
8
- } = require('./utils');
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { createRequire } from 'module'
4
+ import { fileExists, isFile, deepMerge, deepClone } from './utils.js'
9
5
 
10
6
  const defaultConfig = {
11
7
  output: './_output',
@@ -33,18 +29,12 @@ const defaultConfig = {
33
29
  timeout: 0,
34
30
  },
35
31
  ],
36
- };
32
+ }
37
33
 
38
- let hooks = [];
39
- let config = {};
34
+ let hooks = []
35
+ let config = {}
40
36
 
41
- const configFileNames = [
42
- 'codecept.config.js',
43
- 'codecept.conf.js',
44
- 'codecept.js',
45
- 'codecept.config.ts',
46
- 'codecept.conf.ts',
47
- ];
37
+ const configFileNames = ['codecept.config.js', 'codecept.conf.js', 'codecept.js', 'codecept.config.ts', 'codecept.conf.ts']
48
38
 
49
39
  /**
50
40
  * Current configuration
@@ -57,9 +47,9 @@ class Config {
57
47
  * @return {Object<string, *>}
58
48
  */
59
49
  static create(newConfig) {
60
- config = deepMerge(deepClone(defaultConfig), newConfig);
61
- hooks.forEach(f => f(config));
62
- return config;
50
+ config = deepMerge(deepClone(defaultConfig), newConfig)
51
+ hooks.forEach(f => f(config))
52
+ return config
63
53
  }
64
54
 
65
55
  /**
@@ -75,34 +65,34 @@ class Config {
75
65
  * @param {string} configFile
76
66
  * @return {*}
77
67
  */
78
- static load(configFile) {
79
- configFile = path.resolve(configFile || '.');
68
+ static async load(configFile) {
69
+ configFile = path.resolve(configFile || '.')
80
70
 
81
71
  if (!fileExists(configFile)) {
82
- configFile = configFile.replace('.js', '.ts');
72
+ configFile = configFile.replace('.js', '.ts')
83
73
 
84
74
  if (!fileExists(configFile)) {
85
- throw new Error(`Config file ${configFile} does not exist. Execute 'codeceptjs init' to create config`);
75
+ throw new Error(`Config file ${configFile} does not exist. Execute 'codeceptjs init' to create config`)
86
76
  }
87
77
  }
88
78
 
89
79
  // is config file
90
80
  if (isFile(configFile)) {
91
- return loadConfigFile(configFile);
81
+ return await loadConfigFile(configFile)
92
82
  }
93
83
 
94
84
  for (const name of configFileNames) {
95
85
  // is path to directory
96
- const jsConfig = path.join(configFile, name);
86
+ const jsConfig = path.join(configFile, name)
97
87
 
98
88
  if (isFile(jsConfig)) {
99
- return loadConfigFile(jsConfig);
89
+ return await loadConfigFile(jsConfig)
100
90
  }
101
91
  }
102
92
 
103
- const configPaths = configFileNames.map(name => path.join(configFile, name)).join(' or ');
93
+ const configPaths = configFileNames.map(name => path.join(configFile, name)).join(' or ')
104
94
 
105
- throw new Error(`Can not load config from ${configPaths}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`);
95
+ throw new Error(`Can not load config from ${configPaths}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`)
106
96
  }
107
97
 
108
98
  /**
@@ -113,13 +103,13 @@ class Config {
113
103
  */
114
104
  static get(key, val) {
115
105
  if (key) {
116
- return config[key] || val;
106
+ return config[key] || val
117
107
  }
118
- return config;
108
+ return config
119
109
  }
120
110
 
121
111
  static addHook(fn) {
122
- hooks.push(fn);
112
+ hooks.push(fn)
123
113
  }
124
114
 
125
115
  /**
@@ -129,7 +119,7 @@ class Config {
129
119
  * @return {Object<string, *>}
130
120
  */
131
121
  static append(additionalConfig) {
132
- return config = deepMerge(config, additionalConfig);
122
+ return (config = deepMerge(config, additionalConfig))
133
123
  }
134
124
 
135
125
  /**
@@ -137,33 +127,105 @@ class Config {
137
127
  * @return {Object<string, *>}
138
128
  */
139
129
  static reset() {
140
- hooks = [];
141
- return config = { ...defaultConfig };
130
+ hooks = []
131
+ return (config = { ...defaultConfig })
142
132
  }
143
133
  }
144
134
 
145
- module.exports = Config;
135
+ export default Config
146
136
 
147
- function loadConfigFile(configFile) {
148
- const extensionName = path.extname(configFile);
137
+ async function loadConfigFile(configFile) {
138
+ const require = createRequire(import.meta.url)
139
+ const extensionName = path.extname(configFile)
149
140
 
150
- if (extensionName === '.ts') {
141
+ // .conf.js config file
142
+ if (extensionName === '.js' || extensionName === '.ts' || extensionName === '.cjs') {
143
+ let configModule
151
144
  try {
152
- require('ts-node/register');
153
- } catch (err) {
154
- console.log('ts-node package is required to parse codecept.conf.ts config correctly');
145
+ // For .ts files, try to compile and load as JavaScript
146
+ if (extensionName === '.ts') {
147
+ try {
148
+ // Try to load ts-node and compile the file
149
+ const { transpile } = require('typescript')
150
+ const tsContent = fs.readFileSync(configFile, 'utf8')
151
+
152
+ // Transpile TypeScript to JavaScript with ES module output
153
+ const jsContent = transpile(tsContent, {
154
+ module: 99, // ModuleKind.ESNext
155
+ target: 99, // ScriptTarget.ESNext
156
+ esModuleInterop: true,
157
+ allowSyntheticDefaultImports: true,
158
+ })
159
+
160
+ // Create a temporary JS file with .mjs extension to force ES module treatment
161
+ const tempJsFile = configFile.replace('.ts', '.temp.mjs')
162
+ fs.writeFileSync(tempJsFile, jsContent)
163
+
164
+ try {
165
+ configModule = await import(tempJsFile)
166
+ // Clean up temp file
167
+ fs.unlinkSync(tempJsFile)
168
+ } catch (err) {
169
+ // Clean up temp file even on error
170
+ if (fs.existsSync(tempJsFile)) {
171
+ fs.unlinkSync(tempJsFile)
172
+ }
173
+ throw err
174
+ }
175
+ } catch (tsError) {
176
+ // If TypeScript compilation fails, fallback to ts-node
177
+ try {
178
+ require('ts-node/register')
179
+ configModule = require(configFile)
180
+ } catch (tsNodeError) {
181
+ throw new Error(`Failed to load TypeScript config: ${tsError.message}`)
182
+ }
183
+ }
184
+ } else {
185
+ // Try ESM import first for JS files
186
+ configModule = await import(configFile)
187
+ }
188
+ } catch (importError) {
189
+ try {
190
+ // Fall back to CommonJS require for .js/.cjs files
191
+ if (extensionName !== '.ts') {
192
+ configModule = require(configFile)
193
+ } else {
194
+ throw importError
195
+ }
196
+ } catch (requireError) {
197
+ throw new Error(`Failed to load config file ${configFile}: ${importError.message}`)
198
+ }
155
199
  }
156
- }
157
200
 
158
- // .conf.js config file
159
- if (extensionName === '.js' || extensionName === '.ts' || extensionName === '.cjs') {
160
- return Config.create(require(configFile).config);
201
+ const rawConfig = configModule.config || configModule.default?.config || configModule
202
+
203
+ // Process helpers to extract imported classes
204
+ if (rawConfig.helpers) {
205
+ const processedHelpers = {}
206
+ for (const [helperName, helperConfig] of Object.entries(rawConfig.helpers)) {
207
+ // Check if the helper name itself is a class (ESM import)
208
+ if (typeof helperName === 'function' && helperName.prototype) {
209
+ // This is an imported class, use its constructor name
210
+ const className = helperName.name
211
+ processedHelpers[className] = {
212
+ ...helperConfig,
213
+ _helperClass: helperName,
214
+ }
215
+ } else {
216
+ processedHelpers[helperName] = helperConfig
217
+ }
218
+ }
219
+ rawConfig.helpers = processedHelpers
220
+ }
221
+
222
+ return Config.create(rawConfig)
161
223
  }
162
224
 
163
225
  // json config provided
164
226
  if (extensionName === '.json') {
165
- return Config.create(JSON.parse(fs.readFileSync(configFile, 'utf8')));
227
+ return Config.create(JSON.parse(fs.readFileSync(configFile, 'utf8')))
166
228
  }
167
229
 
168
- throw new Error(`Config file ${configFile} can't be loaded`);
230
+ throw new Error(`Config file ${configFile} can't be loaded`)
169
231
  }