@@ -30,7 +30,7 @@ import ElementNotFound from './errors/ElementNotFound.js'
3030import RemoteBrowserConnectionRefused from './errors/RemoteBrowserConnectionRefused.js'
3131import Popup from './extras/Popup.js'
3232import Console from './extras/Console.js'
33- import { findReact , findVue , findByPlaywrightLocator } from './extras/PlaywrightReactVueLocator .js'
33+ import { findReact , findVue } from './extras/PlaywrightLocator .js'
3434import WebElement from '../element/WebElement.js'
3535
3636let playwright
@@ -2489,7 +2489,19 @@ class Playwright extends Helper {
24892489 * {{> selectOption }}
24902490 */
24912491 async selectOption ( select , option ) {
2492- const els = await findFields . call ( this , select )
2492+ const selectLocator = Locator . from ( select , 'css' )
2493+ let els = null
2494+
2495+ if ( selectLocator . isFuzzy ( ) ) {
2496+ els = await findByRole ( this . page , { role : 'listbox' , name : selectLocator . value } )
2497+ if ( ! els || els . length === 0 ) {
2498+ els = await findByRole ( this . page , { role : 'combobox' , name : selectLocator . value } )
2499+ }
2500+ }
2501+
2502+ if ( ! els || els . length === 0 ) {
2503+ els = await findFields . call ( this , select )
2504+ }
24932505 assertElementExists ( els , select , 'Selectable field' )
24942506 const el = els [ 0 ]
24952507
@@ -2790,17 +2802,6 @@ class Playwright extends Helper {
27902802 *
27912803 */
27922804 async grabTextFrom ( locator ) {
2793- // Handle role locators with text/exact options
2794- if ( isRoleLocatorObject ( locator ) ) {
2795- const elements = await handleRoleLocator ( this . page , locator )
2796- if ( elements && elements . length > 0 ) {
2797- const text = await elements [ 0 ] . textContent ( )
2798- assertElementExists ( text , JSON . stringify ( locator ) )
2799- this . debugSection ( 'Text' , text )
2800- return text
2801- }
2802- }
2803-
28042805 const locatorObj = new Locator ( locator , 'css' )
28052806
28062807 if ( locatorObj . isCustom ( ) ) {
@@ -2813,21 +2814,32 @@ class Playwright extends Helper {
28132814 assertElementExists ( text , locatorObj . toString ( ) )
28142815 this . debugSection ( 'Text' , text )
28152816 return text
2816- } else {
2817- locator = this . _contextLocator ( locator )
2818- try {
2819- const text = await this . page . textContent ( locator )
2820- assertElementExists ( text , locator )
2817+ }
2818+
2819+ if ( locatorObj . isRole ( ) ) {
2820+ // Handle role locators with text/exact options
2821+ const roleElements = await findByRole ( this . page , locator )
2822+ if ( roleElements && roleElements . length > 0 ) {
2823+ const text = await roleElements [ 0 ] . textContent ( )
2824+ assertElementExists ( text , JSON . stringify ( locator ) )
28212825 this . debugSection ( 'Text' , text )
28222826 return text
2823- } catch ( error ) {
2824- // Convert Playwright timeout errors to ElementNotFound for consistency
2825- if ( error . message && error . message . includes ( 'Timeout' ) ) {
2826- throw new ElementNotFound ( locator , 'text' )
2827- }
2828- throw error
28292827 }
28302828 }
2829+
2830+ locator = this . _contextLocator ( locator )
2831+ try {
2832+ const text = await this . page . textContent ( locator )
2833+ assertElementExists ( text , locator )
2834+ this . debugSection ( 'Text' , text )
2835+ return text
2836+ } catch ( error ) {
2837+ // Convert Playwright timeout errors to ElementNotFound for consistency
2838+ if ( error . message && error . message . includes ( 'Timeout' ) ) {
2839+ throw new ElementNotFound ( locator , 'text' )
2840+ }
2841+ throw error
2842+ }
28312843 }
28322844
28332845 /**
@@ -4306,50 +4318,26 @@ function buildLocatorString(locator) {
43064318 if ( locator . isXPath ( ) ) {
43074319 return `xpath=${ locator . value } `
43084320 }
4309- return locator . simplify ( )
4321+ return locator . simplify ( )
43104322}
43114323
4312- /**
4313- * Checks if a locator is a role locator object (e.g., {role: 'button', text: 'Submit', exact: true})
4314- */
4315- function isRoleLocatorObject ( locator ) {
4316- return locator && typeof locator === 'object' && locator . role && ! locator . type
4317- }
4318-
4319- /**
4320- * Handles role locator objects by converting them to Playwright's getByRole() API
4321- * Returns elements array if role locator, null otherwise
4322- */
4323- async function handleRoleLocator ( context , locator ) {
4324- if ( ! isRoleLocatorObject ( locator ) ) return null
4325-
4326- const options = { }
4327- if ( locator . text ) options . name = locator . text
4328- if ( locator . exact !== undefined ) options . exact = locator . exact
4329-
4330- return context . getByRole ( locator . role , Object . keys ( options ) . length > 0 ? options : undefined ) . all ( )
4324+ async function findByRole ( context , locator ) {
4325+ const matchedLocator = Locator . from ( locator )
4326+ if ( ! matchedLocator . isRole ( ) ) return null
4327+ const roleOptions = matchedLocator . getRoleOptions ( )
4328+ return context . getByRole ( roleOptions . role , roleOptions . options ) . all ( )
43314329}
43324330
43334331async function findElements ( matcher , locator ) {
4334- // Check if locator is a Locator object with react/vue type, or a raw object with react/vue property
4335- const isReactLocator = locator . type === 'react' || ( locator . locator && locator . locator . react ) || locator . react
4336- const isVueLocator = locator . type === 'vue' || ( locator . locator && locator . locator . vue ) || locator . vue
4337- const isPwLocator = locator . type === 'pw' || ( locator . locator && locator . locator . pw ) || locator . pw
4338-
4339- if ( isReactLocator ) return findReact ( matcher , locator )
4340- if ( isVueLocator ) return findVue ( matcher , locator )
4341- if ( isPwLocator ) return findByPlaywrightLocator . call ( this , matcher , locator )
4342-
4343- // Handle role locators with text/exact options (e.g., {role: 'button', text: 'Submit', exact: true})
4344- const roleElements = await handleRoleLocator ( matcher , locator )
4332+ const matchedLocator = Locator . from ( locator )
4333+ const roleElements = await findByRole ( matcher , matchedLocator )
43454334 if ( roleElements ) return roleElements
43464335
4347- locator = new Locator ( locator , 'css' )
4336+ const isReactLocator = matchedLocator . type === 'react'
4337+ const isVueLocator = matchedLocator . type === 'vue'
43484338
4349- // Handle custom locators directly instead of relying on Playwright selector engines
4350- if ( locator . isCustom ( ) ) {
4351- return findCustomElements . call ( this , matcher , locator )
4352- }
4339+ if ( isReactLocator ) return findReact ( matcher , matchedLocator )
4340+ if ( isVueLocator ) return findVue ( matcher , matchedLocator )
43534341
43544342 // Check if we have a custom context locator and need to search within it
43554343 if ( this . contextLocator ) {
@@ -4362,12 +4350,12 @@ async function findElements(matcher, locator) {
43624350 }
43634351
43644352 // Search within the first context element
4365- const locatorString = buildLocatorString ( locator )
4353+ const locatorString = buildLocatorString ( matchedLocator )
43664354 return contextElements [ 0 ] . locator ( locatorString ) . all ( )
43674355 }
43684356 }
43694357
4370- const locatorString = buildLocatorString ( locator )
4358+ const locatorString = buildLocatorString ( matchedLocator )
43714359
43724360 return matcher . locator ( locatorString ) . all ( )
43734361}
@@ -4460,13 +4448,17 @@ async function findCustomElements(matcher, locator) {
44604448}
44614449
44624450async function findElement ( matcher , locator ) {
4463- if ( locator . react ) return findReact ( matcher , locator )
4464- if ( locator . vue ) return findVue ( matcher , locator )
4465- if ( locator . pw ) return findByPlaywrightLocator . call ( this , matcher , locator )
4451+ const matchedLocator = Locator . from ( locator )
4452+ const roleElements = await findByRole ( matcher , matchedLocator )
4453+ if ( roleElements && roleElements . length > 0 ) return roleElements [ 0 ]
4454+
4455+ const isReactLocator = matchedLocator . type === 'react'
4456+ const isVueLocator = matchedLocator . type === 'vue'
44664457
4467- locator = new Locator ( locator , 'css' )
4458+ if ( isReactLocator ) return findReact ( matcher , matchedLocator )
4459+ if ( isVueLocator ) return findVue ( matcher , matchedLocator )
44684460
4469- return matcher . locator ( buildLocatorString ( locator ) ) . first ( )
4461+ return matcher . locator ( buildLocatorString ( matchedLocator ) ) . first ( )
44704462}
44714463
44724464async function getVisibleElements ( elements ) {
@@ -4518,8 +4510,14 @@ async function proceedClick(locator, context = null, options = {}) {
45184510}
45194511
45204512async function findClickable ( matcher , locator ) {
4513+ // Convert to Locator first to handle JSON strings properly
45214514 const matchedLocator = new Locator ( locator )
45224515
4516+ // Handle role locators from Locator
4517+ if ( matchedLocator . isRole ( ) ) {
4518+ return findByRole ( matcher , matchedLocator )
4519+ }
4520+
45234521 if ( ! matchedLocator . isFuzzy ( ) ) return findElements . call ( this , matcher , matchedLocator )
45244522
45254523 let els
@@ -4592,14 +4590,17 @@ async function findCheckable(locator, context) {
45924590 }
45934591
45944592 // Handle role locators with text/exact options
4595- const roleElements = await handleRoleLocator ( contextEl , locator )
4593+ const roleElements = await findByRole ( contextEl , locator )
45964594 if ( roleElements ) return roleElements
45974595
4598- const matchedLocator = new Locator ( locator )
4596+ const matchedLocator = Locator . from ( locator )
45994597 if ( ! matchedLocator . isFuzzy ( ) ) {
46004598 return findElements . call ( this , contextEl , matchedLocator )
46014599 }
46024600
4601+ const checkboxByRole = await findByRole ( contextEl , { role : 'checkbox' , name : matchedLocator . value } )
4602+ if ( checkboxByRole ) return checkboxByRole
4603+
46034604 const literal = xpathLocator . literal ( matchedLocator . value )
46044605 let els = await findElements . call ( this , contextEl , Locator . checkable . byText ( literal ) )
46054606 if ( els . length ) {
@@ -4621,17 +4622,15 @@ async function proceedIsChecked(assertType, option) {
46214622}
46224623
46234624async function findFields ( locator ) {
4624- // Handle role locators with text/exact options
4625- if ( isRoleLocatorObject ( locator ) ) {
4626- const page = await this . page
4627- const roleElements = await handleRoleLocator ( page , locator )
4628- if ( roleElements ) return roleElements
4629- }
4625+ const page = await this . page
4626+ const roleElements = await findByRole ( page , locator )
4627+ if ( roleElements ) return roleElements
46304628
46314629 const matchedLocator = new Locator ( locator )
46324630 if ( ! matchedLocator . isFuzzy ( ) ) {
46334631 return this . _locate ( matchedLocator )
46344632 }
4633+
46354634 const literal = xpathLocator . literal ( locator )
46364635
46374636 let els = await this . _locate ( { xpath : Locator . field . labelEquals ( literal ) } )
0 commit comments