@@ -38,6 +38,7 @@ let playwright
3838let perfTiming
3939let defaultSelectorEnginesInitialized = false
4040let registeredCustomLocatorStrategies = new Set ( )
41+ let globalCustomLocatorStrategies = new Map ( )
4142
4243const popupStore = new Popup ( )
4344const consoleLogStore = new Console ( )
@@ -348,8 +349,18 @@ class Playwright extends Helper {
348349 this . customLocatorStrategies = typeof config . customLocatorStrategies === 'object' && config . customLocatorStrategies !== null ? config . customLocatorStrategies : null
349350 this . _customLocatorsRegistered = false
350351
352+ // Add custom locator strategies to global registry for early registration
353+ if ( this . customLocatorStrategies ) {
354+ for ( const [ strategyName , strategyFunction ] of Object . entries ( this . customLocatorStrategies ) ) {
355+ globalCustomLocatorStrategies . set ( strategyName , strategyFunction )
356+ }
357+ }
358+
351359 // override defaults with config
352360 this . _setConfig ( config )
361+
362+ // Ensure _init() is called early to register custom selectors
363+ this . _init ( ) . catch ( console . warn )
353364 }
354365
355366 _validateConfig ( config ) {
@@ -472,6 +483,49 @@ class Playwright extends Helper {
472483 await playwright . selectors . register ( '__value' , createValueEngine )
473484 await playwright . selectors . register ( '__disabled' , createDisabledEngine )
474485 if ( process . env . testIdAttribute ) await playwright . selectors . setTestIdAttribute ( process . env . testIdAttribute )
486+
487+ // Register all custom locator strategies from the global registry
488+ for ( const [ strategyName , strategyFunction ] of globalCustomLocatorStrategies . entries ( ) ) {
489+ if ( ! registeredCustomLocatorStrategies . has ( strategyName ) ) {
490+ try {
491+ // Create a selector engine as JavaScript code string
492+ const selectorEngineCode = `
493+ (() => ({
494+ create(root, target) {
495+ return null;
496+ },
497+ query(root, selector) {
498+ try {
499+ const strategyFunction = ${ strategyFunction . toString ( ) } ;
500+ const result = strategyFunction(selector, root);
501+ return Array.isArray(result) ? result[0] : result;
502+ } catch (error) {
503+ console.warn('Error in custom locator "${ strategyName } ":', error);
504+ return null;
505+ }
506+ },
507+ queryAll(root, selector) {
508+ try {
509+ const strategyFunction = ${ strategyFunction . toString ( ) } ;
510+ const result = strategyFunction(selector, root);
511+ return Array.isArray(result) ? result : result ? [result] : [];
512+ } catch (error) {
513+ console.warn('Error in custom locator "${ strategyName } ":', error);
514+ return [];
515+ }
516+ }
517+ }))()
518+ ` ;
519+
520+ await playwright . selectors . register ( strategyName , { content : selectorEngineCode } )
521+ registeredCustomLocatorStrategies . add ( strategyName )
522+ } catch ( error ) {
523+ if ( ! error . message . includes ( 'already registered' ) ) {
524+ console . warn ( `Failed to register global custom locator strategy '${ strategyName } ':` , error )
525+ }
526+ }
527+ }
528+ }
475529 } catch ( e ) {
476530 console . warn ( e )
477531 }
@@ -861,10 +915,6 @@ class Playwright extends Helper {
861915 this . debugSection ( 'Url' , target . url ( ) )
862916 } )
863917
864- // Register custom locator strategies for this helper instance
865- await this . _registerCustomLocatorStrategies ( )
866- this . _customLocatorsRegistered = true
867-
868918 this . isRunning = true
869919 return this . browser
870920 }
@@ -893,47 +943,6 @@ class Playwright extends Helper {
893943 return ! ! ( this . customLocatorStrategies && Object . keys ( this . customLocatorStrategies ) . length > 0 )
894944 }
895945
896- async _registerCustomLocatorStrategies ( ) {
897- if ( this . _isCustomLocatorStrategyDefined ( ) ) {
898- for ( const [ strategyName , strategyFunction ] of Object . entries ( this . customLocatorStrategies ) ) {
899- try {
900- this . debugSection ( 'Playwright' , `registering custom locator strategy: ${ strategyName } ` )
901-
902- // Create a selector engine object similar to the default engines
903- const selectorEngine = {
904- query ( root , selector ) {
905- try {
906- const result = strategyFunction ( selector , root ) ;
907- return Array . isArray ( result ) ? result [ 0 ] : result ;
908- } catch ( error ) {
909- console . warn ( `Error in custom locator "${ strategyName } ":` , error ) ;
910- return null ;
911- }
912- } ,
913-
914- queryAll ( root , selector ) {
915- try {
916- const result = strategyFunction ( selector , root ) ;
917- return Array . isArray ( result ) ? result : result ? [ result ] : [ ] ;
918- } catch ( error ) {
919- console . warn ( `Error in custom locator "${ strategyName } ":` , error ) ;
920- return [ ] ;
921- }
922- }
923- }
924-
925- await playwright . selectors . register ( strategyName , selectorEngine )
926- registeredCustomLocatorStrategies . add ( strategyName )
927- } catch ( error ) {
928- // Ignore "already registered" errors, warn about others
929- if ( ! error . message . includes ( 'already registered' ) ) {
930- console . warn ( `Failed to register custom locator strategy '${ strategyName } ':` , error )
931- }
932- }
933- }
934- }
935- }
936-
937946 /**
938947 * Create a new browser context with a page. \
939948 * Usually it should be run from a custom helper after call of `_startBrowser()`
@@ -1166,9 +1175,6 @@ class Playwright extends Helper {
11661175 const src = new Locator ( srcElement )
11671176 const dst = new Locator ( destElement )
11681177
1169- // Ensure custom locators are registered
1170- await this . _ensureCustomLocatorsRegistered ( )
1171-
11721178 if ( options ) {
11731179 return this . page . dragAndDrop ( buildLocatorString ( src ) , buildLocatorString ( dst ) , options )
11741180 }
@@ -1330,27 +1336,6 @@ class Playwright extends Helper {
13301336 return this . page . title ( )
13311337 }
13321338
1333- async _ensureCustomLocatorsRegistered ( ) {
1334- // Only register once, and only if we have strategies defined
1335- if ( this . _customLocatorsRegistered || ! this . _isCustomLocatorStrategyDefined ( ) ) {
1336- return
1337- }
1338-
1339- try {
1340- // If browser isn't running yet, start it to register selectors
1341- if ( ! this . isRunning && ! this . options . manualStart ) {
1342- await this . _startBrowser ( )
1343- } else {
1344- // If browser is running but custom locators not registered, register them now
1345- await this . _registerCustomLocatorStrategies ( )
1346- }
1347- this . _customLocatorsRegistered = true
1348- } catch ( error ) {
1349- console . warn ( 'Failed to register custom locators:' , error . message )
1350- // Continue execution - the error will surface when the locator is actually used
1351- }
1352- }
1353-
13541339 /**
13551340 * Get elements by different locator types, including strict locator
13561341 * Should be used in custom helpers:
@@ -1360,9 +1345,6 @@ class Playwright extends Helper {
13601345 * ```
13611346 */
13621347 async _locate ( locator ) {
1363- // Ensure custom locators are registered before any locator operations
1364- await this . _ensureCustomLocatorsRegistered ( )
1365-
13661348 const context = await this . _getContext ( )
13671349
13681350 if ( this . frame ) return findElements ( this . frame , locator )
@@ -1394,9 +1376,6 @@ class Playwright extends Helper {
13941376 * ```
13951377 */
13961378 async _locateElement ( locator ) {
1397- // Ensure custom locators are registered before any locator operations
1398- await this . _ensureCustomLocatorsRegistered ( )
1399-
14001379 const context = await this . _getContext ( )
14011380 return findElement ( context , locator )
14021381 }
@@ -2678,9 +2657,6 @@ class Playwright extends Helper {
26782657 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
26792658 locator = new Locator ( locator , 'css' )
26802659
2681- // Ensure custom locators are registered
2682- await this . _ensureCustomLocatorsRegistered ( )
2683-
26842660 const context = await this . _getContext ( )
26852661 try {
26862662 await context . locator ( buildLocatorString ( locator ) ) . first ( ) . waitFor ( { timeout : waitTimeout , state : 'attached' } )
@@ -2698,9 +2674,6 @@ class Playwright extends Helper {
26982674 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
26992675 locator = new Locator ( locator , 'css' )
27002676
2701- // Ensure custom locators are registered
2702- await this . _ensureCustomLocatorsRegistered ( )
2703-
27042677 const context = await this . _getContext ( )
27052678 let count = 0
27062679
@@ -2731,9 +2704,6 @@ class Playwright extends Helper {
27312704 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
27322705 locator = new Locator ( locator , 'css' )
27332706
2734- // Ensure custom locators are registered
2735- await this . _ensureCustomLocatorsRegistered ( )
2736-
27372707 const context = await this . _getContext ( )
27382708 let waiter
27392709 let count = 0
@@ -2765,9 +2735,6 @@ class Playwright extends Helper {
27652735 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
27662736 locator = new Locator ( locator , 'css' )
27672737
2768- // Ensure custom locators are registered
2769- await this . _ensureCustomLocatorsRegistered ( )
2770-
27712738 const context = await this . _getContext ( )
27722739 let waiter
27732740 let count = 0
@@ -2884,9 +2851,6 @@ class Playwright extends Helper {
28842851 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
28852852 const errorMessage = `Text "${ text } " was not found on page after ${ waitTimeout / 1000 } sec.`
28862853
2887- // Ensure custom locators are registered
2888- await this . _ensureCustomLocatorsRegistered ( )
2889-
28902854 const contextObject = await this . _getContext ( )
28912855
28922856 if ( context ) {
@@ -3108,9 +3072,6 @@ class Playwright extends Helper {
31083072 const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
31093073 locator = new Locator ( locator , 'css' )
31103074
3111- // Ensure custom locators are registered
3112- await this . _ensureCustomLocatorsRegistered ( )
3113-
31143075 let waiter
31153076 const context = await this . _getContext ( )
31163077 if ( ! locator . isXPath ( ) ) {
0 commit comments