codeceptjs 4.0.0-beta.5 → 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 (179) hide show
  1. package/README.md +0 -45
  2. package/bin/codecept.js +46 -57
  3. package/lib/actor.js +15 -11
  4. package/lib/ai.js +6 -5
  5. package/lib/assert/empty.js +9 -8
  6. package/lib/assert/equal.js +15 -17
  7. package/lib/assert/error.js +2 -2
  8. package/lib/assert/include.js +9 -11
  9. package/lib/assert/throws.js +1 -1
  10. package/lib/assert/truth.js +8 -5
  11. package/lib/assert.js +18 -18
  12. package/lib/codecept.js +66 -107
  13. package/lib/colorUtils.js +48 -50
  14. package/lib/command/check.js +32 -27
  15. package/lib/command/configMigrate.js +11 -10
  16. package/lib/command/definitions.js +16 -10
  17. package/lib/command/dryRun.js +16 -16
  18. package/lib/command/generate.js +29 -26
  19. package/lib/command/gherkin/init.js +36 -38
  20. package/lib/command/gherkin/snippets.js +14 -14
  21. package/lib/command/gherkin/steps.js +21 -18
  22. package/lib/command/info.js +8 -8
  23. package/lib/command/init.js +34 -31
  24. package/lib/command/interactive.js +11 -10
  25. package/lib/command/list.js +10 -9
  26. package/lib/command/run-multiple/chunk.js +5 -5
  27. package/lib/command/run-multiple/collection.js +5 -5
  28. package/lib/command/run-multiple/run.js +3 -3
  29. package/lib/command/run-multiple.js +16 -13
  30. package/lib/command/run-rerun.js +6 -7
  31. package/lib/command/run-workers.js +10 -24
  32. package/lib/command/run.js +8 -8
  33. package/lib/command/utils.js +20 -18
  34. package/lib/command/workers/runTests.js +117 -269
  35. package/lib/config.js +111 -49
  36. package/lib/container.js +299 -102
  37. package/lib/data/context.js +6 -5
  38. package/lib/data/dataScenarioConfig.js +1 -1
  39. package/lib/data/dataTableArgument.js +1 -1
  40. package/lib/data/table.js +1 -1
  41. package/lib/effects.js +94 -10
  42. package/lib/els.js +11 -9
  43. package/lib/event.js +11 -10
  44. package/lib/globals.js +141 -0
  45. package/lib/heal.js +12 -12
  46. package/lib/helper/AI.js +1 -1
  47. package/lib/helper/ApiDataFactory.js +16 -13
  48. package/lib/helper/FileSystem.js +32 -12
  49. package/lib/helper/GraphQL.js +1 -1
  50. package/lib/helper/GraphQLDataFactory.js +1 -1
  51. package/lib/helper/JSONResponse.js +19 -30
  52. package/lib/helper/Mochawesome.js +9 -28
  53. package/lib/helper/Playwright.js +668 -265
  54. package/lib/helper/Puppeteer.js +284 -169
  55. package/lib/helper/REST.js +29 -12
  56. package/lib/helper/WebDriver.js +191 -71
  57. package/lib/helper/errors/ConnectionRefused.js +6 -6
  58. package/lib/helper/errors/ElementAssertion.js +11 -16
  59. package/lib/helper/errors/ElementNotFound.js +5 -9
  60. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  61. package/lib/helper/extras/Console.js +11 -11
  62. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  63. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  64. package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
  65. package/lib/helper/extras/Popup.js +1 -1
  66. package/lib/helper/extras/React.js +29 -30
  67. package/lib/helper/network/actions.js +33 -48
  68. package/lib/helper/network/utils.js +76 -83
  69. package/lib/helper/scripts/blurElement.js +6 -6
  70. package/lib/helper/scripts/focusElement.js +6 -6
  71. package/lib/helper/scripts/highlightElement.js +9 -9
  72. package/lib/helper/scripts/isElementClickable.js +34 -34
  73. package/lib/helper.js +2 -1
  74. package/lib/history.js +23 -20
  75. package/lib/hooks.js +10 -10
  76. package/lib/html.js +90 -100
  77. package/lib/index.js +48 -21
  78. package/lib/listener/config.js +8 -9
  79. package/lib/listener/emptyRun.js +6 -7
  80. package/lib/listener/exit.js +4 -3
  81. package/lib/listener/globalRetry.js +5 -5
  82. package/lib/listener/globalTimeout.js +11 -10
  83. package/lib/listener/helpers.js +33 -14
  84. package/lib/listener/mocha.js +3 -4
  85. package/lib/listener/result.js +4 -5
  86. package/lib/listener/steps.js +7 -18
  87. package/lib/listener/store.js +3 -3
  88. package/lib/locator.js +213 -192
  89. package/lib/mocha/asyncWrapper.js +108 -75
  90. package/lib/mocha/bdd.js +99 -13
  91. package/lib/mocha/cli.js +60 -27
  92. package/lib/mocha/factory.js +75 -19
  93. package/lib/mocha/featureConfig.js +1 -1
  94. package/lib/mocha/gherkin.js +57 -25
  95. package/lib/mocha/hooks.js +12 -3
  96. package/lib/mocha/index.js +13 -4
  97. package/lib/mocha/inject.js +22 -5
  98. package/lib/mocha/scenarioConfig.js +2 -2
  99. package/lib/mocha/suite.js +9 -2
  100. package/lib/mocha/test.js +10 -13
  101. package/lib/mocha/ui.js +28 -31
  102. package/lib/output.js +11 -9
  103. package/lib/parser.js +44 -44
  104. package/lib/pause.js +15 -16
  105. package/lib/plugin/analyze.js +19 -12
  106. package/lib/plugin/auth.js +20 -21
  107. package/lib/plugin/autoDelay.js +12 -8
  108. package/lib/plugin/coverage.js +12 -8
  109. package/lib/plugin/customLocator.js +3 -3
  110. package/lib/plugin/customReporter.js +3 -2
  111. package/lib/plugin/heal.js +14 -9
  112. package/lib/plugin/pageInfo.js +10 -10
  113. package/lib/plugin/pauseOnFail.js +4 -3
  114. package/lib/plugin/retryFailedStep.js +47 -5
  115. package/lib/plugin/screenshotOnFail.js +75 -37
  116. package/lib/plugin/stepByStepReport.js +14 -14
  117. package/lib/plugin/stepTimeout.js +4 -3
  118. package/lib/plugin/subtitles.js +6 -5
  119. package/lib/recorder.js +33 -23
  120. package/lib/rerun.js +69 -26
  121. package/lib/result.js +4 -4
  122. package/lib/secret.js +18 -17
  123. package/lib/session.js +95 -89
  124. package/lib/step/base.js +6 -6
  125. package/lib/step/config.js +1 -1
  126. package/lib/step/func.js +3 -3
  127. package/lib/step/helper.js +3 -3
  128. package/lib/step/meta.js +4 -4
  129. package/lib/step/record.js +11 -11
  130. package/lib/step/retry.js +3 -3
  131. package/lib/step/section.js +3 -3
  132. package/lib/step.js +7 -10
  133. package/lib/steps.js +9 -5
  134. package/lib/store.js +1 -1
  135. package/lib/timeout.js +1 -7
  136. package/lib/transform.js +8 -8
  137. package/lib/translation.js +32 -18
  138. package/lib/utils.js +68 -97
  139. package/lib/workerStorage.js +16 -17
  140. package/lib/workers.js +145 -171
  141. package/package.json +63 -57
  142. package/translations/de-DE.js +2 -2
  143. package/translations/fr-FR.js +2 -2
  144. package/translations/index.js +23 -10
  145. package/translations/it-IT.js +2 -2
  146. package/translations/ja-JP.js +2 -2
  147. package/translations/nl-NL.js +2 -2
  148. package/translations/pl-PL.js +2 -2
  149. package/translations/pt-BR.js +2 -2
  150. package/translations/ru-RU.js +2 -2
  151. package/translations/utils.js +11 -2
  152. package/translations/zh-CN.js +2 -2
  153. package/translations/zh-TW.js +2 -2
  154. package/typings/index.d.ts +7 -18
  155. package/typings/promiseBasedTypes.d.ts +3769 -5450
  156. package/typings/types.d.ts +3953 -5778
  157. package/bin/test-server.js +0 -53
  158. package/lib/element/WebElement.js +0 -327
  159. package/lib/helper/Nightmare.js +0 -1486
  160. package/lib/helper/Protractor.js +0 -1840
  161. package/lib/helper/TestCafe.js +0 -1391
  162. package/lib/helper/clientscripts/nightmare.js +0 -213
  163. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
  164. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  165. package/lib/helper/testcafe/testcafe-utils.js +0 -61
  166. package/lib/listener/retryEnhancer.js +0 -85
  167. package/lib/plugin/allure.js +0 -15
  168. package/lib/plugin/autoLogin.js +0 -5
  169. package/lib/plugin/commentStep.js +0 -141
  170. package/lib/plugin/eachElement.js +0 -127
  171. package/lib/plugin/fakerTransform.js +0 -49
  172. package/lib/plugin/htmlReporter.js +0 -1947
  173. package/lib/plugin/retryTo.js +0 -16
  174. package/lib/plugin/selenoid.js +0 -364
  175. package/lib/plugin/standardActingHelpers.js +0 -6
  176. package/lib/plugin/tryTo.js +0 -16
  177. package/lib/plugin/wdio.js +0 -247
  178. package/lib/test-server.js +0 -323
  179. package/lib/within.js +0 -90
