@@ -38,6 +38,7 @@ let playwright
38
38
let perfTiming
39
39
let defaultSelectorEnginesInitialized = false
40
40
let registeredCustomLocatorStrategies = new Set ( )
41
+ let globalCustomLocatorStrategies = new Map ( )
41
42
42
43
const popupStore = new Popup ( )
43
44
const consoleLogStore = new Console ( )
@@ -348,8 +349,18 @@ class Playwright extends Helper {
348
349
this . customLocatorStrategies = typeof config . customLocatorStrategies === 'object' && config . customLocatorStrategies !== null ? config . customLocatorStrategies : null
349
350
this . _customLocatorsRegistered = false
350
351
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
+
351
359
// override defaults with config
352
360
this . _setConfig ( config )
361
+
362
+ // Ensure _init() is called early to register custom selectors
363
+ this . _init ( ) . catch ( console . warn )
353
364
}
354
365
355
366
_validateConfig ( config ) {
@@ -472,6 +483,49 @@ class Playwright extends Helper {
472
483
await playwright . selectors . register ( '__value' , createValueEngine )
473
484
await playwright . selectors . register ( '__disabled' , createDisabledEngine )
474
485
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
+ }
475
529
} catch ( e ) {
476
530
console . warn ( e )
477
531
}
@@ -861,10 +915,6 @@ class Playwright extends Helper {
861
915
this . debugSection ( 'Url' , target . url ( ) )
862
916
} )
863
917
864
- // Register custom locator strategies for this helper instance
865
- await this . _registerCustomLocatorStrategies ( )
866
- this . _customLocatorsRegistered = true
867
-
868
918
this . isRunning = true
869
919
return this . browser
870
920
}
@@ -893,47 +943,6 @@ class Playwright extends Helper {
893
943
return ! ! ( this . customLocatorStrategies && Object . keys ( this . customLocatorStrategies ) . length > 0 )
894
944
}
895
945
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
-
937
946
/**
938
947
* Create a new browser context with a page. \
939
948
* Usually it should be run from a custom helper after call of `_startBrowser()`
@@ -1166,9 +1175,6 @@ class Playwright extends Helper {
1166
1175
const src = new Locator ( srcElement )
1167
1176
const dst = new Locator ( destElement )
1168
1177
1169
- // Ensure custom locators are registered
1170
- await this . _ensureCustomLocatorsRegistered ( )
1171
-
1172
1178
if ( options ) {
1173
1179
return this . page . dragAndDrop ( buildLocatorString ( src ) , buildLocatorString ( dst ) , options )
1174
1180
}
@@ -1330,27 +1336,6 @@ class Playwright extends Helper {
1330
1336
return this . page . title ( )
1331
1337
}
1332
1338
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
-
1354
1339
/**
1355
1340
* Get elements by different locator types, including strict locator
1356
1341
* Should be used in custom helpers:
@@ -1360,9 +1345,6 @@ class Playwright extends Helper {
1360
1345
* ```
1361
1346
*/
1362
1347
async _locate ( locator ) {
1363
- // Ensure custom locators are registered before any locator operations
1364
- await this . _ensureCustomLocatorsRegistered ( )
1365
-
1366
1348
const context = await this . _getContext ( )
1367
1349
1368
1350
if ( this . frame ) return findElements ( this . frame , locator )
@@ -1394,9 +1376,6 @@ class Playwright extends Helper {
1394
1376
* ```
1395
1377
*/
1396
1378
async _locateElement ( locator ) {
1397
- // Ensure custom locators are registered before any locator operations
1398
- await this . _ensureCustomLocatorsRegistered ( )
1399
-
1400
1379
const context = await this . _getContext ( )
1401
1380
return findElement ( context , locator )
1402
1381
}
@@ -2678,9 +2657,6 @@ class Playwright extends Helper {
2678
2657
const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
2679
2658
locator = new Locator ( locator , 'css' )
2680
2659
2681
- // Ensure custom locators are registered
2682
- await this . _ensureCustomLocatorsRegistered ( )
2683
-
2684
2660
const context = await this . _getContext ( )
2685
2661
try {
2686
2662
await context . locator ( buildLocatorString ( locator ) ) . first ( ) . waitFor ( { timeout : waitTimeout , state : 'attached' } )
@@ -2698,9 +2674,6 @@ class Playwright extends Helper {
2698
2674
const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
2699
2675
locator = new Locator ( locator , 'css' )
2700
2676
2701
- // Ensure custom locators are registered
2702
- await this . _ensureCustomLocatorsRegistered ( )
2703
-
2704
2677
const context = await this . _getContext ( )
2705
2678
let count = 0
2706
2679
@@ -2731,9 +2704,6 @@ class Playwright extends Helper {
2731
2704
const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
2732
2705
locator = new Locator ( locator , 'css' )
2733
2706
2734
- // Ensure custom locators are registered
2735
- await this . _ensureCustomLocatorsRegistered ( )
2736
-
2737
2707
const context = await this . _getContext ( )
2738
2708
let waiter
2739
2709
let count = 0
@@ -2765,9 +2735,6 @@ class Playwright extends Helper {
2765
2735
const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
2766
2736
locator = new Locator ( locator , 'css' )
2767
2737
2768
- // Ensure custom locators are registered
2769
- await this . _ensureCustomLocatorsRegistered ( )
2770
-
2771
2738
const context = await this . _getContext ( )
2772
2739
let waiter
2773
2740
let count = 0
@@ -2884,9 +2851,6 @@ class Playwright extends Helper {
2884
2851
const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
2885
2852
const errorMessage = `Text "${ text } " was not found on page after ${ waitTimeout / 1000 } sec.`
2886
2853
2887
- // Ensure custom locators are registered
2888
- await this . _ensureCustomLocatorsRegistered ( )
2889
-
2890
2854
const contextObject = await this . _getContext ( )
2891
2855
2892
2856
if ( context ) {
@@ -3108,9 +3072,6 @@ class Playwright extends Helper {
3108
3072
const waitTimeout = sec ? sec * 1000 : this . options . waitForTimeout
3109
3073
locator = new Locator ( locator , 'css' )
3110
3074
3111
- // Ensure custom locators are registered
3112
- await this . _ensureCustomLocatorsRegistered ( )
3113
-
3114
3075
let waiter
3115
3076
const context = await this . _getContext ( )
3116
3077
if ( ! locator . isXPath ( ) ) {
0 commit comments