codeceptjs 4.0.1-beta.2 → 4.0.1-beta.20

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.
package/bin/codecept.js CHANGED
@@ -174,7 +174,7 @@ program
174
174
  .option('-R, --reporter <name>', 'specify the reporter to use')
175
175
  .option('-S, --sort', 'sort test files')
176
176
  .option('-b, --bail', 'bail after first test failure')
177
- .option('-d, --debug', "enable node's debugger, synonym for node --debug")
177
+ .option('--inspec', "enable node's debugger, synonym for node --debug")
178
178
  .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
179
179
  .option('-f, --fgrep <string>', 'only run tests containing <string>')
180
180
  .option('-i, --invert', 'inverts --grep and --fgrep matches')
@@ -276,7 +276,7 @@ program
276
276
  .option('-R, --reporter <name>', 'specify the reporter to use')
277
277
  .option('-S, --sort', 'sort test files')
278
278
  .option('-b, --bail', 'bail after first test failure')
279
- .option('-d, --debug', "enable node's debugger, synonym for node --debug")
279
+ .option('--inspect', "enable node's debugger, synonym for node --debug")
280
280
  .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
281
281
  .option('-f, --fgrep <string>', 'only run tests containing <string>')
282
282
  .option('-i, --invert', 'inverts --grep and --fgrep matches')
package/lib/actor.js CHANGED
@@ -75,7 +75,8 @@ export default function (obj = {}, container) {
75
75
  if (!container) {
76
76
  container = Container
77
77
  }
78
-
78
+
79
+ // Get existing actor or create a new one
79
80
  const actor = container.actor() || new Actor()
80
81
 
81
82
  // load all helpers once container initialized
@@ -111,14 +112,17 @@ export default function (obj = {}, container) {
111
112
  }
112
113
  })
113
114
 
114
- container.append({
115
- support: {
116
- I: actor,
117
- },
118
- })
115
+ // Update container.support.I to ensure it has the latest actor reference
116
+ if (!container.actor() || container.actor() !== actor) {
117
+ container.append({
118
+ support: {
119
+ I: actor,
120
+ },
121
+ })
122
+ }
119
123
  })