package/lib/container.js CHANGED
@@ -1,16 +1,18 @@
1
- const { globSync } = require('glob')
2
- const path = require('path')
3
- const debug = require('debug')('codeceptjs:container')
4
- const { MetaStep } = require('./step')
5
- const { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally } = require('./utils')
6
- const Translation = require('./translation')
7
- const MochaFactory = require('./mocha/factory')
8
- const recorder = require('./recorder')
9
- const event = require('./event')
10
- const WorkerStorage = require('./workerStorage')
11
- const store = require('./store')
12
- const Result = require('./result')
13
- const ai = require('./ai')
1
+ import { globSync } from 'glob'
2
+ import path from 'path'
3
+ import debugModule from 'debug'
4
+ const debug = debugModule('codeceptjs:container')
5
+ import { MetaStep } from './step.js'
6
+ import { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally, deepMerge } from './utils.js'
7
+ import Translation from './translation.js'
8
+ import MochaFactory from './mocha/factory.js'
9
+ import recorder from './recorder.js'
10
+ import event from './event.js'
11
+ import WorkerStorage from './workerStorage.js'
12
+ import store from './store.js'
13
+ import Result from './result.js'
14
+ import ai from './ai.js'
15
+ import actorFactory from './actor.js'
14
16
 
15
17
  let asyncHelperPromise
