Skip to content

Commit 541f5ef

Browse files
Copilotkobenguyent
andcommitted
Fix custom locator registration format and timing issues
Fixed the "Unknown engine" error for Playwright custom locators by: 1. Using correct registration format with {content: selectorEngineCode} instead of object format 2. Moving registration to _init() method for earlier timing 3. Creating global registry to handle multiple helper instances 4. Generating JavaScript code strings for selector engines that execute in browser context The issue was that Playwright's selectors.register() expects JavaScript code as a string with 'content' property, not JavaScript objects directly. Co-authored-by: kobenguyent <[email protected]>
1 parent 2688478 commit 541f5ef

File tree

1 file changed

+54
-93
lines changed

1 file changed

+54
-93
lines changed

lib/helper/Playwright.js

Lines changed: 54 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ let playwright
3838
let perfTiming
3939
let defaultSelectorEnginesInitialized = false
4040
let registeredCustomLocatorStrategies = new Set()
41+
let globalCustomLocatorStrategies = new Map()
4142

4243
const popupStore = new Popup()
4344
const 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

Comments
 (0)