@@ -68,7 +68,44 @@ class Container {
68
68
container . plugins = await createPlugins ( config . plugins || { } , opts )
69
69
container . result = new Result ( )
70
70
71
- 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
+ }
72
109
73
110
if ( opts && opts . ai ) ai . enable ( config . ai ) // enable AI Assistant
74
111
if ( config . gherkin ) await loadGherkinStepsAsync ( config . gherkin . steps || [ ] )
@@ -228,7 +265,7 @@ function createHelpers(config) {
228
265
}
229
266
230
267
// ESM import (legacy check)
231
- if ( ! HelperClass && helperName ?. constructor === Function && helperName . prototype ) {
268
+ if ( ! HelperClass && typeof helperName === 'function' && helperName . prototype ) {
232
269
HelperClass = helperName
233
270
helperName = HelperClass . constructor . name
234
271
}
@@ -445,7 +482,7 @@ function createSupportObjects(config) {
445
482
return {
446
483
enumerable : true ,
447
484
configurable : true ,
448
- value : this . get ( target , prop ) ,
485
+ value : container . support [ name ] [ prop ] ,
449
486
}
450
487
} ,
451
488
ownKeys ( ) {
@@ -472,10 +509,21 @@ function createSupportObjects(config) {
472
509
return {
473
510
enumerable : true ,
474
511
configurable : true ,
475
- value : this . get ( target , prop ) ,
512
+ value : target [ prop ] ,
476
513
}
477
514
} ,
478
515
get ( target , key ) {
516
+ if ( typeof key === 'symbol' ) {
517
+ // safely ignore symbol-based meta properties used by tooling
518
+ return undefined
519
+ }
520
+ // Allow special I even if not declared in includes
521
+ if ( key === 'I' ) {
522
+ return lazyLoad ( 'I' )
523
+ }
524
+ if ( ! keys . includes ( key ) ) {
525
+ throw new Error ( `Support object "${ String ( key ) } " is not defined` )
526
+ }
479
527
return lazyLoad ( key )
480
528
} ,
481
529
} ,
@@ -485,12 +533,8 @@ function createSupportObjects(config) {
485
533
function createActor ( actorPath ) {
486
534
if ( container . support . I ) return container . support . I
487
535
488
- if ( actorPath ) {
489
- // Actor path loading must be handled async during container creation
490
- throw new Error ( `Custom actor loading from path '${ actorPath } ' must be handled asynchronously during container creation` )
491
- } else {
492
- container . support . I = actorFactory ( { } , Container )
493
- }
536
+ // Default actor
537
+ container . support . I = actorFactory ( { } , Container )
494
538
495
539
return container . support . I
496
540
}
@@ -508,7 +552,7 @@ async function loadPluginAsync(modulePath, config) {
508
552
if ( typeof pluginFactory !== 'function' ) {
509
553
throw new Error ( `Plugin '${ modulePath } ' is not a function. Expected a plugin factory function.` )
510
554
}
511
-
555
+
512
556
return pluginFactory ( config )
513
557
}
514
558
@@ -537,7 +581,7 @@ async function createPlugins(config, options = {}) {
537
581
} else {
538
582
module = `./plugin/${ pluginName } .js`
539
583
}
540
-
584
+
541
585
// Use async loading for all plugins (ESM and CJS)
542
586
plugins [ pluginName ] = await loadPluginAsync ( module , config [ pluginName ] )
543
587
debug ( `plugin ${ pluginName } loaded via async import` )
@@ -592,12 +636,31 @@ async function loadSupportObject(modulePath, supportObjectName) {
592
636
if ( ! modulePath ) {
593
637
throw new Error ( `Support object "${ supportObjectName } " is not defined` )
594
638
}
595
- if ( modulePath . charAt ( 0 ) === '.' ) {
639
+ // If function/class provided directly
640
+ if ( typeof modulePath === 'function' ) {
641
+ try {
642
+ // class constructor
643
+ if ( modulePath . prototype && modulePath . prototype . constructor === modulePath ) {
644
+ return new modulePath ( )
645
+ }
646
+ // plain function factory
647
+ return modulePath ( )
648
+ } catch ( err ) {
649
+ throw new Error ( `Could not include object ${ supportObjectName } from function: ${ err . message } ` )
650
+ }
651
+ }
652
+ if ( typeof modulePath === 'string' && modulePath . charAt ( 0 ) === '.' ) {
596
653
modulePath = path . join ( global . codecept_dir , modulePath )
597
654
}
598
655
try {
599
656
// Use dynamic import for both ESM and CJS modules
600
- const obj = await import ( modulePath )
657
+ let importPath = modulePath
658
+ // Append .js if no extension provided (ESM resolution requires it)
659
+ if ( typeof importPath === 'string' ) {
660
+ const ext = path . extname ( importPath )
661
+ if ( ! ext ) importPath = `${ importPath } .js`
662
+ }
663
+ const obj = await import ( importPath )
601
664
602
665
// Handle ESM module wrapper
603
666
let actualObj = obj
0 commit comments