@@ -15,6 +15,7 @@ let asyncHelperPromise
1515let container = {
1616 helpers : { } ,
1717 support : { } ,
18+ proxySupport : { } ,
1819 plugins : { } ,
1920 actor : null ,
2021 /**
@@ -41,19 +42,19 @@ class Container {
4142
4243 container . helpers = createHelpers ( config . helpers || { } )
4344 container . translation = loadTranslation ( config . translation || null , config . vocabularies || [ ] )
44- container . support = createSupportObjects ( config . include || { } )
45+ container . proxySupport = createSupportObjects ( config . include || { } )
4546 container . plugins = createPlugins ( config . plugins || { } , opts )
4647
48+ createActor ( config . include ?. I )
4749 createMocha ( config , opts )
48- createActor ( )
4950
5051 if ( opts && opts . ai ) ai . enable ( config . ai ) // enable AI Assistant
5152 if ( config . gherkin ) loadGherkinSteps ( config . gherkin . steps || [ ] )
5253 if ( opts && typeof opts . timeouts === 'boolean' ) store . timeouts = opts . timeouts
5354 }
5455
5556 static actor ( ) {
56- return container . actor
57+ return container . support . I
5758 }
5859
5960 /**
@@ -79,9 +80,9 @@ class Container {
7980 */
8081 static support ( name ) {
8182 if ( ! name ) {
82- return container . support
83+ return container . proxySupport
8384 }
84- return container . support [ name ]
85+ return container . support [ name ] || container . proxySupport [ name ]
8586 }
8687
8788 /**
@@ -126,7 +127,6 @@ class Container {
126127 static append ( newContainer ) {
127128 const deepMerge = require ( './utils' ) . deepMerge
128129 container = deepMerge ( container , newContainer )
129- container . actor = container . support . I
130130 }
131131
132132 /**
@@ -260,28 +260,6 @@ function requireHelperFromModule(helperName, config, HelperClass) {
260260}
261261
262262function createSupportObjects ( config ) {
263- const objects = { }
264-
265- for ( const name in config ) {
266- objects [ name ] = { } // placeholders
267- }
268-
269- container . support = objects
270-
271- function lazyLoad ( name ) {
272- let newObj = getSupportObject ( config , name )
273- try {
274- if ( typeof newObj === 'function' ) {
275- newObj = newObj ( )
276- } else if ( newObj . _init ) {
277- newObj . _init ( )
278- }
279- } catch ( err ) {
280- throw new Error ( `Initialization failed for ${ name } : ${ newObj } \n${ err . message } \n${ err . stack } ` )
281- }
282- return newObj
283- }
284-
285263 const asyncWrapper = function ( f ) {
286264 return function ( ) {
287265 return f . apply ( this , arguments ) . catch ( ( e ) => {
@@ -291,47 +269,92 @@ function createSupportObjects(config) {
291269 }
292270 }
293271
294- Object . keys ( objects ) . forEach ( ( object ) => {
295- const currentObject = objects [ object ]
296- Object . keys ( currentObject ) . forEach ( ( method ) => {
297- const currentMethod = currentObject [ method ]
298- if ( currentMethod && currentMethod [ Symbol . toStringTag ] === 'AsyncFunction' ) {
299- objects [ object ] [ method ] = asyncWrapper ( currentMethod )
300- }
301- } )
302- } )
272+ function lazyLoad ( name ) {
273+ return new Proxy (
274+ { } ,
275+ {
276+ get ( target , prop ) {
277+ // load actual name from vocabulary
278+ if ( container . translation . name ) {
279+ name = container . translation . name
280+ }
281+
282+ if ( name === 'I' ) {
283+ const actor = createActor ( config . I )
284+ methodsOfObject ( actor )
285+ return actor [ prop ]
286+ }
287+
288+ if ( ! container . support [ name ] ) {
289+ // Load object on first access
290+ const supportObject = loadSupportObject ( config [ name ] )
291+ container . support [ name ] = supportObject
292+ try {
293+ if ( container . support [ name ] . _init ) {
294+ container . support [ name ] . _init ( )
295+ }
296+ } catch ( err ) {
297+ throw new Error (
298+ `Initialization failed for ${ name } : ${ container . support [ name ] } \n${ err . message } \n${ err . stack } ` ,
299+ )
300+ }
301+ }
302+
303+ const currentObject = container . support [ name ]
304+ let currentValue = currentObject [ prop ]
305+
306+ if ( isFunction ( currentValue ) || isAsyncFunction ( currentValue ) ) {
307+ const ms = new MetaStep ( name , currentValue )
308+ ms . setContext ( currentObject )
309+ if ( isAsyncFunction ( currentValue ) ) currentValue = asyncWrapper ( currentValue )
310+ currentObject [ prop ] = ms . run . bind ( ms , currentValue )
311+ }
312+
313+ return currentValue
314+ } ,
315+ has ( target , prop ) {
316+ container . support [ name ] = container . support [ name ] || loadSupportObject ( config [ name ] )
317+ return prop in container . support [ name ]
318+ } ,
319+ ownKeys ( ) {
320+ container . support [ name ] = container . support [ name ] || loadSupportObject ( config [ name ] )
321+ return Reflect . ownKeys ( container . support [ name ] )
322+ } ,
323+ } ,
324+ )
325+ }
303326
304327 return new Proxy (
305328 { } ,
306329 {
307330 has ( target , key ) {
308- return key in config
331+ return key in container . support
309332 } ,
310333 ownKeys ( ) {
311- return Reflect . ownKeys ( config )
334+ return Reflect . ownKeys ( container . support )
312335 } ,
313336 get ( target , key ) {
314- // configured but not in support object, yet: load the module
315- if ( key in objects && ! ( key in target ) ) {
316- // load default I
317- if ( key in objects && ! ( key in config ) ) {
318- return ( target [ key ] = objects [ key ] )
319- }
320-
321- // load new object
322- const object = lazyLoad ( key )
323- // check that object is a real object and not an array
324- if ( Object . prototype . toString . call ( object ) === '[object Object]' ) {
325- return ( target [ key ] = Object . assign ( objects [ key ] , object ) )
326- }
327- target [ key ] = object
328- }
329- return target [ key ]
337+ return lazyLoad ( key )
330338 } ,
331339 } ,
332340 )
333341}
334342
343+ function createActor ( actorPath ) {
344+ if ( container . support . I ) return container . support . I
345+
346+ if ( actorPath ) {
347+ container . support . I = loadSupportObject ( actorPath )
348+ } else {
349+ const actor = require ( './actor' )
350+ container . support . I = actor ( )
351+ }
352+
353+ container . support . I = container . support . I
354+
355+ return container . support . I
356+ }
357+
335358function createPlugins ( config , options = { } ) {
336359 const plugins = { }
337360
@@ -360,22 +383,6 @@ function createPlugins(config, options = {}) {
360383 return plugins
361384}
362385
363- function createActor ( ) {
364- const actor = require ( './actor' )
365- container . support . I = container . support . I || actor ( )
366-
367- container . actor = container . support . I
368- if ( container . translation . I !== 'I' ) container . support [ container . translation . I ] = container . actor
369- }
370-
371- function getSupportObject ( config , name ) {
372- const module = config [ name ]
373- if ( typeof module === 'string' ) {
374- return loadSupportObject ( module , name )
375- }
376- return module
377- }
378-
379386function loadGherkinSteps ( paths ) {
380387 global . Before = ( fn ) => event . dispatcher . on ( event . test . started , fn )
381388 global . After = ( fn ) => event . dispatcher . on ( event . test . finished , fn )
@@ -406,38 +413,26 @@ function loadSupportObject(modulePath, supportObjectName) {
406413 if ( modulePath . charAt ( 0 ) === '.' ) {
407414 modulePath = path . join ( global . codecept_dir , modulePath )
408415 }
409-
410416 try {
411417 const obj = require ( modulePath )
412418
413- if ( typeof obj !== 'function' && Object . getPrototypeOf ( obj ) !== Object . prototype && ! Array . isArray ( obj ) ) {
414- const methods = methodsOfObject ( obj )
415- Object . keys ( methods )
416- . filter ( ( key ) => ! key . startsWith ( '_' ) )
417- . forEach ( ( key ) => {
418- const currentMethod = methods [ key ]
419- if ( isFunction ( currentMethod ) || isAsyncFunction ( currentMethod ) ) {
420- const ms = new MetaStep ( supportObjectName , key )
421- ms . setContext ( methods )
422- methods [ key ] = ms . run . bind ( ms , currentMethod )
423- }
424- } )
425- return methods
419+ // Handle different types of imports
420+ if ( typeof obj === 'function' ) {
421+ // If it's a class (constructor function)
422+ if ( obj . prototype && obj . prototype . constructor === obj ) {
423+ return new obj ( )
424+ }
425+ // If it's a regular function
426+ return obj ( )
426427 }
427- if ( ! Array . isArray ( obj ) ) {
428- Object . keys ( obj )
429- . filter ( ( key ) => ! key . startsWith ( '_' ) )
430- . forEach ( ( key ) => {
431- const currentMethod = obj [ key ]
432- if ( isFunction ( currentMethod ) || isAsyncFunction ( currentMethod ) ) {
433- const ms = new MetaStep ( supportObjectName , key )
434- ms . setContext ( obj )
435- obj [ key ] = ms . run . bind ( ms , currentMethod )
436- }
437- } )
428+ // If it's a plain object
429+ if ( obj && typeof obj === 'object' && ! Array . isArray ( obj ) ) {
430+ return obj
438431 }
439432
440- return obj
433+ throw new Error (
434+ `Support object "${ supportObjectName } " should be an object, class, or function, but got ${ typeof obj } ` ,
435+ )
441436 } catch ( err ) {
442437 throw new Error (
443438 `Could not include object ${ supportObjectName } from module '${ modulePath } '\n${ err . message } \n${ err . stack } ` ,
0 commit comments