120
- // store.actor = actor;
121
- // add custom steps from actor
124
+
125
+ // add custom steps from actor immediately
122
126
  Object.keys(obj).forEach(key => {
123
127
  const ms = new MetaStep('I', key)
124
128
  ms.setContext(actor)
@@ -41,7 +41,7 @@ const getDefinitionsFileContent = ({ hasCustomHelper, hasCustomStepsFile, helper
41
41
 
42
42
  const importPathsFragment = importPaths.join('\n')
43
43
  const supportObjectsTypeFragment = convertMapToType(supportObject)
44
- const methodsTypeFragment = helperNames.length > 0 ? `interface Methods extends ${helperNames.join(', ')} {}` : ''
44
+ const methodsTypeFragment = helperNames.length > 0 ? `interface Methods extends ${helperNames.join(', ')} {}` : 'interface Methods {}'
45
45
  const translatedActionsFragment = JSON.stringify(translations.vocabulary.actions, null, 2)
46
46
 
47
47
  return generateDefinitionsContent({
@@ -239,8 +239,13 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue)
239
239
  }
240
240
 
241
241
  for (const name in pathsToValue) {
242
- const relativePath = getPath(pathsToValue[name], targetFolderPath, testsPath)
243
- importStrings.push(`type ${name} = import('${relativePath}');`)
242
+ const originalPath = pathsToValue[name]
243
+ const relativePath = getPath(originalPath, targetFolderPath, testsPath)
244
+ if (originalPath.endsWith('.js') || originalPath.endsWith('.ts')) {
245
+ importStrings.push(`type ${name} = InstanceType<typeof import('${relativePath}').default>;`)
246
+ } else {
247
+ importStrings.push(`type ${name} = import('${relativePath}');`)
248
+ }
244
249
  }
245
250
 
246
251
  return importStrings
package/lib/container.js CHANGED
@@ -22,6 +22,7 @@ let container = {
22
22
  helpers: {},
23
23
  support: {},
24
24
  proxySupport: {},
25
+ proxySupportConfig: {}, // Track config used to create proxySupport
25
26
  plugins: {},
26
27
  actor: null,
27
28
  /**
@@ -67,14 +68,15 @@ class Container {
67
68
  container.support = {}
68
69
  container.helpers = await createHelpers(config.helpers || {})
69
70
  container.translation = await loadTranslation(config.translation || null, config.vocabularies || [])
70
- container.proxySupport = createSupportObjects(config.include || {})
71
+ container.proxySupportConfig = config.include || {}
72
+ container.proxySupport = createSupportObjects(container.proxySupportConfig)
71
73
  container.plugins = await createPlugins(config.plugins || {}, opts)
72
74
  container.result = new Result()
73
75
 
74
76
  // Preload includes (so proxies can expose real objects synchronously)
75
77
  const includes = config.include || {}
76
78
 
77
- // Ensure I is available for DI modules at import time
79
+ // Check if custom I is provided
78
80
  if (Object.prototype.hasOwnProperty.call(includes, 'I')) {
79
81
  try {
80
82
  const mod = includes.I
@@ -89,7 +91,7 @@ class Container {
89
91
  throw new Error(`Could not include object I: ${e.message}`)
90
92
  }
91
93
  } else {
92
- // Create default actor if not provided via includes
94
+ // Create default actor - this sets up the callback in asyncHelperPromise
93
95
  createActor()
94
96
  }
95
97
 
@@ -110,6 +112,9 @@ class Container {
110
112
  }
111
113
  }
112
114
 
115
+ // Wait for all async helpers to finish loading and populate the actor
116
+ await asyncHelperPromise
117
+
113
118
  if (opts && opts.ai) ai.enable(config.ai) // enable AI Assistant
114
119
  if (config.gherkin) await loadGherkinStepsAsync(config.gherkin.steps || [])
115
120
  if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts
@@ -204,8 +209,10 @@ class Container {
204
209
 
205
210
  // If new support objects are added, update the proxy support
206
211
  if (newContainer.support) {
207
- const newProxySupport = createSupportObjects(newContainer.support)
208
- container.proxySupport = { ...container.proxySupport, ...newProxySupport }
212
+ // Merge the new support config with existing config
213
+ container.proxySupportConfig = { ...container.proxySupportConfig, ...newContainer.support }
214
+ // Recreate the proxy with merged config
215
+ container.proxySupport = createSupportObjects(container.proxySupportConfig)
209
216
  }
210
217
 
211
218
  debug('appended', JSON.stringify(newContainer).slice(0, 300))
@@ -221,6 +228,7 @@ class Container {
221
228
  static async clear(newHelpers = {}, newSupport = {}, newPlugins = {}) {
222
229
  container.helpers = newHelpers
223
230
  container.translation = await loadTranslation()
231
+ container.proxySupportConfig = newSupport
224
232
  container.proxySupport = createSupportObjects(newSupport)
225
233
  container.plugins = newPlugins
226
234
  container.sharedKeys = new Set() // Clear shared keys
@@ -347,16 +355,17 @@ async function createHelpers(config) {
347
355
  }
348
356
  }
349
357
 
350
- // Wait for all async helpers to be fully loaded
351
- await asyncHelperPromise
352
-
353
- // Call _init on all helpers after they're all loaded
354
- for (const name in helpers) {
355
- if (helpers[name]._init) {
356
- await helpers[name]._init()
357
- debug(`helper ${name} _init() called`)
358
+ // Don't await here - let Container.create() handle the await
359
+ // This allows actor callbacks to be registered before resolution
360
+ asyncHelperPromise = asyncHelperPromise.then(async () => {
361
+ // Call _init on all helpers after they're all loaded
362
+ for (const name in helpers) {
363
+ if (helpers[name]._init) {
364
+ await helpers[name]._init()
365
+ debug(`helper ${name} _init() called`)
366
+ }
358
367
  }
359
- }
368
+ })
360
369
 
361
370
  return helpers
362
371
  }
@@ -389,20 +398,52 @@ async function requireHelperFromModule(helperName, config, HelperClass) {
389
398
  throw err
390
399
  }
391
400
  } else {
401
+ // Handle TypeScript files
402
+ let importPath = moduleName
403
+ let tempJsFile = null
404
+ const ext = path.extname(moduleName)
405
+
406
+ if (ext === '.ts') {
407
+ try {
408
+ // Use the TypeScript transpilation utility
409
+ const typescript = await import('typescript')
410
+ const { tempFile, allTempFiles } = await transpileTypeScript(importPath, typescript)
411
+
412
+ debug(`Transpiled TypeScript helper: ${importPath} -> ${tempFile}`)
413
+
414
+ importPath = tempFile
415
+ tempJsFile = allTempFiles
416
+ } catch (tsError) {
417
+ throw new Error(`Failed to load TypeScript helper ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
418
+ }
419
+ }
420
+
392
421
  // check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
393
422
  try {
394
423
  // Try dynamic import for both CommonJS and ESM modules
395
- const mod = await import(moduleName)
424
+ const mod = await import(importPath)
396
425
  if (!mod && !mod.default) {
397
426
  throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
398
427
  }
399
428
  HelperClass = mod.default || mod
429
+
430
+ // Clean up temp files if created
431
+ if (tempJsFile) {
432
+ const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
433
+ cleanupTempFiles(filesToClean)
434
+ }
400
435
  } catch (err) {
436
+ // Clean up temp files before rethrowing
437
+ if (tempJsFile) {
438
+ const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
439
+ cleanupTempFiles(filesToClean)
440
+ }
441
+
401
442
  if (err.code === 'ERR_REQUIRE_ESM' || (err.message && err.message.includes('ES module'))) {
402
443
  // This is an ESM module, use dynamic import
403
444
  try {
404
445
  const pathModule = await import('path')
405
- const absolutePath = pathModule.default.resolve(moduleName)
446
+ const absolutePath = pathModule.default.resolve(importPath)
406
447
  const mod = await import(absolutePath)
407
448
  HelperClass = mod.default || mod
408
449
  debug(`helper ${helperName} loaded via ESM import`)
@@ -531,10 +572,17 @@ function createSupportObjects(config) {
531
572
  return [...new Set([...keys, ...container.sharedKeys])]
532
573
  },
533
574
  getOwnPropertyDescriptor(target, prop) {
575
+ // For destructuring to work, we need to return the actual value from the getter
576
+ let value
577
+ if (container.sharedKeys.has(prop) && prop in container.support) {
578
+ value = container.support[prop]
579
+ } else {
580
+ value = lazyLoad(prop)
581
+ }
534
582
  return {
535
583
  enumerable: true,
536
584
  configurable: true,
537
- value: target[prop],
585
+ value: value,
538
586
  }
539
587
  },
540
588
  get(target, key) {
@@ -45,6 +45,8 @@ class GraphQL extends Helper {
45
45
  timeout: 10000,
46
46
  defaultHeaders: {},
47
47
  endpoint: '',
48
+ onRequest: null,
49
+ onResponse: null,
48
50
  }
49
51
  this.options = Object.assign(this.options, config)
50
52
  this.headers = { ...this.options.defaultHeaders }
@@ -87,8 +89,8 @@ class GraphQL extends Helper {
87
89
 
88
90
  request.headers = { ...this.headers, ...request.headers }
89
91
 
90
- if (this.config.onRequest) {
91
- await this.config.onRequest(request)
92
+ if (this.options.onRequest) {
93
+ await this.options.onRequest(request)
92
94
  }
93
95
 
94
96
  this.debugSection('Request', JSON.stringify(request))
@@ -102,8 +104,8 @@ class GraphQL extends Helper {
102
104
  response = err.response
103
105
  }
104
106
 
105
- if (this.config.onResponse) {
106
- await this.config.onResponse(response)
107
+ if (this.options.onResponse) {
108
+ await this.options.onResponse(response)
107
109
  }
108
110
 
109
111
  this.debugSection('Response', JSON.stringify(response.data))
@@ -72,8 +72,8 @@ class JSONResponse extends Helper {
72
72
  if (!this.helpers[this.options.requestHelper]) {
73
73
  throw new Error(`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`)
74
74
  }
75
- const origOnResponse = this.helpers[this.options.requestHelper].config.onResponse
76
- this.helpers[this.options.requestHelper].config.onResponse = response => {
75
+ const origOnResponse = this.helpers[this.options.requestHelper].options.onResponse
76
+ this.helpers[this.options.requestHelper].options.onResponse = response => {
77
77
  this.response = response
78
78
  if (typeof origOnResponse === 'function') origOnResponse(response)
79
79
  }
@@ -83,7 +83,6 @@ class JSONResponse extends Helper {
83
83
  this.response = null
84
84
  }
85
85
 
86
-
87
86
  /**
88
87
  * Checks that response code is equal to the provided one
89
88
  *
@@ -372,4 +371,4 @@ class JSONResponse extends Helper {
372
371
  }
373
372
  }
374
373
 
375
- export { JSONResponse as default }
374
+ export { JSONResponse, JSONResponse as default }