16
18
 
@@ -28,7 +30,6 @@ let container = {
28
30
  translation: {},
29
31
  /** @type {Result | null} */
30
32
  result: null,
31
- sharedKeys: new Set() // Track keys shared via share() function
32
33
  }
33
34
 
34
35
  /**
@@ -40,7 +41,7 @@ class Container {
40
41
  *
41
42
  */
42
43
  static get STANDARD_ACTING_HELPERS() {
43
- return ['Playwright', 'WebDriver', 'Puppeteer', 'Appium', 'TestCafe']
44
+ return ['Playwright', 'WebDriver', 'Puppeteer', 'Appium']
44
45
  }
45
46
  /**
46
47
  * Create container with all required helpers and support objects
@@ -49,7 +50,7 @@ class Container {
49
50
  * @param {*} config
50
51
  * @param {*} opts
51
52
  */
52
- static create(config, opts) {
53
+ static async create(config, opts) {
53
54
  debug('creating container')
54
55
  asyncHelperPromise = Promise.resolve()
55
56
 
@@ -61,16 +62,53 @@ class Container {
61
62
 
62
63
  // create support objects
63
64
  container.support = {}
64
- container.helpers = createHelpers(config.helpers || {})
65
- container.translation = loadTranslation(config.translation || null, config.vocabularies || [])
65
+ container.helpers = await createHelpers(config.helpers || {})
66
+ container.translation = await loadTranslation(config.translation || null, config.vocabularies || [])
66
67
  container.proxySupport = createSupportObjects(config.include || {})
67
- container.plugins = createPlugins(config.plugins || {}, opts)
68
+ container.plugins = await createPlugins(config.plugins || {}, opts)
68
69
  container.result = new Result()
69
70
 
70
- createActor(config.include?.I)
71
+ // Preload includes (so proxies can expose real objects synchronously)
72
+ const includes = config.include || {}
73
+
74
+ // Ensure I is available for DI modules at import time
75
+ if (Object.prototype.hasOwnProperty.call(includes, 'I')) {
76
+ try {
77
+ const mod = includes.I
78
+ if (typeof mod === 'string') {
79
+ container.support.I = await loadSupportObject(mod, 'I')
80
+ } else if (typeof mod === 'function') {
81
+ container.support.I = await loadSupportObject(mod, 'I')
82
+ } else if (mod && typeof mod === 'object') {
83
+ container.support.I = mod
84
+ }
85
+ } catch (e) {
86
+ throw new Error(`Could not include object I: ${e.message}`)
87
+ }
88
+ } else {
89
+ // Create default actor if not provided via includes
90
+ createActor()
91
+ }
92
+
93
+ // Load remaining includes except I
94
+ for (const [name, mod] of Object.entries(includes)) {
95
+ if (name === 'I') continue
96
+ try {
97
+ if (typeof mod === 'string') {
98
+ container.support[name] = await loadSupportObject(mod, name)
99
+ } else if (typeof mod === 'function') {
100
+ // function or class
101
+ container.support[name] = await loadSupportObject(mod, name)
102
+ } else if (mod && typeof mod === 'object') {
103
+ container.support[name] = mod
104
+ }
105
+ } catch (e) {
106
+ throw new Error(`Could not include object ${name}: ${e.message}`)
107
+ }
108
+ }
71
109
 
72
110
  if (opts && opts.ai) ai.enable(config.ai) // enable AI Assistant
73
- if (config.gherkin) loadGherkinSteps(config.gherkin.steps || [])
111
+ if (config.gherkin) await loadGherkinStepsAsync(config.gherkin.steps || [])
74
112
  if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts
75
113
  }
76
114
 
@@ -103,7 +141,8 @@ class Container {
103
141
  if (!name) {
104
142
  return container.proxySupport
105
143
  }
106
- return container.support[name] || container.proxySupport[name]
144
+ // Always return the proxy to ensure MetaStep creation works
145
+ return container.proxySupport[name]
107
146
  }
108
147
 
109
148
  /**
@@ -158,8 +197,14 @@ class Container {
158
197
  * @param {Object<string, *>} newContainer
159
198
  */
160
199
  static append(newContainer) {
161
- const deepMerge = require('./utils').deepMerge
162
200
  container = deepMerge(container, newContainer)
201
+
202
+ // If new support objects are added, update the proxy support
203
+ if (newContainer.support) {
204
+ const newProxySupport = createSupportObjects(newContainer.support)
205
+ container.proxySupport = { ...container.proxySupport, ...newProxySupport }
206
+ }
207
+
163
208
  debug('appended', JSON.stringify(newContainer).slice(0, 300))
164
209
  }
165
210
 
@@ -170,12 +215,11 @@ class Container {
170
215
  * @param {Object<string, *>} newSupport
171
216
  * @param {Object<string, *>} newPlugins
172
217
  */
173
- static clear(newHelpers = {}, newSupport = {}, newPlugins = {}) {
218
+ static async clear(newHelpers = {}, newSupport = {}, newPlugins = {}) {
174
219
  container.helpers = newHelpers
175
- container.translation = loadTranslation()
220
+ container.translation = await loadTranslation()
176
221
  container.proxySupport = createSupportObjects(newSupport)
177
222
  container.plugins = newPlugins
178
- container.sharedKeys = new Set() // Clear shared keys
179
223
  asyncHelperPromise = Promise.resolve()
180
224
  store.actor = null
181
225
  debug('container cleared')
@@ -199,13 +243,7 @@ class Container {
199
243
  * @param {Object} options - set {local: true} to not share among workers
200
244
  */
201
245
  static share(data, options = {}) {
202
- // Instead of using append which replaces the entire container,
203
- // directly update the support object to maintain proxy references
204
- Object.assign(container.support, data)
205
-
206
- // Track which keys were explicitly shared
207
- Object.keys(data).forEach(key => container.sharedKeys.add(key))
208
-
246
+ Container.append({ support: data })
209
247
  if (!options.local) {
210
248
  WorkerStorage.share(data)
211
249
  }
@@ -220,23 +258,56 @@ class Container {
220
258
  }
221
259
  }
222
260
 
223
- module.exports = Container
261
+ export default Container
224
262
 
225
- function createHelpers(config) {
263
+ async function createHelpers(config) {
226
264
  const helpers = {}
227
265
  for (let helperName in config) {
228
266
  try {
229
267
  let HelperClass
230
268
 
231
- // ESM import
232
- if (helperName?.constructor === Function && helperName.prototype) {
269
+ // Check if helper class was stored in config during ESM import processing
270
+ if (config[helperName]._helperClass) {
271
+ HelperClass = config[helperName]._helperClass
272
+ debug(`helper ${helperName} loaded from ESM import`)
273
+ }
274
+
275
+ // ESM import (legacy check)
276
+ if (!HelperClass && typeof helperName === 'function' && helperName.prototype) {
233
277
  HelperClass = helperName
234
278
  helperName = HelperClass.constructor.name
235
279
  }
236
280
 
237
- // classical require
281
+ // classical require - may be async for ESM modules
238
282
  if (!HelperClass) {
239
- HelperClass = requireHelperFromModule(helperName, config)
283
+ const helperResult = requireHelperFromModule(helperName, config)
284
+ if (helperResult instanceof Promise) {
285
+ // Handle async ESM loading
286
+ helpers[helperName] = {}
287
+ asyncHelperPromise = asyncHelperPromise
288
+ .then(() => helperResult)
289
+ .then(async ResolvedHelperClass => {
290
+ debug(`helper ${helperName} resolved type: ${typeof ResolvedHelperClass}`, ResolvedHelperClass)
291
+
292
+ // Extract default export from ESM module wrapper if needed
293
+ if (ResolvedHelperClass && ResolvedHelperClass.__esModule && ResolvedHelperClass.default) {
294
+ ResolvedHelperClass = ResolvedHelperClass.default
295
+ debug(`extracted default export for ${helperName}, new type: ${typeof ResolvedHelperClass}`)
296
+ }
297
+
298
+ if (typeof ResolvedHelperClass !== 'function') {
299
+ throw new Error(`Helper '${helperName}' is not a class. Got: ${typeof ResolvedHelperClass}`)
300
+ }
301
+
302
+ checkHelperRequirements(ResolvedHelperClass)
303
+ helpers[helperName] = new ResolvedHelperClass(config[helperName])
304
+ if (helpers[helperName]._init) await helpers[helperName]._init()
305
+ debug(`helper ${helperName} async initialized`)
306
+ })
307
+ continue
308
+ } else {
309
+ HelperClass = helperResult
310
+ }
240
311
  }
241
312
 
242
313
  // handle async CJS modules that use dynamic import
@@ -269,7 +340,7 @@ function createHelpers(config) {
269
340
  }
270
341
 
271
342
  for (const name in helpers) {
272
- if (helpers[name]._init) helpers[name]._init()
343
+ if (helpers[name]._init) await helpers[name]._init()
273
344
  }
274
345
  return helpers
275
346
  }
@@ -290,23 +361,43 @@ function checkHelperRequirements(HelperClass) {
290
361
  }
291
362
  }
292
363
 
293
- function requireHelperFromModule(helperName, config, HelperClass) {
364
+ async function requireHelperFromModule(helperName, config, HelperClass) {
294
365
  const moduleName = getHelperModuleName(helperName, config)
295
366
  if (moduleName.startsWith('./helper/')) {
296
- HelperClass = require(moduleName)
367
+ try {
368
+ // For built-in helpers, use direct relative import with .js extension
369
+ const helperPath = `${moduleName}.js`
370
+ const mod = await import(helperPath)
371
+ HelperClass = mod.default || mod
372
+ } catch (err) {
373
+ throw err
374
+ }
297
375
  } else {
298
376
  // check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
299
377
  try {
300
- const mod = require(moduleName)
378
+ // Try dynamic import for both CommonJS and ESM modules
379
+ const mod = await import(moduleName)
301
380
  if (!mod && !mod.default) {
302
381
  throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
303
382
  }
304
383
  HelperClass = mod.default || mod
305
384
  } catch (err) {
306
- if (err.code === 'MODULE_NOT_FOUND') {
385
+ if (err.code === 'ERR_REQUIRE_ESM' || (err.message && err.message.includes('ES module'))) {
386
+ // This is an ESM module, use dynamic import
387
+ try {
388
+ const pathModule = await import('path')
389
+ const absolutePath = pathModule.default.resolve(moduleName)
390
+ const mod = await import(absolutePath)
391
+ HelperClass = mod.default || mod
392
+ debug(`helper ${helperName} loaded via ESM import`)
393
+ } catch (importErr) {
394
+ throw new Error(`Helper module '${moduleName}' could not be imported as ESM: ${importErr.message}`)
395
+ }
396
+ } else if (err.code === 'MODULE_NOT_FOUND') {
307
397
  throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
398
+ } else {
399
+ throw err
308
400
  }
309
- throw err
310
401
  }
311
402
  }
312
403
  return HelperClass
@@ -338,14 +429,21 @@ function createSupportObjects(config) {
338
429
  }
339
430
 
340
431
  // load actual name from vocabulary
341
- if (container.translation.name) {
342
- name = container.translation.name
432
+ if (container.translation && container.translation.I && name === 'I') {
433
+ // Use translated name for I
434
+ const actualName = container.translation.I
435
+ if (actualName !== 'I') {
436
+ name = actualName
437
+ }
343
438
  }
344
439
 
345
440
  if (name === 'I') {
346
- const actor = createActor(config.I)
347
- methodsOfObject(actor)
348
- return actor[prop]
441
+ if (!container.support.I) {
442
+ // Actor will be created during container.create()
443
+ return undefined
444
+ }
445
+ methodsOfObject(container.support.I)
446
+ return container.support.I[prop]
349
447
  }
350
448
 
351
449
  if (!container.support[name] && typeof config[name] === 'object') {
@@ -353,17 +451,10 @@ function createSupportObjects(config) {
353
451
  }
354
452
 
355
453
  if (!container.support[name]) {
356
- // Load object on first access
357
- const supportObject = loadSupportObject(config[name])
358
- container.support[name] = supportObject
359
- try {
360
- if (container.support[name]._init) {
361
- container.support[name]._init()
362
- }
363
- debug(`support object ${name} initialized`)
364
- } catch (err) {
365
- throw new Error(`Initialization failed for ${name}: ${container.support[name]}\n${err.message}\n${err.stack}`)
366
- }
454
+ // Cannot load object synchronously in proxy getter
455
+ // Return undefined and log warning - object should be pre-loaded during container creation
456
+ debug(`Support object ${name} not pre-loaded, returning undefined`)
457
+ return undefined
367
458
  }
368
459
 
369
460
  const currentObject = container.support[name]
@@ -380,19 +471,32 @@ function createSupportObjects(config) {
380
471
  return currentValue
381
472
  },
382
473
  has(target, prop) {
383
- container.support[name] = container.support[name] || loadSupportObject(config[name])
474
+ if (!container.support[name]) {
475
+ // Note: This is sync, so we can't use async loadSupportObject here
476
+ // The object will be loaded lazily on first property access
477
+ return false
478
+ }
384
479
  return prop in container.support[name]
385
480
  },
386
481
  getOwnPropertyDescriptor(target, prop) {
387
- container.support[name] = container.support[name] || loadSupportObject(config[name])
482
+ if (!container.support[name]) {
483
+ // Object will be loaded on property access
484
+ return {
485
+ enumerable: true,
486
+ configurable: true,
487
+ value: undefined,
488
+ }
489
+ }
388
490
  return {
389
491
  enumerable: true,
390
492
  configurable: true,
391
- value: this.get(target, prop),
493
+ value: container.support[name][prop],
392
494
  }
393
495
  },
394
496
  ownKeys() {
395
- container.support[name] = container.support[name] || loadSupportObject(config[name])
497
+ if (!container.support[name]) {
498
+ return []
499
+ }
396
500
  return Reflect.ownKeys(container.support[name])
397
501
  },
398
502
  },
@@ -404,23 +508,29 @@ function createSupportObjects(config) {
404
508
  {},
405
509
  {
406
510
  has(target, key) {
407
- return keys.includes(key) || container.sharedKeys.has(key)
511
+ return keys.includes(key)
408
512
  },
409
513
  ownKeys() {
410
- // Return both original config keys and explicitly shared keys
411
- return [...new Set([...keys, ...container.sharedKeys])]
514
+ return keys
412
515
  },
413
516
  getOwnPropertyDescriptor(target, prop) {
414
517
  return {
415
518
  enumerable: true,
416
519
  configurable: true,
417
- value: this.get(target, prop),
520
+ value: target[prop],
418
521
  }
419
522
  },
420
523
  get(target, key) {
421
- // First check if this is an explicitly shared property
422
- if (container.sharedKeys.has(key) && key in container.support) {
423
- return container.support[key]
524
+ if (typeof key === 'symbol') {
525
+ // safely ignore symbol-based meta properties used by tooling
526
+ return undefined
527
+ }
528
+ // Allow special I even if not declared in includes
529
+ if (key === 'I') {
530
+ return lazyLoad('I')
531
+ }
532
+ if (!keys.includes(key)) {
533
+ throw new Error(`Support object "${String(key)}" is not defined`)
424
534
  }
425
535
  return lazyLoad(key)
426
536
  },
@@ -431,17 +541,35 @@ function createSupportObjects(config) {
431
541
  function createActor(actorPath) {
432
542
  if (container.support.I) return container.support.I
433
543
 
434
- if (actorPath) {
435
- container.support.I = loadSupportObject(actorPath)
436
- } else {
437
- const actor = require('./actor')
438
- container.support.I = actor()
439
- }
544
+ // Default actor
545
+ container.support.I = actorFactory({}, Container)
440
546
 
441
547
  return container.support.I
442
548
  }
443
549
 
444
- function createPlugins(config, options = {}) {
550
+ async function loadPluginAsync(modulePath, config) {
551
+ let pluginMod
552
+ try {
553
+ // Try dynamic import first (works for both ESM and CJS)
554
+ pluginMod = await import(modulePath)
555
+ } catch (err) {
556
+ throw new Error(`Could not load plugin from '${modulePath}': ${err.message}`)
557
+ }
558
+
559
+ const pluginFactory = pluginMod.default || pluginMod
560
+ if (typeof pluginFactory !== 'function') {
561
+ throw new Error(`Plugin '${modulePath}' is not a function. Expected a plugin factory function.`)
562
+ }
563
+
564
+ return pluginFactory(config)
565
+ }
566
+
567
+ async function loadPluginFallback(modulePath, config) {
568
+ // This function is kept for backwards compatibility but now uses dynamic import
569
+ return await loadPluginAsync(modulePath, config)
570
+ }
571
+
572
+ async function createPlugins(config, options = {}) {
445
573
  const plugins = {}
446
574
 
447
575
  const enabledPluginsByOptions = (options.plugins || '').split(',')
@@ -459,9 +587,12 @@ function createPlugins(config, options = {}) {
459
587
  module = path.resolve(global.codecept_dir, module) // custom plugin
460
588
  }
461
589
  } else {
462
- module = `./plugin/${pluginName}`
590
+ module = `./plugin/${pluginName}.js`
463
591
  }
464
- plugins[pluginName] = require(module)(config[pluginName])
592
+
593
+ // Use async loading for all plugins (ESM and CJS)
594
+ plugins[pluginName] = await loadPluginAsync(module, config[pluginName])
595
+ debug(`plugin ${pluginName} loaded via async import`)
465
596
  } catch (err) {
466
597
  throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`)
467
598
  }
@@ -469,24 +600,34 @@ function createPlugins(config, options = {}) {
469
600
  return plugins
470
601
  }
471
602
 
472
- function loadGherkinSteps(paths) {
603
+ async function loadGherkinStepsAsync(paths) {
473
604
  global.Before = fn => event.dispatcher.on(event.test.started, fn)
474
605
  global.After = fn => event.dispatcher.on(event.test.finished, fn)
475
606
  global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
476
607
 
608
+ // Import BDD module to access step file tracking functions
609
+ const bddModule = await import('./mocha/bdd.js')
610
+
477
611
  // If gherkin.steps is string, then this will iterate through that folder and send all step def js files to loadSupportObject
478
612
  // If gherkin.steps is Array, it will go the old way
479
613
  // This is done so that we need not enter all Step Definition files under config.gherkin.steps
480
614
  if (Array.isArray(paths)) {
481
615
  for (const path of paths) {
482
- loadSupportObject(path, `Step Definition from ${path}`)
616
+ // Set context for step definition file location tracking
617
+ bddModule.setCurrentStepFile(path)
618
+ await loadSupportObject(path, `Step Definition from ${path}`)
619
+ bddModule.clearCurrentStepFile()
483
620
  }
484
621
  } else {
485
622
  const folderPath = paths.startsWith('.') ? normalizeAndJoin(global.codecept_dir, paths) : ''
486
623
  if (folderPath !== '') {
487
- globSync(folderPath).forEach(file => {
488
- loadSupportObject(file, `Step Definition from ${file}`)
489
- })
624
+ const files = globSync(folderPath)
625
+ for (const file of files) {
626
+ // Set context for step definition file location tracking
627
+ bddModule.setCurrentStepFile(file)
628
+ await loadSupportObject(file, `Step Definition from ${file}`)
629
+ bddModule.clearCurrentStepFile()
630
+ }
490
631
  }
491
632
  }
492
633
 
@@ -495,47 +636,97 @@ function loadGherkinSteps(paths) {
495
636
  delete global.Fail
496
637
  }
497
638
 
498
- function loadSupportObject(modulePath, supportObjectName) {
639
+ function loadGherkinSteps(paths) {
640
+ global.Before = fn => event.dispatcher.on(event.test.started, fn)
641
+ global.After = fn => event.dispatcher.on(event.test.finished, fn)
642
+ global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
643
+
644
+ // Gherkin step loading must be handled asynchronously
645
+ throw new Error('Gherkin step loading must be converted to async. Use loadGherkinStepsAsync() instead.')
646
+
647
+ delete global.Before
648
+ delete global.After
649
+ delete global.Fail
650
+ }
651
+
652
+ async function loadSupportObject(modulePath, supportObjectName) {
499
653
  if (!modulePath) {
500
654
  throw new Error(`Support object "${supportObjectName}" is not defined`)
501
655
  }
502
- if (modulePath.charAt(0) === '.') {
656
+ // If function/class provided directly
657
+ if (typeof modulePath === 'function') {
658
+ try {
659
+ // class constructor
660
+ if (modulePath.prototype && modulePath.prototype.constructor === modulePath) {
661
+ return new modulePath()
662
+ }
663
+ // plain function factory
664
+ return modulePath()
665
+ } catch (err) {
666
+ throw new Error(`Could not include object ${supportObjectName} from function: ${err.message}`)
667
+ }
668
+ }
669
+ if (typeof modulePath === 'string' && modulePath.charAt(0) === '.') {
503
670
  modulePath = path.join(global.codecept_dir, modulePath)
504
671
  }
505
672
  try {
506
- const obj = require(modulePath)
673
+ // Use dynamic import for both ESM and CJS modules
674
+ let importPath = modulePath
675
+ // Append .js if no extension provided (ESM resolution requires it)
676
+ if (typeof importPath === 'string') {
677
+ const ext = path.extname(importPath)
678
+ if (!ext) importPath = `${importPath}.js`
679
+ }
680
+ const obj = await import(importPath)
681
+
682
+ // Handle ESM module wrapper
683
+ let actualObj = obj
684
+ if (obj && obj.__esModule && obj.default) {
685
+ actualObj = obj.default
686
+ } else if (obj.default) {
687
+ actualObj = obj.default
688
+ }
507
689
 
508
690
  // Handle different types of imports
509
- if (typeof obj === 'function') {
691
+ if (typeof actualObj === 'function') {
510
692
  // If it's a class (constructor function)
511
- if (obj.prototype && obj.prototype.constructor === obj) {
512
- const ClassName = obj
693
+ if (actualObj.prototype && actualObj.prototype.constructor === actualObj) {
694
+ const ClassName = actualObj
513
695
  return new ClassName()
514
696
  }
515
697
  // If it's a regular function
516
- return obj()
698
+ return actualObj()
517
699
  }
518
700
 
519
- if (obj && Array.isArray(obj)) {
520
- return obj
701
+ if (actualObj && Array.isArray(actualObj)) {
702
+ return actualObj
521
703
  }
522
704
 
523
705
  // If it's a plain object
524
- if (obj && typeof obj === 'object') {
525
- return obj
706
+ if (actualObj && typeof actualObj === 'object') {
707
+ // Call _init if it exists (for page objects)
708
+ if (actualObj._init && typeof actualObj._init === 'function') {
709
+ actualObj._init()
710
+ }
711
+ return actualObj
526
712
  }
527
713
 
528
- throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof obj}`)
714
+ throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof actualObj}`)
529
715
  } catch (err) {
530
716
  throw new Error(`Could not include object ${supportObjectName} from module '${modulePath}'\n${err.message}\n${err.stack}`)
531
717
  }
532
718
  }
533
719
 
720
+ // Backwards compatibility function that throws an error for sync usage
721
+ function loadSupportObjectSync(modulePath, supportObjectName) {
722
+ throw new Error(`loadSupportObjectSync is deprecated. Support object "${supportObjectName || 'undefined'}" from '${modulePath}' must be loaded asynchronously. Use loadSupportObject() instead.`)
723
+ }
724
+
534
725
  /**
535
726
  * Method collect own property and prototype
536
727
  */
537
728
 
538
- function loadTranslation(locale, vocabularies) {
729
+ async function loadTranslation(locale, vocabularies) {
539
730
  if (!locale) {
540
731
  return Translation.createEmpty()
541
732
  }
@@ -543,8 +734,9 @@ function loadTranslation(locale, vocabularies) {
543
734
  let translation
544
735
 
545
736
  // check if it is a known translation
546
- if (Translation.langs[locale]) {
547
- translation = new Translation(Translation.langs[locale])
737
+ const langs = await Translation.getLangs()
738
+ if (langs[locale]) {
739
+ translation = new Translation(langs[locale])
548
740
  } else if (fileExists(path.join(global.codecept_dir, locale))) {
549
741
  // get from a provided file instead
550
742
  translation = Translation.createDefault()
@@ -562,7 +754,12 @@ function getHelperModuleName(helperName, config) {
562
754
  // classical require
563
755
  if (config[helperName].require) {
564
756
  if (config[helperName].require.startsWith('.')) {
565
- return path.resolve(global.codecept_dir, config[helperName].require) // custom helper
757
+ let helperPath = path.resolve(global.codecept_dir, config[helperName].require)
758
+ // Add .js extension if not present for ESM compatibility
759
+ if (!path.extname(helperPath)) {
760
+ helperPath += '.js'
761
+ }
762
+ return helperPath // custom helper
566
763
  }
567
764
  return config[helperName].require // plugin helper
568
765
  }
@@ -1,9 +1,10 @@
1
- const { isGenerator } = require('../utils')
2
- const DataTable = require('./table')
3
- const DataScenarioConfig = require('./dataScenarioConfig')
4
- const Secret = require('../secret')
1
+ import { isGenerator } from '../utils.js'
2
+ import DataTable from './table.js'
3
+ import DataScenarioConfig from './dataScenarioConfig.js'
4
+ import secretModule from '../secret.js'
5
+ const Secret = secretModule.default || secretModule
5
6
 
6
- module.exports = function (context) {
7
+ export default function (context) {
7
8
  context.Data = function (dataTable) {
8
9
  const data = detectDataType(dataTable)
9
10
  return {
@@ -81,4 +81,4 @@ class DataScenarioConfig {
81
81
  }
82
82
  }
83
83
 
84
- module.exports = DataScenarioConfig
84
+ export default DataScenarioConfig
@@ -63,4 +63,4 @@ class DataTableArgument {
63
63
  }
64
64
  }
65
65
 
66
- module.exports = DataTableArgument
66
+ export default DataTableArgument